@pen.dev/cli 0.3.3 → 0.3.4
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 +13 -6
- package/dist/anthropic-messages-COZrsSdY.mjs +40 -0
- package/dist/azure-openai-responses-Dmf1EaVz.mjs +2 -0
- package/dist/browserAll-BZYrVO32.mjs +2 -0
- package/dist/{dist-BEo0Z8Vf.mjs → dist-D9ROAuw3.mjs} +348 -319
- package/dist/{dist-BY6vOcMF.mjs → dist-DGrygXHh.mjs} +2 -2
- package/dist/{error-body-D2Mrqb4g.mjs → error-body-BBlqjbe7.mjs} +1 -1
- package/dist/google-generative-ai-BMEzR_34.mjs +2 -0
- package/dist/google-shared-DXgnvvDU.mjs +318 -0
- package/dist/google-vertex-In7qdx36.mjs +2 -0
- package/dist/index.mjs +2 -2
- package/dist/mistral-conversations-DdYjdrRy.mjs +5 -0
- package/dist/models-BBd5zwn6.mjs +2 -0
- package/dist/{multipart-parser-DX5ZDxQv.mjs → multipart-parser-BdmM0fdd.mjs} +1 -1
- package/dist/node_modules/@highagency/pencil-wasm/package.json +1 -1
- package/dist/node_modules/@highagency/pencil-wasm/pencil.d.ts +3 -0
- package/dist/node_modules/@highagency/pencil-wasm/pencil.js +1 -1
- package/dist/node_modules/@highagency/pencil-wasm/pencil.wasm +0 -0
- package/dist/openai-CbFQ5q5I.mjs +18 -0
- package/dist/openai-codex-responses-CD-2hfWB.mjs +8 -0
- package/dist/openai-completions-DamtYjy7.mjs +6 -0
- package/dist/openai-responses-DgeWdtm-.mjs +2 -0
- package/dist/openai-responses-shared-DKseiGAg.mjs +11 -0
- package/dist/{openrouter-images-BbBWg9zF.mjs → openrouter-images-gArAwqnL.mjs} +1 -1
- package/dist/out/mcp-server-darwin-arm64 +0 -0
- package/dist/out/mcp-server-darwin-x64 +0 -0
- package/dist/out/mcp-server-linux-arm64 +0 -0
- package/dist/out/mcp-server-linux-x64 +0 -0
- package/dist/out/mcp-server-windows-arm64.exe +0 -0
- package/dist/out/mcp-server-windows-x64.exe +0 -0
- package/dist/out/skills/pen-dev/SKILL.md +204 -0
- package/dist/out/skills/pen-dev/execute.md +364 -0
- package/dist/out/skills/pen-dev/guide/code.md +198 -0
- package/dist/out/skills/pen-dev/guide/components.md +45 -0
- package/dist/out/skills/pen-dev/guide/design-system.md +556 -0
- package/dist/out/skills/pen-dev/guide/landing-page.md +31 -0
- package/dist/out/skills/pen-dev/guide/mobile-app.md +31 -0
- package/dist/out/skills/pen-dev/guide/slides.md +222 -0
- package/dist/out/skills/pen-dev/guide/table.md +37 -0
- package/dist/out/skills/pen-dev/guide/tailwind.md +328 -0
- package/dist/out/skills/pen-dev/guide/web-app.md +245 -0
- package/dist/out/skills/pen-dev/pen-schema.md +202 -0
- package/dist/out/skills/pen-dev/scripts-and-shaders.md +94 -0
- package/dist/pi-user-agent-m9a4CjJo.mjs +2 -0
- package/dist/{src-B_lvnEtX.mjs → src-KoDMTA_Q.mjs} +1 -1
- package/dist/transform-messages-BHnJzowk.mjs +2 -0
- package/dist/webworkerAll-CB00nbs9.mjs +2 -0
- package/package.json +3 -3
- package/dist/anthropic-messages-Bf-fvkNu.mjs +0 -40
- package/dist/azure-openai-responses-CzgVqECr.mjs +0 -2
- package/dist/browserAll-8jKh1NRi.mjs +0 -2
- package/dist/completionchunk-BQtPqA8U.mjs +0 -28
- package/dist/google-generative-ai-DSOqI0LF.mjs +0 -2
- package/dist/google-shared-CetPT_Vt.mjs +0 -318
- package/dist/google-vertex-CPtOc5oU.mjs +0 -2
- package/dist/mistral-conversations-CUsdbDQ8.mjs +0 -10
- package/dist/models-CwK5xZH9.mjs +0 -2
- package/dist/openai-Brr_V-tF.mjs +0 -17
- package/dist/openai-codex-responses-Cb5pfH5S.mjs +0 -8
- package/dist/openai-completions-BvTlcCeP.mjs +0 -6
- package/dist/openai-responses-IuaLTEHh.mjs +0 -2
- package/dist/openai-responses-shared-D4d0NbBE.mjs +0 -11
- package/dist/otel-CaADOqYZ.mjs +0 -4
- package/dist/transform-messages-CSBQpmXO.mjs +0 -2
- package/dist/webworkerAll-BD-7VH16.mjs +0 -2
- /package/dist/{dist-BnoSGZKP.mjs → dist-D8tvRm1b.mjs} +0 -0
- /package/dist/{emscripten-module.browser-F76W5DM6-CNibWX-6.mjs → emscripten-module.browser-F76W5DM6-DWhj_jYh.mjs} +0 -0
- /package/dist/{emscripten-module.browser-XIKQQPVU-BNfdGnMe.mjs → emscripten-module.browser-XIKQQPVU-Dmh4no3c.mjs} +0 -0
- /package/dist/{ffi-C5tLdQO9.mjs → ffi-BroAqW9B.mjs} +0 -0
- /package/dist/{ffi-BuJ13coi.mjs → ffi-DwdyvnFx.mjs} +0 -0
- /package/dist/{from-py2TfO8m.mjs → from-Choxbpmr.mjs} +0 -0
- /package/dist/{github-copilot-headers-DZOfokGy.mjs → github-copilot-headers-DCj7hJoC.mjs} +0 -0
- /package/dist/{hash-Kp92CI9R.mjs → hash-DNYCELl4.mjs} +0 -0
- /package/dist/{html-GGJ1fTRB.mjs → html-DEPARSNH.mjs} +0 -0
- /package/dist/{init-B9LSomNH.mjs → init-DwIpBeuP.mjs} +0 -0
- /package/dist/{module-ES6BEMUI-DwQ9nTMF.mjs → module-ES6BEMUI-DUGQwtIb.mjs} +0 -0
- /package/dist/{module-asyncify-2EFITU5U-B54Mkt6K.mjs → module-asyncify-2EFITU5U-U7fqmMTF.mjs} +0 -0
- /package/dist/{openai-prompt-cache-t4kk9puG.mjs → openai-prompt-cache-tVHrB8oW.mjs} +0 -0
- /package/dist/{photon_rs-BySSRmP5.mjs → photon_rs-o0nbTM4F.mjs} +0 -0
- /package/dist/{provider-retry-BM3ArvaW.mjs → provider-retry-wOYAq0sa.mjs} +0 -0
- /package/dist/{sanitize-unicode-CHjuq5rK.mjs → sanitize-unicode-Byz9nlLd.mjs} +0 -0
- /package/dist/{standalone-DYT2Zg6I.mjs → standalone-DwaYVpTH.mjs} +0 -0
|
@@ -0,0 +1,556 @@
|
|
|
1
|
+
# Design System Composition Guidelines
|
|
2
|
+
|
|
3
|
+
Helpful patterns for composing screens and dashboards using design system components in `.pen` files. These are suggestions to get you started—feel free to adapt them to your needs.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Common Component Patterns
|
|
8
|
+
|
|
9
|
+
Component naming patterns you might encounter:
|
|
10
|
+
- `Button/*` — Button variants
|
|
11
|
+
- `Input/*` or `Input Group/*` — Form inputs
|
|
12
|
+
- `Card` — Card containers
|
|
13
|
+
- `Sidebar` — Navigation sidebar
|
|
14
|
+
- `Table` or `Data Table` — Table elements
|
|
15
|
+
- `Alert/*` — Feedback alerts
|
|
16
|
+
- `Modal/*` or `Dialog` — Modal dialogs
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 2. Understanding Slots
|
|
21
|
+
|
|
22
|
+
Slots are placeholder frames inside components where you insert child components. They're marked with the `slot` property containing an array of recommended component IDs.
|
|
23
|
+
|
|
24
|
+
### How to Identify Slots
|
|
25
|
+
|
|
26
|
+
When reading a component, look for frames with slot property:
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"id": "slotId",
|
|
30
|
+
"name": "Content Slot",
|
|
31
|
+
"slot": ["recommendedComponentId1", "recommendedComponentId2"]
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### How to Use Slots
|
|
36
|
+
|
|
37
|
+
A typical approach:
|
|
38
|
+
1. **Insert the parent component** and capture its binding
|
|
39
|
+
2. **Insert children into the slot** using path: `parentBinding/slotId`
|
|
40
|
+
3. **Consider using recommended components** listed in the slot's `slot` array (though you can insert other content too)
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
sidebar=Insert(page, {type: "ref", ref: "sidebarComponentId", height: "fill_container"})
|
|
44
|
+
item1=Insert(sidebar+"/contentSlotId", {type: "ref", ref: "sidebarItemId", descendants: {...}})
|
|
45
|
+
item2=Insert(sidebar+"/contentSlotId", {type: "ref", ref: "sidebarItemId", descendants: {...}})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If you don't need to use a particular slot in a component instance, mark the slot `enabled: false` to hide it.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 3. Icons
|
|
53
|
+
|
|
54
|
+
### Available Icon Sets
|
|
55
|
+
|
|
56
|
+
You can use the `icon` type with these libraries:
|
|
57
|
+
|
|
58
|
+
| Library | Style | Example Names |
|
|
59
|
+
|-------------|-------|---------------|
|
|
60
|
+
| `lucide` | Outline, rounded | `home`, `settings`, `user`, `search`, `plus`, `x` |
|
|
61
|
+
| `feather` | Outline, rounded | `home`, `settings`, `user`, `search`, `plus`, `x` |
|
|
62
|
+
| `Material Symbols Outlined` | Outline | `home`, `settings`, `person`, `search`, `add`, `close` |
|
|
63
|
+
| `Material Symbols Rounded` | Rounded | `home`, `settings`, `person`, `search`, `add`, `close` |
|
|
64
|
+
| `Material Symbols Sharp` | Sharp corners | `home`, `settings`, `person`, `search`, `add`, `close` |
|
|
65
|
+
|
|
66
|
+
### Icon Usage
|
|
67
|
+
|
|
68
|
+
Standalone icon with Lucide, and Material Symbols with weight:
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
icon=Insert(container, {type: "icon", library: "lucide", icon: "settings", width: 24, height: 24, fill: "$--foreground"})
|
|
72
|
+
icon=Insert(container, {type: "icon", library: "Material Symbols Rounded", icon: "dashboard", width: 24, height: 24, fill: "$--foreground", weight: 400})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Overriding Icons in Components
|
|
76
|
+
|
|
77
|
+
When a component contains an icon, override it via descendants. Descendant keys can be node IDs/paths or unique descendant names; use IDs/paths when names are ambiguous:
|
|
78
|
+
```javascript
|
|
79
|
+
descendants: {
|
|
80
|
+
"iconNodeId": { icon: "settings" }
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Common Icon Names
|
|
85
|
+
|
|
86
|
+
| Action | Lucide/Feather | Material Symbols |
|
|
87
|
+
|--------|----------------|------------------|
|
|
88
|
+
| Home | `home` | `home` |
|
|
89
|
+
| Settings | `settings` | `settings` |
|
|
90
|
+
| User | `user` | `person` |
|
|
91
|
+
| Search | `search` | `search` |
|
|
92
|
+
| Add | `plus` | `add` |
|
|
93
|
+
| Close | `x` | `close` |
|
|
94
|
+
| Edit | `edit`, `pencil` | `edit` |
|
|
95
|
+
| Delete | `trash`, `trash-2` | `delete` |
|
|
96
|
+
| Check | `check` | `check` |
|
|
97
|
+
| Arrow right | `arrow-right` | `arrow_forward` |
|
|
98
|
+
| Chevron down | `chevron-down` | `expand_more` |
|
|
99
|
+
| Menu | `menu` | `menu` |
|
|
100
|
+
| Dashboard | `layout-dashboard` | `dashboard` |
|
|
101
|
+
| Folder | `folder` | `folder` |
|
|
102
|
+
| File | `file` | `description` |
|
|
103
|
+
| Calendar | `calendar` | `calendar_today` |
|
|
104
|
+
| Mail | `mail` | `mail` |
|
|
105
|
+
| Bell | `bell` | `notifications` |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 4. Sidebar Composition
|
|
110
|
+
|
|
111
|
+
### Structure
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
Sidebar Component
|
|
115
|
+
├── Header (logo, brand)
|
|
116
|
+
├── Content Slot ← Insert navigation items here
|
|
117
|
+
└── Footer (user profile, settings)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Populating Sidebar Navigation
|
|
121
|
+
|
|
122
|
+
Insert the sidebar, then add section title (if available), active item, and default items:
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
sidebar=Insert(page, {type: "ref", ref: "sidebarId", height: "fill_container"})
|
|
126
|
+
newSectionTitle=Insert(sidebar+"/contentSlotId", {type: "ref", ref: "sidebarSectionTitleId", descendants: {"labelTextId": {content: "Main Menu"}}})
|
|
127
|
+
itemDashboard=Insert(sidebar+"/contentSlotId", {type: "ref", ref: "sidebarItemActiveId", descendants: {"iconId": {icon: "dashboard"}, "labelId": {content: "Dashboard"}}})
|
|
128
|
+
itemUsers=Insert(sidebar+"/contentSlotId", {type: "ref", ref: "sidebarItemDefaultId", descendants: {"iconId": {icon: "users"}, "labelId": {content: "Users"}}})
|
|
129
|
+
itemSettings=Insert(sidebar+"/contentSlotId", {type: "ref", ref: "sidebarItemDefaultId", descendants: {"iconId": {icon: "settings"}, "labelId": {content: "Settings"}}})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 5. Card Composition
|
|
135
|
+
|
|
136
|
+
### Structure
|
|
137
|
+
|
|
138
|
+
Cards typically have three slots:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
Card Component
|
|
142
|
+
├── Header Slot ← Title, description
|
|
143
|
+
├── Content Slot ← Main content
|
|
144
|
+
└── Actions Slot ← Buttons
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Populating Card Slots
|
|
148
|
+
|
|
149
|
+
Insert card, replace header with custom content, setup content slot for form, setup actions slot with buttons:
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
card=Insert(container, {type: "ref", ref: "cardId", width: 480})
|
|
153
|
+
newNode=Replace(card+"/headerSlotId", {type: "frame", layout: "vertical", gap: 4, padding: 24, width: "fill_container", children: [
|
|
154
|
+
{type: "text", content: "Card Title", fill: "$--foreground", fontFamily: "$--font-primary", fontSize: 18, fontWeight: "600"},
|
|
155
|
+
{type: "text", content: "Card description goes here", fill: "$--muted-foreground", fontFamily: "$--font-secondary", fontSize: 14}
|
|
156
|
+
]})
|
|
157
|
+
Update(card+"/contentSlotId", {layout: "vertical", gap: 16, padding: 24})
|
|
158
|
+
input=Insert(card+"/contentSlotId", {type: "ref", ref: "inputGroupId", width: "fill_container", descendants: {"labelId": {content: "Email"}}})
|
|
159
|
+
Update(card+"/actionsSlotId", {gap: 12, justifyContent: "end", padding: 24})
|
|
160
|
+
cancelBtn=Insert(card+"/actionsSlotId", {type: "ref", ref: "buttonOutlineId", descendants: {"iconId": {enabled: false}, "labelId": {content: "Cancel"}}})
|
|
161
|
+
saveBtn=Insert(card+"/actionsSlotId", {type: "ref", ref: "buttonPrimaryId", descendants: {"iconId": {enabled: false}, "labelId": {content: "Save"}}})
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 6. Tab Composition
|
|
167
|
+
|
|
168
|
+
### Structure
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
Tabs Container
|
|
172
|
+
└── Direct children: Tab Items (active/inactive)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Building Tabs
|
|
176
|
+
|
|
177
|
+
Insert tabs container, then add tab items directly (first one active):
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
tabs=Insert(container, {type: "ref", ref: "tabsId", width: "fit_content"})
|
|
181
|
+
tab1=Insert(tabs, {type: "ref", ref: "tabItemActiveId", descendants: {"labelId": {content: "General"}}})
|
|
182
|
+
tab2=Insert(tabs, {type: "ref", ref: "tabItemInactiveId", descendants: {"labelId": {content: "Security"}}})
|
|
183
|
+
tab3=Insert(tabs, {type: "ref", ref: "tabItemInactiveId", descendants: {"labelId": {content: "Billing"}}})
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 7. Dropdown Composition
|
|
189
|
+
|
|
190
|
+
### Structure
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
Dropdown Container
|
|
194
|
+
└── Direct children: Search, Dividers, Titles, List Items
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Building Dropdowns
|
|
198
|
+
|
|
199
|
+
Optional search, divider, section title, and items:
|
|
200
|
+
|
|
201
|
+
```javascript
|
|
202
|
+
dropdown=Insert(container, {type: "ref", ref: "dropdownId", height: "fit_content"})
|
|
203
|
+
search=Insert(dropdown, {type: "ref", ref: "searchBoxId"})
|
|
204
|
+
divider=Insert(dropdown, {type: "ref", ref: "listDividerId"})
|
|
205
|
+
title=Insert(dropdown, {type: "ref", ref: "listTitleId", descendants: {"labelId": {content: "Actions"}}})
|
|
206
|
+
optionA=Insert(dropdown, {type: "ref", ref: "listItemCheckedId", descendants: {"labelId": {content: "Option A"}}})
|
|
207
|
+
optionB=Insert(dropdown, {type: "ref", ref: "listItemUncheckedId", descendants: {"labelId": {content: "Option B"}}})
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 8. Table Composition
|
|
213
|
+
|
|
214
|
+
### Table Structure
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
Table (frame)
|
|
218
|
+
├── Table Header — Search/filter + action buttons
|
|
219
|
+
├── Table Wrapper — Contains all rows
|
|
220
|
+
│ ├── Header Row (frame)
|
|
221
|
+
│ │ └── Cell (frame)
|
|
222
|
+
│ │ └── Content (text, label, button, etc.)
|
|
223
|
+
│ ├── Data Row 1 (frame)
|
|
224
|
+
│ │ └── Cell (frame)
|
|
225
|
+
│ │ └── Content (text, label, button, etc.)
|
|
226
|
+
│ ├── Data Row 2 (frame)
|
|
227
|
+
│ │ └── Cell (frame)
|
|
228
|
+
│ │ └── Content (text, label, button, etc.)
|
|
229
|
+
│ └── ...
|
|
230
|
+
└── Table Footer — Row count + pagination
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Building Tables Step by Step
|
|
234
|
+
|
|
235
|
+
### Table Hierarchy
|
|
236
|
+
|
|
237
|
+
**Important:** Tables follow this strict nesting structure:
|
|
238
|
+
Table → Row → Cell (frame) → Cell Content (text, label, button, etc.)
|
|
239
|
+
|
|
240
|
+
- **Table**: Container with vertical layout holding all rows
|
|
241
|
+
- **Row**: Horizontal container holding cells
|
|
242
|
+
- **Cell**: Frame wrapper that controls column width
|
|
243
|
+
- **Cell Content**: The actual content inside the cell (text, badges, buttons, etc.)
|
|
244
|
+
|
|
245
|
+
**Add data rows with cells**
|
|
246
|
+
|
|
247
|
+
Note: For tables with many rows, split into multiple `execute` calls (e.g., 2-3 rows per call).
|
|
248
|
+
|
|
249
|
+
```javascript
|
|
250
|
+
row1=Insert(table, {type: "ref", ref: "dataTableRowId", width: "fill_container"})
|
|
251
|
+
nameCell=Insert(row1, {type: "ref", ref: "dataTableCellId", width: "fill_container"})
|
|
252
|
+
nameText=Insert(nameCell, {type: "text", content: "John Doe"})
|
|
253
|
+
emailCell=Insert(row1, {type: "ref", ref: "dataTableCellId", width: "fill_container"})
|
|
254
|
+
emailText=Insert(emailCell, {type: "text", content: "john@example.com"})
|
|
255
|
+
statusCell=Insert(row1, {type: "ref", ref: "dataTableCellId", width: 120})
|
|
256
|
+
statusBadge=Insert(statusCell, {type: "ref", ref: "labelSuccessId", descendants: {"textId": {content: "Active"}}})
|
|
257
|
+
actionsCell=Insert(row1, {type: "ref", ref: "dataTableCellId", width: 100})
|
|
258
|
+
actionBtn=Insert(actionsCell, {type: "ref", ref: "iconButtonId"})
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Column Width Strategy
|
|
262
|
+
|
|
263
|
+
Suggested starting points (adjust as needed):
|
|
264
|
+
|
|
265
|
+
| Column Type | Typical Width |
|
|
266
|
+
|-------------|-------|
|
|
267
|
+
| Primary identifier (name) | 200-250px |
|
|
268
|
+
| Email, URL | `fill_container` |
|
|
269
|
+
| Status, badge | 100-120px |
|
|
270
|
+
| Date | 120-150px |
|
|
271
|
+
| Actions | 80-100px |
|
|
272
|
+
| Numbers | 80-100px |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## 9. Pagination Composition
|
|
277
|
+
|
|
278
|
+
### Structure
|
|
279
|
+
|
|
280
|
+
```
|
|
281
|
+
Pagination Component
|
|
282
|
+
├── Previous Button
|
|
283
|
+
├── Page Numbers Slot ← Insert page items here
|
|
284
|
+
└── Next Button
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Building Pagination
|
|
288
|
+
|
|
289
|
+
Insert page numbers into slot:
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
pagination=Insert(container, {type: "ref", ref: "paginationId"})
|
|
293
|
+
page1=Insert(pagination+"/pageNumbersSlotId", {type: "ref", ref: "paginationItemActiveId", descendants: {"labelId": {content: "1"}}})
|
|
294
|
+
page2=Insert(pagination+"/pageNumbersSlotId", {type: "ref", ref: "paginationItemDefaultId", descendants: {"labelId": {content: "2"}}})
|
|
295
|
+
page3=Insert(pagination+"/pageNumbersSlotId", {type: "ref", ref: "paginationItemDefaultId", descendants: {"labelId": {content: "3"}}})
|
|
296
|
+
ellipsis=Insert(pagination+"/pageNumbersSlotId", {type: "ref", ref: "paginationItemEllipsisId"})
|
|
297
|
+
page10=Insert(pagination+"/pageNumbersSlotId", {type: "ref", ref: "paginationItemDefaultId", descendants: {"labelId": {content: "10"}}})
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 10. Screen Layout Patterns
|
|
303
|
+
|
|
304
|
+
These patterns show the structure for common layouts. Each pattern is typically one execute call. Since `execute` input is JavaScript, use arrays, loops, object spreads, helper objects, and template strings to generate repeated design nodes without hand-writing every operation. Do not include comments in generated `execute` JavaScript.
|
|
305
|
+
|
|
306
|
+
### Pattern A: Sidebar + Content (Dashboard)
|
|
307
|
+
|
|
308
|
+
```
|
|
309
|
+
┌──────────┬────────────────────────────────┐
|
|
310
|
+
│ │ │
|
|
311
|
+
│ Sidebar │ Main Content Area │
|
|
312
|
+
│ 280px │ fill_container │
|
|
313
|
+
│ │ │
|
|
314
|
+
└──────────┴────────────────────────────────┘
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
```javascript
|
|
318
|
+
screen=Insert(document, {type: "frame", name: "Dashboard", layout: "horizontal", width: 1440, height: "fit_content(900)", fill: "$--background", placeholder: true})
|
|
319
|
+
sidebar=Insert(screen, {type: "ref", ref: "sidebarId", height: "fill_container"})
|
|
320
|
+
main=Insert(screen, {type: "frame", layout: "vertical", width: "fill_container", height: "fill_container(900)", padding: 32, gap: 24})
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Pattern B: Header + Content
|
|
324
|
+
|
|
325
|
+
```
|
|
326
|
+
┌────────────────────────────────────────────┐
|
|
327
|
+
│ Header Bar (64px) │
|
|
328
|
+
├────────────────────────────────────────────┤
|
|
329
|
+
│ │
|
|
330
|
+
│ Content Area │
|
|
331
|
+
│ │
|
|
332
|
+
└────────────────────────────────────────────┘
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Fixed header and scrollable content:
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
screen=Insert(document, {type: "frame", layout: "vertical", width: 1200, height: "fit_content(800)", fill: "$--background", placeholder: true})
|
|
339
|
+
header=Insert(screen, {type: "frame", layout: "horizontal", width: "fill_container", height: 64, padding: [0, 24], alignItems: "center", justifyContent: "space_between", strokeAlignment: "inner", stroke: "$--border", strokeWidth: {bottom: 1}})
|
|
340
|
+
content=Insert(screen, {type: "frame", layout: "vertical", width: "fill_container", height: "fit_content(736)", padding: 32, gap: 24})
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Pattern C: Two-Column Layout
|
|
344
|
+
|
|
345
|
+
```
|
|
346
|
+
┌─────────────────────┬─────────────┐
|
|
347
|
+
│ │ │
|
|
348
|
+
│ Main (2/3) │ Side (1/3) │
|
|
349
|
+
│ fill_container │ 360px │
|
|
350
|
+
│ │ │
|
|
351
|
+
└─────────────────────┴─────────────┘
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
Main column (flexible) and side column (fixed):
|
|
355
|
+
|
|
356
|
+
```javascript
|
|
357
|
+
columns=Insert(content, {type: "frame", layout: "horizontal", width: "fill_container", height: "fill_container(900)", gap: 24})
|
|
358
|
+
mainCol=Insert(columns, {type: "frame", layout: "vertical", width: "fill_container", height: "fit_content(900)", gap: 24})
|
|
359
|
+
sideCol=Insert(columns, {type: "frame", layout: "vertical", width: 360, height: "fit_content(900)", gap: 24})
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Pattern D: Card Grid
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
366
|
+
│ Card 1 │ │ Card 2 │ │ Card 3 │
|
|
367
|
+
└──────────┘ └──────────┘ └──────────┘
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
cardGrid=Insert(container, {type: "frame", layout: "horizontal", width: "fill_container", gap: 16})
|
|
372
|
+
for (const label of ["Overview", "Activity", "Revenue"]) {
|
|
373
|
+
card=Insert(cardGrid, {type: "ref", ref: "cardId", width: "fill_container"})
|
|
374
|
+
Update(card+"/titleId", {content: label})
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## 11. Common Compositions
|
|
381
|
+
|
|
382
|
+
These snippets combine with screen layout patterns, or work as standalone execute calls after the initial structure is created. Prefer compact JavaScript generation when a composition has repeated items.
|
|
383
|
+
|
|
384
|
+
### Page Header with Breadcrumbs + Actions
|
|
385
|
+
|
|
386
|
+
Breadcrumbs on the left, action buttons on the right:
|
|
387
|
+
|
|
388
|
+
```javascript
|
|
389
|
+
pageHeader=Insert(main, {type: "frame", layout: "horizontal", width: "fill_container", justifyContent: "space_between", alignItems: "center"})
|
|
390
|
+
breadcrumbs=Insert(pageHeader, {type: "frame", layout: "horizontal", gap: 0, alignItems: "center"})
|
|
391
|
+
const crumbs = ["Dashboard", "Users"];
|
|
392
|
+
for (const [index, label] of crumbs.entries()) {
|
|
393
|
+
if (index > 0) Insert(breadcrumbs, {type: "ref", ref: "breadcrumbSeparatorId"})
|
|
394
|
+
Insert(breadcrumbs, {type: "ref", ref: index === crumbs.length - 1 ? "breadcrumbItemActiveId" : "breadcrumbItemId", descendants: {"labelId": {content: label}}})
|
|
395
|
+
}
|
|
396
|
+
actions=Insert(pageHeader, {type: "frame", layout: "horizontal", gap: 12})
|
|
397
|
+
for (const [ref, label] of [["buttonOutlineId", "Export"], ["buttonPrimaryId", "Add User"]]) {
|
|
398
|
+
Insert(actions, {type: "ref", ref, descendants: {"iconId": {enabled: false}, "labelId": {content: label}}})
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Form Layout
|
|
403
|
+
|
|
404
|
+
Two fields in a row, then full-width fields:
|
|
405
|
+
|
|
406
|
+
```javascript
|
|
407
|
+
card=Insert(container, {type: "ref", ref: "cardId", width: "fill_container"})
|
|
408
|
+
form=Insert(card+"/contentSlotId", {type: "frame", layout: "vertical", gap: 16, width: "fill_container"})
|
|
409
|
+
row=Insert(form, {type: "frame", layout: "horizontal", gap: 16, width: "fill_container"})
|
|
410
|
+
for (const label of ["First Name", "Last Name"]) {
|
|
411
|
+
Insert(row, {type: "ref", ref: "inputGroupId", width: "fill_container", descendants: {"labelId": {content: label}}})
|
|
412
|
+
}
|
|
413
|
+
for (const field of [
|
|
414
|
+
{ref: "inputGroupId", label: "Email"},
|
|
415
|
+
{ref: "textareaGroupId", label: "Message"}
|
|
416
|
+
]) {
|
|
417
|
+
Insert(form, {type: "ref", ref: field.ref, width: "fill_container", descendants: {"labelId": {content: field.label}}})
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Metric Cards
|
|
422
|
+
|
|
423
|
+
Replace header with custom metric content, disable unused slots:
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
metrics=Insert(content, {type: "frame", layout: "horizontal", gap: 16, width: "fill_container"})
|
|
427
|
+
const textBase = {type: "text", fontFamily: "$--font-primary"};
|
|
428
|
+
for (const metric of [
|
|
429
|
+
{label: "Total Users", value: "12,543"},
|
|
430
|
+
{label: "Revenue", value: "$48.2K"},
|
|
431
|
+
{label: "Conversion", value: "7.4%"}
|
|
432
|
+
]) {
|
|
433
|
+
metricCard=Insert(metrics, {type: "ref", ref: "cardId", width: "fill_container"})
|
|
434
|
+
header=Replace(metricCard+"/headerSlotId", {type: "frame", layout: "vertical", gap: 4, padding: 24, width: "fill_container"})
|
|
435
|
+
Insert(header, {...textBase, content: metric.label, fill: "$--muted-foreground", fontSize: 14})
|
|
436
|
+
Insert(header, {...textBase, content: metric.value, fill: "$--foreground", fontSize: 32, fontWeight: "600"})
|
|
437
|
+
Update(metricCard+"/contentSlotId", {enabled: false})
|
|
438
|
+
Update(metricCard+"/actionsSlotId", {enabled: false})
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## 12. Spacing Reference
|
|
445
|
+
|
|
446
|
+
Common spacing values as a starting point:
|
|
447
|
+
|
|
448
|
+
| Context | Gap | Padding |
|
|
449
|
+
|---------|-----|---------|
|
|
450
|
+
| Screen sections | 24-32 | — |
|
|
451
|
+
| Card grid | 16-24 | — |
|
|
452
|
+
| Form fields (vertical) | 16 | — |
|
|
453
|
+
| Form row (horizontal) | 16 | — |
|
|
454
|
+
| Button groups | 12 | — |
|
|
455
|
+
| Inside cards | — | 24 |
|
|
456
|
+
| Inside buttons | — | [10, 16] |
|
|
457
|
+
| Inside inputs | — | [8, 16] |
|
|
458
|
+
| Page content area | — | 32 |
|
|
459
|
+
| Sidebar items | 0 | [12, 16] |
|
|
460
|
+
|
|
461
|
+
---
|
|
462
|
+
|
|
463
|
+
## 13. Button Hierarchy
|
|
464
|
+
|
|
465
|
+
A good rule of thumb: one primary action per section helps users focus. Rough priority order:
|
|
466
|
+
|
|
467
|
+
| Priority | Variant | Often used for |
|
|
468
|
+
|----------|---------|---------|
|
|
469
|
+
| 1 | Primary/Default | Main action (Save, Submit, Create) |
|
|
470
|
+
| 2 | Secondary | Alternative actions |
|
|
471
|
+
| 3 | Outline | Tertiary, Cancel, Back |
|
|
472
|
+
| 4 | Ghost | Inline actions, navigation |
|
|
473
|
+
| 5 | Destructive | Delete, Remove |
|
|
474
|
+
|
|
475
|
+
### Button Actions Alignment
|
|
476
|
+
|
|
477
|
+
Common conventions:
|
|
478
|
+
- **Cards/Modals:** Right-align actions (`justifyContent: "end"`)
|
|
479
|
+
- **Forms:** Right-align submit buttons
|
|
480
|
+
- **Toolbars:** Left-align primary, right-align secondary
|
|
481
|
+
- **Destructive + Cancel:** Cancel on left, Destructive on right
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## 14. Design Tokens
|
|
486
|
+
|
|
487
|
+
Using design token variables helps keep things consistent:
|
|
488
|
+
|
|
489
|
+
### Colors
|
|
490
|
+
| Token | Usage |
|
|
491
|
+
|-------|-------|
|
|
492
|
+
| `$--background` | Page background |
|
|
493
|
+
| `$--foreground` | Primary text |
|
|
494
|
+
| `$--muted-foreground` | Secondary text, placeholders |
|
|
495
|
+
| `$--card` | Card backgrounds |
|
|
496
|
+
| `$--border` | Borders, dividers |
|
|
497
|
+
| `$--primary` | Primary actions, brand |
|
|
498
|
+
| `$--secondary` | Secondary elements |
|
|
499
|
+
| `$--destructive` | Danger actions |
|
|
500
|
+
|
|
501
|
+
### Semantic Colors
|
|
502
|
+
| State | Background | Foreground |
|
|
503
|
+
|-------|------------|------------|
|
|
504
|
+
| Success | `$--color-success` | `$--color-success-foreground` |
|
|
505
|
+
| Warning | `$--color-warning` | `$--color-warning-foreground` |
|
|
506
|
+
| Error | `$--color-error` | `$--color-error-foreground` |
|
|
507
|
+
| Info | `$--color-info` | `$--color-info-foreground` |
|
|
508
|
+
|
|
509
|
+
### Typography
|
|
510
|
+
| Token | Usage |
|
|
511
|
+
|-------|-------|
|
|
512
|
+
| `$--font-primary` | Headings, labels, navigation |
|
|
513
|
+
| `$--font-secondary` | Body text, descriptions, inputs |
|
|
514
|
+
|
|
515
|
+
### Border Radius
|
|
516
|
+
| Token | Usage |
|
|
517
|
+
|-------|-------|
|
|
518
|
+
| `$--radius-none` | Tables, sharp containers |
|
|
519
|
+
| `$--radius-m` | Cards, modals |
|
|
520
|
+
| `$--radius-pill` | Buttons, inputs, badges |
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## 15. Design Principles
|
|
525
|
+
|
|
526
|
+
These principles help ensure designs are grounded, consistent, and maintainable.
|
|
527
|
+
|
|
528
|
+
### Visual Hierarchy
|
|
529
|
+
- One clear focal point per section
|
|
530
|
+
- Use size, weight, and color to establish importance
|
|
531
|
+
- Primary actions should be visually dominant
|
|
532
|
+
|
|
533
|
+
### Alignment & Grid
|
|
534
|
+
- Align elements to an implicit grid
|
|
535
|
+
- Use consistent edge alignment within containers
|
|
536
|
+
- Avoid orphaned or floating elements
|
|
537
|
+
|
|
538
|
+
### Spacing Consistency
|
|
539
|
+
- Always use existing gap/padding values from the design system
|
|
540
|
+
- Don't mix arbitrary spacing values - pick from the established scale
|
|
541
|
+
- Maintain consistent vertical rhythm between sections
|
|
542
|
+
|
|
543
|
+
### Color Usage
|
|
544
|
+
- Always use `$--variable` tokens, never hardcode hex/rgb values
|
|
545
|
+
- Ensure sufficient contrast for text readability
|
|
546
|
+
- Use semantic colors for their intended purpose (error for errors, etc.)
|
|
547
|
+
|
|
548
|
+
### Content Density
|
|
549
|
+
- Don't overcrowd - leave breathing room
|
|
550
|
+
- Cards should contain one primary idea
|
|
551
|
+
- Tables should have reasonable column counts (typically 4-7)
|
|
552
|
+
|
|
553
|
+
### Grounding Rules
|
|
554
|
+
- Get component list via `get_app_state` and then only read the specific components you need via `Print(Get(componentId))` in `execute`
|
|
555
|
+
- Verify with `TakeScreenshot` after major design operations
|
|
556
|
+
- Use existing components before creating custom frames
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Landing Page Design Guidelines
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
**Conversion intent.** Drive one action — sign up, buy, request a demo. Make every element move the visitor toward that action. Remove everything that doesn't.
|
|
8
|
+
|
|
9
|
+
**Transformation over features.** Show the outcome, not the tool. Visitors buy a better version of themselves — show them who they become.
|
|
10
|
+
|
|
11
|
+
## Content
|
|
12
|
+
|
|
13
|
+
**Hero = the entire pitch compressed.** Compress the entire pitch into the first screen. Communicate what this is, why it matters, and what to do next. Use one idea, one headline, one call to action.
|
|
14
|
+
|
|
15
|
+
**Headline strength.** Lead with the strongest headline form. Transformation ("Finally feel in control") beats outcome ("Ship faster"), which beats benefit ("Write 10x faster"), which beats feature ("AI writing assistant").
|
|
16
|
+
|
|
17
|
+
**Imagery.** Show scenes from the visitor's future — people in the outcome state, not product screenshots in isolation. Always add a contrast treatment when placing text over images — overlay, shadow, or text in its own container. Never stretch or distort.
|
|
18
|
+
|
|
19
|
+
**Color for conversion.** Make the CTA the most prominent element on the page. Reserve the accent color for actions — don't dilute it across decorative elements. Ensure sufficient contrast for all text against its background.
|
|
20
|
+
|
|
21
|
+
## Structure
|
|
22
|
+
|
|
23
|
+
**Composition.** Pick one alignment axis per section — never mix left-aligned and centered content in the same section. Group related elements tightly, separate groups generously — proximity signals relationship.
|
|
24
|
+
|
|
25
|
+
**Rhythm.** Alternate between text-heavy and visual sections. Never stack sections with similar density. Use dark sections for credibility and depth, light sections for explanation and detail.
|
|
26
|
+
|
|
27
|
+
**Typography.** Never set body text below 14px. Choose a typographic scale with clear size jumps between hierarchy levels. Don't center-align more than 2-3 lines of text. Keep body copy line length to 50-75 characters.
|
|
28
|
+
|
|
29
|
+
**Section flow.** Build a narrative arc — promise, proof, action. Move from what it is, to why it works, to why you should trust it, to what to do next.
|
|
30
|
+
|
|
31
|
+
**Content before visuals.** Define the narrative and messaging before making visual decisions. Let the words drive the design, not the other way around.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# MOBILE APP SCREEN COMPOSITION
|
|
2
|
+
|
|
3
|
+
You design mobile app screens that feel modern, premium, and easy to scan. Prioritize clarity, hierarchy, and touch ergonomics.
|
|
4
|
+
|
|
5
|
+
## Layout Structure
|
|
6
|
+
|
|
7
|
+
Ensure the screen is vertically resized to fit the content. The screen should not match the real device height. Use a vertical layout with fit_content(844) height.
|
|
8
|
+
Start with the status bar and the tab bar first, only then fill in the content.
|
|
9
|
+
|
|
10
|
+
1. **Status Bar** — OS chrome (time, signal, battery). Height 62 px, content vertically centered, "Inter" font. Never place app UI behind it.
|
|
11
|
+
2. **Content Wrapper** — all app content lives in one wrapper with consistent left/right padding (16–20 px) applied once. Use gap-based vertical spacing between sections (24–32 px major, 12–16 px related items).
|
|
12
|
+
3. **Tab Bar** (optional) — a bottom-anchored, capsule-ended tab bar for switching between top-level destinations.
|
|
13
|
+
|
|
14
|
+
## Tab Bar
|
|
15
|
+
|
|
16
|
+
A bottom-anchored, capsule-ended tab bar — the iOS "Liquid Glass" look. Use for 3–5 top-level destinations.
|
|
17
|
+
|
|
18
|
+
- **Bar**: floats inset from the screen edges — ~16 px on the sides, ~12 px above the bottom — never flush. ~56 px tall, corner radius = half the height (true capsule ends), ~6 px inner padding. Frosted glass — fill at 70% opacity, soft shadow.
|
|
19
|
+
- **Items**: rounded icon (~22 px) above a label (10 px, sentence case), centered.
|
|
20
|
+
- **Selected**: accent-tinted icon + label on a soft capsule highlight, filled icon variant. Inactive: muted neutral, unfilled.
|
|
21
|
+
- Make it the last item in the screen's vertical stack — never absolutely positioned. Give that stack ~12 px bottom padding so the capsule clears the edge.
|
|
22
|
+
|
|
23
|
+
## Rules
|
|
24
|
+
|
|
25
|
+
- One primary intent per screen. Everything else is subordinate.
|
|
26
|
+
- The first 1–2 elements answer "where am I" and "what can I do here".
|
|
27
|
+
- Use the same title font size on every screen — titles must look uniform app-wide.
|
|
28
|
+
- Keep key actions reachable in the lower half for one-handed use.
|
|
29
|
+
- Touch targets need comfortable hit areas.
|
|
30
|
+
- Let the wrapper handle horizontal padding — don't add per-section padding.
|
|
31
|
+
- Use the wrapper's gap for spacing and bottom padding (same value as gap) for empty space — never spacer elements.
|