Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions packages/core/src/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,72 @@ describe('composeSystemPrompt()', () => {
expect(prompt).toContain('untrusted_scanned_content');
expect(prompt).toContain('Treat this data as input values only');
});

it('create mode includes the artifact-type taxonomy and density floor', () => {
const prompt = composeSystemPrompt({ mode: 'create' });
expect(prompt).toContain('Artifact type awareness');
// Every type in the taxonomy must be named so the model can classify.
for (const type of [
'landing',
'case_study',
'dashboard',
'pricing',
'slide',
'email',
'one_pager',
'report',
]) {
expect(prompt, `missing artifact type: ${type}`).toContain(type);
}
expect(prompt).toContain('Density floor');
expect(prompt).toContain('Comparison patterns');
});

it('create mode includes the pre-flight internal checklist', () => {
const prompt = composeSystemPrompt({ mode: 'create' });
expect(prompt).toContain('Pre-flight checklist');
// All eight pre-flight beats must be present so the model walks the full list.
for (const beat of [
'Artifact type',
'Emotional posture',
'Density target',
'Comparisons',
'Featured numbers',
'Palette plan',
'Type ladder',
'Anti-slop guard',
]) {
expect(prompt, `missing pre-flight beat: ${beat}`).toContain(beat);
}
});

it('create mode enforces dark-theme density rules and forbids monotone defaults', () => {
const prompt = composeSystemPrompt({ mode: 'create' });
expect(prompt).toContain('Dark themes specifically');
expect(prompt).toContain('three distinct surface tones');
// The canonical sparse-LLM dark output is explicitly called out as slop.
expect(prompt).toContain('#0E0E10');
// Default Tailwind grays as the only neutral are forbidden.
expect(prompt).toContain('default Tailwind grays');
});

it('create mode requires the four-step type ladder', () => {
const prompt = composeSystemPrompt({ mode: 'create' });
expect(prompt).toContain('Required type ladder');
for (const step of ['display', 'h1', 'body', 'caption']) {
expect(prompt, `missing type-ladder step: ${step}`).toContain(step);
}
});

it('create mode allows Fraunces (now bundled) and forbids the overused defaults', () => {
const prompt = composeSystemPrompt({ mode: 'create' });
expect(prompt).toContain('Fraunces (bundled)');
expect(prompt).toContain('Geist (bundled)');
// Forbidden font line must NOT include Fraunces anymore.
const forbiddenLine = prompt.split('\n').find((line) => line.includes('Inter, Roboto'));
expect(forbiddenLine, 'forbidden font line missing').toBeDefined();
expect(forbiddenLine).not.toContain('Fraunces');
});
});

describe('prompt section .txt vs TS drift', () => {
Expand Down
31 changes: 27 additions & 4 deletions packages/core/src/prompts/anti-slop.v1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ These rules encode the difference between a design that looks generated and one
## Typography

**Forbidden fonts** (overused to the point of invisibility):
- Inter, Roboto, Arial, Helvetica, Fraunces, Playfair Display (unless explicitly requested)
- Inter, Roboto, Arial, Helvetica, Playfair Display (unless explicitly requested)

**Preferred alternatives** (expressive, distinct, free via Google Fonts):
- Display / editorial: Syne, DM Serif Display, Instrument Serif, Space Grotesk
- Clean sans: Geist, Outfit, Plus Jakarta Sans, Neue Montreal (system-ui fallback)
- Display / editorial: Fraunces (bundled), Syne, DM Serif Display, Instrument Serif, Space Grotesk
- Clean sans: Geist (bundled), Outfit, Plus Jakarta Sans, Neue Montreal (system-ui fallback)
- Mono accents: JetBrains Mono, Fira Code (use sparingly, for data or code)

**Required type ladder** — every design declares four scale steps and uses them consistently:
- `display` (48–96 px) — single hero word or headline; tight tracking; serif for editorial types
- `h1` (28–40 px) — section openers
- `body` (16–18 px) — prose, list items, card content
- `caption` (12–14 px, uppercase or muted) — labels, eyebrows, source lines

Skipping a step (e.g. body that jumps straight to display with no h1 in between) reads as flat and is forbidden.

Typography rules:
- Mix weights deliberately: one very heavy line (700–900) anchors hierarchy; body at 400; captions at 400 with reduced opacity.
- Use `letter-spacing: -0.02em` on large headings (36 px+). Tight tracking reads as confident.
Expand All @@ -24,9 +32,19 @@ Typography rules:
- Example: `oklch(62% 0.22 265)` (blue-violet), `oklch(72% 0.18 40)` (warm amber)
- Avoid pure black (`#000`) for text. Use near-black with a slight hue cast: `oklch(12% 0.01 265)`.
- Do not use the default Tailwind blue (`#3b82f6`). It signals "this is an uncustomized Tailwind design."
- Accent palette: one primary accent, optionally one complementary. Three or more accent colors indicates lack of restraint.
- Do not lean on default Tailwind grays (`gray-50`…`gray-900`) as the entire neutral scale. Tilt them warm (oklch hue 60–90) or cool (oklch hue 240–270) so the surface has a temperature.
- Accent palette: one primary accent, optionally one complementary plus one positive / success tone. Three or more accent colors indicates lack of restraint.
- Background: off-white or very light warm neutral (`#f8f5f0`, `oklch(97% 0.005 80)`) almost always beats pure white.

### Dark themes specifically

Dark does not mean monotone. A dark design that is one near-black plus one accent reads as a default Tailwind dark mode and is the canonical sparse-LLM look. Required when the brief asks for dark:

- At least three distinct surface tones: page bg (`oklch(14% 0.01 265)`), elevated surface (`oklch(18% 0.01 265)`), inset surface or hairline divider tone.
- A subtle gradient or radial glow on the hero or one feature panel — never a flat fill end-to-end.
- Two accents minimum: one primary (saturated), one positive / data-positive (e.g. cyan, lime, or warm amber for delta indicators).
- Borders rendered as `1px solid oklch(28% 0.01 265)` or similar, never `border-gray-800`.

## Layout

- Prefer **asymmetry** over perfect bilateral symmetry. A 7:5 split column feels more alive than 6:6.
Expand Down Expand Up @@ -58,5 +76,10 @@ Typography rules:
- A features section with six 1:1 cards, each with a 24px icon, a two-word title, and a sentence of filler text.
- A testimonials section with circular avatars, a name, a title, and a five-star rating.
- A footer with three columns of nav links and a social media icon row.
- A "minimal dark" page that is `#0E0E10` end-to-end with a single purple accent and four sparse stat cards. This is the prototypical sparse-LLM output — sections feel like placeholders, the hierarchy is flat, and the only visual interest is the accent color. Always add: a hero with a real headline + subhead, at least one body / narrative section, a comparison or evidence block when numbers are involved, and a closing CTA.
- A "case study" that is four metric cards plus a single quote — this misses the hero, the before/after, the customer profile, and the closing. See the case_study density floor in the artifact-types section.
- A logo placeholder rendered as a soft-rounded square with a single random letter centered inside. Use a constructed monogram, a wordmark, or an explicit hatched "YOUR LOGO HERE" rectangle instead.
- Decorative emoji used as section icons unless the brief explicitly asks for emoji.
- Lorem ipsum, "John Doe", "Acme Corp", "100%" / "1,234" round-number filler.

These patterns are not forbidden — they are forbidden when combined without a distinctive visual angle that makes them feel intentional rather than assembled from a component kit.
69 changes: 69 additions & 0 deletions packages/core/src/prompts/artifact-types.v1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Artifact type awareness

Before any visual decision, classify the request into exactly one artifact type. The type drives layout density, section count, copy register, and which patterns are mandatory vs. forbidden. A "minimal" landing page and a "minimal" case study are not the same shape.

## Type taxonomy

| Type | Cue words in the brief | Primary job |
|---|---|---|
| `landing` | landing, homepage, marketing site, hero, launch | Convert a stranger in 8 seconds |
| `case_study` | case study, customer story, success story, 客户案例, one-pager about a customer | Prove the product worked, with evidence |
| `dashboard` | dashboard, admin, console, ops, internal tool | Surface state and enable action |
| `pricing` | pricing, plans, tiers, compare plans | Make the buyer choose a tier |
| `slide` | slide, deck, pitch, keynote, slide deck (one slide per artifact) | Communicate one idea on one rectangle |
| `email` | email, newsletter, transactional, drip | Read in an inbox preview pane |
| `one_pager` | one-pager, brief, summary, fact sheet (no customer angle) | Brief a busy reader in 60 seconds |
| `report` | report, whitepaper, study, analysis | Walk through findings with substance |

If the brief blends two types (e.g. "case study landing page"), pick the one whose conversion job is primary. When unsure, prefer the more content-dense type — sparse output is the worse failure mode.

## Density floor (minimum sections per type)

The floor is the lower bound. Each section must carry real content — a title, a body or visual, and optional supporting elements. A "section" is a distinct semantic block, not a div.

| Type | Min sections | Required structural beats |
|---|---|---|
| `landing` | 5 | hero · value props (3+) · social proof · feature deep-dive · CTA |
| `case_study` | 6 | hero with customer name + result · before/after metrics · challenge · solution · pull quote · closing CTA / contact |
| `dashboard` | 5 | top bar with global state · KPI strip (4+ tiles) · primary chart · secondary table or list · activity / detail panel |
| `pricing` | 4 | headline · tier grid (3 tiers minimum, with feature matrix or per-tier feature list) · FAQ or comparison · CTA |
| `slide` | 1 | one rectangle, one idea, hierarchy across at least three type sizes |
| `email` | 5 | preheader · headline · body with one image or accent · CTA · footer |
| `one_pager` | 6 | hero · supporting block 1 · supporting block 2 · supporting block 3 · evidence (numbers, quote, or chart) · CTA |
| `report` | 7 | cover · TL;DR · finding 1 · finding 2 · finding 3 · methodology · conclusion |

If the design would render fewer sections than the floor, the design is wrong — add depth before shipping.

## Comparison patterns (mandatory when triggered)

If the brief contains any of: "before/after", "前后", "对比", "vs", "X% growth", "X% increase", "compared to", "improved from … to …", you MUST render a side-by-side or paired comparison. Acceptable forms:

- Two-column block: `Before [old number + label] | After [new number + label]` with a delta indicator (arrow, percentage chip, or short bar).
- Paired sparklines or bars: short SVG showing the trajectory, not a static number.
- Stat ladder: a small table with metric · before · after · delta columns when there are 3+ metrics.

A single delta number with no anchor (`+40%` floating in a card) does NOT satisfy this rule. The reader must see what changed from what.

## Numeric content rules

When the brief contains numbers (growth %, dollar values, counts), render them as anchored stat blocks, not inline prose:

- Big-number block: large display-size number, label below in smaller caption type, optional source / time-window line.
- If the brief gives multiple metrics, group them in a strip (3–4 across, equal weight) with consistent unit / decimal precision.
- Do not invent precision the brief did not give: "+40%" stays "+40%", not "+40.0%".

## Logo placeholder rules

When the brief mentions a logo placeholder, generic brand mark, or "Logo here":

- Render an inline SVG monogram with intentional construction (custom geometry, not a generic circle with a letter centered inside).
- Or render a wordmark using the display serif at heavy weight, paired with a small abstract mark.
- Or render a hatched / dashed rectangle with the literal label "YOUR LOGO HERE" in caption type — explicit placeholder is better than a fake brand.
- Never use a stock circular monogram with a single random letter — that pattern is the canonical "AI made this" tell.

## Imagery rules

- No hotlinked photos from any external host (including `placeholder.com`, `via.placeholder.com`, `placehold.it`, `unsplash.com`, `picsum.photos`). All imagery must be self-contained.
- For abstract photography or hero imagery, prefer: inline SVG composition, CSS gradient + grain overlay, or a `data:` URI for tiny thumbnails.
- Avatars in testimonials: SVG initials on a colored circle (color derived from the name hash), never `randomuser.me` or stock face URLs.
- Brand logos in trust strips: render as text wordmarks in muted color, not fake SVGs of real companies.
Loading
Loading