Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6990a8f3c6 |
506
scripts/generate_code_patterns_frontend_creative.py
Normal file
506
scripts/generate_code_patterns_frontend_creative.py
Normal 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()
|
||||||
1000
training-data/code-patterns-frontend-&-creative.jsonl
Normal file
1000
training-data/code-patterns-frontend-&-creative.jsonl
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user