@promptui-lib/codegen 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,416 @@
1
+ <p align="center">
2
+ <img src="./logo.png" alt="PromptUI Logo" width="200" />
3
+ </p>
4
+
5
+ <h1 align="center">PromptUI</h1>
6
+
7
+ <p align="center">
8
+ <a href="https://www.npmjs.com/package/@promptui-lib/cli"><img src="https://img.shields.io/npm/v/@promptui-lib/cli.svg" alt="npm version" /></a>
9
+ <a href="https://www.npmjs.com/package/@promptui-lib/cli"><img src="https://img.shields.io/npm/dm/@promptui-lib/cli.svg" alt="npm downloads" /></a>
10
+ <img src="https://img.shields.io/badge/license-proprietary-red.svg" alt="license" />
11
+ </p>
12
+
13
+ <p align="center">
14
+ <strong>Transform Figma designs into production-ready code in seconds.</strong>
15
+ </p>
16
+
17
+ <p align="center">
18
+ 100% deterministic. No AI. Same input = same output, always.
19
+ </p>
20
+
21
+ ---
22
+
23
+ ## What is it?
24
+
25
+ **PromptUI** is a library that automatically converts your Figma designs into clean, production-ready code.
26
+
27
+ Supports multiple frameworks:
28
+ - **React + SCSS** (BEM methodology)
29
+ - **Material UI** (MUI)
30
+ - **Tailwind CSS**
31
+ - **Bootstrap**
32
+ - **Flutter** (Dart StatelessWidgets)
33
+ - **SwiftUI** (iOS/macOS Views)
34
+
35
+ ---
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ npm install -g @promptui-lib/cli
41
+ ```
42
+
43
+ ---
44
+
45
+ ## How It Works
46
+
47
+ ### 1. Mark your components in Figma
48
+
49
+ In Figma, add `#` at the beginning of frame names you want to export:
50
+
51
+ ```
52
+ #Button → Will be exported as component
53
+ #CardProduct → Will be exported as component
54
+ #HeaderNav → Will be exported as component
55
+ Button → Ignored (no #)
56
+ ```
57
+
58
+ ### 2. Configure the project
59
+
60
+ ```bash
61
+ npx @promptui-lib/cli init
62
+ ```
63
+
64
+ Set your credentials:
65
+
66
+ ```bash
67
+ export FIGMA_TOKEN=your_token_here
68
+ export FIGMA_FILE_ID=file_id_here
69
+ ```
70
+
71
+ ### 3. Generate components
72
+
73
+ ```bash
74
+ # React + SCSS
75
+ npx @promptui-lib/cli generate
76
+
77
+ # Bootstrap
78
+ npx @promptui-lib/cli generate bootstrap
79
+
80
+ # Material UI
81
+ npx @promptui-lib/cli generate mui
82
+
83
+ # Tailwind CSS
84
+ npx @promptui-lib/cli generate tailwind
85
+
86
+ # Flutter
87
+ npx @promptui-lib/cli generate flutter
88
+
89
+ # SwiftUI
90
+ npx @promptui-lib/cli generate swiftui
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Examples
96
+
97
+ ### Example 1: Button with React + SCSS
98
+
99
+ **In Figma:** Frame named `#Button`
100
+
101
+ ```
102
+ #Button (Frame)
103
+ ├── Padding: 12px 24px
104
+ ├── Background: #3B82F6
105
+ ├── Border Radius: 8px
106
+ └── label (Text: "Click me")
107
+ ```
108
+
109
+ **Command:**
110
+ ```bash
111
+ npx @promptui-lib/cli generate
112
+ ```
113
+
114
+ **Result:**
115
+
116
+ ```tsx
117
+ // src/components/atoms/Button/button.tsx
118
+ import type { ReactNode } from 'react';
119
+ import './button.scss';
120
+
121
+ export interface IButtonProps {
122
+ children?: ReactNode;
123
+ className?: string;
124
+ }
125
+
126
+ export const Button = ({ children, className = '' }: IButtonProps) => {
127
+ return (
128
+ <button className={`button ${className}`.trim()}>
129
+ <span className="button__label">{children}</span>
130
+ </button>
131
+ );
132
+ };
133
+ ```
134
+
135
+ ```scss
136
+ // src/components/atoms/Button/button.scss
137
+ .button {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ padding: $spacing-sm $spacing-lg;
142
+ background-color: $color-primary;
143
+ border-radius: $radius-medium;
144
+
145
+ &__label {
146
+ color: $color-text-inverse;
147
+ font-weight: $font-weight-medium;
148
+ }
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ### Example 2: Card with Bootstrap
155
+
156
+ **In Figma:** Frame named `#CardProduct`
157
+
158
+ ```
159
+ #CardProduct (Frame)
160
+ ├── image (Rectangle)
161
+ ├── content (Frame)
162
+ │ ├── title (Text: "Product Name")
163
+ │ ├── description (Text: "Lorem ipsum...")
164
+ │ └── price (Text: "$99.00")
165
+ └── button (Instance of #Button)
166
+ ```
167
+
168
+ **Command:**
169
+ ```bash
170
+ npx @promptui-lib/cli generate bootstrap
171
+ ```
172
+
173
+ **Result:**
174
+
175
+ ```tsx
176
+ // src/components/molecules/CardProduct/card-product.tsx
177
+ import type { ReactNode } from 'react';
178
+
179
+ export interface ICardProductProps {
180
+ children?: ReactNode;
181
+ className?: string;
182
+ }
183
+
184
+ export const CardProduct = ({ children, className = '' }: ICardProductProps) => {
185
+ return (
186
+ <div className={`card ${className}`.trim()}>
187
+ <img className="card-img-top" alt="Product" />
188
+ <div className="card-body">
189
+ <h5 className="card-title fw-bold">Product Name</h5>
190
+ <p className="card-text text-muted">Lorem ipsum...</p>
191
+ <p className="h4 text-primary fw-bold">$99.00</p>
192
+ <button className="btn btn-primary w-100">Buy Now</button>
193
+ </div>
194
+ </div>
195
+ );
196
+ };
197
+ ```
198
+
199
+ ---
200
+
201
+ ### Example 3: Header with Tailwind CSS
202
+
203
+ **In Figma:** Frame named `#HeaderNav`
204
+
205
+ **Command:**
206
+ ```bash
207
+ npx @promptui-lib/cli generate tailwind
208
+ ```
209
+
210
+ **Result:**
211
+
212
+ ```tsx
213
+ // src/components/organisms/HeaderNav/header-nav.tsx
214
+ export const HeaderNav = ({ className = '' }: IHeaderNavProps) => {
215
+ return (
216
+ <header className={`flex items-center justify-between px-6 py-4 bg-white shadow-sm ${className}`.trim()}>
217
+ <div className="flex items-center gap-2">
218
+ <img src="/logo.svg" className="h-8 w-8" alt="Logo" />
219
+ <span className="text-xl font-bold text-gray-900">Brand</span>
220
+ </div>
221
+ <nav className="flex items-center gap-8">
222
+ <a className="text-gray-600 hover:text-gray-900">Home</a>
223
+ <a className="text-gray-600 hover:text-gray-900">Products</a>
224
+ <a className="text-gray-600 hover:text-gray-900">About</a>
225
+ </nav>
226
+ <button className="px-4 py-2 bg-blue-500 text-white rounded-lg">
227
+ Sign In
228
+ </button>
229
+ </header>
230
+ );
231
+ };
232
+ ```
233
+
234
+ ---
235
+
236
+ ### Example 4: Button with Flutter
237
+
238
+ **In Figma:** Frame named `#Button`
239
+
240
+ **Command:**
241
+ ```bash
242
+ npx @promptui-lib/cli generate flutter
243
+ ```
244
+
245
+ **Result:**
246
+
247
+ ```dart
248
+ /// Button
249
+ /// Generated by PromptUI (Flutter)
250
+
251
+ import 'package:flutter/material.dart';
252
+
253
+ class Button extends StatelessWidget {
254
+ const Button({super.key});
255
+
256
+ @override
257
+ Widget build(BuildContext context) {
258
+ return Container(
259
+ padding: EdgeInsets.symmetric(vertical: 8, horizontal: 24),
260
+ decoration: BoxDecoration(
261
+ color: Theme.of(context).primaryColor,
262
+ borderRadius: BorderRadius.circular(8),
263
+ ),
264
+ child: Text('Click me'),
265
+ );
266
+ }
267
+ }
268
+ ```
269
+
270
+ ---
271
+
272
+ ### Example 5: Button with SwiftUI
273
+
274
+ **In Figma:** Frame named `#Button`
275
+
276
+ **Command:**
277
+ ```bash
278
+ npx @promptui-lib/cli generate swiftui
279
+ ```
280
+
281
+ **Result:**
282
+
283
+ ```swift
284
+ /// Button
285
+ /// Generated by PromptUI (SwiftUI)
286
+
287
+ import SwiftUI
288
+
289
+ struct Button: View {
290
+ var body: some View {
291
+ Text("Click me")
292
+ .padding(.horizontal, 24)
293
+ .padding(.vertical, 8)
294
+ .background(.blue)
295
+ .foregroundColor(.white)
296
+ .cornerRadius(8)
297
+ }
298
+ }
299
+
300
+ #Preview {
301
+ Button()
302
+ }
303
+ ```
304
+
305
+ ---
306
+
307
+ ## Commands
308
+
309
+ | Command | Description |
310
+ |---------|-------------|
311
+ | `npx @promptui-lib/cli init` | Configure the project |
312
+ | `npx @promptui-lib/cli generate` | Generate components (React + SCSS) |
313
+ | `npx @promptui-lib/cli generate bootstrap` | Generate with Bootstrap |
314
+ | `npx @promptui-lib/cli generate mui` | Generate with Material UI |
315
+ | `npx @promptui-lib/cli generate tailwind` | Generate with Tailwind CSS |
316
+ | `npx @promptui-lib/cli generate flutter` | Generate with Flutter/Dart |
317
+ | `npx @promptui-lib/cli generate swiftui` | Generate with SwiftUI |
318
+ | `npx @promptui-lib/cli sync tokens` | Sync design tokens |
319
+
320
+ ## Options
321
+
322
+ | Option | Description |
323
+ |--------|-------------|
324
+ | `-p, --preview` | Preview without saving |
325
+ | `-o, --output <dir>` | Output directory |
326
+ | `-f, --force` | Overwrite existing files |
327
+
328
+ ---
329
+
330
+ ## Atomic Design
331
+
332
+ Components are automatically organized:
333
+
334
+ | Layer | Description | Examples |
335
+ |-------|-------------|----------|
336
+ | **atoms** | Simple components | Button, Input, Label, Icon |
337
+ | **molecules** | Medium compositions | Card, SearchBar, FormField |
338
+ | **organisms** | Complex compositions | Header, Footer, Sidebar |
339
+
340
+ ---
341
+
342
+ ## Configuration
343
+
344
+ Create `promptui.config.json`:
345
+
346
+ ```json
347
+ {
348
+ "figma": {
349
+ "fileId": "your-file-id"
350
+ },
351
+ "output": {
352
+ "basePath": "src/components"
353
+ }
354
+ }
355
+ ```
356
+
357
+ ---
358
+
359
+ ## For Designers
360
+
361
+ ### Figma Rules
362
+
363
+ 1. **`#` prefix** - Add to frame name to export
364
+ 2. **Auto Layout** - Always use to maintain structure
365
+ 3. **Descriptive names** - `title`, `content`, not "Frame 1"
366
+ 4. **PascalCase** - `#ButtonPrimary`, not `#button-primary`
367
+
368
+ ### Correct structure example
369
+
370
+ ```
371
+ #CardProduct (Frame, Auto Layout Vertical)
372
+ ├── image (Rectangle, Aspect Ratio 16:9)
373
+ ├── content (Frame, Auto Layout Vertical, Padding 16px)
374
+ │ ├── title (Text, Heading/H3)
375
+ │ ├── description (Text, Body/Small)
376
+ │ └── price (Text, Heading/H2)
377
+ └── actions (Frame, Auto Layout Horizontal)
378
+ └── button (Instance of #Button)
379
+ ```
380
+
381
+ ---
382
+
383
+ ## Environment Variables
384
+
385
+ | Variable | Description |
386
+ |----------|-------------|
387
+ | `FIGMA_TOKEN` | Figma access token |
388
+ | `FIGMA_FILE_ID` | Figma file ID |
389
+
390
+ ### How to get Figma Token
391
+
392
+ 1. Go to [Figma Account Settings](https://www.figma.com/settings)
393
+ 2. Navigate to "Personal Access Tokens"
394
+ 3. Create a new token
395
+
396
+ ### How to get File ID
397
+
398
+ The File ID is in your Figma file URL:
399
+
400
+ ```
401
+ https://www.figma.com/file/ABC123xyz/MyProject
402
+ ^^^^^^^^^^^
403
+ This is the File ID
404
+ ```
405
+
406
+ ---
407
+
408
+ ## Author
409
+
410
+ **Desiree Menezes** - [@desireemenezes](https://github.com/desireemenezes)
411
+
412
+ ---
413
+
414
+ ## License
415
+
416
+ Proprietary - All rights reserved.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Flutter Template
3
+ * Generates Flutter/Dart widgets from Figma designs
4
+ */
5
+ import type { IComponentAST } from '@promptui-lib/core';
6
+ /**
7
+ * Generates Flutter StatelessWidget
8
+ */
9
+ export declare function generateFlutterComponent(ast: IComponentAST): string;
10
+ //# sourceMappingURL=flutter.template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flutter.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/flutter.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA4B,MAAM,oBAAoB,CAAC;AA0NlF;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAwBnE"}
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Flutter Template
3
+ * Generates Flutter/Dart widgets from Figma designs
4
+ */
5
+ /**
6
+ * Mapping of tokens to Flutter values
7
+ */
8
+ const TOKEN_TO_FLUTTER = {
9
+ // Colors
10
+ '$color-primary': 'Theme.of(context).primaryColor',
11
+ '$color-secondary': 'Theme.of(context).colorScheme.secondary',
12
+ '$color-success': 'Colors.green',
13
+ '$color-error': 'Colors.red',
14
+ '$color-warning': 'Colors.orange',
15
+ '$color-bg-primary': 'Colors.white',
16
+ '$color-bg-secondary': 'Colors.grey[100]',
17
+ '$color-text-primary': 'Colors.black87',
18
+ '$color-text-secondary': 'Colors.black54',
19
+ '$color-text-inverse': 'Colors.white',
20
+ // Spacing
21
+ '$spacing-xs': '4',
22
+ '$spacing-sm': '8',
23
+ '$spacing-md': '16',
24
+ '$spacing-lg': '24',
25
+ '$spacing-xl': '32',
26
+ '$spacing-2xl': '48',
27
+ // Border radius
28
+ '$radius-none': '0',
29
+ '$radius-small': '4',
30
+ '$radius-medium': '8',
31
+ '$radius-large': '12',
32
+ '$radius-xl': '16',
33
+ '$radius-full': '999',
34
+ // Font sizes
35
+ '$font-size-xs': '10',
36
+ '$font-size-sm': '12',
37
+ '$font-size-md': '14',
38
+ '$font-size-lg': '16',
39
+ '$font-size-xl': '20',
40
+ '$font-size-2xl': '24',
41
+ '$font-size-h1': '32',
42
+ '$font-size-h2': '28',
43
+ // Font weights
44
+ '$font-weight-regular': 'FontWeight.w400',
45
+ '$font-weight-medium': 'FontWeight.w500',
46
+ '$font-weight-semibold': 'FontWeight.w600',
47
+ '$font-weight-bold': 'FontWeight.w700',
48
+ };
49
+ /**
50
+ * Converts CSS value to Flutter
51
+ */
52
+ function cssToFlutter(property, value) {
53
+ // Token mapping
54
+ if (value.startsWith('$')) {
55
+ return TOKEN_TO_FLUTTER[value] ?? value;
56
+ }
57
+ // Parse numeric values
58
+ const numValue = parseFloat(value);
59
+ if (!isNaN(numValue)) {
60
+ return numValue.toString();
61
+ }
62
+ return value;
63
+ }
64
+ /**
65
+ * Generates EdgeInsets from padding/margin
66
+ */
67
+ function generateEdgeInsets(styles) {
68
+ const padding = {};
69
+ for (const style of styles) {
70
+ const value = cssToFlutter(style.property, style.token ?? style.value);
71
+ if (style.property === 'padding') {
72
+ return `EdgeInsets.all(${value})`;
73
+ }
74
+ if (style.property === 'padding-top')
75
+ padding.top = value;
76
+ if (style.property === 'padding-bottom')
77
+ padding.bottom = value;
78
+ if (style.property === 'padding-left')
79
+ padding.left = value;
80
+ if (style.property === 'padding-right')
81
+ padding.right = value;
82
+ }
83
+ if (Object.keys(padding).length > 0) {
84
+ const top = padding.top ?? '0';
85
+ const right = padding.right ?? '0';
86
+ const bottom = padding.bottom ?? '0';
87
+ const left = padding.left ?? '0';
88
+ if (top === bottom && left === right) {
89
+ if (top === left) {
90
+ return `EdgeInsets.all(${top})`;
91
+ }
92
+ return `EdgeInsets.symmetric(vertical: ${top}, horizontal: ${left})`;
93
+ }
94
+ return `EdgeInsets.only(top: ${top}, right: ${right}, bottom: ${bottom}, left: ${left})`;
95
+ }
96
+ return null;
97
+ }
98
+ /**
99
+ * Gets Flutter widget for element type
100
+ */
101
+ function getFlutterWidget(node) {
102
+ switch (node.tag) {
103
+ case 'button':
104
+ return 'ElevatedButton';
105
+ case 'img':
106
+ return 'Image.network';
107
+ case 'input':
108
+ return 'TextField';
109
+ case 'span':
110
+ case 'p':
111
+ case 'h1':
112
+ case 'h2':
113
+ case 'h3':
114
+ return 'Text';
115
+ default:
116
+ return 'Container';
117
+ }
118
+ }
119
+ /**
120
+ * Generates Flutter widget code
121
+ */
122
+ function generateFlutterWidget(node, styles, indent = 4) {
123
+ const spaces = ' '.repeat(indent);
124
+ const nodeStyles = styles.get(`.${node.className}`) ?? [];
125
+ // Determine widget type
126
+ const widget = getFlutterWidget(node);
127
+ // Build properties
128
+ const props = [];
129
+ // Padding
130
+ const padding = generateEdgeInsets(nodeStyles);
131
+ if (padding) {
132
+ props.push(`padding: ${padding}`);
133
+ }
134
+ // Background color
135
+ const bgColor = nodeStyles.find(s => s.property === 'background-color');
136
+ if (bgColor) {
137
+ const color = cssToFlutter('background-color', bgColor.token ?? bgColor.value);
138
+ props.push(`color: ${color}`);
139
+ }
140
+ // Border radius
141
+ const borderRadius = nodeStyles.find(s => s.property === 'border-radius');
142
+ if (borderRadius) {
143
+ const radius = cssToFlutter('border-radius', borderRadius.token ?? borderRadius.value);
144
+ props.push(`borderRadius: BorderRadius.circular(${radius})`);
145
+ }
146
+ // Children
147
+ const children = node.children.map(child => {
148
+ if (typeof child === 'string' || (typeof child === 'object' && 'type' in child && child.type === 'text')) {
149
+ const text = typeof child === 'string' ? child : child.value;
150
+ return `${spaces} Text('${text}')`;
151
+ }
152
+ return generateFlutterWidget(child, styles, indent + 2);
153
+ });
154
+ // Build widget
155
+ if (widget === 'Container') {
156
+ const decoration = [];
157
+ if (bgColor) {
158
+ decoration.push(`color: ${cssToFlutter('background-color', bgColor.token ?? bgColor.value)}`);
159
+ }
160
+ if (borderRadius) {
161
+ decoration.push(`borderRadius: BorderRadius.circular(${cssToFlutter('border-radius', borderRadius.token ?? borderRadius.value)})`);
162
+ }
163
+ const hasDecoration = decoration.length > 0;
164
+ const containerProps = [];
165
+ if (padding)
166
+ containerProps.push(`padding: ${padding}`);
167
+ if (hasDecoration) {
168
+ containerProps.push(`decoration: BoxDecoration(\n${spaces} ${decoration.join(',\n' + spaces + ' ')},\n${spaces} )`);
169
+ }
170
+ if (children.length === 1) {
171
+ containerProps.push(`child: ${children[0].trim()}`);
172
+ }
173
+ else if (children.length > 1) {
174
+ // Use Column or Row based on flex-direction
175
+ const flexDir = nodeStyles.find(s => s.property === 'flex-direction');
176
+ const layout = flexDir?.value === 'row' ? 'Row' : 'Column';
177
+ containerProps.push(`child: ${layout}(\n${spaces} children: [\n${children.join(',\n')},\n${spaces} ],\n${spaces} )`);
178
+ }
179
+ return `${spaces}Container(\n${spaces} ${containerProps.join(',\n' + spaces + ' ')},\n${spaces})`;
180
+ }
181
+ if (widget === 'Text') {
182
+ const text = node.children[0];
183
+ const textContent = typeof text === 'string' ? text : text?.value ?? '';
184
+ return `${spaces}Text('${textContent}')`;
185
+ }
186
+ if (widget === 'ElevatedButton') {
187
+ const buttonChild = children.length > 0 ? children[0].trim() : "Text('Button')";
188
+ return `${spaces}ElevatedButton(\n${spaces} onPressed: () {},\n${spaces} child: ${buttonChild},\n${spaces})`;
189
+ }
190
+ return `${spaces}Container()`;
191
+ }
192
+ /**
193
+ * Generates Flutter StatelessWidget
194
+ */
195
+ export function generateFlutterComponent(ast) {
196
+ // Create styles map
197
+ const stylesMap = new Map();
198
+ for (const block of ast.styles) {
199
+ stylesMap.set(block.selector, block.properties);
200
+ }
201
+ // Widget code
202
+ const widgetBody = generateFlutterWidget(ast.jsx, stylesMap);
203
+ return `/// ${ast.name}
204
+ /// Generated by PromptUI (Flutter)
205
+
206
+ import 'package:flutter/material.dart';
207
+
208
+ class ${ast.name} extends StatelessWidget {
209
+ const ${ast.name}({super.key});
210
+
211
+ @override
212
+ Widget build(BuildContext context) {
213
+ return ${widgetBody.trim()};
214
+ }
215
+ }
216
+ `;
217
+ }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Framework Templates
3
- * Templates de geração para diferentes frameworks UI
3
+ * Templates for generating different UI frameworks
4
4
  */
5
- export type FrameworkType = 'react' | 'mui' | 'tailwind' | 'bootstrap';
5
+ export type FrameworkType = 'react' | 'mui' | 'tailwind' | 'bootstrap' | 'flutter' | 'swiftui';
6
6
  export interface IFrameworkConfig {
7
7
  name: FrameworkType;
8
8
  displayName: string;
@@ -16,4 +16,6 @@ export declare const FRAMEWORKS: Record<FrameworkType, IFrameworkConfig>;
16
16
  export { generateMuiComponent, generateMuiStyles } from './mui.template.js';
17
17
  export { generateTailwindComponent, generateTailwindClasses } from './tailwind.template.js';
18
18
  export { generateBootstrapComponent } from './bootstrap.template.js';
19
+ export { generateFlutterComponent } from './flutter.template.js';
20
+ export { generateSwiftUIComponent } from './swiftui.template.js';
19
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/frameworks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,CAAC;AAEvE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAqC9D,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/frameworks/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;AAE/F,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAqD9D,CAAC;AAEF,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Framework Templates
3
- * Templates de geração para diferentes frameworks UI
3
+ * Templates for generating different UI frameworks
4
4
  */
5
5
  export const FRAMEWORKS = {
6
6
  react: {
@@ -39,7 +39,25 @@ export const FRAMEWORKS = {
39
39
  styleExtension: null, // Bootstrap classes
40
40
  imports: ["import type { ReactNode } from 'react';"],
41
41
  },
42
+ flutter: {
43
+ name: 'flutter',
44
+ displayName: 'Flutter',
45
+ description: 'Flutter/Dart StatelessWidget components',
46
+ fileExtension: 'dart',
47
+ styleExtension: null, // Inline styles
48
+ imports: ["import 'package:flutter/material.dart';"],
49
+ },
50
+ swiftui: {
51
+ name: 'swiftui',
52
+ displayName: 'SwiftUI',
53
+ description: 'SwiftUI View structs for iOS/macOS',
54
+ fileExtension: 'swift',
55
+ styleExtension: null, // Inline modifiers
56
+ imports: ['import SwiftUI'],
57
+ },
42
58
  };
43
59
  export { generateMuiComponent, generateMuiStyles } from './mui.template.js';
44
60
  export { generateTailwindComponent, generateTailwindClasses } from './tailwind.template.js';
45
61
  export { generateBootstrapComponent } from './bootstrap.template.js';
62
+ export { generateFlutterComponent } from './flutter.template.js';
63
+ export { generateSwiftUIComponent } from './swiftui.template.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * SwiftUI Template
3
+ * Generates SwiftUI views from Figma designs
4
+ */
5
+ import type { IComponentAST } from '@promptui-lib/core';
6
+ /**
7
+ * Generates SwiftUI View struct
8
+ */
9
+ export declare function generateSwiftUIComponent(ast: IComponentAST): string;
10
+ //# sourceMappingURL=swiftui.template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swiftui.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/swiftui.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA4B,MAAM,oBAAoB,CAAC;AA0PlF;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAyBnE"}
@@ -0,0 +1,247 @@
1
+ /**
2
+ * SwiftUI Template
3
+ * Generates SwiftUI views from Figma designs
4
+ */
5
+ /**
6
+ * Mapping of tokens to SwiftUI values
7
+ */
8
+ const TOKEN_TO_SWIFTUI = {
9
+ // Colors
10
+ '$color-primary': '.blue',
11
+ '$color-secondary': '.gray',
12
+ '$color-success': '.green',
13
+ '$color-error': '.red',
14
+ '$color-warning': '.orange',
15
+ '$color-bg-primary': '.white',
16
+ '$color-bg-secondary': 'Color(.systemGray6)',
17
+ '$color-text-primary': '.primary',
18
+ '$color-text-secondary': '.secondary',
19
+ '$color-text-inverse': '.white',
20
+ // Spacing
21
+ '$spacing-xs': '4',
22
+ '$spacing-sm': '8',
23
+ '$spacing-md': '16',
24
+ '$spacing-lg': '24',
25
+ '$spacing-xl': '32',
26
+ '$spacing-2xl': '48',
27
+ // Border radius
28
+ '$radius-none': '0',
29
+ '$radius-small': '4',
30
+ '$radius-medium': '8',
31
+ '$radius-large': '12',
32
+ '$radius-xl': '16',
33
+ '$radius-full': '999',
34
+ // Font sizes
35
+ '$font-size-xs': '.caption2',
36
+ '$font-size-sm': '.caption',
37
+ '$font-size-md': '.body',
38
+ '$font-size-lg': '.headline',
39
+ '$font-size-xl': '.title2',
40
+ '$font-size-2xl': '.title',
41
+ '$font-size-h1': '.largeTitle',
42
+ '$font-size-h2': '.title',
43
+ // Font weights
44
+ '$font-weight-regular': '.regular',
45
+ '$font-weight-medium': '.medium',
46
+ '$font-weight-semibold': '.semibold',
47
+ '$font-weight-bold': '.bold',
48
+ };
49
+ /**
50
+ * Converts CSS value to SwiftUI
51
+ */
52
+ function cssToSwiftUI(property, value) {
53
+ // Token mapping
54
+ if (value.startsWith('$')) {
55
+ return TOKEN_TO_SWIFTUI[value] ?? value;
56
+ }
57
+ // Parse numeric values
58
+ const numValue = parseFloat(value);
59
+ if (!isNaN(numValue)) {
60
+ return numValue.toString();
61
+ }
62
+ // Color hex to SwiftUI
63
+ if (value.startsWith('#')) {
64
+ return `Color(hex: "${value}")`;
65
+ }
66
+ return value;
67
+ }
68
+ /**
69
+ * Gets SwiftUI view for element type
70
+ */
71
+ function getSwiftUIView(node) {
72
+ switch (node.tag) {
73
+ case 'button':
74
+ return 'Button';
75
+ case 'img':
76
+ return 'Image';
77
+ case 'input':
78
+ return 'TextField';
79
+ case 'span':
80
+ case 'p':
81
+ case 'h1':
82
+ case 'h2':
83
+ case 'h3':
84
+ return 'Text';
85
+ default:
86
+ return 'VStack'; // Default container
87
+ }
88
+ }
89
+ /**
90
+ * Generates SwiftUI modifiers from styles
91
+ */
92
+ function generateModifiers(styles, indent) {
93
+ const spaces = ' '.repeat(indent);
94
+ const modifiers = [];
95
+ for (const style of styles) {
96
+ const value = cssToSwiftUI(style.property, style.token ?? style.value);
97
+ switch (style.property) {
98
+ case 'padding':
99
+ modifiers.push(`${spaces}.padding(${value})`);
100
+ break;
101
+ case 'padding-top':
102
+ modifiers.push(`${spaces}.padding(.top, ${value})`);
103
+ break;
104
+ case 'padding-bottom':
105
+ modifiers.push(`${spaces}.padding(.bottom, ${value})`);
106
+ break;
107
+ case 'padding-left':
108
+ modifiers.push(`${spaces}.padding(.leading, ${value})`);
109
+ break;
110
+ case 'padding-right':
111
+ modifiers.push(`${spaces}.padding(.trailing, ${value})`);
112
+ break;
113
+ case 'background-color':
114
+ modifiers.push(`${spaces}.background(${value})`);
115
+ break;
116
+ case 'border-radius':
117
+ modifiers.push(`${spaces}.cornerRadius(${value})`);
118
+ break;
119
+ case 'color':
120
+ modifiers.push(`${spaces}.foregroundColor(${value})`);
121
+ break;
122
+ case 'font-size':
123
+ modifiers.push(`${spaces}.font(${value})`);
124
+ break;
125
+ case 'font-weight':
126
+ modifiers.push(`${spaces}.fontWeight(${value})`);
127
+ break;
128
+ case 'width':
129
+ if (value !== 'auto') {
130
+ modifiers.push(`${spaces}.frame(width: ${value})`);
131
+ }
132
+ break;
133
+ case 'height':
134
+ if (value !== 'auto') {
135
+ modifiers.push(`${spaces}.frame(height: ${value})`);
136
+ }
137
+ break;
138
+ }
139
+ }
140
+ return modifiers;
141
+ }
142
+ /**
143
+ * Generates SwiftUI view code
144
+ */
145
+ function generateSwiftUIView(node, styles, indent = 8) {
146
+ const spaces = ' '.repeat(indent);
147
+ const nodeStyles = styles.get(`.${node.className}`) ?? [];
148
+ // Determine view type
149
+ const view = getSwiftUIView(node);
150
+ // Get flex direction for layout
151
+ const flexDir = nodeStyles.find(s => s.property === 'flex-direction');
152
+ const layout = flexDir?.value === 'row' ? 'HStack' : 'VStack';
153
+ // Get alignment
154
+ const alignItems = nodeStyles.find(s => s.property === 'align-items');
155
+ let alignment = '';
156
+ if (alignItems) {
157
+ switch (alignItems.value) {
158
+ case 'center':
159
+ alignment = ', alignment: .center';
160
+ break;
161
+ case 'flex-start':
162
+ alignment = flexDir?.value === 'row' ? ', alignment: .top' : ', alignment: .leading';
163
+ break;
164
+ case 'flex-end':
165
+ alignment = flexDir?.value === 'row' ? ', alignment: .bottom' : ', alignment: .trailing';
166
+ break;
167
+ }
168
+ }
169
+ // Get gap/spacing
170
+ const gap = nodeStyles.find(s => s.property === 'gap');
171
+ const spacing = gap ? cssToSwiftUI('gap', gap.token ?? gap.value) : '0';
172
+ // Generate modifiers
173
+ const modifiers = generateModifiers(nodeStyles, indent);
174
+ // Children
175
+ const children = node.children.map(child => {
176
+ if (typeof child === 'string' || (typeof child === 'object' && 'type' in child && child.type === 'text')) {
177
+ const text = typeof child === 'string' ? child : child.value;
178
+ return `${spaces} Text("${text}")`;
179
+ }
180
+ return generateSwiftUIView(child, styles, indent + 4);
181
+ });
182
+ // Build view
183
+ if (view === 'Text') {
184
+ const text = node.children[0];
185
+ const textContent = typeof text === 'string' ? text : text?.value ?? '';
186
+ const textView = `${spaces}Text("${textContent}")`;
187
+ if (modifiers.length > 0) {
188
+ return textView + '\n' + modifiers.join('\n');
189
+ }
190
+ return textView;
191
+ }
192
+ if (view === 'Button') {
193
+ const buttonLabel = children.length > 0 ? children[0].trim() : 'Text("Button")';
194
+ const buttonView = `${spaces}Button(action: {}) {\n${spaces} ${buttonLabel}\n${spaces}}`;
195
+ if (modifiers.length > 0) {
196
+ return buttonView + '\n' + modifiers.join('\n');
197
+ }
198
+ return buttonView;
199
+ }
200
+ if (view === 'Image') {
201
+ return `${spaces}Image(systemName: "photo")\n${spaces} .resizable()\n${spaces} .aspectRatio(contentMode: .fit)`;
202
+ }
203
+ // Container (VStack/HStack)
204
+ if (children.length === 0) {
205
+ const emptyView = `${spaces}${layout}(spacing: ${spacing}${alignment}) {}`;
206
+ if (modifiers.length > 0) {
207
+ return emptyView + '\n' + modifiers.join('\n');
208
+ }
209
+ return emptyView;
210
+ }
211
+ const containerView = [
212
+ `${spaces}${layout}(spacing: ${spacing}${alignment}) {`,
213
+ ...children,
214
+ `${spaces}}`,
215
+ ].join('\n');
216
+ if (modifiers.length > 0) {
217
+ return containerView + '\n' + modifiers.join('\n');
218
+ }
219
+ return containerView;
220
+ }
221
+ /**
222
+ * Generates SwiftUI View struct
223
+ */
224
+ export function generateSwiftUIComponent(ast) {
225
+ // Create styles map
226
+ const stylesMap = new Map();
227
+ for (const block of ast.styles) {
228
+ stylesMap.set(block.selector, block.properties);
229
+ }
230
+ // View body
231
+ const viewBody = generateSwiftUIView(ast.jsx, stylesMap);
232
+ return `/// ${ast.name}
233
+ /// Generated by PromptUI (SwiftUI)
234
+
235
+ import SwiftUI
236
+
237
+ struct ${ast.name}: View {
238
+ var body: some View {
239
+ ${viewBody}
240
+ }
241
+ }
242
+
243
+ #Preview {
244
+ ${ast.name}()
245
+ }
246
+ `;
247
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptui-lib/codegen",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "description": "Code generator for PromptUI - generates React TSX and SCSS",
6
6
  "license": "UNLICENSED",
@@ -30,7 +30,7 @@
30
30
  "dist"
31
31
  ],
32
32
  "dependencies": {
33
- "@promptui-lib/core": "0.1.0"
33
+ "@promptui-lib/core": "0.1.2"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^20.0.0",