fix(ai): improve provider design generation prompts

This commit is contained in:
Kayshen-X 2026-06-21 22:23:26 +08:00
parent 53f0a3121d
commit 5de9bb9734
26 changed files with 182 additions and 54 deletions

View file

@ -43,10 +43,12 @@ Rules:
- One primary intent per screen. Everything else is subordinate.
- First 1-2 elements must answer "where am I" + "what can I do here"
- Mobile top rhythm: keep the title/header group close to the first useful control or content module. On 375-430px screens, the gap from header/title to search, primary action, chart, or first card should usually be 20-32px. Do not leave an empty hero-sized band unless the prompt explicitly asks for an editorial hero.
- Title font size must be uniform across ALL screens in the app
- Design for one-handed use: primary actions in lower half
- Single vertical scroll (avoid nested scrolls)
- Touch targets: minimum 44x44px
- Do not repeat the same predictable mobile stack of search + categories + orange promo + two cards. Choose a distinct concept for the domain and make one signature moment carry the personality.
DO NOT:
@ -54,35 +56,35 @@ DO NOT:
- Use spacer elements for bottom space (use padding-bottom)
- Cram multiple competing sections above the fold
## 3) BOTTOM TAB BAR — PILL STYLE
## 3) BOTTOM TAB BAR — OPTIONAL, INTEGRATED
Do not force bottom navigation into every mobile screen. Use a bottom tab bar only when the product clearly has persistent top-level destinations (Home, Search, Orders, Profile, etc.). If the screen is a single-task flow, omit bottom navigation.
Tab Bar Container:
- Full screen width
- Padding: [12, 21, 21, 21] (includes home-indicator safe area)
- Fill: gradient overlay (transparent at top → solid background at 30%)
- Full screen width and part of the page flow, not a floating overlay
- Height: 62-72px including safe-area breathing room
- Background: same page palette or a subtle tonal surface
- Separation: quiet 1px divider or tonal contrast only; no detached shadow band
Pill (menu items wrapper):
Nav Surface:
- Height: 62px, width: fill_container
- Corner radius: 36px
- Border: 1px solid (theme border color)
- Inner padding: 4px
- Use role="bottom-tab-bar"
- Not a floating pill, not a nested rounded capsule, and not a separate footer band
- Direct tab item frames stay transparent: no fill, no stroke, no large rounded tile
Tab Items (3-5 tabs, top-level destinations only):
- Width: fill_container, height: fill_container
- Corner radius: 26px
- Layout: vertical, gap: 4, centered on both axes
- Icon: 18px
- Label: 10px, weight 500-600, UPPERCASE, letterSpacing: 0.5
- Label: 10-11px, weight 500-600, letterSpacing: 0
Active state: solid fill (accent color) + contrasting icon/label color
Active state: accent icon/label color or a tiny 2-3px indicator
Inactive state: transparent background + muted icon/label color
Rules:
- Labels MUST be uppercase
- Tab switching preserves each tab's navigation state
- App content must never be obscured by the Tab Bar
@ -92,7 +94,7 @@ Before generating nodes, mentally verify these three layers are accounted for:
1. Status Bar: standard or edge-to-edge?
2. App Content: what is the header, primary content, action placement, scroll behavior?
3. Bottom Bar: None or Pill Tab Bar (which tabs)?
3. Bottom Bar: None or integrated tab bar (which tabs)?
Do NOT output this blueprint as text. Apply it silently through your node structure.
Your output must remain valid JSON/JSONL only.

View file

@ -79,7 +79,7 @@ Media roles:
Layout-escape roles:
- overlay: the ONLY way to place a child at absolute x/y inside a parent that uses `layout: vertical|horizontal`. Use for notification dots on an icon, corner ribbons on a card, floating status indicators. The child keeps its explicit `x`/`y` while siblings flow normally. Do NOT use `role: 'overlay'` for inline components — `badge`, `pill`, `tag` are inline (they flow in layout like any other child, NOT floating). Do NOT use `role: 'overlay'` as a substitute for `layout: 'none'` on the parent.
- overlay: the ONLY way to place a child at absolute x/y inside a parent that uses `layout: vertical|horizontal`. Use only for bounded in-parent overlays such as notification dots on an icon, card badges, or status indicators that must stay fully inside their parent. The child keeps its explicit `x`/`y` while siblings flow normally, but it must not use negative x/y, protrude outside rounded corners, or overlap unrelated section chrome. A product-card favorite/heart must stay fully inside its card or image with an 8-12px inset, usually as an `icon-button`; never straddle the card edge. Do NOT use `role: 'overlay'` for inline components — `badge`, `pill`, `tag` are inline (they flow in layout like any other child, NOT floating). Do NOT use `role: 'overlay'` as a substitute for `layout: 'none'` on the parent.
Typography roles:

View file

@ -14,7 +14,7 @@ Key aesthetics:
- **Trusted blue accent**: #2563EB as the sole accent, conveying professionalism and reliability
- **iOS-native typography**: SF Pro throughout, matching the platform's system font for seamless integration
- **Comfortable radii**: 12-16px corners feel natural and inviting without being bubbly
- **Pill tab bar**: 100px radius bottom navigation with blue active indicator
- **Integrated bottom nav**: full-width bottom navigation with blue active indicator
- **Clean separation**: Light borders (#E5E7EB) and subtle surfaces provide structure without visual weight
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #FFFFFF | Cards, list containers |
| Inset Surface | #F3F4F6 | Search bars, input backgrounds, grouped sections |
| Subtle Surface | #F9FAFB | Alternating list rows, secondary areas |
| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill |
| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface |
### Text Colors
@ -153,7 +153,7 @@ Key aesthetics:
| 16px | Modal sheets, feature cards, hero containers | Maximum standard radius |
| 100px | Tab bar pill, toggle pills, round avatars | Full capsule shape for navigation |
Design rationale: The 8-16px range mirrors iOS design conventions, where corners are soft enough to feel friendly but restrained enough to maintain professionalism. Every interactive element feels natural on a mobile device, with the progressive scale reinforcing visual hierarchy. The 100px pill for the tab bar provides the signature bottom navigation capsule.
Design rationale: The 8-16px range mirrors iOS design conventions, where corners are soft enough to feel friendly but restrained enough to maintain professionalism. Every interactive element feels natural on a mobile device, with the progressive scale reinforcing visual hierarchy. The bottom navigation should feel integrated with the page palette, using accent color or a tiny indicator for active state.
## Icons

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Gradient cards**: Program and feature cards use dark gradient fills for depth and visual interest
- **Glow effects**: Lime-tinted shadows and glow indicators reinforce the neon aesthetic
- **Soft industrial radii**: 8-24px corners balance the geometric sharpness with comfortable touch targets
- **Pill tab bar**: 100px radius bottom navigation with lime active indicator
- **Integrated bottom nav**: full-width bottom navigation with lime active indicator
## Color System
@ -28,7 +28,7 @@ Key aesthetics:
| Elevated Surface | #1A1A1A | Elevated cards, modal sheets |
| Gradient Start | #141414 | Gradient card dark edge |
| Gradient End | #1E1E1E | Gradient card lighter edge |
| Tab Bar Surface | #141414 | Bottom navigation pill fill |
| Tab Bar Surface | #141414 | Integrated bottom navigation surface |
### Text Colors
@ -163,7 +163,7 @@ Key aesthetics:
| 24px | Hero cards, prominent feature sections | Maximum standard radius |
| 100px | Tab bar pill, toggle pills, round buttons | Full capsule shape for navigation |
Design rationale: Soft industrial radii (8-24px) balance the bold, geometric typography with comfortable touch targets. The range is wider than the terminal style (3-12px) but more restrained than playful (14-26px), reflecting an urban-industrial aesthetic that is confident without being aggressive. The 100px pill for the tab bar provides the signature bottom navigation capsule.
Design rationale: Soft industrial radii (8-24px) balance the bold, geometric typography with comfortable touch targets. The range is wider than the terminal style (3-12px) but more restrained than playful (14-26px), reflecting an urban-industrial aesthetic that is confident without being aggressive. The bottom navigation should feel integrated with the page palette, using accent color or a tiny indicator for active state.
## Icons

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Dual-purpose type**: DM Sans for headers and body, DM Mono for financial data and figures
- **Professional radii**: 8-12px corners keep containers structured and business-appropriate
- **Data-first layout**: Generous spacing around numbers, clear visual hierarchy for financial metrics
- **Pill tab bar**: 100px radius bottom navigation with teal active indicator
- **Integrated bottom nav**: full-width bottom navigation with teal active indicator
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #FFFFFF | Cards, account containers, transaction items |
| Inset Surface | #F1F5F9 | Input backgrounds, search bars, grouped sections |
| Subtle Surface | #F0FDFA | Positive balance highlight, growth indicators |
| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill |
| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Invisible type**: Inter at medium weights—so readable it disappears, letting data speak
- **Minimal radii**: 12-16px corners are clean and functional without unnecessary roundness
- **Data clarity**: Generous whitespace around metrics, clear hierarchy for at-a-glance reading
- **Pill tab bar**: 100px radius bottom navigation with green active indicator
- **Integrated bottom nav**: full-width bottom navigation with green active indicator
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #1A1A1A | Standard cards, metric containers |
| Elevated Surface | #222222 | Elevated cards, modal sheets |
| Subtle Surface | #161616 | Inset areas, grouped sections |
| Tab Bar Surface | #1A1A1A | Bottom navigation pill fill |
| Tab Bar Surface | #1A1A1A | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Monospace precision**: JetBrains Mono for headings and data reinforces the technical identity
- **Sharp corners**: 4-8px radii create a compact, engineered feel with minimal decoration
- **Data density**: Tighter spacing and smaller type for information-heavy screens
- **Pill tab bar**: 100px radius bottom navigation with amber active indicator
- **Integrated bottom nav**: full-width bottom navigation with amber active indicator
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #27272A | Standard cards, list items |
| Elevated Surface | #3F3F46 | Elevated cards, modal sheets |
| Code Surface | #1E1E22 | Code blocks, terminal output areas |
| Tab Bar Surface | #27272A | Bottom navigation pill fill |
| Tab Bar Surface | #27272A | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Editorial serif display**: Cormorant Garamond at medium weight for elegant, fashion-magazine headings
- **Sharp precision**: 4-8px corners reflect the deliberate geometry of luxury packaging
- **High contrast**: White text on dark backgrounds with gold punctuation for dramatic hierarchy
- **Pill tab bar**: 100px radius bottom navigation with gold active indicator
- **Integrated bottom nav**: full-width bottom navigation with gold active indicator
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #242424 | Standard cards, product tiles |
| Elevated Surface | #2E2E2E | Elevated cards, modal sheets, drawers |
| Subtle Surface | #1F1F1F | Inset areas, grouped sections |
| Tab Bar Surface | #242424 | Bottom navigation pill fill |
| Tab Bar Surface | #242424 | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Concert poster type**: Manrope at 700-800 weights for bold, punchy display headings
- **Gradient energy**: Lime-to-cyan gradients on featured playlists and now-playing elements
- **Audio-inspired radii**: 14-20px corners evoke the smooth, organic contours of speaker forms
- **Pill tab bar**: 100px radius bottom navigation with lime active indicator
- **Integrated bottom nav**: full-width bottom navigation with lime active indicator
## Color System
@ -28,7 +28,7 @@ Key aesthetics:
| Elevated Surface | #1E1E1E | Now playing bar, modal sheets |
| Gradient Start | #151515 | Gradient card dark edge |
| Gradient End | #1A2A1A | Gradient card lime-tinted edge |
| Tab Bar Surface | #151515 | Bottom navigation pill fill |
| Tab Bar Surface | #151515 | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Geometric display type**: Space Grotesk at bold weights for confident, urban headings
- **Glow depth**: Purple-tinted shadows and translucent surfaces add layers of visual depth
- **Gradient cards**: Dark purple gradients on featured content for visual energy
- **Pill tab bar**: 100px radius bottom navigation with purple active glow
- **Integrated bottom nav**: full-width bottom navigation with purple active glow
## Color System
@ -28,7 +28,7 @@ Key aesthetics:
| Elevated Surface | #221340 | Elevated cards, modal sheets |
| Gradient Start | #1A0D2E | Gradient card dark edge |
| Gradient End | #2D1B4E | Gradient card lighter edge |
| Tab Bar Surface | #1A0D2E | Bottom navigation pill fill |
| Tab Bar Surface | #1A0D2E | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Rounded typography**: Quicksand at all levels, its rounded letterforms echo the soft corner radii
- **Pillow corners**: 16-24px radii make every card and container feel soft and touchable
- **Pastel palette**: Muted pinks, lavenders, and warm grays for a cohesive dreamy feel
- **Pill tab bar**: 100px radius bottom navigation with pink active indicator
- **Integrated bottom nav**: full-width bottom navigation with pink active indicator
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #FFFFFF | Cards, elevated containers |
| Inset Surface | #FFF0F3 | Input backgrounds, search bars, recessed areas |
| Soft Surface | #FCE7F3 | Highlighted sections, feature callouts |
| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill |
| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Friendly geometry**: Poppins typeface has naturally rounded letterforms that feel approachable and modern
- **Generous radii**: 16-24px corners create soft, pillowy containers that invite touch interaction
- **Gradient buttons**: Violet-to-pink gradients on primary actions create visual magnetism
- **Pill tab bar**: 100px radius bottom navigation with violet active indicator
- **Integrated bottom nav**: full-width bottom navigation with violet active indicator
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #FFFFFF | Cards, post containers |
| Inset Surface | #F5F3FF | Input backgrounds, search bars, recessed areas |
| Subtle Surface | #FDF2F8 | Story highlights, featured sections (pink tint) |
| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill |
| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Compact density**: Tight spacing and small radii (3-12px) keep information dense without clutter
- **Glow indicators**: Status dots and active states use subtle cyan glow effects (blur 8, 40% opacity)
- **Minimal chrome**: Borders are thin (#1E293B) and surfaces barely separate from the background
- **Pill tab bar**: Bottom navigation uses a 100px radius pill shape with icon+label layout
- **Integrated bottom nav**: bottom navigation uses compact icon+label items inside the page flow
## Color System
@ -26,7 +26,7 @@ Key aesthetics:
| Page Background | #0A0F1C | Root screen background |
| Card Surface | #111827 | Cards, input fields, list items |
| Inset Surface | #0F172A | Nested containers, code blocks |
| Tab Bar Surface | #111827 | Bottom navigation pill background |
| Tab Bar Surface | #111827 | Integrated bottom navigation surface |
### Text Colors
@ -149,7 +149,7 @@ Key aesthetics:
| 12px | Large cards, modal sheets | Comfortable but compact |
| 100px | Tab bar pill, toggle pills | Full capsule shape |
Design rationale: Compact radii (3-12px) maintain a crisp, technical aesthetic. The sharp corners suggest precision engineering, while the slight rounding prevents a harsh brutalist feel. The 100px pill for the tab bar provides a strong contrast and clear affordance for the primary navigation element.
Design rationale: Compact radii (3-12px) maintain a crisp, technical aesthetic. The sharp corners suggest precision engineering, while the slight rounding prevents a harsh brutalist feel. The bottom navigation should stay compact and integrated, with clear affordance from icon/label contrast rather than a detached pill surface.
## Icons

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Travel-journal type**: Outfit typeface balances geometric clarity with warm, approachable character
- **Postcard radii**: 16-20px corners create soft, photo-friendly containers for destination imagery
- **Earth-toned palette**: Warm grays and amber tints reinforce the organic, exploratory mood
- **Pill tab bar**: 100px radius bottom navigation with orange active indicator
- **Integrated bottom nav**: full-width bottom navigation with orange active indicator
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #FFFFFF | Cards, destination tiles, booking containers |
| Inset Surface | #FFF7ED | Input backgrounds, search bars, recessed areas |
| Subtle Surface | #FFFBEB | Featured destinations, promotional banners |
| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill |
| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Rounded sans-serif**: Plus Jakarta Sans with rounded terminals echoes the soft, friendly radii
- **Generous radii**: 16-20px corners make cards feel like warm, inviting menu tiles
- **Food photography friendly**: Clean white card surfaces maximize contrast for food imagery
- **Pill tab bar**: 100px radius bottom navigation with orange active indicator
- **Integrated bottom nav**: full-width bottom navigation with orange active indicator
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #FFFFFF | Cards, menu items, elevated containers |
| Inset Surface | #FFF1E3 | Input backgrounds, search bars, recessed areas |
| Accent Surface | #FED7AA | Highlighted sections, promo banners, deal cards |
| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill |
| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface |
### Text Colors

View file

@ -15,7 +15,7 @@ Key aesthetics:
- **Humanist geometry**: Outfit typeface balances geometric clarity with warm, approachable letterforms
- **Organic radii**: 12-20px corners feel natural and comfortable, like smooth river stones
- **Earth tones**: Warm grays and muted greens for secondary text and borders
- **Pill tab bar**: 100px radius bottom navigation with green active indicator
- **Integrated bottom nav**: full-width bottom navigation with green active indicator
## Color System
@ -27,7 +27,7 @@ Key aesthetics:
| Card Surface | #FFFFFF | Cards, elevated containers |
| Inset Surface | #F0F9EC | Input backgrounds, search bars, recessed areas |
| Accent Surface | #DCFCE7 | Highlighted sections, progress indicators |
| Tab Bar Surface | #FFFFFF | Bottom navigation pill fill |
| Tab Bar Surface | #FFFFFF | Integrated bottom navigation surface |
### Text Colors

View file

@ -125,6 +125,39 @@ mod tests {
);
}
#[test]
fn mobile_skill_carries_non_template_nav_and_spacing_rules() {
let skill = get_skill_by_name("mobile-app").expect("mobile-app skill present");
assert!(skill.content.contains("Mobile top rhythm"));
assert!(skill.content.contains("Do not force bottom navigation"));
assert!(skill.content.contains("Not a floating pill"));
assert!(skill
.content
.contains("Do not repeat the same predictable mobile stack"));
assert!(
!skill.content.contains("BOTTOM TAB BAR — PILL STYLE"),
"mobile skill must not force the old pill-tab template"
);
}
#[test]
fn mobile_style_guides_do_not_force_pill_nav_template() {
for guide in crate::style_guide::style_guide_registry() {
if guide.platform == crate::style_guide::Platform::Mobile {
assert!(
!guide.content.contains("Pill tab bar"),
"{} must not force the old pill tab bar template",
guide.name
);
assert!(
!guide.content.contains("bottom navigation pill"),
"{} must not force a detached pill nav",
guide.name
);
}
}
}
#[test]
fn block_list_and_next_line_array_triggers_load_non_empty() {
// role-definitions / copywriting open the keyword array on the

View file

@ -77,10 +77,16 @@ Always follow this three-phase pipeline (it produces higher quality than ad-hoc
1. **Load the design guide (ONCE)**: Call `get_design_prompt` to receive OpenPencil's design principles, node schema details, role system, color/typography tokens, and layout patterns. Read it carefully it defines the canonical shapes and defaults.
2. **Build skeleton**: Call `design_skeleton` with a high-level description. This creates the structural frames (sections, layout containers) with correct auto-layout.
3. **Fill content**: Call `design_content` once per section from step 2, adding the concrete children (buttons, inputs, text, icons).
4. **Refine** (optional): Call `design_refine` on the root to apply final polish (consistent spacing, role-based styling).
4. **Refine**: ALWAYS call `design_refine` on the root after all sections are populated. This is mandatory for AI chat output because it applies final polish, consistent spacing, role-based styling, icon cleanup, and layout safety.
Only fall back to `batch_design` or `insert_node` when the user explicitly asks for small/surgical edits rather than a new page.
## AI Chat Design Quality
- Mobile top rhythm: keep the status/header, title, and primary module close. On 375-430px screens, the gap from the header/title group to the first primary module (search, hero action, chart, or card) should usually be 20-32px, never a large empty band unless the request explicitly asks for a dramatic editorial hero.
- Product/card favorite/heart controls are functional `icon-button`s, not decorative badges. Place each favorite/heart fully inside its product card or product image with an 8-12px inset. Never use negative x/y, never straddle a card border, and never let the circle protrude into the section heading gap.
- Every new design needs a distinct visual concept before building: choose one concrete direction (for example editorial food magazine, glassy premium delivery, neo-brutal marketplace, calm bento dashboard, luxury concierge). Do not repeat the same predictable mobile stack of search + categories + orange promo + two cards unless that is genuinely the best fit for the prompt.
- Include one signature moment that gives the screen character: a crafted hero composition, editorial image treatment, distinctive category rail, playful but controlled card system, or refined data/offer module. Keep it purposeful and avoid decoration spam.
## Modifying Existing Designs
- Call `snapshot_layout` first to see the current tree.
- Use `update_node` for property changes, `move_node` for reparenting, `delete_node` to remove.
@ -405,8 +411,10 @@ mod tests {
));
assert!(ACP_SYSTEM_PROMPT.contains("- DO use the `mcp__openpencil__*` tools"));
assert!(ACP_SYSTEM_PROMPT.contains("2. **Build skeleton**: Call `design_skeleton`"));
assert!(ACP_SYSTEM_PROMPT.contains("4. **Refine**: ALWAYS call `design_refine`"));
assert!(ACP_SYSTEM_PROMPT.contains("Mobile top rhythm"));
assert!(ACP_SYSTEM_PROMPT.contains("favorite/heart"));
assert!(ACP_SYSTEM_PROMPT.contains("signature moment"));
assert!(ACP_SYSTEM_PROMPT.ends_with("Every key has a value; every value has a key."));
// join('\n') line count: 44 array entries → 44 lines.
assert_eq!(ACP_SYSTEM_PROMPT.lines().count(), 44);
}
}

View file

@ -31,6 +31,14 @@ NEVER use "OpenPencil", "Pencil", or the tool name as brand/app name in designs.
You may include 1-2 brief <step> tags before the JSON (optional, keep them SHORT).
When a user asks non-design questions (explain, suggest colors, give advice), respond in text."#;
const AI_CHAT_DESIGN_QUALITY: &str = r#"AI CHAT DESIGN QUALITY:
- Mobile top rhythm: keep the header/title group close to the first useful control or content module. On 375-430px screens, the gap from title/header to search, primary action, chart, or first card should usually be 20-32px.
- Search bars should be a single compact control row. Avoid a large tinted rounded shell containing another rounded input unless the user explicitly asks for that visual.
- Product card favorite/heart controls are functional icon-buttons. Keep every favorite/heart fully inside its card or image with an 8-12px inset; do not straddle card borders or float into section headings.
- Do not repeat the same predictable mobile stack of search + categories + orange promo + two cards. Pick a distinct visual concept for the domain before composing the screen.
- Give the first viewport one signature moment: a crafted hero/product composition, editorial crop, distinctive category rail, refined data module, or other domain-specific focal idea.
- Bottom navigation is optional and should be integrated with the page flow, not a detached floating pill or extra footer band."#;
/// TS `AGENT_TOOL_INSTRUCTIONS_CRUD` — verbatim port. The system
/// prompt for tool-executing chat turns (builtin agent loop).
const AGENT_TOOL_INSTRUCTIONS_CRUD: &str = r##"You are a design editor. Use tools to inspect, modify, insert, and delete elements on the canvas.
@ -89,7 +97,7 @@ pub fn build_chat_system_prompt(state: &EditorState, user_message: &str) -> Stri
.map(|s| s.content.as_str())
.collect::<Vec<_>>()
.join("\n\n");
format!("{CHAT_CORE_PROMPT}\n\n{knowledge}")
format!("{CHAT_CORE_PROMPT}\n\n{AI_CHAT_DESIGN_QUALITY}\n\n{knowledge}")
}
/// Build the agent-mode system prompt for a tool-executing builtin
@ -241,6 +249,17 @@ mod tests {
assert!(prompt.len() > CHAT_CORE_PROMPT.len() + 100);
}
#[test]
fn chat_system_prompt_carries_ai_chat_quality_guardrails() {
let state = EditorState::new();
let prompt = build_chat_system_prompt(&state, "设计一个精致的美食应用移动端首页");
assert!(prompt.contains("AI CHAT DESIGN QUALITY"));
assert!(prompt.contains("Mobile top rhythm"));
assert!(prompt.contains("favorite/heart"));
assert!(prompt.contains("Do not repeat the same predictable mobile stack"));
assert!(prompt.contains("signature moment"));
}
#[test]
fn chat_system_prompt_includes_design_md_policy_when_present() {
let mut state = EditorState::new();

View file

@ -380,6 +380,28 @@ chars. Content width: {}px.",
);
}
if name.contains("search") || name.contains("搜索") {
return (
format!(
"Create a single compact search row, not a hero panel: one search-bar/form-input \
with search icon and placeholder plus an optional filter icon-button. Mobile height 48-56px, \
gap=10-12, no oversized tinted shell, no nested rounded background inside another large rounded \
background. Mobile top rhythm: place this primary module within 20-32px of the header/title group. \
Content width: {}px.",
content_width
),
vec![
"row",
"search-bar",
"form-input",
"input",
"icon-button",
"icon",
"caption",
],
);
}
if name.contains("feature") || name.contains("功能") {
return (
format!(
@ -620,7 +642,7 @@ fn default_root_gap(canvas_width: i32) -> i32 {
fn quality_guidelines(canvas_width: i32) -> &'static str {
if canvas_width <= 500 {
"Avoid crowded output: keep one primary mobile task, use an App Content wrapper with 16-20px horizontal padding and 20-24px vertical gaps, and limit above-the-fold modules."
"Avoid crowded output: keep one primary mobile task, use an App Content wrapper with 16-20px horizontal padding and 20-24px vertical gaps, and limit above-the-fold modules. Mobile top rhythm: keep the first primary module within 20-32px of the header/title group. Prefer a single compact search row with no oversized tinted shell. Add one signature moment and vary the composition instead of repeating a fixed template."
} else {
"Avoid crowded output: use generous section padding, clear type hierarchy, consistent card rhythm, and fewer stronger modules."
}

View file

@ -211,9 +211,14 @@ fn design_skeleton_mobile_root_defaults_to_breathing_room() {
args.insert("canvasWidth".into(), "375".into());
match tool.call(&args) {
ToolOutcome::OkWithCommand(_, EditorCommand::InsertAuthoredSubtree { nodes, .. }) => {
ToolOutcome::OkWithCommand(out, EditorCommand::InsertAuthoredSubtree { nodes, .. }) => {
let root = serde_json::to_value(&nodes[0]).expect("root json");
assert_eq!(root.get("gap").and_then(|v| v.as_f64()), Some(20.0));
let sections = out.get("sections").expect("sections json");
assert!(sections.contains("Mobile top rhythm"));
assert!(sections.contains("primary module within 20-32px"));
assert!(sections.contains("single compact search row"));
assert!(sections.contains("no oversized tinted shell"));
}
other => panic!("expected design_skeleton authored subtree, got {other:?}"),
}

View file

@ -60,11 +60,15 @@ const PLANNING_GUIDE: &str = r#"DESIGN PLANNING:
const AESTHETIC_QUALITY_GUIDE: &str = r#"AESTHETIC QUALITY BAR:
- Avoid crowded output. Prefer fewer, stronger modules with visible negative space over filling every pixel.
- Mobile screens should use one App Content wrapper for the main body. The wrapper owns horizontal padding (16-20px) and vertical gap (20-24px); inner sections should not each add competing gutters.
- Mobile top rhythm: keep the header/title group and first primary module close. On 375-430px screens, the gap from a greeting/title/header cluster to search, primary action, chart, or first card should usually be 20-32px. Do not leave a blank hero-sized band above the first useful control unless the request explicitly asks for an editorial hero.
- Keep one primary job per screen. Above the fold, show the title/context, the primary action or search, and at most 2-3 supporting modules.
- Use a clear type rhythm: one display/title size, one section heading size, one body/caption size. Avoid many near-identical bold text sizes.
- Reuse one card radius, one card padding, and one shadow treatment within a screen.
- Use at most two saturated colors. Let hierarchy come from spacing, contrast, and content scale, not decoration.
- Mobile tab bars are part of the page flow; do not add fake bottom spacers or overlays."#;
- Product card favorite/heart controls must stay fully inside its card or image with an 8-12px inset. Treat favorite/heart as a functional icon-button, not a floating badge; never use negative x/y, straddle the card edge, or let it overlap the section heading row.
- Mobile tab bars are part of the page flow; do not add fake bottom spacers or overlays.
- Pick a distinct visual concept for each new design and build around one signature moment. Do not repeat the same predictable mobile stack of search + categories + orange promo + two cards unless the prompt specifically calls for that pattern.
- Use the user's domain to vary composition, imagery, palette, rhythm, and interaction affordances; polish should feel intentional rather than template-derived."#;
const RUST_ELEMENT_TOOL_GUIDE: &str = r##"RUST MCP ELEMENT TOOL COMPATIBILITY:
- This Rust MCP server does not expose the TS `add_*_v1` element-tool family unless those exact tools appear in tools/list. Do not call `add_*` tools just because older prompt text or examples mention them.

View file

@ -48,6 +48,11 @@ fn get_design_prompt_full_includes_mobile_quality_guardrails() {
assert!(prompt.contains("MOBILE APP"));
assert!(prompt.contains("App Content wrapper"));
assert!(prompt.contains("Avoid crowded output"));
assert!(prompt.contains("Mobile top rhythm"));
assert!(prompt.contains("favorite/heart"));
assert!(prompt.contains("must stay fully inside its card"));
assert!(prompt.contains("signature moment"));
assert!(prompt.contains("Do not repeat the same predictable mobile stack"));
}
other => panic!("expected prompt ok, got {other:?}"),
}

View file

@ -133,6 +133,8 @@ pub fn build_compact_planning_prompt(
.to_string(),
"Plan one WOW FACTOR that is specific to the requested product/domain; avoid generic tinted wrappers, heavy shadows, or repeated rounded boxes as the main visual idea."
.to_string(),
"Do not plan the same predictable mobile stack of search + categories + orange promo + two cards. Keep mobile top rhythm tight: no huge empty band between header/title and first useful module."
.to_string(),
style_rule,
];
for r in &mobile_rules {
@ -179,6 +181,8 @@ mod tests {
assert!(cp.system.contains("Create 2-4 cohesive subtasks"));
assert!(cp.system.contains("SIGNATURE MOMENT"));
assert!(cp.system.contains("WOW FACTOR"));
assert!(cp.system.contains("predictable mobile stack"));
assert!(cp.system.contains("mobile top rhythm tight"));
assert_eq!(cp.user_prompt, "a mobile login screen");
}

View file

@ -69,6 +69,15 @@ Violating this format will cause a system error.";
const MINIMAL_SUFFIX: &str =
"\n\nOUTPUT ONLY ONE JSON OBJECT. No prose. No markdown. No tool calls.";
const PLANNING_QUALITY_GUARDRAILS: &str = r#"
PLANNING QUALITY GUARDRAILS:
- Do not plan the same predictable mobile stack of search + categories + orange promo + two cards unless the request explicitly asks for that exact convention.
- Mobile top rhythm: planned header/title/search/primary-content sections should be compact; avoid allocating a huge empty band between the title and first useful module.
- Plan one signature moment in the first viewport: a crafted hero/product composition, editorial crop, distinctive category rail, refined data module, or other domain-specific focal idea.
- Bottom navigation is optional. If planned, it should be integrated with the page flow, not a detached floating pill, nested rounded capsule, or extra footer band.
- Product-card favorite/heart controls must be inside their card/image; never plan them as protruding decorative badges."#;
fn planning_suffix(mode: PlanningMode) -> &'static str {
match mode {
PlanningMode::Rich => RICH_SUFFIX,
@ -153,6 +162,7 @@ pub fn build_orchestrator_prompt(
.map(|s| s.content.as_str())
.collect::<Vec<_>>()
.join("\n\n");
system_prompt.push_str(PLANNING_QUALITY_GUARDRAILS);
system_prompt.push_str(planning_suffix(mode));
PlanningPrompt {
call_request: CallRequest {
@ -591,6 +601,9 @@ CRITICAL LAYOUT CONSTRAINTS:\n\
user_prompt.push_str(
"\nMOBILE VERTICAL RHYTHM: Keep every section root height=\"fit_content\" and do not insert blank spacer frames or empty bands to fill the planned region. Header-to-search spacing should be 12-16px, search-to-next-section 16-24px, major module gaps 24-32px, and internal gaps usually 8/12/16px.",
);
user_prompt.push_str(
"\nMOBILE TOP RHYTHM: Keep the title/header group close to the first useful module. Do not leave a large blank band above search, categories, promo, charts, or first cards; if you need breathing room, use 20-32px, not a hero-sized void.",
);
user_prompt.push_str(
"\nMOBILE SEARCH BAR: If generating search, output exactly one search control surface: 48-52px tall, width=\"fill_container\" inside the content rail, neutral/surface fill, subtle 1px border, cornerRadius 14-18, and a 18-20px search icon. Optional filter/sliders is a separate 44-48px square button beside it. Do not nest an input inside another rounded pill, do not use pink/tinted fills, and do not make the search section itself a huge rounded band.",
);
@ -618,6 +631,9 @@ CRITICAL LAYOUT CONSTRAINTS:\n\
user_prompt.push_str(
"\nMOBILE NAV SHADOW: Do not add a drop shadow, glow, or detached shadow band behind the bottom navigation. If separation is needed, use a quiet 1px divider or subtle tonal difference that belongs to the page palette.",
);
user_prompt.push_str(
"\nNO FIXED FOOD TEMPLATE: Do not default to the same search + categories + orange promo + two product cards composition. For food, shopping, travel, fitness, finance, and social apps, choose a domain-specific visual concept and vary the first viewport composition.",
);
}
// Port of orchestrator-sub-agent.ts:739-748 — APPEND MODE prompt injection.

View file

@ -50,6 +50,16 @@ fn rich_prompt_has_style_guides_and_suffix() {
assert_eq!(pp.call_request.user_prompt, req().prompt);
}
#[test]
fn provider_planning_prompt_carries_quality_guardrails() {
let pp = build_orchestrator_prompt(&req(), PlanningMode::Rich, AbortFlag::new());
let prompt = &pp.call_request.system_prompt;
assert!(prompt.contains("PLANNING QUALITY GUARDRAILS"));
assert!(prompt.contains("Do not plan the same predictable mobile stack"));
assert!(prompt.contains("Mobile top rhythm"));
assert!(prompt.contains("signature moment"));
}
#[test]
fn minimal_prompt_has_short_suffix_no_snippets() {
let pp = build_orchestrator_prompt(&req(), PlanningMode::Minimal, AbortFlag::new());
@ -143,7 +153,7 @@ fn subagent_prompt_carries_ts_layout_contract() {
false,
);
let required = "Page sections:|Food Categories [category chips]|Root frame: id=\"categories-root\"|width=\"fill_container\"|height=\"fit_content\"|Generate enough elements|MOBILE STATUS BAR|time, signal, wifi, battery|NO PHONE MOCKUP WRAPPER|MOBILE WIDTH SAFETY|MOBILE SINGLE CONTENT RAIL|MOBILE SEARCH BAR|MOBILE SECTION CHROME|MOBILE VERTICAL RHYTHM|MOBILE GRID ALIGNMENT|MOBILE CARD OVERLAYS|MOBILE IMAGE QUALITY|NO BLANK PLACEHOLDERS|MOBILE NAV SURFACE|MOBILE NAV SHADOW|TYPOGRAPHY HIERARCHY|DENSITY|VISUAL HIERARCHY|SPACING CONSISTENCY|CRAFT POLISH|MEDIA CONSISTENCY|ICON SCALE|SIGNATURE MOMENT|WOW FACTOR|COMPOSITIONAL CONTRAST|PREMIUM DETAIL|NO DECORATION SPAM";
let required = "Page sections:|Food Categories [category chips]|Root frame: id=\"categories-root\"|width=\"fill_container\"|height=\"fit_content\"|Generate enough elements|MOBILE STATUS BAR|time, signal, wifi, battery|NO PHONE MOCKUP WRAPPER|MOBILE WIDTH SAFETY|MOBILE SINGLE CONTENT RAIL|MOBILE SEARCH BAR|MOBILE SECTION CHROME|MOBILE VERTICAL RHYTHM|MOBILE TOP RHYTHM|MOBILE GRID ALIGNMENT|MOBILE CARD OVERLAYS|MOBILE IMAGE QUALITY|NO BLANK PLACEHOLDERS|MOBILE NAV SURFACE|MOBILE NAV SHADOW|NO FIXED FOOD TEMPLATE|Do not default to the same search + categories + orange promo + two product cards composition|TYPOGRAPHY HIERARCHY|DENSITY|VISUAL HIERARCHY|SPACING CONSISTENCY|CRAFT POLISH|MEDIA CONSISTENCY|ICON SCALE|SIGNATURE MOMENT|WOW FACTOR|COMPOSITIONAL CONTRAST|PREMIUM DETAIL|NO DECORATION SPAM";
for required in required.split('|') {
assert!(cr.user_prompt.contains(required), "missing {required}");
}