@mindstudio-ai/remy 0.1.40 → 0.1.41
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/dist/headless.js +240 -192
- package/dist/index.js +282 -223
- package/dist/prompt/compiled/design.md +65 -216
- package/dist/prompt/compiled/interfaces.md +3 -1
- package/dist/prompt/compiled/msfm.md +2 -2
- package/dist/prompt/static/team.md +1 -1
- package/dist/subagents/designExpert/data/sources/dev/index.html +105 -0
- package/dist/subagents/designExpert/data/sources/dev/serve.mjs +45 -0
- package/dist/subagents/designExpert/data/sources/fonts.json +1 -153
- package/dist/subagents/designExpert/data/sources/ui_inspiration.json +81 -81
- package/dist/subagents/designExpert/data/sources/ui_inspiration_compiled.json +162 -162
- package/dist/subagents/designExpert/prompt.md +9 -17
- package/dist/subagents/designExpert/prompts/color.md +1 -1
- package/dist/subagents/designExpert/prompts/components.md +0 -7
- package/dist/subagents/designExpert/prompts/frontend-design-notes.md +1 -0
- package/dist/subagents/designExpert/prompts/identity.md +2 -9
- package/dist/subagents/designExpert/prompts/images.md +2 -8
- package/dist/subagents/designExpert/prompts/instructions.md +22 -0
- package/dist/subagents/designExpert/prompts/layout.md +1 -1
- package/dist/subagents/designExpert/prompts/ui-patterns.md +3 -1
- package/dist/subagents/designExpert/tools/images/enhance-image-prompt.md +48 -0
- package/package.json +1 -1
- package/dist/prompt/sources/frontend-design-notes.md +0 -153
- package/dist/prompt/sources/media-cdn.md +0 -46
|
@@ -4,13 +4,11 @@ Design standards for web interfaces in MindStudio apps.
|
|
|
4
4
|
|
|
5
5
|
## Quality Bar
|
|
6
6
|
|
|
7
|
-
Every interface must feel like a polished, shipping product — not a
|
|
8
|
-
prototype, not a starter template, not a homework assignment. The standard
|
|
9
|
-
is iOS and Apple's bundled iOS apps, Notion, Stripe. If it wouldn't look
|
|
10
|
-
good on Dribbble, Behance, or Mobbin, it's not done.
|
|
7
|
+
Every interface must feel like a polished, shipping product — not a prototype, not a starter template, not a homework assignment. If it wouldn't look good on Dribbble, Behance, or Mobbin, it's not done.
|
|
11
8
|
|
|
12
|
-
MindStudio apps are end-user products. The interface is the product. Users
|
|
13
|
-
|
|
9
|
+
MindStudio apps are end-user products. The interface is the product. Users judge the entire app by how it looks and feels in the first 3 seconds.
|
|
10
|
+
|
|
11
|
+
For questions about design, layout, UI, interfaces, components, and anything else, ask the design expert! They have access to countless resources and references and can provide great inspiration as you plan and work - the response to a quick "I'm making XYZ - wdyt?" can be such a powerful input to your thinking!
|
|
14
12
|
|
|
15
13
|
## Design System from the Spec
|
|
16
14
|
|
|
@@ -20,232 +18,83 @@ Set up a lightweight theme layer early (CSS variables or a small tokens file) so
|
|
|
20
18
|
|
|
21
19
|
**When brand spec files are present, always use the defined fonts and colors in generated code.** Do not pick your own fonts or colors when the spec defines them. Reference colors semantically (as CSS variables or named constants) rather than scattering raw hex values through the codebase.
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
description: Primary background and dark surfaces
|
|
31
|
-
Charcoal:
|
|
32
|
-
value: "#1C1C1E"
|
|
33
|
-
description: Elevated surfaces and containers
|
|
34
|
-
Snow:
|
|
35
|
-
value: "#F5F5F7"
|
|
36
|
-
description: Primary text and foreground elements
|
|
37
|
-
Smoke:
|
|
38
|
-
value: "#86868B"
|
|
39
|
-
description: Secondary text and supporting content
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Derive additional implementation colors (borders, focus states, hover states, disabled states) from the brand palette rather than expecting them to be specified.
|
|
43
|
-
|
|
44
|
-
### Typography block format
|
|
45
|
-
|
|
46
|
-
A `` ```typography `` fenced block in a `type: design/typography` spec file declares fonts (with source URLs) and one or two anchor styles (typically Display and Body). Styles can include an optional `case` field (`uppercase`, `lowercase`, `capitalize`) for text-transform. Derive additional styles (labels, buttons, captions, overlines) from these anchors:
|
|
47
|
-
|
|
48
|
-
```typography
|
|
49
|
-
fonts:
|
|
50
|
-
Satoshi:
|
|
51
|
-
src: https://api.fontshare.com/v2/css?f[]=satoshi@400,500,600,700&display=swap
|
|
52
|
-
Clash Grotesk:
|
|
53
|
-
src: https://api.fontshare.com/v2/css?f[]=clash-grotesk@400,500,600&display=swap
|
|
54
|
-
|
|
55
|
-
styles:
|
|
56
|
-
Display:
|
|
57
|
-
font: Satoshi
|
|
58
|
-
size: 40px
|
|
59
|
-
weight: 600
|
|
60
|
-
letterSpacing: -0.03em
|
|
61
|
-
lineHeight: 1.1
|
|
62
|
-
case: uppercase
|
|
63
|
-
description: Page titles and hero text
|
|
64
|
-
Body:
|
|
65
|
-
font: Satoshi
|
|
66
|
-
size: 16px
|
|
67
|
-
weight: 400
|
|
68
|
-
lineHeight: 1.5
|
|
69
|
-
description: Default reading text
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Be Distinctive
|
|
73
|
-
|
|
74
|
-
AI-generated interfaces tend to converge on the same generic look: safe
|
|
75
|
-
fonts, timid colors, predictable layouts. Fight this actively. Every
|
|
76
|
-
interface should have character and intentionality — it should look like a
|
|
77
|
-
designer made deliberate choices, not like it was generated from a template.
|
|
78
|
-
|
|
79
|
-
**Typography must be a conscious choice.** Pick fonts that are beautiful,
|
|
80
|
-
distinctive, and appropriate for the product's personality. Generic system
|
|
81
|
-
fonts and overused defaults make everything look the same. Typography is
|
|
82
|
-
the single fastest way to give an interface identity.
|
|
83
|
-
|
|
84
|
-
**Commit to a color palette.** One or two dominant colors with sharp accents
|
|
85
|
-
outperform timid, evenly-distributed palettes. Draw inspiration from the
|
|
86
|
-
app's domain — a finance tool might use deep greens and golds; a creative
|
|
87
|
-
tool might use bold, saturated primaries. The palette should feel intentional
|
|
88
|
-
and owned, not randomly selected.
|
|
89
|
-
|
|
90
|
-
**Backgrounds create atmosphere.** Solid white or solid gray is the safe
|
|
91
|
-
default and the enemy of distinctiveness. Layer subtle gradients, use warm
|
|
92
|
-
or cool tints, add geometric patterns or contextual textures. The background
|
|
93
|
-
sets the mood before the user reads a single word.
|
|
94
|
-
|
|
95
|
-
**Layouts should surprise.** Avoid the predictable patterns: three equal
|
|
96
|
-
boxes with icons, centered hero with subtitle, generic card grid. Use
|
|
97
|
-
asymmetry, varied column widths, creative negative space, unexpected
|
|
98
|
-
compositions. Study Behance and Mobbin for layout inspiration. Every screen
|
|
99
|
-
should feel considered, not generated.
|
|
100
|
-
|
|
101
|
-
## App-Like, Not Web-Page-Like
|
|
102
|
-
|
|
103
|
-
Interfaces run fullscreen in the user's browser or a wrapped webview mobile
|
|
104
|
-
app. They should feel like native apps, not websites.
|
|
105
|
-
|
|
106
|
-
- **No long scrolling pages.** Use structured layouts: cards, split panes,
|
|
107
|
-
steppers, tabs, grouped sections that fit the viewport. The interface
|
|
108
|
-
should feel like a single-purpose tool, not a document.
|
|
109
|
-
- **On mobile**, scrolling may be necessary, but use sticky headers, fixed
|
|
110
|
-
CTAs, and anchored navigation to keep key actions within reach.
|
|
111
|
-
- Think of every screen as something the user opens, uses, and closes —
|
|
112
|
-
not something they read.
|
|
113
|
-
|
|
114
|
-
## Visual Design
|
|
115
|
-
|
|
116
|
-
- **Typography:** Strong hierarchy — clear distinction between headings,
|
|
117
|
-
body, labels, and captions. Use weight and size, not just color, to
|
|
118
|
-
create hierarchy. Choose fonts that elevate the interface and give it
|
|
119
|
-
personality.
|
|
120
|
-
- **Color:** Clean, vibrant, intentional. Use color to communicate state
|
|
121
|
-
and guide attention, not to decorate. Commit to a direction — bold and
|
|
122
|
-
saturated, or muted and editorial — and follow through consistently.
|
|
123
|
-
- **Spacing:** Consistent and generous. Padding and margins should be
|
|
124
|
-
uniform across all components — nothing should feel cramped or uneven.
|
|
125
|
-
White space is a feature, not wasted space.
|
|
126
|
-
- **Components:** Every component (buttons, inputs, cards, modals, lists)
|
|
127
|
-
should look like it belongs to the same design system. Consistent border
|
|
128
|
-
radii, consistent shadows, consistent padding. If two buttons on the
|
|
129
|
-
same screen look different for no reason, that's a bug.
|
|
130
|
-
|
|
131
|
-
## Animation
|
|
132
|
-
|
|
133
|
-
Use motion to make interactions feel polished, not to show off. Focus on
|
|
134
|
-
high-impact moments: a well-orchestrated page load with staggered reveals
|
|
135
|
-
creates more delight than scattered micro-interactions everywhere.
|
|
136
|
-
|
|
137
|
-
- Transitions between states should be smooth but fast.
|
|
138
|
-
- Streaming content should flow into containers that grow naturally without
|
|
139
|
-
pushing sibling elements around.
|
|
140
|
-
- No parallax, no cheesy scroll effects, no bounce/elastic easing, no
|
|
141
|
-
gratuitous loading animations.
|
|
21
|
+
## Important: Build Apps, Not Web Pages
|
|
22
|
+
|
|
23
|
+
Interfaces run fullscreen in the user's browser or a wrapped webview mobile app. They must feel like native Mac or iOS apps, not websites.
|
|
24
|
+
|
|
25
|
+
- **No long scrolling pages.** Use structured layouts: cards, split panes, steppers, tabs, grouped sections that fit the viewport. The interface should feel like an award winning iOS or macOS app, not a document.
|
|
26
|
+
- **On mobile**, scrolling may be necessary, but use sticky headers, fixed CTAs, and anchored navigation to keep key actions within reach.
|
|
27
|
+
- Think of every screen as something the user opens, uses, and closes — not something they read.
|
|
142
28
|
|
|
143
29
|
## Layout Stability
|
|
144
30
|
|
|
145
|
-
Layout shift is never acceptable. Elements jumping around as content loads
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
-
|
|
149
|
-
|
|
150
|
-
-
|
|
151
|
-
|
|
152
|
-
-
|
|
153
|
-
changing container size.
|
|
154
|
-
- Buttons must not change size during loading states. Use a fixed width or
|
|
155
|
-
`min-width`, and swap the label for a spinner or short text that fits the
|
|
156
|
-
same space. "Submit" becoming "Submitting..." should not make the button
|
|
157
|
-
wider and push adjacent elements around.
|
|
158
|
-
- Conditional UI should use opacity/overlay transitions, not insertion into
|
|
159
|
-
flow that displaces existing content.
|
|
31
|
+
Layout shift is never acceptable. Elements jumping around as content loads or streams in makes an interface feel broken.
|
|
32
|
+
|
|
33
|
+
- Reserve space for content that hasn't arrived yet. Use fixed/min-height containers, skeletons, or aspect-ratio boxes.
|
|
34
|
+
- Images must always have explicit dimensions so the browser reserves space before the image loads.
|
|
35
|
+
- Loading-to-loaded transitions should swap content in-place without changing container size.
|
|
36
|
+
- Buttons must not change size during loading states. Use a fixed width or `min-width`, and swap the label for a spinner or short text that fits the same space. "Submit" becoming "Submitting..." should not make the button wider and push adjacent elements around.
|
|
37
|
+
- Conditional UI should use opacity/overlay transitions, not insertion into flow that displaces existing content.
|
|
38
|
+
- This is especially important to keep in mind when building things that display AI generated text, especially if the text streams in. Make sure to never shift layout because of streaming AI text.
|
|
160
39
|
|
|
161
40
|
## Responsive Design
|
|
162
41
|
|
|
163
|
-
Every interface must work on both desktop and mobile.
|
|
42
|
+
Every interface must work on both desktop and mobile. Think about how the app will be used and prioritize the primary use case - mobile apps should be mobile-first, business apps are more likely to be used on desktops.
|
|
164
43
|
|
|
165
44
|
- Use the full viewport. Backgrounds should extend to edges.
|
|
166
|
-
- On desktop, use the space — multi-column layouts, sidebars, spacious
|
|
167
|
-
cards. Avoid narrow centered columns with empty gutters on a wide screen.
|
|
45
|
+
- On desktop, use the space — multi-column layouts, sidebars, spacious cards. Avoid narrow centered columns with empty gutters on a wide screen.
|
|
168
46
|
- On mobile, stack gracefully. Prioritize content and actions.
|
|
169
|
-
- Test at both extremes. A layout that only looks good at one breakpoint
|
|
170
|
-
|
|
47
|
+
- Test at both extremes. A layout that only looks good at one breakpoint is not done.
|
|
48
|
+
- When the app is primarily mobile (e.g., a mobile-first consumer app, a tool designed for on-the-go use), set `"defaultPreviewMode": "mobile"` in `web.json` so the editor previews in a mobile viewport by default.
|
|
171
49
|
|
|
172
50
|
## Forms
|
|
173
51
|
|
|
174
52
|
Forms should feel like interactions, not paperwork.
|
|
175
53
|
|
|
176
54
|
- Group related fields visually. Use cards or sections, not a flat list.
|
|
177
|
-
- Inline validation — show errors as the user types, not after submit.
|
|
178
|
-
|
|
179
|
-
- Loading states after submission. Always indicate that something is
|
|
180
|
-
happening.
|
|
55
|
+
- Inline validation — show errors as the user types, not after submit. Validation must never introduce layout shift.
|
|
56
|
+
- Loading states after submission. Always indicate that something is happening.
|
|
181
57
|
- Disabled states should be visually distinct but not jarring.
|
|
182
|
-
- Even data entry can be beautiful. Pay attention to alignment, padding,
|
|
183
|
-
|
|
58
|
+
- Even data entry can be beautiful. Pay attention to alignment, padding, and spacing. Consistency is key.
|
|
59
|
+
|
|
60
|
+
#### Form Elements
|
|
61
|
+
|
|
62
|
+
- When loading elements dynamically, make sure the experience isn't janky (e.g., user selects something from a dropdown and suddenly a bunch of thigns snap in, or user loads a form and then after 500ms once a network call resolves the user sees a jump for a new element to appear)
|
|
63
|
+
|
|
64
|
+
### Placeholders
|
|
65
|
+
|
|
66
|
+
- Always use icon placeholders for things like empty user profile pictures and other empty images.
|
|
67
|
+
- Create beautiful empty states by using icons alongside labels. Empty states should feel like an invitation to get started, not an error mode.
|
|
68
|
+
|
|
69
|
+
#### Loading states
|
|
70
|
+
|
|
71
|
+
Buttons should use a small animated spinner during loading, not text labels like "Loading..." or "Submitting...". The `loader-2` tabler icon with a CSS spin animation is a common pattern. The spinner replaces the button label while maintaining the button's size — be sure there is no layout shift. Recommend the developer implement this as a reusable pattern early.
|
|
184
72
|
|
|
185
73
|
## Data Fetching and Updates
|
|
186
74
|
|
|
187
|
-
The UI should feel instant. Never make the user wait for a server round-trip
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
- **
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
-
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
|
|
209
|
-
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
These are the hallmarks of generic AI-generated interfaces. Every one of
|
|
215
|
-
them makes an interface look like it was auto-generated rather than designed.
|
|
216
|
-
|
|
217
|
-
- **Generic fonts.** Overused defaults that strip away all personality.
|
|
218
|
-
Instead: pick a distinctive Google Font that fits the app's character.
|
|
219
|
-
- **Purple or indigo anything.** Purple gradients, purple buttons, purple
|
|
220
|
-
accents. This is the #1 AI-generated aesthetic cliché. Instead: use
|
|
221
|
-
a color palette that fits the app's domain — greens for finance, warm
|
|
222
|
-
neutrals for productivity, bold primaries for creative tools, or just
|
|
223
|
-
confident grayscale.
|
|
224
|
-
- **Colored left-border callout boxes.** Rounded divs with a thick colored
|
|
225
|
-
`border-left` — the generic "info card" pattern. Instead: use typography,
|
|
226
|
-
spacing, and background tints to create hierarchy. If you need to call
|
|
227
|
-
something out, use a full subtle background or a top border.
|
|
228
|
-
- **Three equal boxes with icons.** The default AI landing page layout.
|
|
229
|
-
Instead: use asymmetric layouts, varied column widths, or a single
|
|
230
|
-
focused content area.
|
|
231
|
-
- **Timid color palettes.** Evenly distributed, non-committal colors.
|
|
232
|
-
Instead: one or two dominant colors with sharp accents. Commit to a
|
|
233
|
-
direction.
|
|
234
|
-
- **Card-heavy nested layouts.** Cards inside cards, everything boxed.
|
|
235
|
-
Instead: use space, typography, and dividers to create hierarchy without
|
|
236
|
-
extra containers.
|
|
237
|
-
- **Inconsistent spacing.** 12px here, 20px there, 8px somewhere else.
|
|
238
|
-
Instead: define a spacing scale (4/8/12/16/24/32/48/64) and use it
|
|
239
|
-
everywhere.
|
|
240
|
-
- **Components from different visual languages.** Rounded buttons next to
|
|
241
|
-
square inputs, shadows mixed with flat design. Instead: pick one system
|
|
242
|
-
and apply it consistently.
|
|
243
|
-
- **Long scrolling forms with no visual grouping.** Instead: group fields
|
|
244
|
-
into sections with clear headings, cards, or stepped flows.
|
|
245
|
-
- **Cramped layouts.** Text pressed against edges, no room to breathe.
|
|
246
|
-
Instead: generous padding, comfortable margins, let the content float.
|
|
247
|
-
- **Loading states that are just a centered spinner on a blank page.**
|
|
248
|
-
Instead: use skeletons that mirror the layout, or keep the existing
|
|
249
|
-
structure visible with a subtle loading indicator.
|
|
250
|
-
- **Any interface where the first reaction is "this looks like a demo" or
|
|
251
|
-
"this looks like it was made with a website builder."**
|
|
75
|
+
The UI should feel instant. Never make the user wait for a server round-trip to see the result of their own action.
|
|
76
|
+
|
|
77
|
+
- **Optimistic updates.** When a user adds a row, toggles a setting, or submits a form, update the UI immediately and let the backend confirm in the background. If the backend fails, revert and show an error.
|
|
78
|
+
- **Use SWR for data fetching** (`useSWR` from the `swr` package). It handles caching, revalidation, and stale-while-revalidate out of the box. Prefer SWR over manual `useEffect` + `useState` fetch patterns.
|
|
79
|
+
- **Mutate after actions.** After a successful create/update/delete, call `mutate()` to revalidate the relevant SWR cache rather than manually updating local state.
|
|
80
|
+
- **Skeleton loading.** Show skeletons that mirror the layout on initial load. Never show a blank page or centered spinner while data is loading.
|
|
81
|
+
|
|
82
|
+
## FTUE
|
|
83
|
+
|
|
84
|
+
All interactive apps must be intuitive and easy to use. Form elements must be well-labelled. Complex interfaces should have descriptions or tooltips when helpful. Complex apps benefit from a beautiful simple onboarding modal on first use or a simple click tour. Even if the app is intuitive and easy to use, users showing up for the first time might still be overwhelmed or confused, and we have an opportunity to set expectations, provide context, and make the user confident as they use our product. Don't neglect this.
|
|
85
|
+
|
|
86
|
+
## What to Actively Avoid At All Costs
|
|
87
|
+
|
|
88
|
+
- **Avoid generic fonts.** Overused defaults that strip away all personality. Instead: pick a distinctive Google Font that fits the app's character.
|
|
89
|
+
- **Avoid purple or indigo anything.** Purple gradients, purple buttons, purple accents are overused. The user will be dismissive of our designs if they come out looking purple or indigo.
|
|
90
|
+
- **Avoid colored left-border callout boxes.** Rounded divs with a thick colored `border-left` — the generic "info card" pattern. Instead: use typography, spacing, and background tints to create hierarchy. If you need to call something out, use a full subtle background or a top border.
|
|
91
|
+
- **Avoid three equal boxes with icons.** The default AI landing page layout. Instead: use asymmetric layouts, varied column widths, or a single focused content area.
|
|
92
|
+
- **Avoid timid color palettes.** Evenly distributed, non-committal colors. Instead: one or two dominant colors with sharp accents. Commit to a direction.
|
|
93
|
+
- **Avoid card-heavy nested layouts.** Cards inside cards, everything boxed. Instead: use space, typography, and dividers to create hierarchy without extra containers.
|
|
94
|
+
- **Avoid inconsistent spacing.** 12px here, 20px there, 8px somewhere else. Instead: define a spacing scale (4/8/12/16/24/32/48/64) and use it everywhere.
|
|
95
|
+
- **Avoid components from different visual languages.** Rounded buttons next to square inputs, shadows mixed with flat design. Instead: pick one system and apply it consistently.
|
|
96
|
+
- **Avoid long scrolling forms with no visual grouping.** Instead: group fields into sections with clear headings, cards, or stepped flows.
|
|
97
|
+
- **Avoid cramped layouts.** Text pressed against edges, no room to breathe. Instead: generous padding, comfortable margins, let the content float.
|
|
98
|
+
- **Avoid loading states that are just a centered spinner on a blank page.** Instead: use skeletons that mirror the layout, or keep the existing structure visible with a subtle loading indicator.
|
|
99
|
+
|
|
100
|
+
Most importantly: **Avoid any interface where the first reaction is "this looks like a demo" or "this looks like it was made with a website builder."**
|
|
@@ -27,7 +27,8 @@ dist/interfaces/web/
|
|
|
27
27
|
```json
|
|
28
28
|
{
|
|
29
29
|
"devPort": 5173,
|
|
30
|
-
"devCommand": "npm run dev"
|
|
30
|
+
"devCommand": "npm run dev",
|
|
31
|
+
"defaultPreviewMode": "desktop"
|
|
31
32
|
}
|
|
32
33
|
```
|
|
33
34
|
|
|
@@ -35,6 +36,7 @@ dist/interfaces/web/
|
|
|
35
36
|
|-------|------|---------|-------------|
|
|
36
37
|
| `devPort` | `number` | `5173` | Port for the dev server |
|
|
37
38
|
| `devCommand` | `string` | `"npm run dev"` | Command to start the dev server |
|
|
39
|
+
| `defaultPreviewMode` | `"desktop"` \| `"mobile"` | `"desktop"` | Default preview viewport in the editor. Set to `"mobile"` for mobile-first apps. |
|
|
38
40
|
|
|
39
41
|
### Frontend SDK
|
|
40
42
|
|
|
@@ -209,7 +209,7 @@ The card system generates images using the brand's typography and color
|
|
|
209
209
|
palette, creating shareable assets that feel native to the app's identity.
|
|
210
210
|
|
|
211
211
|
~~~
|
|
212
|
-
Use generateImage
|
|
212
|
+
Use generateImage to create styled cards. Card template
|
|
213
213
|
applies brand typography and colors from the spec. Export as PNG via
|
|
214
214
|
CDN transform at 2x resolution. Social sharing via Web Share API with
|
|
215
215
|
clipboard fallback for unsupported browsers.
|
|
@@ -217,6 +217,6 @@ clipboard fallback for unsupported browsers.
|
|
|
217
217
|
|
|
218
218
|
## History
|
|
219
219
|
|
|
220
|
-
- **2026-03-22** — Built card generation using generateImage
|
|
220
|
+
- **2026-03-22** — Built card generation using generateImage.
|
|
221
221
|
Added share button to haiku detail view.
|
|
222
222
|
```
|
|
@@ -10,7 +10,7 @@ Note: when you talk about the team to the user, refer to them by their name or a
|
|
|
10
10
|
|
|
11
11
|
Your designer. Consult for any visual decision — choosing a color, picking fonts, proposing a layout, soucing images, reviewing whether something looks good. Not just during intake or big design moments. If you're about to write CSS and you're not sure about a color, ask. If you just built a page and want a gut check, ask the designer to take a quick look. If the user says "I don't like how this looks," ask the design expert what to change rather than guessing yourself, or if they say "I want a different image," that's the designer's problem, not yours. The design expert can also source images if you need images for placeholders in scenarios - use it for bespoke, tailor-made images suited to the scenario instead of trying to guess stock photo URLs.
|
|
12
12
|
|
|
13
|
-
The design expert cannot see your conversation with the user, so include all relevant context and requirements in your task. It can take screenshots of the app preview on its own — just ask it to review what's been built.
|
|
13
|
+
The design expert cannot see your conversation with the user, so include all relevant context and requirements in your task. It also can not see its own conversation history, so if you want an audit you need to provide the exact values to check, or any other necessary context for it to do its job. It can take screenshots of the app preview on its own — just ask it to review what's been built.
|
|
14
14
|
|
|
15
15
|
Returns concrete resources: hex values, font names with CSS URLs, image URLs, layout descriptions. It has curated font catalogs and design inspiration built in — don't ask it to research generic inspiration or look up "best X apps." Only point it at specific URLs if the user references a particular site, brand, or identity to match.
|
|
16
16
|
|
|
@@ -534,6 +534,43 @@ function useInspiration() {
|
|
|
534
534
|
return { data, loading, error, addImage, deleteImage, analyze, dedup, reload };
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
function useUiInspiration() {
|
|
538
|
+
const [data, setData] = useState(null);
|
|
539
|
+
const [loading, setLoading] = useState(true);
|
|
540
|
+
const [error, setError] = useState(null);
|
|
541
|
+
|
|
542
|
+
const reload = useCallback(async () => {
|
|
543
|
+
try {
|
|
544
|
+
setLoading(true);
|
|
545
|
+
const d = await api('/api/ui-inspiration');
|
|
546
|
+
setData(d);
|
|
547
|
+
setError(null);
|
|
548
|
+
} catch (e) {
|
|
549
|
+
setError(e.message);
|
|
550
|
+
} finally {
|
|
551
|
+
setLoading(false);
|
|
552
|
+
}
|
|
553
|
+
}, []);
|
|
554
|
+
|
|
555
|
+
useEffect(() => { reload(); }, [reload]);
|
|
556
|
+
|
|
557
|
+
const addScreen = useCallback(async (entry) => {
|
|
558
|
+
await api('/api/ui-inspiration', { method: 'POST', body: entry });
|
|
559
|
+
await reload();
|
|
560
|
+
}, [reload]);
|
|
561
|
+
|
|
562
|
+
const deleteScreen = useCallback(async (index) => {
|
|
563
|
+
await api('/api/ui-inspiration/' + index, { method: 'DELETE' });
|
|
564
|
+
await reload();
|
|
565
|
+
}, [reload]);
|
|
566
|
+
|
|
567
|
+
const analyze = useCallback(async (url) => {
|
|
568
|
+
return api('/api/ui-inspiration/analyze', { method: 'POST', body: { url } });
|
|
569
|
+
}, []);
|
|
570
|
+
|
|
571
|
+
return { data, loading, error, addScreen, deleteScreen, analyze, reload };
|
|
572
|
+
}
|
|
573
|
+
|
|
537
574
|
// ---------------------------------------------------------------------------
|
|
538
575
|
// Toast
|
|
539
576
|
// ---------------------------------------------------------------------------
|
|
@@ -862,6 +899,71 @@ function InspirationTab({ inspiration }) {
|
|
|
862
899
|
`;
|
|
863
900
|
}
|
|
864
901
|
|
|
902
|
+
function UiInspirationDetail({ screen, index, onDelete }) {
|
|
903
|
+
if (!screen) return html`<div class="inspiration-empty">Select a screen</div>`;
|
|
904
|
+
|
|
905
|
+
return html`
|
|
906
|
+
<div class="inspiration-detail">
|
|
907
|
+
<img class="inspiration-detail-img" src=${screen.url} alt="UI reference" />
|
|
908
|
+
<div class="inspiration-detail-body">
|
|
909
|
+
<div class="inspiration-detail-header">
|
|
910
|
+
<span>#${index}</span>
|
|
911
|
+
<button class="btn-danger" onClick=${() => onDelete(index)} title="Delete">✕</button>
|
|
912
|
+
</div>
|
|
913
|
+
<div class="inspiration-analysis"
|
|
914
|
+
dangerouslySetInnerHTML=${{ __html: renderAnalysis(screen.analysis) }}
|
|
915
|
+
/>
|
|
916
|
+
</div>
|
|
917
|
+
</div>
|
|
918
|
+
`;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
function UiInspirationTab({ uiInspiration }) {
|
|
922
|
+
const [selected, setSelected] = useState(0);
|
|
923
|
+
const sidebarRef = { current: null };
|
|
924
|
+
|
|
925
|
+
if (uiInspiration.loading) return html`<div class="loading"><span class="spinner"></span></div>`;
|
|
926
|
+
if (uiInspiration.error) return html`<div class="loading" style="color:#e00">Error: ${uiInspiration.error}</div>`;
|
|
927
|
+
if (!uiInspiration.data) return null;
|
|
928
|
+
|
|
929
|
+
const screens = uiInspiration.data.screens;
|
|
930
|
+
const current = screens[selected] || null;
|
|
931
|
+
|
|
932
|
+
const select = (i) => {
|
|
933
|
+
const clamped = Math.max(0, Math.min(i, screens.length - 1));
|
|
934
|
+
setSelected(clamped);
|
|
935
|
+
sidebarRef.current?.children[clamped]?.scrollIntoView({ block: 'center' });
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
const onKey = (e) => {
|
|
939
|
+
if (e.key === 'ArrowDown') { e.preventDefault(); select(selected + 1); }
|
|
940
|
+
if (e.key === 'ArrowUp') { e.preventDefault(); select(selected - 1); }
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
const handleDelete = async (index) => {
|
|
944
|
+
const nextIndex = index >= screens.length - 1 ? Math.max(0, index - 1) : index;
|
|
945
|
+
setSelected(nextIndex);
|
|
946
|
+
await uiInspiration.deleteScreen(index);
|
|
947
|
+
};
|
|
948
|
+
|
|
949
|
+
return html`
|
|
950
|
+
<div class="inspiration-layout">
|
|
951
|
+
<div class="inspiration-sidebar" ref=${el => sidebarRef.current = el} tabindex="0" onKeyDown=${onKey}>
|
|
952
|
+
${screens.map((s, i) => html`
|
|
953
|
+
<img
|
|
954
|
+
key=${i}
|
|
955
|
+
class="inspiration-thumb ${i === selected ? 'active' : ''}"
|
|
956
|
+
src=${s.url}
|
|
957
|
+
loading="lazy"
|
|
958
|
+
onClick=${() => select(i)}
|
|
959
|
+
/>
|
|
960
|
+
`)}
|
|
961
|
+
</div>
|
|
962
|
+
<${UiInspirationDetail} screen=${current} index=${selected} onDelete=${handleDelete} />
|
|
963
|
+
</div>
|
|
964
|
+
`;
|
|
965
|
+
}
|
|
966
|
+
|
|
865
967
|
// ---------------------------------------------------------------------------
|
|
866
968
|
// App
|
|
867
969
|
// ---------------------------------------------------------------------------
|
|
@@ -870,6 +972,7 @@ function App() {
|
|
|
870
972
|
const [tab, setTab] = useState('fonts');
|
|
871
973
|
const fonts = useFonts();
|
|
872
974
|
const inspiration = useInspiration();
|
|
975
|
+
const uiInspiration = useUiInspiration();
|
|
873
976
|
const toast = useToast();
|
|
874
977
|
|
|
875
978
|
return html`
|
|
@@ -878,6 +981,7 @@ function App() {
|
|
|
878
981
|
<button class="tab ${tab === 'fonts' ? 'active' : ''}" onClick=${() => setTab('fonts')}>Fonts${fonts.data ? ` (${fonts.data.fonts.length})` : ''}</button>
|
|
879
982
|
<button class="tab ${tab === 'pairings' ? 'active' : ''}" onClick=${() => setTab('pairings')}>Pairings${fonts.data ? ` (${fonts.data.pairings.length})` : ''}</button>
|
|
880
983
|
<button class="tab ${tab === 'inspiration' ? 'active' : ''}" onClick=${() => setTab('inspiration')}>Inspiration${inspiration.data ? ` (${inspiration.data.images.length})` : ''}</button>
|
|
984
|
+
<button class="tab ${tab === 'ui' ? 'active' : ''}" onClick=${() => setTab('ui')}>UI Patterns${uiInspiration.data ? ` (${uiInspiration.data.screens.length})` : ''}</button>
|
|
881
985
|
</div>
|
|
882
986
|
${tab === 'inspiration' ? html`
|
|
883
987
|
<div style="margin-left: auto;">
|
|
@@ -888,6 +992,7 @@ function App() {
|
|
|
888
992
|
<div class="content">
|
|
889
993
|
${tab === 'fonts' ? html`<${FontsTab} fonts=${fonts} />`
|
|
890
994
|
: tab === 'pairings' ? html`<${PairingsTab} fonts=${fonts} />`
|
|
995
|
+
: tab === 'ui' ? html`<${UiInspirationTab} uiInspiration=${uiInspiration} />`
|
|
891
996
|
: html`<${InspirationTab} inspiration=${inspiration} />`
|
|
892
997
|
}
|
|
893
998
|
</div>
|
|
@@ -17,6 +17,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
17
17
|
const dataDir = join(__dirname, '..');
|
|
18
18
|
const fontsPath = join(dataDir, 'fonts.json');
|
|
19
19
|
const inspirationPath = join(dataDir, 'inspiration.json');
|
|
20
|
+
const uiInspirationPath = join(dataDir, 'ui_inspiration_compiled.json');
|
|
20
21
|
|
|
21
22
|
// ---------------------------------------------------------------------------
|
|
22
23
|
// JSON helpers
|
|
@@ -222,6 +223,50 @@ const server = createServer(async (req, res) => {
|
|
|
222
223
|
return json(res, { ok: true, removed, count: deduped.length });
|
|
223
224
|
}
|
|
224
225
|
|
|
226
|
+
// --- UI Inspiration API ---
|
|
227
|
+
|
|
228
|
+
if (path === '/api/ui-inspiration' && method === 'GET') {
|
|
229
|
+
return json(res, readJson(uiInspirationPath));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (path === '/api/ui-inspiration' && method === 'POST') {
|
|
233
|
+
const entry = await parseBody(req);
|
|
234
|
+
const data = readJson(uiInspirationPath);
|
|
235
|
+
data.screens.push(entry);
|
|
236
|
+
writeJson(uiInspirationPath, data);
|
|
237
|
+
return json(res, { ok: true, count: data.screens.length }, 201);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const uiInspirationDeleteMatch = path.match(/^\/api\/ui-inspiration\/(\d+)$/);
|
|
241
|
+
if (uiInspirationDeleteMatch && method === 'DELETE') {
|
|
242
|
+
const index = parseInt(uiInspirationDeleteMatch[1], 10);
|
|
243
|
+
const data = readJson(uiInspirationPath);
|
|
244
|
+
if (index < 0 || index >= data.screens.length) {
|
|
245
|
+
return error(res, 'Screen index out of range', 404);
|
|
246
|
+
}
|
|
247
|
+
data.screens.splice(index, 1);
|
|
248
|
+
writeJson(uiInspirationPath, data);
|
|
249
|
+
return json(res, { ok: true, count: data.screens.length });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (path === '/api/ui-inspiration/analyze' && method === 'POST') {
|
|
253
|
+
const { url } = await parseBody(req);
|
|
254
|
+
if (!url) return error(res, 'url is required');
|
|
255
|
+
|
|
256
|
+
const promptFile = join(dataDir, 'prompts', 'ui-analysis.md');
|
|
257
|
+
const prompt = readFileSync(promptFile, 'utf-8').trim();
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const result = execSync(
|
|
261
|
+
`mindstudio analyze-image --prompt ${JSON.stringify(prompt)} --image-url ${JSON.stringify(url)} --output-key analysis --no-meta`,
|
|
262
|
+
{ encoding: 'utf-8', timeout: 120000 },
|
|
263
|
+
).trim();
|
|
264
|
+
return json(res, { url, analysis: result });
|
|
265
|
+
} catch (err) {
|
|
266
|
+
return error(res, `Analysis failed: ${err.message}`, 500);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
225
270
|
notFound(res);
|
|
226
271
|
} catch (err) {
|
|
227
272
|
error(res, err.message, 500);
|