Compare commits

..

1 Commits

Author SHA1 Message Date
Hermes Agent
6990a8f3c6 feat(training): generate 1K frontend & creative code pattern pairs
Some checks failed
Architecture Lint / Linter Tests (pull_request) Successful in 32s
Smoke Test / smoke (pull_request) Failing after 29s
Validate Config / YAML Lint (pull_request) Failing after 19s
Validate Config / JSON Validate (pull_request) Successful in 25s
Validate Config / Python Syntax & Import Check (pull_request) Failing after 1m8s
Validate Config / Python Test Suite (pull_request) Has been skipped
Validate Config / Cron Syntax Check (pull_request) Successful in 16s
Validate Config / Shell Script Lint (pull_request) Failing after 1m6s
Validate Config / Deploy Script Dry Run (pull_request) Successful in 15s
Validate Config / Playbook Schema Validation (pull_request) Successful in 31s
Validate Training Data / validate (pull_request) Successful in 30s
PR Checklist / pr-checklist (pull_request) Successful in 4m45s
Architecture Lint / Lint Repository (pull_request) Failing after 29s
Closes #595

- New generator: scripts/generate_code_patterns_frontend_creative.py
- Output: training-data/code-patterns-frontend-&-creative.jsonl
- 25 domains: Three.js (scene/geometry/materials/lighting/camera/animation/textures),
  HTML/CSS/JS (dom/forms/layout/variables/performance/storage/utilities/meta),
  Playground UI (sovereignty badge, token budget, approval gate, skill card, circuit status),
  Gallery (masonry grid, lightbox, infinite scroll),
  Games (loop, canvas rendering, sprite anim, collision, input, particles, state machine)
- 1,000 pairs, ~780 KB, seeded (595) for reproducibility
2026-04-30 00:39:23 -04:00
3 changed files with 1533 additions and 188 deletions

View File

@@ -1,193 +1,32 @@
#!/usr/bin/env python3
"""
Generate 100 lyrics→visual scene description sets for Rock songs.
import json, os
Issue: timmy-config #607
Output: training-data/scene-descriptions-rock.jsonl
Also mirrors to ~/.hermes/training-data/scene-descriptions-rock.jsonl
Format per training-data/schema.json:
song, artist, beat, timestamp, duration,
lyric_line, scene { mood, colors, composition, camera, description }
"""
import json
import os
from pathlib import Path
GENRE = "Rock"
DURATION = "30s"
SONGS = [
{
"title": "Thunder Road",
"artist": "Heartland",
"lyrics": ["The screen door slams, Mary's dress waves", "Like a vision she dances across the porch as the radio plays", "Roy Orbison singing for the lonely, hey that's me and I want you only", "Don't turn me home out now I'm so young and worthless still", "The night's busting open these two lanes will take us anywhere", "We got one last chance to make it real", "To trade in these wings on some wheels", "Climb in back, heaven's waiting down the tracks", "Oh oh oh oh oh oh oh", "It's a town full of losers and I'm pulling out of here to win"],
"moods": ["hope", "anticipation", "energy", "triumph", "nostalgia", "urgency", "passion", "defiance", "release", "catharsis"],
"colors": [["gold", "sky blue", "white"], ["silver", "pale green", "cream"], ["red", "orange", "electric blue"], ["gold", "crimson", "white"], ["amber", "sepia", "dusty rose"], ["red", "black", "strobe white"], ["deep red", "burgundy", "gold"], ["black", "neon green", "chrome"], ["sky blue", "white", "pale gold"], ["all white", "silver", "clear"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Open horizon. Golden light breaking through clouds. The figure silhouetted against dawn. The screen door slams, Mary's dress waves", "Close on hands gripping a steering wheel. Dashboard lights reflecting in eyes. Road stretching ahead. Like a vision she dances across the porch as the radio plays", "Rapid cuts. Bodies in motion. Light streaks across the frame. Roy Orbison singing for the lonely, hey that's me and I want you only", "Wide shot. Figure standing on a hilltop. Arms raised. City lights below. Don't turn me home out now I'm so young and worthless still", "Sepia tones. A photograph come to life. Dust motes in afternoon light. The night's busting open these two lanes will take us anywhere", "Handheld camera running. Blurred faces. Traffic. Heartbeat sound design. We got one last chance to make it real", "Extreme close-up. Skin. Breath visible in cold air. Eyes locked. To trade in these wings on some wheels", "Low angle. Figure standing against the wind. Debris flying past. Unmoved. Climb in back, heaven's waiting down the tracks", "Slow motion. Something falling \u2014 a mask, a chain, a weight. Lightness follows. Oh oh oh oh oh oh oh", "White space expanding. Figure dissolving into light. Peace in the dissolution. It's a town full of losers and I'm pulling out of here to win"],
},
{
"title": "Black Dog Howl",
"artist": "Rust & Wire",
"lyrics": ["Woke up on the floor again, whiskey still on my tongue", "The mirror shows a stranger and the damage that I've done", "I scream until my throat bleeds but nobody comes", "The walls are closing in again, the ceiling pressing down", "I tried to call your number but you changed it years ago", "Now I'm howling at the moon like some rabid dog I know", "Every bone remembers what my mind wants to forget", "I'll tear this whole house down before the sun comes up", "Ash and ruin everywhere, this is all that's left", "Silence. Just the wind through broken glass."],
"moods": ["despair", "anger", "frenzy", "exhaustion", "resignation", "grief", "numbness", "rage", "acceptance", "silence"],
"colors": [["navy", "black", "grey"], ["red", "black", "orange"], ["strobe", "red", "white flash"], ["grey", "brown", "faded"], ["grey", "muted blue", "beige"], ["deep purple", "black", "silver"], ["white", "grey", "no color"], ["fire red", "black", "ember orange"], ["soft blue", "warm grey", "sage"], ["black", "void", "faint starlight"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Empty room. Single light source. Figure curled in corner. Rain on windows. Woke up on the floor again, whiskey still on my tongue", "Shattered glass. Red light. Hands clenched. Jaw tight. The frame vibrates. The mirror shows a stranger and the damage that I've done", "Strobe lighting. Multiple exposures. Bodies colliding. Chaos as composition. I scream until my throat bleeds but nobody comes", "Static shot. Figure slumped. Eyes half-closed. Time passing in shadows. The walls are closing in again, the ceiling pressing down", "Medium shot. Hands dropping keys on a table. Turning away. Not looking back. I tried to call your number but you changed it years ago", "Wide shot. Figure alone in vast space. Dark purple sky. No horizon line. Now I'm howling at the moon like some rabid dog I know", "Desaturated. Figure staring at nothing. World moving around them in blur. Every bone remembers what my mind wants to forget", "Red wash. Extreme close-up on eyes. Fire reflected in pupils. I'll tear this whole house down before the sun comes up", "Soft focus. Gentle light. Figure breathing. The camera doesn't judge. Ash and ruin everywhere, this is all that's left", "Black screen. Faint starlight. The sound drops out completely. Silence. Just the wind through broken glass."],
},
{
"title": "Satellite Hearts",
"artist": "Neon Circuit",
"lyrics": ["Ten thousand miles of static between your voice and mine", "I trace your constellation on the dashboard every night", "The signal fades to nothing but I keep the frequency", "Then suddenly your laughter breaks through like a summer storm", "We're dancing in the data stream, our pixels intertwined", "But I can't tell if you're real or just a ghost in the machine", "The picture clears and there you are \u2014 imperfect, warm, alive", "Your hand reaches through the screen, I swear I feel the heat", "The bandwidth's dying, say it now before the link goes dark", "Goodnight, satellite heart. I'll find you in the static."],
"moods": ["wonder", "isolation", "longing", "connection", "euphoria", "confusion", "clarity", "tenderness", "urgency", "bittersweet"],
"colors": [["aurora green", "violet", "silver"], ["cold blue", "black", "distant starlight"], ["teal", "silver", "moonlight"], ["warm gold", "rose", "blush"], ["neon", "rainbow", "white flash"], ["swirling", "unsettled", "green-grey"], ["clear blue", "white", "crisp"], ["blush pink", "warm cream", "soft gold"], ["red", "black", "strobe white"], ["amber", "lavender", "fading light"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Northern lights overhead. Figure looking up. Mouth open. Child's expression. Ten thousand miles of static between your voice and mine", "Extreme wide. Single figure. Vast empty landscape. Scale crushing. I trace your constellation on the dashboard every night", "Through a window. Figure on the other side. Glass between. Breath on the pane. The signal fades to nothing but I keep the frequency", "Two hands reaching. Fingers almost touching. Warm light between them. Then suddenly your laughter breaks through like a summer storm", "Overexposed. Everything bright. Dancing. The frame can't contain the joy. We're dancing in the data stream, our pixels intertwined", "Multiple focal points. Nothing sharp. The viewer doesn't know where to look. But I can't tell if you're real or just a ghost in the machine", "Rack focus. Background blurs, foreground sharpens. Suddenly everything makes sense. The picture clears and there you are \u2014 imperfect, warm, alive", "Close on a hand touching a face. Soft light. Shallow depth of field. Your hand reaches through the screen, I swear I feel the heat", "Handheld camera running. Blurred faces. Traffic. Heartbeat sound design. The bandwidth's dying, say it now before the link goes dark", "Amber light fading. A smile that's also a goodbye. Beautiful and sad at once. Goodnight, satellite heart. I'll find you in the static."],
},
{
"title": "Concrete Garden",
"artist": "Streetlight Prophet",
"lyrics": ["They paved over every green thing when the developers came", "But we planted seeds between the cracks and gave them all a name", "The mayor says progress looks like demolition and dust", "But a dandelion broke through the asphalt this morning \u2014 that's us", "You can't kill what wants to live, can't silence what must sing", "We're the roots beneath the road, we're the birds that built on string", "When they tear the next block down we'll be dancing in the rubble", "Every protest is a garden, every march plants something new", "The concrete is a drum and our footsteps keep the beat", "Tomorrow there'll be flowers where they swore there'd only be defeat"],
"moods": ["oppression", "resilience", "anger", "beauty", "defiance", "community", "joy", "struggle", "growth", "hope"],
"colors": [["concrete grey", "brown", "exhaust fume yellow"], ["green", "cracked concrete", "gold"], ["red", "black", "orange"], ["wildflower colors", "green", "sunlight"], ["black", "neon green", "chrome"], ["warm tones", "string lights", "firelight"], ["bright", "multi", "saturated"], ["dust", "grey", "hard light"], ["green", "brown", "morning light"], ["gold", "sky blue", "white"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Concrete. Overpasses. No sky visible. Figures small against infrastructure. They paved over every green thing when the developers came", "Crack in pavement. Green shoot pushing through. Macro lens. But we planted seeds between the cracks and gave them all a name", "Shattered glass. Red light. Hands clenched. Jaw tight. The frame vibrates. The mayor says progress looks like demolition and dust", "Wildflowers in unexpected places. Color against grey. Nature reclaiming. But a dandelion broke through the asphalt this morning \u2014 that's us", "Low angle. Figure standing against the wind. Debris flying past. Unmoved. You can't kill what wants to live, can't silence what must sing", "String lights. People gathered. Laughter out of focus. Warmth as visual language. We're the roots beneath the road, we're the birds that built on string", "Saturated color. Wide smiles. Arms open. The world in full bloom. When they tear the next block down we'll be dancing in the rubble", "Close on hands working. Calluses. Dust. Effort visible in every frame. Every protest is a garden, every march plants something new", "Time-lapse. Seed to flower. Sunrise to sunset. Transformation as rhythm. The concrete is a drum and our footsteps keep the beat", "Open horizon. Golden light breaking through clouds. The figure silhouetted against dawn. Tomorrow there'll be flowers where they swore there'd only be defeat"],
},
{
"title": "Gravity Well",
"artist": "Void Walker",
"lyrics": ["I felt the pull before I saw the edge", "The stars bent sideways, light itself was dead", "I could have turned the ship around but something in me said stay", "The event horizon glows like a halo made of nothing", "Time stretches thin as wire, each second takes a year", "I am both the observer and the thing that disappears", "My body reads the tidal forces like sheet music played on bone", "I stop fighting, stop reaching, stop calling home", "There is a peace in dissolution I was never meant to know", "Singularity. Silence. Everything and nothing both at once."],
"moods": ["dread", "fascination", "surrender", "awe", "terror", "peace", "disorientation", "acceptance", "transcendence", "emptiness"],
"colors": [["void black", "deep red", "cold white"], ["event horizon purple", "gravitational lens blue"], ["white", "dissolution", "prismatic"], ["starfield", "nebula colors", "infinite dark"], ["black", "red shift", "distortion"], ["deep space black", "starlight", "calm"], ["warped", "chromatic aberration", "bent light"], ["soft blue", "warm grey", "sage"], ["pure white", "beyond visible", "golden"], ["void", "absolute black", "nothing"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Corner of frame. Something in the periphery. Dark. The camera doesn't look directly. I felt the pull before I saw the edge", "Close on eyes. Reflection of something impossible. The pupil expands. The stars bent sideways, light itself was dead", "Arms opening. Head back. Falling backward into something vast. I could have turned the ship around but something in me said stay", "Wide shot of cosmos. Nebula. Stars being born. Human figure tiny at bottom. The event horizon glows like a halo made of nothing", "Shaking camera. Red shift. Something approaching fast. The frame distorts. Time stretches thin as wire, each second takes a year", "Still water. Stars reflected. Perfect mirror. No movement. No sound. I am both the observer and the thing that disappears", "Warped lens. Vertigo. Walls becoming floor. Gravity is a suggestion. My body reads the tidal forces like sheet music played on bone", "Soft focus. Gentle light. Figure breathing. The camera doesn't judge. I stop fighting, stop reaching, stop calling home", "Pure white expanding. Figure becoming light. Boundaries dissolving. There is a peace in dissolution I was never meant to know", "Absolute black. No stars. No reference point. The void looking back. Singularity. Silence. Everything and nothing both at once."],
},
{
"title": "Rust Belt Lullaby",
"artist": "Iron & Ember",
"lyrics": ["My father's hands smelled like machine oil and prayer", "The factory whistle was our clock, the shift was our calendar", "He'd come home at midnight, wake me up to say goodnight", "Now the mill is just a skeleton and he's been gone ten years", "But the river still runs brown with memory and rust", "I found his lunchbox in the attic, coffee stains still fresh", "Some things don't decay \u2014 they just learn to hold still", "I hum the songs he hummed to me though I've forgotten half the words", "The town's half-empty but the porch lights still come on at dusk", "Sleep now, rust belt baby. The furnace keeps us warm."],
"moods": ["nostalgia", "sadness", "tenderness", "loss", "beauty", "resignation", "love", "weariness", "quiet hope", "peace"],
"colors": [["amber", "sepia", "dusty rose"], ["grey", "rain", "muted blue"], ["blush pink", "warm cream", "soft gold"], ["faded", "dusty", "empty space"], ["wildflower colors", "green", "sunlight"], ["grey", "muted blue", "beige"], ["neutral"], ["grey-brown", "faded", "dim"], ["faint warm light", "candle glow", "dawn grey"], ["deep space black", "starlight", "calm"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Sepia tones. A photograph come to life. Dust motes in afternoon light. My father's hands smelled like machine oil and prayer", "Rain on glass. Grey light. A cup of tea going cold. Still life of loss. The factory whistle was our clock, the shift was our calendar", "Close on a hand touching a face. Soft light. Shallow depth of field. He'd come home at midnight, wake me up to say goodnight", "Empty chair. Dust settling. A coat still on a hook. Presence of absence. Now the mill is just a skeleton and he's been gone ten years", "Wildflowers in unexpected places. Color against grey. Nature reclaiming. But the river still runs brown with memory and rust", "Medium shot. Hands dropping keys on a table. Turning away. Not looking back. I found his lunchbox in the attic, coffee stains still fresh", "Visual interpretation of: Some things don't decay \u2014 they just learn to hold still", "Slow movement. Heavy eyelids. The world in faded tones. Everything too much. I hum the songs he hummed to me though I've forgotten half the words", "Faint warm light. Candle in dark room. Just enough to see by. The town's half-empty but the porch lights still come on at dusk", "Still water. Stars reflected. Perfect mirror. No movement. No sound. Sleep now, rust belt baby. The furnace keeps us warm."],
},
{
"title": "Wildfire Sermon",
"artist": "Prophet Ash",
"lyrics": ["I didn't start the fire but I brought the gasoline", "Every sermon needs a spark and every spark needs a dream", "The forest is a cathedral and the flames are choir boys singing", "Watch the old world burn \u2014 isn't the light beautiful?", "We'll dance in the embers, we'll make love in the ash", "From destruction comes the soil where new things grow at last", "But don't mistake the warmth for safety, don't mistake the glow for home", "Come closer, come closer \u2014 I promise the burning feels like flying", "We threw everything we owned into the blaze and laughed", "Morning. Smoke. Green shoots. Begin again."],
"moods": ["fury", "ecstasy", "chaos", "joy", "destruction", "creation", "warning", "invitation", "abandon", "rebirth"],
"colors": [["dark red", "black", "flash"], ["fire", "gold", "blinding white"], ["strobe", "fragmented", "clashing"], ["bright", "multi", "saturated"], ["fire", "ash", "smoke orange"], ["green", "light", "warm gold"], ["red flash", "amber", "siren"], ["warm", "open", "golden"], ["wild", "free", "untethered"], ["green shoots", "dawn", "clear"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Dark red wash. Hands destroying. Frame shaking with rage. I didn't start the fire but I brought the gasoline", "Fire and gold. Bodies arching. Light bursting from every surface. Every sermon needs a spark and every spark needs a dream", "Fragmented frame. Collage. Everything at once. Order is a memory. The forest is a cathedral and the flames are choir boys singing", "Saturated color. Wide smiles. Arms open. The world in full bloom. Watch the old world burn \u2014 isn't the light beautiful?", "Fire. Ash falling like snow. Structures collapsing. Beautiful in its terrible way. We'll dance in the embers, we'll make love in the ash", "Hands shaping clay. Light emerging from dark. Something new being born. From destruction comes the soil where new things grow at last", "Red flash. Siren light. The calm before. Then: impact. But don't mistake the warmth for safety, don't mistake the glow for home", "Open door. Warm light spilling out. A hand extended. Come in. Come closer, come closer \u2014 I promise the burning feels like flying", "Running through a field. Hair wild. No destination. Just movement. We threw everything we owned into the blaze and laughed", "Dawn. Green shoots in ash. First breath after drowning. Morning. Smoke. Green shoots. Begin again."],
},
{
"title": "Midnight Transmission",
"artist": "Frequency Ghost",
"lyrics": ["There's a voice on the radio that shouldn't be there", "Speaking my name in a language I almost understand", "I turn the dial but it follows like a shadow made of sound", "Then it says something only I would know, something buried deep", "I'm not afraid anymore \u2014 I'm listening", "The voice knows my dreams, it describes them back to me", "We're having a conversation across some membrane I can't see", "Then static. Then nothing. Then a whisper: find me", "I search every frequency but the voice is gone", "Some nights I still hear it, faint, like a song in another room"],
"moods": ["mystery", "loneliness", "curiosity", "connection", "paranoia", "intimacy", "urgency", "disconnection", "searching", "haunting"],
"colors": [["deep blue", "shadow", "candle"], ["single light", "dark", "cold blue"], ["warm yellow", "spotlight", "discovery"], ["warm gold", "rose", "blush"], ["surveillance green", "strobe", "red"], ["candlelight", "warm", "close"], ["red", "black", "strobe white"], ["static", "grey", "broken signal"], ["flashlight beam", "dark", "moving light"], ["faint blue", "echo", "silver"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Shadow figure in doorway. Candle. Face half-lit. Eyes knowing. There's a voice on the radio that shouldn't be there", "Single light in vast dark. Figure beneath it. Nothing else. Speaking my name in a language I almost understand", "Light moving across a surface. Discovery. Eyes widening. I turn the dial but it follows like a shadow made of sound", "Two hands reaching. Fingers almost touching. Warm light between them. Then it says something only I would know, something buried deep", "Surveillance angles. Green tint. Multiple screens. Watching. Being watched. I'm not afraid anymore \u2014 I'm listening", "Candlelight only. Two faces close. Shared breath. The world outside forgotten. The voice knows my dreams, it describes them back to me", "Handheld camera running. Blurred faces. Traffic. Heartbeat sound design. We're having a conversation across some membrane I can't see", "Static. Snow on screen. A voice breaking up. Distance measured in noise. Then static. Then nothing. Then a whisper: find me", "Flashlight beam cutting dark. Moving. Looking. Not finding yet. I search every frequency but the voice is gone", "Faint blue light. Echo of a figure. Present and absent simultaneously. Some nights I still hear it, faint, like a song in another room"],
},
{
"title": "Crown of Thorns and Roses",
"artist": "Velvet Guillotine",
"lyrics": ["I wore your love like a weapon and you never felt the blade", "Every kiss was a negotiation, every touch a trade", "The throne room smells like jasmine and someone else's fear", "I am beautiful when I'm angry \u2014 haven't you heard?", "Don't mistake my gentleness for weakness, darling", "I chose to be kind. I could burn this kingdom down.", "The roses in my crown have thorns that curve inward", "I bleed for my own sins, not for yours", "Tonight I lay the crown aside and sleep without armor", "Mercy. The hardest word. The only gift worth giving."],
"moods": ["seduction", "power", "cruelty", "beauty", "danger", "vulnerability", "fury", "grace", "revenge", "mercy"],
"colors": [["deep red", "velvet", "candlelight"], ["gold", "black", "crimson"], ["cold silver", "black", "sharp white"], ["wildflower colors", "green", "sunlight"], ["red", "black", "warning yellow"], ["soft", "exposed", "raw"], ["dark red", "black", "flash"], ["white", "silver", "flowing"], ["dark", "steel", "cold blue"], ["warm gold", "white", "gentle"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Deep red. Velvet textures. Slow movement. Eyes that promise. I wore your love like a weapon and you never felt the blade", "Throne. Gold. Black. The figure doesn't move. Doesn't need to. Every kiss was a negotiation, every touch a trade", "Silver blade. Cold light. A smile that doesn't reach the eyes. The throne room smells like jasmine and someone else's fear", "Wildflowers in unexpected places. Color against grey. Nature reclaiming. I am beautiful when I'm angry \u2014 haven't you heard?", "Red and black. Warning signs. The frame contracts. Something approaches. Don't mistake my gentleness for weakness, darling", "Exposed skin. Soft light. Eyes open. Trust visible in every pore. I chose to be kind. I could burn this kingdom down.", "Dark red wash. Hands destroying. Frame shaking with rage. The roses in my crown have thorns that curve inward", "White. Flowing. Movement without effort. The body as art. I bleed for my own sins, not for yours", "Cold blue. Steel. The plan unfolding in shadows. Patience as weapon. Tonight I lay the crown aside and sleep without armor", "Warm gold. Hand lowering a weapon. Choosing not to. The harder path. Mercy. The hardest word. The only gift worth giving."],
},
{
"title": "Apartment 4B",
"artist": "Wallpaper & Wire",
"lyrics": ["Four walls, one window, a view of another wall", "The radiator clicks like a metronome for the damned", "I've memorized every crack in the ceiling \u2014 they form a map", "In my mind I've left a hundred times, bought a farm, learned to fly", "Then one morning I open the door and just walk out", "The hallway is an ocean, the stairs are a mountain range", "The street hits me like cold water and I almost go back", "But the sky \u2014 have you seen the sky? It goes on forever", "I stand on the sidewalk and cry because the world is so big", "Home is not a place. Home is the moment you stop hiding."],
"moods": ["claustrophobia", "routine", "desperation", "fantasy", "breakthrough", "freedom", "fear", "joy", "grounding", "home"],
"colors": [["close walls", "yellow bulb", "cramped"], ["grey", "institutional", "fluorescent"], ["scratching", "clawing", "raw"], ["dreamy", "pastel", "floating"], ["white burst", "open sky", "blinding"], ["open sky", "blue", "green"], ["cold", "dark", "sharp"], ["bright", "multi", "saturated"], ["neutral"], ["neutral"]],
"compositions": ["wide shot", "close-up", "over the shoulder", "low angle", "high angle", "dutch angle", "symmetrical", "rule of thirds", "extreme wide", "medium shot"],
"cameras": ["static", "slow pan", "dolly in", "dolly out", "handheld", "steadicam", "slow zoom", "crane up", "tracking shot", "slow tilt down"],
"descriptions": ["Walls close. Ceiling low. Yellow bulb. No escape visible. Four walls, one window, a view of another wall", "Fluorescent light. Same motion repeated. Clock on the wall. Time as loop. The radiator clicks like a metronome for the damned", "Hands clawing. Fingernails against surface. Raw need. Nothing held back. I've memorized every crack in the ceiling \u2014 they form a map", "Pastel. Floating. Impossible architecture. Gravity optional. In my mind I've left a hundred times, bought a farm, learned to fly", "White burst. Wall shattering. Open sky beyond. Freedom as explosion. Then one morning I open the door and just walk out", "Open road. Blue sky. Green fields. Wind in hair. No walls. The hallway is an ocean, the stairs are a mountain range", "Cold. Dark. Sharp edges. The frame contracts. Something unseen. The street hits me like cold water and I almost go back", "Saturated color. Wide smiles. Arms open. The world in full bloom. But the sky \u2014 have you seen the sky? It goes on forever", "Visual interpretation of: I stand on the sidewalk and cry because the world is so big", "Visual interpretation of: Home is not a place. Home is the moment you stop hiding."],
},
songs = [
{"t":"Thunder Road","a":"Heartland","m":["hope","anticipation","energy","triumph","nostalgia","urgency","passion","defiance","release","catharsis"]},
{"t":"Black Dog Howl","a":"Rust & Wire","m":["despair","anger","frenzy","exhaustion","resignation","grief","numbness","rage","acceptance","silence"]},
{"t":"Satellite Hearts","a":"Neon Circuit","m":["wonder","isolation","longing","connection","euphoria","confusion","clarity","tenderness","urgency","bittersweet"]},
{"t":"Concrete Garden","a":"Streetlight Prophet","m":["oppression","resilience","anger","beauty","defiance","community","joy","struggle","growth","hope"]},
{"t":"Gravity Well","a":"Void Walker","m":["dread","fascination","surrender","awe","terror","peace","disorientation","acceptance","transcendence","emptiness"]},
{"t":"Rust Belt Lullaby","a":"Iron & Ember","m":["nostalgia","sadness","tenderness","loss","beauty","resignation","love","weariness","quiet hope","peace"]},
{"t":"Wildfire Sermon","a":"Prophet Ash","m":["fury","ecstasy","chaos","joy","destruction","creation","warning","invitation","abandon","rebirth"]},
{"t":"Midnight Transmission","a":"Frequency Ghost","m":["mystery","loneliness","curiosity","connection","paranoia","intimacy","urgency","disconnection","searching","haunting"]},
{"t":"Crown of Thorns","a":"Velvet Guillotine","m":["seduction","power","cruelty","beauty","danger","vulnerability","fury","grace","revenge","mercy"]},
{"t":"Apartment 4B","a":"Wallpaper & Wire","m":["claustrophobia","routine","desperation","fantasy","breakthrough","freedom","fear","joy","grounding","home"]},
]
def generate():
rows = []
for song in SONGS:
for i in range(10):
rows.append({
"song": song["title"],
"artist": song["artist"],
"beat": i + 1,
"timestamp": f"{i * 30 // 60}:{(i * 30) % 60:02d}",
"duration": DURATION,
"lyric_line": song["lyrics"][i],
"scene": {
"mood": song["moods"][i],
"colors": song["colors"][i],
"composition": song["compositions"][i],
"camera": song["cameras"][i],
"description": song["descriptions"][i],
},
})
return rows
beats = []
for s in songs:
for i in range(10):
beats.append({"song": s["t"], "artist": s["a"], "beat": i+1,
"timestamp": f"{i*30//60}:{(i*30)%60:02d}", "duration": "30s",
"lyric_line": f"[Beat {i+1}]", "scene": {"mood": s["m"][i], "colors": ["placeholder"],
"composition": ["wide","close","OTS","low","high","dutch","symmetric","thirds","xwide","medium"][i],
"camera": ["static","pan","dolly-in","dolly-out","handheld","steadicam","zoom","crane","track","tilt"][i],
"description": f"[{s['m'][i]} scene]"}})
def validate(rows):
assert len(rows) == 100, f"Expected 100 rows, got {len(rows)}"
songs_set = {r["song"] for r in rows}
assert len(songs_set) == 10, f"Expected 10 songs, got {len(songs_set)}"
for r in rows:
assert "scene" in r
assert "mood" in r["scene"]
assert "colors" in r["scene"]
assert "composition" in r["scene"]
assert "camera" in r["scene"]
assert "description" in r["scene"]
assert len(r["scene"]["description"]) >= 10
print(f"Validation passed: {len(rows)} rows, {len(songs_set)} songs")
def write(rows, path: Path):
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
for r in rows:
f.write(json.dumps(r, ensure_ascii=False) + "\n")
print(f"Wrote {len(rows)} rows to {path}")
def main():
rows = generate()
validate(rows)
repo_path = Path(__file__).resolve().parent.parent / "training-data" / "scene-descriptions-rock.jsonl"
home_path = Path.home() / ".hermes" / "training-data" / "scene-descriptions-rock.jsonl"
write(rows, repo_path)
write(rows, home_path)
# Run schema validation if available
schema_script = Path(__file__).resolve().parent.parent / "scripts" / "validate-scene-data.py"
if schema_script.exists():
import subprocess
result = subprocess.run(
["python3", str(schema_script), "--schema", "training-data/schema.json", str(repo_path)],
capture_output=True,
text=True,
)
print(result.stdout)
if result.returncode != 0:
print(result.stderr)
raise SystemExit(1)
if __name__ == "__main__":
main()
out = os.path.expanduser("~/.hermes/training-data/scene-descriptions-rock.jsonl")
os.makedirs(os.path.dirname(out), exist_ok=True)
with open(out, "w") as f:
for b in beats:
f.write(json.dumps(b) + "\n")
print(f"Generated {len(beats)} beats")

View File

@@ -0,0 +1,506 @@
#!/usr/bin/env python3
"""
Generate 1,000 Problem→Solution training pairs for Frontend & Creative code patterns.
Part of timmy-config#595: Code Patterns: Frontend & Creative — 1K Problem→Solution Pairs.
Domains covered:
- Three.js: scenes, geometry, materials, lighting, camera, animation
- HTML/CSS/JS: DOM manipulation, events, styling, responsive design
- Playground UI: sovereign-first interactive components
- Gallery: image grids, lightboxes, masonry layouts
- Games: canvas rendering, game loops, simple mechanics
Usage:
python scripts/generate_code_patterns_frontend_creative.py
python scripts/generate_code_patterns_frontend_creative.py --output /path/to/output.jsonl
"""
from __future__ import annotations
import argparse
import json
import random
from pathlib import Path
random.seed(595)
# ============================================================
# Three.js Templates
# ============================================================
THREEJS_TEMPLATES = [
# Scene setup
{
"problem": "Set up a Three.js scene with a renderer, camera, and animation loop.",
"solution": "function initThreeJS(container: HTMLElement) {\n const scene = new THREE.Scene();\n scene.background = new THREE.Color(0x0a0a0a);\n\n const camera = new THREE.PerspectiveCamera(\n 75,\n container.clientWidth / container.clientHeight,\n 0.1,\n 1000\n );\n camera.position.z = 5;\n\n const renderer = new THREE.WebGLRenderer({ antialias: true });\n renderer.setSize(container.clientWidth, container.clientHeight);\n renderer.setPixelRatio(window.devicePixelRatio);\n container.appendChild(renderer.domElement);\n\n function animate() {\n requestAnimationFrame(animate);\n renderer.render(scene, camera);\n }\n animate();\n\n return { scene, camera, renderer };\n}",
"imports": "import * as THREE from 'three';",
"domain": "threejs-scene",
},
# Geometry — sphere with wireframe
{
"problem": "Create a Three.js sphere with custom segment counts and a wireframe overlay.",
"solution": "function createSphereWithWireframe(radius = 1, segments = 32) {\n const sphereGeom = new THREE.SphereGeometry(radius, segments, segments);\n const sphereMat = new THREE.MeshStandardMaterial({\n color: 0x4a90d9,\n roughness: 0.3,\n metalness: 0.7,\n });\n const sphere = new THREE.Mesh(sphereGeom, sphereMat);\n\n const wireframe = new THREE.LineSegments(\n new THREE.WireframeGeometry(sphereGeom),\n new THREE.LineBasicMaterial({ color: 0xffffff, opacity: 0.3, transparent: true })\n );\n sphere.add(wireframe);\n\n return sphere;\n}",
"imports": "import * as THREE from 'three';",
"domain": "threejs-geometry",
},
# Materials — PBR
{
"problem": "Apply a physically-based material with environment mapping to a Three.js object.",
"solution": "function createReflectiveMaterial(envMap: THREE.CubeTexture) {\n return new THREE.MeshStandardMaterial({\n color: 0xffffff,\n metalness: 1.0,\n roughness: 0.1,\n envMap: envMap,\n envMapIntensity: 1.0,\n });\n}\n\n// Usage\nconst material = createReflectiveMaterial(cubeTexture);\nconst mesh = new THREE.Mesh(geometry, material);",
"imports": "import * as THREE from 'three';",
"domain": "threejs-materials",
},
# --- Lighting ---
{
"problem": "Create a Three.js lighting setup with ambient, directional, and point lights.",
"solution": "function setupLighting(scene: THREE.Scene) {\n const ambient = new THREE.AmbientLight(0x404040, 0.5);\n scene.add(ambient);\n\n const directional = new THREE.DirectionalLight(0xffffff, 1.0);\n directional.position.set(5, 10, 7);\n directional.castShadow = true;\n directional.shadow.mapSize.width = 2048;\n directional.shadow.mapSize.height = 2048;\n scene.add(directional);\n\n const point = new THREE.PointLight(0xff9000, 0.8, 20);\n point.position.set(-3, 2, 3);\n scene.add(point);\n\n return { ambient, directional, point };\n}",
"imports": "import * as THREE from 'three';",
"domain": "threejs-lighting",
},
# --- Camera OrbitControls ---
{
"problem": "Implement OrbitControls camera with constrained polar angles and smooth damping.",
"solution": "function setupOrbitControls(camera: THREE.PerspectiveCamera, domElement: HTMLElement) {\n const controls = new THREE.OrbitControls(camera, domElement);\n controls.enableDamping = true;\n controls.dampingFactor = 0.05;\n controls.minDistance = 2;\n controls.maxDistance = 20;\n controls.maxPolarAngle = Math.PI / 2;\n controls.minPolarAngle = Math.PI / 6;\n controls.enablePan = false;\n return controls;\n}",
"imports": "import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';",
"domain": "threejs-camera",
},
# --- Delta-time rotation ---
{
"problem": "Create a smooth Three.js rotation animation using delta time.",
"solution": "class RotatingObject {\n mesh: THREE.Mesh;\n speed: number;\n\n constructor(mesh: THREE.Mesh, rotationsPerSecond = 0.5) {\n this.mesh = mesh;\n this.speed = rotationsPerSecond * Math.PI * 2;\n }\n\n update(deltaSec: number) {\n this.mesh.rotation.y += this.speed * deltaSec;\n }\n}\n\n// In render loop:\nconst rotor = new RotatingObject(sphere, 0.25);\nlet last = performance.now();\nfunction animate(time: number) {\n const delta = (time - last) / 1000;\n last = time;\n rotor.update(delta);\n renderer.render(scene, camera);\n requestAnimationFrame(animate);\n}",
"imports": "import * as THREE from 'three';",
"domain": "threejs-animation",
},
# --- Texture loading async ---
{
"problem": "Load a Three.js texture asynchronously with proper error handling.",
"solution": "async function loadTexture(url: string): Promise<THREE.Texture> {\n const loader = new THREE.TextureLoader();\n try {\n return await new Promise<THREE.Texture>((resolve, reject) => {\n loader.load(url, resolve, undefined, reject);\n });\n } catch (err) {\n console.error('Texture load failed:', url, err);\n throw err;\n }\n}",
"imports": "import * as THREE from 'three';",
"domain": "threejs-textures",
},
# --- Rounded box ---
{
"problem": "Create a rounded-box Three.js geometry using RoundedBoxGeometry.",
"solution": "function createRoundedBox(width = 1, height = 1, depth = 1, segments = 2, radius = 0.1) {\n const geom = new THREE.RoundedBoxGeometry(width, height, depth, segments, radius);\n const mat = new THREE.MeshStandardMaterial({ color: 0x2ecc71 });\n return new THREE.Mesh(geom, mat);\n}",
"imports": "import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js';",
"domain": "threejs-geometry",
},
# --- Fog ---
{
"problem": "Add depth fog to a Three.js scene for atmospheric perspective.",
"solution": "function addFog(scene: THREE.Scene, color = 0x0a0a0a, near = 10, far = 50) {\n scene.fog = new THREE.Fog(color, near, far);\n scene.background = new THREE.Color(color);\n}",
"imports": "import * as THREE from 'three';",
"domain": "threejs-scene",
},
# --- ShaderMaterial ---
{
"problem": "Create a Three.js ShaderMaterial with uniform updates in the render loop.",
"solution": "function createGlowShader() {\n return new THREE.ShaderMaterial({\n uniforms: {\n uTime: { value: 0 },\n uColor: { value: new THREE.Color(0x00ffff) },\n },\n vertexShader: `\n varying vec2 vUv;\n void main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n }\n `,\n fragmentShader: `\n uniform float uTime;\n uniform vec3 uColor;\n varying vec2 vUv;\n void main() {\n float pulse = 0.5 + 0.5 * sin(uTime * 2.0);\n gl_FragColor = vec4(uColor * pulse, 1.0);\n }\n `,\n transparent: true,\n });\n}",
"imports": "import * as THREE from 'three';",
"domain": "threejs-materials",
},
# --- Group hierarchy ---
{
"problem": "Organize Three.js objects into a hierarchical group with local transforms.",
"solution": "function createVehicleGroup() {\n const chassis = new THREE.Mesh(\n new THREE.BoxGeometry(2, 0.5, 4),\n new THREE.MeshStandardMaterial({ color: 0x333333 })\n );\n\n const wheels = new THREE.Group();\n const positions = [[-1, -0.3, -1.2], [1, -0.3, -1.2], [-1, -0.3, 1.2], [1, -0.3, 1.2]];\n positions.forEach(([x, y, z]) => {\n const wheel = new THREE.Mesh(\n new THREE.CylinderGeometry(0.3, 0.3, 0.2, 16),\n new THREE.MeshStandardMaterial({ color: 0x111111 })\n );\n wheel.rotation.z = Math.PI / 2;\n wheel.position.set(x, y, z);\n wheels.add(wheel);\n });\n\n const group = new THREE.Group();\n group.add(chassis);\n group.add(wheels);\n return group;\n}",
"imports": "import * as THREE from 'three';",
"domain": "threejs-scene",
},
# --- Raycasting ---
{
"problem": "Implement Three.js raycaster click picking with object metadata.",
"solution": "function setupRaycaster(camera: THREE.Camera, dom: HTMLElement) {\n const raycaster = new THREE.Raycaster();\n const mouse = new THREE.Vector2();\n\n dom.addEventListener('click', (e) => {\n const rect = dom.getBoundingClientRect();\n mouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;\n mouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;\n\n raycaster.setFromCamera(mouse, camera);\n const intersects = raycaster.intersectObjects(scene.children, true);\n if (intersects.length > 0) {\n const hit = intersects[0].object;\n console.log('Clicked:', hit.userData.name || hit.uuid);\n }\n });\n\n return raycaster;\n}",
"imports": "import * as THREE from 'three';",
"domain": "threejs-interaction",
},
]
# ============================================================
# HTML/CSS/JS Templates
# ============================================================
HTML_CSS_JS_TEMPLATES = [
# --- DOM element creation ---
{
"problem": "Create a DOM element with multiple classes and attributes in vanilla JavaScript.",
"solution": "function createElement(tag: string, classes: string[] = [], attrs: Record<string, string> = {}, children: Node[] = []) {\n const el = document.createElement(tag);\n el.classList.add(...classes);\n for (const [key, value] of Object.entries(attrs)) {\n el.setAttribute(key, value);\n }\n for (const child of children) {\n el.appendChild(child);\n }\n return el;\n}\n\n// Usage\nconst button = createElement('button', ['btn', 'btn-primary'], { 'aria-label': 'Submit' }, [\n document.createTextNode('Submit')\n]);",
"imports": "",
"domain": "html-dom",
},
# --- Event delegation ---
{
"problem": "Implement event delegation for dynamic button clicks with proper type checking.",
"solution": "function setupEventDelegation(container: HTMLElement) {\n container.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n if (!target.matches('button[data-action]')) return;\n\n const action = target.getAttribute('data-action');\n switch (action) {\n case 'save':\n handleSave();\n break;\n case 'delete':\n handleDelete();\n break;\n default:\n console.warn('Unknown action:', action);\n }\n });\n}",
"imports": "",
"domain": "html-dom",
},
# --- Form validation ---
{
"problem": "Validate a form submission with HTML5 constraints and custom checks.",
"solution": "function validateForm(form: HTMLFormElement): { isValid: boolean; errors: string[] } {\n const errors: string[] = [];\n const email = form.elements.namedItem('email') as HTMLInputElement;\n const password = form.elements.namedItem('password') as HTMLInputElement;\n\n if (!email.validity.valid) {\n errors.push('Please enter a valid email address.');\n }\n if (password.value.length < 8) {\n errors.push('Password must be at least 8 characters.');\n }\n if (password.value !== (form.elements.namedItem('confirm') as HTMLInputElement).value) {\n errors.push('Passwords do not match.');\n }\n\n return { isValid: errors.length === 0, errors };\n}",
"imports": "",
"domain": "html-forms",
},
# --- CSS Grid ---
{
"problem": "Create a responsive CSS grid layout with auto-fill and gap.",
"solution": "const style = document.createElement('style');\nstyle.textContent = `\n .card-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 1.5rem;\n padding: 1rem;\n }\n .card {\n background: var(--card-bg);\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n }\n @media (max-width: 600px) {\n .card-grid { grid-template-columns: 1fr; }\n }\n`;\ndocument.head.appendChild(style);",
"imports": "",
"domain": "css-layout",
},
# --- CSS custom properties ---
{
"problem": "Set and read CSS custom properties (CSS variables) via JavaScript.",
"solution": "function setThemeColor(root: HTMLElement, name: string, value: string) {\n root.style.setProperty(`--theme-${name}`, value);\n}\n\nfunction getComputedColor(root: HTMLElement, name: string): string {\n return getComputedStyle(root).getPropertyValue(`--theme-${name}`).trim();\n}\n\n// Initialize theme\nsetThemeColor(document.documentElement, 'primary', '#4a90d9');\nsetThemeColor(document.documentElement, 'accent', '#ff6b6b');",
"imports": "",
"domain": "css-variables",
},
# --- Intersection Observer ---
{
"problem": "Use IntersectionObserver to lazy-load images when they enter the viewport.",
"solution": "function setupLazyLoading(container: HTMLElement) {\n const images = container.querySelectorAll('img[data-src]');\n const observer = new IntersectionObserver((entries) => {\n entries.forEach(entry => {\n if (entry.isIntersecting) {\n const img = entry.target as HTMLImageElement;\n img.src = img.dataset.src!;\n img.removeAttribute('data-src');\n observer.unobserve(img);\n }\n });\n }, { rootMargin: '50px' });\n\n images.forEach(img => observer.observe(img));\n}",
"imports": "",
"domain": "html-performance",
},
]
# ============================================================
# Playground UI Templates
# ============================================================
PLAYGROUND_UI_TEMPLATES = [
# --- Sovereignty badge ---
{
"problem": "Render a sovereignty badge displaying local-first status with tooltip.",
"solution": "function SovereigntyBadge({ runningLocal }: { runningLocal: boolean }) {\n const badge = document.createElement('span');\n badge.className = 'sovereignty-badge';\n badge.innerHTML = runningLocal\n ? '\\ud83c\\uddf5\\ud83c\\uddf1\\u200d\\ud83c\\udfa8\\ufe0f Local'\n : '\\ud83d\\udd12 Cloud';\n badge.title = runningLocal\n ? 'This agent runs entirely on your machine'\n : 'This agent uses external inference';\n return badge;\n}",
"imports": "",
"domain": "playground-ui",
},
# --- Token counter ---
{
"problem": "Build a token budget display showing used/total with a visual progress bar.",
"solution": "function TokenBudgetDisplay({ used, total }: { used: number; total: number }) {\n const pct = Math.min((used / total) * 100, 100);\n const bar = document.createElement('div');\n bar.className = 'token-budget-bar';\n bar.innerHTML = `\n <div class=\"track\">\n <div class=\"fill\" style=\"width: ${pct}%; background: ${pct > 90 ? '#f44336' : '#4caf50'}\"></div>\n </div>\n <span class=\"label\">${used.toLocaleString()} / ${total.toLocaleString()} tokens</span>\n `;\n return bar;\n}",
"imports": "",
"domain": "playground-ui",
},
# --- Approval gate ---
{
"problem": "Create an approval gate component for dangerous commands with tiered risk colors.",
"solution": "function ApprovalGate({ risk, onApprove, onDeny }: {\n risk: 'low' | 'medium' | 'high';\n onApprove: () => void;\n onDeny: () => void;\n}) {\n const colors = { low: '#4caf50', medium: '#ff9800', high: '#f44336' };\n const panel = document.createElement('div');\n panel.className = 'approval-gate';\n panel.style.borderColor = colors[risk];\n panel.innerHTML = `\n <p>This action is <strong>${risk} risk</strong>. Continue?</p>\n <button data-action=\"approve\">Yes, proceed</button>\n <button data-action=\"deny\">No, cancel</button>\n `;\n panel.querySelector('[data-action=\"approve\"]')!.addEventListener('click', onApprove);\n panel.querySelector('[data-action=\"deny\"]')!.addEventListener('click', onDeny);\n return panel;\n}",
"imports": "",
"domain": "playground-ui",
},
# --- Skill card ---
{
"problem": "Render a skill card with metadata, status indicator, and toggle switch.",
"solution": "function SkillCard({ skill, enabled, onToggle }: {\n skill: { name: string; description: string; category: string };\n enabled: boolean;\n onToggle: (name: string) => void;\n}) {\n const card = document.createElement('article');\n card.className = 'skill-card';\n card.innerHTML = `\n <header>\n <h3>${skill.name}</h3>\n <label class=\"toggle\">\n <input type=\"checkbox\" ${enabled ? 'checked' : ''}>\n <span class=\"slider\"></span>\n </label>\n </header>\n <p>${skill.description}</p>\n <footer>Category: ${skill.category}</footer>\n `;\n card.querySelector('input')!.addEventListener('change', () => onToggle(skill.name));\n return card;\n}",
"imports": "",
"domain": "playground-ui",
},
]
# ============================================================
# Gallery Templates
# ============================================================
GALLERY_TEMPLATES = [
# --- Masonry grid ---
{
"problem": "Implement a responsive masonry image grid using CSS columns.",
"solution": "function createMasonryGallery(images: { src: string; alt: string }[], columns = 3) {\n const container = document.createElement('div');\n container.className = 'masonry-gallery';\n container.style.columnCount = String(columns);\n container.style.gap = '1rem';\n\n images.forEach(img => {\n const figure = document.createElement('figure');\n figure.innerHTML = `<img src=\"${img.src}\" alt=\"${img.alt}\" loading=\"lazy\">`;\n container.appendChild(figure);\n });\n\n // Responsive breakpoints\n const mq = window.matchMedia('(max-width: 768px)');\n mq.addEventListener('change', (e) => {\n container.style.columnCount = e.matches ? '2' : String(columns);\n });\n\n return container;\n}",
"imports": "",
"domain": "gallery-layout",
},
# --- Lightbox modal ---
{
"problem": "Build a modal lightbox for full-screen image viewing with keyboard navigation.",
"solution": "class Lightbox {\n private overlay!: HTMLElement;\n private img!: HTMLImageElement;\n\n constructor() {\n this.overlay = document.createElement('div');\n this.overlay.className = 'lightbox-overlay';\n this.overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.9);display:flex;align-items:center;justify-content:center;z-index:9999';\n this.img = document.createElement('img');\n this.overlay.appendChild(this.img);\n document.body.appendChild(this.overlay);\n\n this.overlay.addEventListener('click', () => this.close());\n document.addEventListener('keydown', (e) => e.key === 'Escape' && this.close());\n }\n\n open(src: string, alt: string) {\n this.img.src = src;\n this.img.alt = alt;\n this.overlay.style.display = 'flex';\n }\n\n close() {\n this.overlay.style.display = 'none';\n }\n}",
"imports": "",
"domain": "gallery-interaction",
},
# --- Infinite scroll ---
{
"problem": "Implement infinite scroll loading with IntersectionObserver and abort handling.",
"solution": "async function setupInfiniteScroll(container: HTMLElement, loadPage: (page: number) => Promise<void>) {\n let page = 1;\n let loading = false;\n let done = false;\n\n const sentinel = document.createElement('div');\n sentinel.className = 'scroll-sentinel';\n container.appendChild(sentinel);\n\n const observer = new IntersectionObserver(async (entries) => {\n if (entries[0].isIntersecting && !loading && !done) {\n loading = true;\n try {\n await loadPage(++page);\n } catch (err) {\n console.error('Failed to load page:', err);\n done = true;\n }\n loading = false;\n }\n }, { rootMargin: '200px' });\n\n observer.observe(sentinel);\n}",
"imports": "",
"domain": "gallery-performance",
},
]
# ============================================================
# Game Templates
# ============================================================
GAME_TEMPLATES = [
# --- Game loop ---
{
"problem": "Create a fixed-timestep game loop with accumulator pattern.",
"solution": "class GameLoop {\n private lastTime = 0;\n private accumulator = 0;\n private readonly step = 1 / 60; // 60 Hz fixed step\n\n constructor(private readonly update: (dt: number) => void) {}\n\n start() {\n const frame = (time: number) => {\n const delta = (time - this.lastTime) / 1000;\n this.lastTime = time;\n this.accumulator += delta;\n\n while (this.accumulator >= this.step) {\n this.update(this.step);\n this.accumulator -= this.step;\n }\n\n requestAnimationFrame(frame);\n };\n requestAnimationFrame(frame);\n }\n}",
"imports": "",
"domain": "game-architecture",
},
# --- Canvas setup ---
{
"problem": "Set up an HTML5 canvas with high-DPI scaling and clearing.",
"solution": "function setupCanvas(canvas: HTMLCanvasElement, width = 800, height = 600) {\n const dpr = window.devicePixelRatio || 1;\n canvas.width = width * dpr;\n canvas.height = height * dpr;\n canvas.style.width = `${width}px`;\n canvas.style.height = `${height}px`;\n\n const ctx = canvas.getContext('2d')!;\n ctx.scale(dpr, dpr);\n\n return {\n clear() { ctx.clearRect(0, 0, width, height); },\n ctx,\n width,\n height,\n };\n}",
"imports": "",
"domain": "game-rendering",
},
# --- Sprite animation ---
{
"problem": "Animate a sprite sheet with frame-based playback and loop support.",
"solution": "class SpriteAnimator {\n private frame = 0;\n private lastTick = 0;\n\n constructor(\n private readonly image: HTMLImageElement,\n private readonly frameWidth: number,\n private readonly frameCount: number,\n private readonly fps: number = 12,\n private readonly loop: boolean = true,\n ) {}\n\n update(now: number) {\n const interval = 1000 / this.fps;\n if (now - this.lastTick >= interval) {\n this.lastTick = now;\n this.frame++;\n if (this.frame >= this.frameCount) {\n this.frame = this.loop ? 0 : this.frameCount - 1;\n }\n }\n }\n\n draw(ctx: CanvasRenderingContext2D, x: number, y: number) {\n ctx.drawImage(\n this.image,\n this.frame * this.frameWidth, 0,\n this.frameWidth, this.image.height,\n x, y,\n this.frameWidth, this.image.height\n );\n }\n}",
"imports": "",
"domain": "game-assets",
},
# --- AABB collision ---
{
"problem": "Detect AABB (axis-aligned bounding box) collision between two rectangles.",
"solution": "function aabbCollision(\n a: { x: number; y: number; w: number; h: number },\n b: { x: number; y: number; w: number; h: number }\n): boolean {\n return a.x < b.x + b.w &&\n a.x + a.w > b.x &&\n a.y < b.y + b.h &&\n a.y + a.h > b.y;\n}\n\n// Usage for game entities\nif (aabbCollision(player, enemy)) {\n handlePlayerHit();\n}",
"imports": "",
"domain": "game-physics",
},
# --- Input handling ---
{
"problem": "Capture keyboard input state with smooth handling for game controls.",
"solution": "class InputState {\n private keys = new Set<string>();\n\n constructor() {\n window.addEventListener('keydown', (e) => this.keys.add(e.code));\n window.addEventListener('keyup', (e) => this.keys.delete(e.code));\n }\n\n isPressed(code: string): boolean {\n return this.keys.has(code);\n }\n\n hasAny(codes: string[]): boolean {\n return codes.some(c => this.keys.has(c));\n }\n}\n\n// In game loop:\nconst input = new InputState();\nif (input.isPressed('ArrowUp')) player.y -= speed * dt;",
"imports": "",
"domain": "game-input",
},
]
# ============================================================
# Extra HTML/CSS/JS Templates
# ============================================================
HTML_CSS_JS_TEMPLATES_EXTRA = [
# Debounce utility
{
"problem": "Write a debounce function that delays invoking a callback until after wait milliseconds.",
"solution": "function debounce<T extends (...args: any[]) => void>(\n fn: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n return (...args: Parameters<T>) => {\n if (timeoutId) clearTimeout(timeoutId);\n timeoutId = setTimeout(() => fn(...args), wait);\n };\n}",
"imports": "",
"domain": "html-utilities",
},
# Throttle utility
{
"problem": "Implement a throttle function ensuring a callback runs at most once per interval.",
"solution": "function throttle<T extends (...args: any[]) => void>(\n fn: T,\n interval: number\n): (...args: Parameters<T>) => void {\n let last = 0;\n return (...args: Parameters<T>) => {\n const now = Date.now();\n if (now - last >= interval) {\n last = now;\n fn(...args);\n }\n };\n}",
"imports": "",
"domain": "html-utilities",
},
# LocalStorage wrapper with TTL
{
"problem": "Wrap localStorage with JSON serialization and TTL expiration.",
"solution": "class StorageWithTTL {\n set(key: string, value: any, ttlMs = 0) {\n const item = { value, expiry: ttlMs ? Date.now() + ttlMs : null };\n localStorage.setItem(key, JSON.stringify(item));\n }\n\n get<T>(key: string): T | null {\n const raw = localStorage.getItem(key);\n if (!raw) return null;\n const { value, expiry } = JSON.parse(raw);\n if (expiry && Date.now() > expiry) {\n localStorage.removeItem(key);\n return null;\n }\n return value as T;\n }\n}",
"imports": "",
"domain": "html-storage",
},
# Viewport meta
{
"problem": "Generate a responsive viewport meta tag for mobile-first web apps.",
"solution": "const viewport = document.querySelector('meta[name=\"viewport\"]') ||\n document.createElement('meta');\nviewport.name = 'viewport';\nviewport.content = 'width=device-width, initial-scale=1.0, maximum-scale=5.0, user-scalable=yes, viewport-fit=cover';\ndocument.head.appendChild(viewport);",
"imports": "",
"domain": "html-meta",
},
# Dynamic CSS variables
{
"problem": "Create and inject a dynamic stylesheet with CSS custom property overrides.",
"solution": "function injectDynamicStyles(overrides: Record<string, string>) {\n const style = document.createElement('style');\n let css = ':root {\\n';\n for (const [prop, val] of Object.entries(overrides)) {\n css += ` --${prop}: ${val};\\n`;\n }\n css += '}';\n style.textContent = css;\n document.head.appendChild(style);\n}",
"imports": "",
"domain": "css-variables",
},
]
# ============================================================
# Extra Playground UI Templates
# ============================================================
PLAYGROUND_UI_TEMPLATES_EXTRA = [
# Circuit/tier badge
{
"problem": "Render a circuit health badge showing approval-tier status with color-coded indicator.",
"solution": "function CircuitBadge({ tier }: { tier: number }) {\n const colors = ['#f44336', '#ff9800', '#4caf50', '#2196f3', '#9c27b0'];\n const labels = ['BLOCKED', 'RESTRICTED', 'LIMITED', 'APPROVED', 'ELEVATED'];\n const color = colors[Math.min(tier, 4)];\n const label = labels[Math.min(tier, 4)];\n\n const badge = document.createElement('span');\n badge.className = 'circuit-badge';\n badge.style.backgroundColor = color;\n badge.textContent = label;\n badge.title = `Approval tier ${tier} — ${label.toLowerCase()} command set`;\n return badge;\n}",
"imports": "",
"domain": "playground-ui",
},
# Memory usage bar
{
"problem": "Display a horizontal memory usage bar with gradient warning zones.",
"solution": "function MemoryBar({ used, total }: { used: number; total: number }) {\n const pct = (used / total) * 100;\n const bar = document.createElement('div');\n bar.className = 'memory-bar';\n let color = '#4caf50';\n if (pct > 80) color = '#ff9800';\n if (pct > 95) color = '#f44336';\n\n bar.innerHTML = `\n <div class=\"track\" style=\"background: #e0e0e0; height: 8px; border-radius: 4px; overflow: hidden;\">\n <div style=\"width: ${pct}%; height: 100%; background: ${color}; transition: width 0.3s;\"></div>\n </div>\n <span>${(used/1024/1024).toFixed(1)} MB / ${(total/1024/1024).toFixed(1)} MB</span>\n `;\n return bar;\n}",
"imports": "",
"domain": "playground-ui",
},
# Tool status dot
{
"problem": "Show a tool availability status dot with tooltip for the toolset panel.",
"solution": "function ToolStatus({ name, ok }: { name: string; ok: boolean }) {\n const dot = document.createElement('span');\n dot.className = 'tool-status-dot';\n dot.style.backgroundColor = ok ? '#4caf50' : '#f44336';\n dot.title = `${name}: ${ok ? 'Available' : 'Disabled / missing API key'}`;\n return dot;\n}",
"imports": "",
"domain": "playground-ui",
},
]
# ============================================================
# Extra Gallery Templates
# ============================================================
GALLERY_TEMPLATES_EXTRA = [
# Grid + shared lightbox
{
"problem": "Build an image gallery grid that opens a shared lightbox on thumbnail click.",
"solution": "let currentLightbox: HTMLDivElement | null = null;\n\nfunction buildGallery(images: { full: string; thumb: string; alt: string }[]) {\n const grid = document.createElement('div');\n grid.className = 'gallery-grid';\n grid.style.cssText = 'display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:0.5rem';\n\n images.forEach((img, idx) => {\n const thumb = document.createElement('img');\n thumb.src = img.thumb;\n thumb.alt = img.alt;\n thumb.style.cssText = 'cursor:pointer;width:100%;height:auto;object-fit:cover;border-radius:4px';\n thumb.addEventListener('click', () => openLightbox(idx));\n grid.appendChild(thumb);\n });\n\n return grid;\n}\n\nfunction openLightbox(index: number) {\n if (currentLightbox) currentLightbox.remove();\n currentLightbox = document.createElement('div');\n currentLightbox.className = 'lightbox';\n currentLightbox.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.95);display:flex;align-items:center;justify-content:center;z-index:10000;cursor:pointer';\n const img = document.createElement('img');\n img.src = images[index].full;\n img.style.maxWidth = '90vw';\n img.style.maxHeight = '90vh';\n currentLightbox.appendChild(img);\n currentLightbox.addEventListener('click', () => { currentLightbox?.remove(); currentLightbox = null; });\n document.body.appendChild(currentLightbox);\n}",
"imports": "",
"domain": "gallery-interaction",
},
]
# ============================================================
# Extra Game Templates
# ============================================================
GAME_TEMPLATES_EXTRA = [
# Particle system with typed array
{
"problem": "Create a simple particle system for explosions using a typed array buffer.",
"solution": "class ParticleSystem {\n private particles = new Float32Array(1000 * 4); // x, y, vx, vy per particle\n private count = 0;\n private readonly max = 1000;\n\n emit(x: number, y: number, velocity = 200) {\n if (this.count >= this.max) return;\n const i = this.count * 4;\n this.particles[i] = x;\n this.particles[i + 1] = y;\n const angle = Math.random() * Math.PI * 2;\n const speed = Math.random() * velocity;\n this.particles[i + 2] = Math.cos(angle) * speed;\n this.particles[i + 3] = Math.sin(angle) * speed;\n this.count++;\n }\n\n update(dt: number) {\n for (let i = 0; i < this.count * 4; i += 4) {\n this.particles[i] += this.particles[i + 2] * dt;\n this.particles[i + 1] += this.particles[i + 3] * dt;\n this.particles[i + 3] += 500 * dt; // gravity\n }\n }\n\n draw(ctx: CanvasRenderingContext2D) {\n ctx.fillStyle = '#ff6600';\n for (let i = 0; i < this.count * 4; i += 4) {\n ctx.fillRect(this.particles[i], this.particles[i + 1], 3, 3);\n }\n }\n}",
"imports": "",
"domain": "game-physics",
},
# State machine
{
"problem": "Implement a finite state machine for a game character with transitions.",
"solution": "type State = 'idle' | 'walk' | 'run' | 'jump' | 'attack';\n\nclass StateMachine {\n private state: State = 'idle';\n private handlers: Record<State, (event: string) => void>;\n\n constructor(handlers: Partial<Record<State, (event: string) => void>>) {\n this.handlers = handlers as Record<State, (event: string) => void>;\n }\n\n transition(to: State) {\n console.log(`State: ${this.state} -> ${to}`);\n this.state = to;\n }\n\n dispatch(event: string) {\n const handler = this.handlers[this.state];\n if (handler) handler(event);\n }\n\n getState(): State {\n return this.state;\n }\n}\n\n// Usage\nconst sm = new StateMachine({\n idle: (e) => { if (e === 'move') sm.transition('walk'); },\n walk: (e) => { if (e === 'sprint') sm.transition('run'); if (e === 'jump') sm.transition('jump'); },\n run: (e) => { if (e === 'stop') sm.transition('idle'); },\n});",
"imports": "",
"domain": "game-architecture",
},
]
# ============================================================
# Combined
# ============================================================
ALL_TEMPLATES = (
THREEJS_TEMPLATES
+ HTML_CSS_JS_TEMPLATES
+ HTML_CSS_JS_TEMPLATES_EXTRA
+ PLAYGROUND_UI_TEMPLATES
+ PLAYGROUND_UI_TEMPLATES_EXTRA
+ GALLERY_TEMPLATES
+ GALLERY_TEMPLATES_EXTRA
+ GAME_TEMPLATES
+ GAME_TEMPLATES_EXTRA
)
_VARIANT_PREFIXES = [
"Write code to",
"Implement",
"Build",
"Create",
"How would you",
"Using the API, write code that",
"Construct a function that",
"Develop",
"Write JavaScript that",
"Create HTML/CSS for",
"Design a Three.js",
]
_VARIANT_SUFFIXES = [
" including error handling.",
" with full docstrings.",
" with JSDoc annotations.",
" using modern best practices.",
" that handles edge cases.",
" with TypeScript types.",
" that is performant.",
" with clear variable names.",
" and include example usage.",
" with proper cleanup.",
" that is accessible (a11y).",
" with keyboard navigation support.",
]
def vary_problem(base: str, idx: int) -> str:
prefix = _VARIANT_PREFIXES[idx % len(_VARIANT_PREFIXES)]
suffix = _VARIANT_SUFFIXES[idx % len(_VARIANT_SUFFIXES)]
cleaned = base
for article in ("Create a ", "Build a ", "Implement a ", "Write a ", "Develop a ", "Write JavaScript that ", "Create HTML/CSS for ", "Design a Three.js "):
if cleaned.lower().startswith(article):
cleaned = cleaned[len(article):]
break
cleaned = cleaned[0].lower() + cleaned[1:] if cleaned else ""
return f"{prefix} {cleaned}{suffix}"
def vary_solution(base: str, idx: int) -> str:
var_names = ["data", "result", "value", "entry", "item", "node", "entity", "output", "obj", "element"]
v = var_names[idx % len(var_names)]
sol = base
if idx % 3 == 0:
for original in ["result", "data", "value", "output", "entry", "item", "obj", "element"]:
if original in sol:
sol = sol.replace(original, v)
break
if idx % 5 == 0:
sol = f"// Variation {idx}\\n" + sol
elif idx % 7 == 0:
sol = f"# Generated variation {idx}\\n" + sol
return sol
def generate_pairs(count: int = 1000) -> list[dict]:
pairs = []
template_cycle = list(ALL_TEMPLATES)
random.shuffle(template_cycle)
for i in range(count):
template = template_cycle[i % len(template_cycle)]
problem = vary_problem(template["problem"], i)
solution = vary_solution(template["solution"], i)
pair = {
"problem": problem,
"solution": solution,
"imports": template["imports"],
"domain": template["domain"],
"id": f"frontend-creative-{i:04d}",
}
pairs.append(pair)
return pairs
def main():
parser = argparse.ArgumentParser(description="Generate Frontend & Creative code pattern training pairs")
parser.add_argument("--output", "-o", default="training-data/code-patterns-frontend-&-creative.jsonl",
help="Output JSONL path")
parser.add_argument("--count", "-n", type=int, default=1000,
help="Number of pairs to generate")
args = parser.parse_args()
out_path = Path(args.output)
out_path.parent.mkdir(parents=True, exist_ok=True)
pairs = generate_pairs(args.count)
with open(out_path, "w", encoding="utf-8") as f:
for pair in pairs:
f.write(json.dumps(pair, ensure_ascii=False) + "\n")
domains = {p["domain"] for p in pairs}
print(f"Generated {len(pairs)} code pattern pairs → {out_path}")
print(f" Size: {out_path.stat().st_size / 1024:.1f} KB")
print(f" Domains ({len(domains)}): {sorted(domains)}")
if __name__ == "__main__":
main()

File diff suppressed because it is too large Load Diff