#!/usr/bin/env python3 """ generate_code_patterns_frontend.py — Generate 1K problem→solution training pairs for frontend & creative code (Three.js, HTML/CSS/JS, playground UI, gallery, games). Usage: python3 training/scripts/generate_code_patterns_frontend.py python3 training/scripts/generate_code_patterns_frontend.py --count 1000 --output training-data/code-patterns-frontend-&-creative.jsonl """ import argparse import json import random import sys from pathlib import Path random.seed(42) # ── Variation pools ── COLORS = ["#ff6b6b", "#4ecdc4", "#45b7d1", "#96ceb4", "#ffeaa7", "#dfe6e9", "#fd79a8", "#a29bfe", "#00b894", "#e17055", "#74b9ff", "#55efc4", "#fab1a0", "#fdcb6e", "#6c5ce7", "#0984e3", "#00cec9", "#e84393"] NAMES = ["hero", "scene", "viewer", "canvas", "stage", "world", "panel", "card", "grid", "list", "item", "box", "circle", "shape", "model", "sprite", "player", "enemy", "obstacle", "target", "button", "slider", "menu"] ADJECTIVES = ["dynamic", "interactive", "responsive", "animated", "stylish", "minimal", "modern", "elegant", "bold", "soft", "vibrant", "dark"] SIZES = ["100px", "200px", "300px", "50%", "100%", "80vh", "60vw", "400px", "250px"] SPEEDS = [0.01, 0.02, 0.05, 0.1, 0.005, 0.03, 0.015] COUNTS = [3, 5, 8, 10, 12, 15, 20, 50] def pick(pool, rng=random): return rng.choice(pool) def picks(pool, k, rng=random): return rng.sample(pool, k=min(k, len(pool))) # ── Domain: Three.js ── def threejs_templates(): t = [] t.append(( "Create a Three.js scene with a rotating colored cube", '''import * as THREE from 'three'; function createCubeScene(containerId = 'canvas-container') { const container = document.getElementById(containerId); if (!container) throw new Error('Container not found: ' + containerId); const scene = new THREE.Scene(); scene.background = new THREE.Color('{color1}'); const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000); camera.position.z = 5; const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(container.clientWidth, container.clientHeight); renderer.setPixelRatio(window.devicePixelRatio); container.appendChild(renderer.domElement); const geometry = new THREE.BoxGeometry(1, 1, 1); const material = new THREE.MeshStandardMaterial({ color: '{color2}', roughness: 0.3, metalness: 0.1 }); const cube = new THREE.Mesh(geometry, material); scene.add(cube); const light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(5, 5, 5); scene.add(light); scene.add(new THREE.AmbientLight(0x404040)); function animate() { requestAnimationFrame(animate); cube.rotation.x += {speed}; cube.rotation.y += {speed}; renderer.render(scene, camera); } animate(); window.addEventListener('resize', () => { camera.aspect = container.clientWidth / container.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(container.clientWidth, container.clientHeight); }); return { scene, camera, renderer, cube }; } // Usage: createCubeScene();''' )) t.append(( "Load a GLTF 3D model in Three.js with orbit controls", '''import * as THREE from 'three'; import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'; import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; async function loadModel(url, containerId = 'canvas-container') { const container = document.getElementById(containerId); if (!container) throw new Error('Container not found'); const scene = new THREE.Scene(); scene.background = new THREE.Color('{color1}'); scene.fog = new THREE.Fog('{color1}', 10, 50); const camera = new THREE.PerspectiveCamera(45, container.clientWidth / container.clientHeight, 0.1, 100); camera.position.set(0, 2, 5); const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(container.clientWidth, container.clientHeight); renderer.shadowMap.enabled = true; container.appendChild(renderer.domElement); const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.05; scene.add(new THREE.AmbientLight(0xffffff, 0.5)); const dirLight = new THREE.DirectionalLight(0xffffff, 1); dirLight.position.set(5, 10, 7); dirLight.castShadow = true; scene.add(dirLight); const loader = new GLTFLoader(); try { const gltf = await loader.loadAsync(url); const model = gltf.scene; model.traverse(child => { if (child.isMesh) child.castShadow = true; }); scene.add(model); } catch (err) { console.error('Failed to load model:', err); throw err; } function animate() { requestAnimationFrame(animate); controls.update(); renderer.render(scene, camera); } animate(); return { scene, camera, controls }; } // Usage: loadModel('/models/character.glb');''' )) t.append(( "Create a particle system in Three.js with random motion", '''import * as THREE from 'three'; function createParticles(count = {count}, containerId = 'canvas-container') { const container = document.getElementById(containerId); if (!container) throw new Error('Container not found'); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000); camera.position.z = 30; const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setSize(container.clientWidth, container.clientHeight); container.appendChild(renderer.domElement); const geometry = new THREE.BufferGeometry(); const positions = new Float32Array(count * 3); const velocities = []; for (let i = 0; i < count; i++) { positions[i * 3] = (Math.random() - 0.5) * 50; positions[i * 3 + 1] = (Math.random() - 0.5) * 50; positions[i * 3 + 2] = (Math.random() - 0.5) * 50; velocities.push({ x: (Math.random() - 0.5) * {speed}, y: (Math.random() - 0.5) * {speed}, z: (Math.random() - 0.5) * {speed} }); } geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); const material = new THREE.PointsMaterial({ color: '{color2}', size: 0.5, transparent: true, opacity: 0.8 }); const particles = new THREE.Points(geometry, material); scene.add(particles); function animate() { requestAnimationFrame(animate); const pos = geometry.attributes.position.array; for (let i = 0; i < count; i++) { pos[i * 3] += velocities[i].x; pos[i * 3 + 1] += velocities[i].y; pos[i * 3 + 2] += velocities[i].z; if (Math.abs(pos[i * 3]) > 25) velocities[i].x *= -1; if (Math.abs(pos[i * 3 + 1]) > 25) velocities[i].y *= -1; if (Math.abs(pos[i * 3 + 2]) > 25) velocities[i].z *= -1; } geometry.attributes.position.needsUpdate = true; renderer.render(scene, camera); } animate(); return { scene, particles, renderer }; } // Usage: createParticles(200);''' )) t.append(( "Create a reflective sphere with environment mapping in Three.js", '''import * as THREE from 'three'; function createReflectiveSphere(containerId = 'canvas-container') { const container = document.getElementById(containerId); if (!container) throw new Error('Container not found'); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000); camera.position.z = 3; const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(container.clientWidth, container.clientHeight); renderer.toneMapping = THREE.ACESFilmicToneMapping; container.appendChild(renderer.domElement); const pmremGenerator = new THREE.PMREMGenerator(renderer); const envScene = new THREE.Scene(); envScene.background = new THREE.Color('{color1}'); envScene.add(new THREE.Mesh( new THREE.SphereGeometry(10, 32, 32), new THREE.MeshBasicMaterial({ color: '{color2}', side: THREE.BackSide }) )); const envMap = pmremGenerator.fromScene(envScene).texture; const geometry = new THREE.SphereGeometry(1, 64, 64); const material = new THREE.MeshPhysicalMaterial({ color: '{color3}', metalness: 1.0, roughness: 0.1, envMap, envMapIntensity: 1.0 }); const sphere = new THREE.Mesh(geometry, material); scene.add(sphere); function animate() { requestAnimationFrame(animate); sphere.rotation.y += {speed}; renderer.render(scene, camera); } animate(); return { scene, sphere, renderer }; } // Usage: createReflectiveSphere();''' )) t.append(( "Build a Three.js first-person camera controller with WASD movement", '''import * as THREE from 'three'; function createFPSController(containerId = 'canvas-container') { const container = document.getElementById(containerId); if (!container) throw new Error('Container not found'); const scene = new THREE.Scene(); scene.background = new THREE.Color('{color1}'); const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000); camera.position.y = 1.7; const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(container.clientWidth, container.clientHeight); container.appendChild(renderer.domElement); // Floor const floor = new THREE.Mesh( new THREE.PlaneGeometry(50, 50), new THREE.MeshStandardMaterial({ color: '{color2}' }) ); floor.rotation.x = -Math.PI / 2; floor.receiveShadow = true; scene.add(floor); scene.add(new THREE.AmbientLight(0x404040)); const dirLight = new THREE.DirectionalLight(0xffffff, 0.8); dirLight.position.set(5, 10, 5); scene.add(dirLight); const keys = { w: false, a: false, s: false, d: false }; const velocity = new THREE.Vector3(); const direction = new THREE.Vector3(); document.addEventListener('keydown', (e) => { if (keys.hasOwnProperty(e.key.toLowerCase())) keys[e.key.toLowerCase()] = true; }); document.addEventListener('keyup', (e) => { if (keys.hasOwnProperty(e.key.toLowerCase())) keys[e.key.toLowerCase()] = false; }); container.addEventListener('click', () => container.requestPointerLock()); document.addEventListener('mousemove', (e) => { if (document.pointerLockElement === container) { camera.rotation.y -= e.movementX * 0.002; camera.rotation.x -= e.movementY * 0.002; camera.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, camera.rotation.x)); } }); const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); velocity.x -= velocity.x * 10 * delta; velocity.z -= velocity.z * 10 * delta; direction.z = Number(keys.w) - Number(keys.s); direction.x = Number(keys.a) - Number(keys.d); direction.normalize(); if (keys.w || keys.s) velocity.z -= direction.z * 40 * delta; if (keys.a || keys.d) velocity.x -= direction.x * 40 * delta; camera.translateX(-velocity.x * delta); camera.translateZ(-velocity.z * delta); camera.position.y = 1.7; renderer.render(scene, camera); } animate(); return { scene, camera, renderer }; } // Usage: createFPSController();''' )) t.append(( "Create an animated torus knot with wireframe overlay in Three.js", '''import * as THREE from 'three'; function createTorusKnot(containerId = 'canvas-container') { const container = document.getElementById(containerId); if (!container) throw new Error('Container not found'); const scene = new THREE.Scene(); scene.background = new THREE.Color('{color1}'); const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000); camera.position.z = 5; const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(container.clientWidth, container.clientHeight); container.appendChild(renderer.domElement); const geometry = new THREE.TorusKnotGeometry(1, 0.3, 100, 16); const material = new THREE.MeshStandardMaterial({ color: '{color2}', roughness: 0.4, metalness: 0.3 }); const torus = new THREE.Mesh(geometry, material); scene.add(torus); const wireGeo = new THREE.WireframeGeometry(geometry); const wireMat = new THREE.LineBasicMaterial({ color: '{color3}' }); const wireframe = new THREE.LineSegments(wireGeo, wireMat); torus.add(wireframe); scene.add(new THREE.AmbientLight(0x404040)); const pointLight = new THREE.PointLight(0xffffff, 1, 100); pointLight.position.set(2, 3, 4); scene.add(pointLight); function animate() { requestAnimationFrame(animate); torus.rotation.x += {speed}; torus.rotation.y += {speed} * 0.7; renderer.render(scene, camera); } animate(); return { scene, torus, renderer }; } // Usage: createTorusKnot();''' )) t.append(( "Raycast from mouse to detect 3D object clicks in Three.js", '''import * as THREE from 'three'; function setupRaycasting(scene, camera, renderer, onIntersect) { const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); renderer.domElement.addEventListener('click', (event) => { const rect = renderer.domElement.getBoundingClientRect(); mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; raycaster.setFromCamera(mouse, camera); const intersects = raycaster.intersectObjects(scene.children, true); if (intersects.length > 0) { const hit = intersects[0]; console.log('Clicked:', hit.object.name || hit.object.uuid); if (typeof onIntersect === 'function') onIntersect(hit); } }); } // Usage example: // const mesh = new THREE.Mesh(geometry, material); // mesh.name = 'clickable-box'; // scene.add(mesh); // setupRaycasting(scene, camera, renderer, (hit) => { // hit.object.material.color.setHex(Math.random() * 0xffffff); // });''' )) return t # ── Domain: HTML/CSS/JS ── def htmlcss_templates(): t = [] t.append(( "Build a responsive CSS grid layout with auto-fit columns", '''/* Responsive grid layout */ .grid-container { display: grid; grid-template-columns: repeat(auto-fit, minmax({size}, 1fr)); gap: 1.5rem; padding: 2rem; max-width: 1200px; margin: 0 auto; } .grid-item { background: {color1}; border-radius: 12px; padding: 1.5rem; box-shadow: 0 4px 6px rgba(0,0,0,0.1); transition: transform 0.2s ease, box-shadow 0.2s ease; } .grid-item:hover { transform: translateY(-4px); box-shadow: 0 12px 24px rgba(0,0,0,0.15); } @media (max-width: 600px) { .grid-container { grid-template-columns: 1fr; padding: 1rem; } }''' )) t.append(( "Create a sticky navigation bar that changes style on scroll", '''/* Sticky nav styles */ .nav-bar { position: sticky; top: 0; z-index: 1000; background: transparent; padding: 1rem 2rem; transition: background 0.3s ease, box-shadow 0.3s ease; } .nav-bar.scrolled { background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); box-shadow: 0 2px 10px rgba(0,0,0,0.1); } /* JavaScript */ function initStickyNav(navSelector = '.nav-bar') { const nav = document.querySelector(navSelector); if (!nav) throw new Error('Nav element not found'); function onScroll() { if (window.scrollY > 50) { nav.classList.add('scrolled'); } else { nav.classList.remove('scrolled'); } } window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); // Initialize state return nav; } // Usage: initStickyNav();''' )) t.append(( "Implement debounced search input with fetch API", '''async function debouncedSearch(inputSelector, endpoint, renderFn, delay = 300) { const input = document.querySelector(inputSelector); if (!input) throw new Error('Input element not found'); let timeoutId = null; let controller = null; input.addEventListener('input', (e) => { const query = e.target.value.trim(); clearTimeout(timeoutId); if (controller) controller.abort(); if (!query) { renderFn([]); return; } timeoutId = setTimeout(async () => { controller = new AbortController(); try { const res = await fetch(`${endpoint}?q=${encodeURIComponent(query)}`, { signal: controller.signal }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); renderFn(data); } catch (err) { if (err.name !== 'AbortError') { console.error('Search failed:', err); renderFn([], err); } } }, delay); }); } // Usage: // debouncedSearch('#search', '/api/search', (results, err) => { // if (err) return showError(err); // updateDOM(results); // });''' )) t.append(( "Create a CSS-only modal with backdrop blur and focus trap", '''/* Modal styles */ .modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.5); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; opacity: 0; visibility: hidden; transition: opacity 0.3s ease, visibility 0.3s ease; } .modal-overlay.active { opacity: 1; visibility: visible; } .modal-content { background: white; border-radius: 16px; padding: 2rem; max-width: 500px; width: 90%; transform: scale(0.9); transition: transform 0.3s ease; box-shadow: 0 20px 60px rgba(0,0,0,0.3); } .modal-overlay.active .modal-content { transform: scale(1); } /* JavaScript for focus trap and keyboard */ function initModal(triggerSelector, modalSelector) { const trigger = document.querySelector(triggerSelector); const modal = document.querySelector(modalSelector); if (!trigger || !modal) throw new Error('Modal elements not found'); const content = modal.querySelector('.modal-content'); const focusables = content.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'); function open() { modal.classList.add('active'); (focusables[0] || content).focus(); document.addEventListener('keydown', onKey); } function close() { modal.classList.remove('active'); document.removeEventListener('keydown', onKey); trigger.focus(); } function onKey(e) { if (e.key === 'Escape') close(); if (e.key === 'Tab' && focusables.length > 0) { const first = focusables[0]; const last = focusables[focusables.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } } trigger.addEventListener('click', open); modal.addEventListener('click', (e) => { if (e.target === modal) close(); }); return { open, close }; } // Usage: initModal('#open-modal', '#my-modal');''' )) t.append(( "Build a custom dropdown select with keyboard navigation", '''function createCustomSelect(selectElement) { if (!(selectElement instanceof HTMLSelectElement)) { throw new TypeError('Expected HTMLSelectElement'); } const wrapper = document.createElement('div'); wrapper.className = 'custom-select'; wrapper.style.position = 'relative'; wrapper.style.width = selectElement.offsetWidth + 'px'; const trigger = document.createElement('button'); trigger.type = 'button'; trigger.className = 'select-trigger'; trigger.textContent = selectElement.options[selectElement.selectedIndex]?.text || 'Select...'; trigger.setAttribute('aria-haspopup', 'listbox'); const list = document.createElement('ul'); list.className = 'select-options'; list.setAttribute('role', 'listbox'); list.style.cssText = 'position:absolute;top:100%;left:0;right:0;max-height:200px;overflow:auto;list-style:none;margin:0;padding:0;border:1px solid #ccc;background:#fff;z-index:100;display:none;'; Array.from(selectElement.options).forEach((opt, i) => { const li = document.createElement('li'); li.textContent = opt.text; li.setAttribute('role', 'option'); li.setAttribute('aria-selected', String(opt.selected)); li.dataset.value = opt.value; li.style.padding = '0.5rem 1rem'; li.style.cursor = 'pointer'; li.addEventListener('click', () => { selectElement.value = opt.value; trigger.textContent = opt.text; close(); selectElement.dispatchEvent(new Event('change')); }); list.appendChild(li); }); wrapper.appendChild(trigger); wrapper.appendChild(list); selectElement.style.display = 'none'; selectElement.parentNode.insertBefore(wrapper, selectElement); let activeIndex = -1; function open() { list.style.display = 'block'; trigger.setAttribute('aria-expanded', 'true'); activeIndex = Array.from(selectElement.options).findIndex(o => o.selected); } function close() { list.style.display = 'none'; trigger.setAttribute('aria-expanded', 'false'); } trigger.addEventListener('click', () => { list.style.display === 'block' ? close() : open(); }); document.addEventListener('click', (e) => { if (!wrapper.contains(e.target)) close(); }); trigger.addEventListener('keydown', (e) => { const items = list.querySelectorAll('li'); if (e.key === 'ArrowDown') { open(); activeIndex = Math.min(activeIndex + 1, items.length - 1); items[activeIndex]?.focus(); e.preventDefault(); } if (e.key === 'ArrowUp') { open(); activeIndex = Math.max(activeIndex - 1, 0); items[activeIndex]?.focus(); e.preventDefault(); } if (e.key === 'Enter' || e.key === ' ') { if (list.style.display === 'block' && items[activeIndex]) items[activeIndex].click(); else open(); e.preventDefault(); } if (e.key === 'Escape') close(); }); return wrapper; } // Usage: createCustomSelect(document.getElementById('my-select'));''' )) t.append(( "Implement smooth scroll-to-section with intersection observer highlighting", '''function initScrollSpy(navSelector, sectionSelector, options = {}) { const navLinks = document.querySelectorAll(`${navSelector} a[href^="#"]`); const sections = document.querySelectorAll(sectionSelector); if (!navLinks.length || !sections.length) { console.warn('Scroll spy: no nav links or sections found'); return; } const offset = options.offset || 80; // Smooth scroll on click navLinks.forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const targetId = link.getAttribute('href').slice(1); const target = document.getElementById(targetId); if (target) { window.scrollTo({ top: target.offsetTop - offset, behavior: 'smooth' }); } }); }); // Intersection observer for active state const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { navLinks.forEach(l => l.classList.remove('active')); const active = document.querySelector(`${navSelector} a[href="#${entry.target.id}"]`); if (active) active.classList.add('active'); } }); }, { rootMargin: `-${offset}px 0px -60% 0px` }); sections.forEach(section => observer.observe(section)); return observer; } // Usage: initScrollSpy('.side-nav', 'section[data-section]');''' )) return t # ── Domain: Playground UI ── def playground_templates(): t = [] t.append(( "Create a draggable range slider with real-time value display", '''function createRangeSlider(container, options = {}) { const { min = 0, max = 100, step = 1, value = 50, onChange } = options; container = typeof container === 'string' ? document.querySelector(container) : container; if (!container) throw new Error('Slider container not found'); const wrapper = document.createElement('div'); wrapper.className = 'range-slider'; wrapper.style.cssText = 'display:flex;align-items:center;gap:1rem;font-family:sans-serif;'; const input = document.createElement('input'); input.type = 'range'; input.min = min; input.max = max; input.step = step; input.value = value; input.style.flex = '1'; const valueDisplay = document.createElement('span'); valueDisplay.className = 'slider-value'; valueDisplay.textContent = value; valueDisplay.style.minWidth = '3ch'; valueDisplay.style.textAlign = 'right'; valueDisplay.style.fontVariantNumeric = 'tabular-nums'; input.addEventListener('input', (e) => { valueDisplay.textContent = e.target.value; if (typeof onChange === 'function') onChange(Number(e.target.value)); }); wrapper.appendChild(input); wrapper.appendChild(valueDisplay); container.appendChild(wrapper); return { input, valueDisplay, getValue: () => Number(input.value) }; } // Usage: // createRangeSlider('#slider-box', { // min: 0, max: 255, value: 128, // onChange: (v) => { document.body.style.background = `rgb(${v},${v},${v})`; } // });''' )) t.append(( "Build a color picker canvas with eyedropper and palette export", '''function createColorPicker(canvasId, exportBtnId) { const canvas = document.getElementById(canvasId); const exportBtn = document.getElementById(exportBtnId); if (!canvas || !exportBtn) throw new Error('Color picker elements not found'); const ctx = canvas.getContext('2d', { willReadFrequently: true }); let isDragging = false; const palette = new Set(); // Draw a hue-saturation gradient function drawGradient() { const w = canvas.width; const h = canvas.height; for (let x = 0; x < w; x++) { const hue = (x / w) * 360; const grad = ctx.createLinearGradient(0, 0, 0, h); grad.addColorStop(0, `hsl(${hue}, 100%, 50%)`); grad.addColorStop(1, `hsl(${hue}, 100%, 0%)`); ctx.fillStyle = grad; ctx.fillRect(x, 0, 1, h); } } drawGradient(); function pickColor(x, y) { const pixel = ctx.getImageData(x, y, 1, 1).data; const hex = '#' + [pixel[0], pixel[1], pixel[2]].map(c => c.toString(16).padStart(2, '0')).join(''); return hex; } function handleMove(e) { const rect = canvas.getBoundingClientRect(); const x = Math.min(Math.max(e.clientX - rect.left, 0), canvas.width - 1); const y = Math.min(Math.max(e.clientY - rect.top, 0), canvas.height - 1); const color = pickColor(x, y); canvas.style.cursor = 'crosshair'; if (isDragging) { palette.add(color); canvas.dispatchEvent(new CustomEvent('colorpicked', { detail: { color, x, y } })); } return color; } canvas.addEventListener('mousedown', (e) => { isDragging = true; handleMove(e); }); canvas.addEventListener('mousemove', handleMove); canvas.addEventListener('mouseup', () => { isDragging = false; }); canvas.addEventListener('mouseleave', () => { isDragging = false; }); exportBtn.addEventListener('click', () => { const colors = Array.from(palette); const blob = new Blob([JSON.stringify(colors, null, 2)], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'palette.json'; a.click(); URL.revokeObjectURL(url); }); return { canvas, palette, pickColor }; } // Usage: createColorPicker('picker-canvas', 'export-btn');''' )) t.append(( "Create a live code preview playground with iframe sandbox", '''function createCodePlayground(containerSelector) { const container = document.querySelector(containerSelector); if (!container) throw new Error('Playground container not found'); container.innerHTML = `
`; const editor = container.querySelector('.editor'); const preview = container.querySelector('.preview'); const files = { html: '

Hello World

', css: 'h1 { color: {color1}; }', js: 'console.log("ready");' }; let currentLang = 'html'; editor.value = files.html; container.querySelectorAll('button[data-lang]').forEach(btn => { btn.addEventListener('click', () => { files[currentLang] = editor.value; currentLang = btn.dataset.lang; editor.value = files[currentLang]; }); }); container.querySelector('button[data-action="run"]').addEventListener('click', () => { files[currentLang] = editor.value; const doc = ` ${files.html} `; preview.srcdoc = doc; }); return { editor, preview, getFiles: () => ({ ...files }) }; } // Usage: createCodePlayground('#playground');''' )) t.append(( "Build a resizable split-pane layout with drag handle", '''function createSplitPane(containerSelector, options = {}) { const container = document.querySelector(containerSelector); if (!container) throw new Error('Split pane container not found'); const { direction = 'horizontal', initialRatio = 0.5, minSize = 100 } = options; const isHorizontal = direction === 'horizontal'; container.style.display = 'flex'; container.style.flexDirection = isHorizontal ? 'row' : 'column'; container.style.height = '100%'; container.style.overflow = 'hidden'; const pane1 = document.createElement('div'); pane1.className = 'pane pane-1'; pane1.style.flex = `0 0 calc(${initialRatio * 100}% - 4px)`; pane1.style.overflow = 'auto'; pane1.style.minWidth = isHorizontal ? minSize + 'px' : 'auto'; pane1.style.minHeight = !isHorizontal ? minSize + 'px' : 'auto'; const handle = document.createElement('div'); handle.className = 'split-handle'; handle.style.flex = '0 0 8px'; handle.style.background = '#e0e0e0'; handle.style.cursor = isHorizontal ? 'col-resize' : 'row-resize'; handle.style.userSelect = 'none'; const pane2 = document.createElement('div'); pane2.className = 'pane pane-2'; pane2.style.flex = '1 1 auto'; pane2.style.overflow = 'auto'; pane2.style.minWidth = isHorizontal ? minSize + 'px' : 'auto'; pane2.style.minHeight = !isHorizontal ? minSize + 'px' : 'auto'; container.appendChild(pane1); container.appendChild(handle); container.appendChild(pane2); let isDragging = false; const sizeProp = isHorizontal ? 'clientWidth' : 'clientHeight'; handle.addEventListener('mousedown', (e) => { isDragging = true; document.body.style.cursor = isHorizontal ? 'col-resize' : 'row-resize'; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const rect = container.getBoundingClientRect(); const pos = isHorizontal ? e.clientX - rect.left : e.clientY - rect.top; const ratio = Math.max(minSize, Math.min(pos, rect[sizeProp] - minSize)) / rect[sizeProp]; pane1.style.flex = `0 0 calc(${ratio * 100}% - 4px)`; }); document.addEventListener('mouseup', () => { isDragging = false; document.body.style.cursor = ''; }); return { pane1, pane2, handle }; } // Usage: createSplitPane('#editor-layout', { direction: 'horizontal', initialRatio: 0.4 });''' )) return t # ── Domain: Gallery ── def gallery_templates(): t = [] t.append(( "Implement a masonry image grid with lazy loading and lightbox", '''function createMasonryGallery(containerSelector, imageUrls) { const container = document.querySelector(containerSelector); if (!container) throw new Error('Gallery container not found'); container.style.columnCount = '3'; container.style.columnGap = '1rem'; if (!Array.isArray(imageUrls)) throw new TypeError('imageUrls must be an array'); // Lightbox overlay const lightbox = document.createElement('div'); lightbox.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.9);display:none;align-items:center;justify-content:center;z-index:1000;'; const img = document.createElement('img'); img.style.maxWidth = '90vw'; img.style.maxHeight = '90vh'; img.style.objectFit = 'contain'; lightbox.appendChild(img); lightbox.addEventListener('click', () => { lightbox.style.display = 'none'; }); document.body.appendChild(lightbox); imageUrls.forEach((src, i) => { const wrapper = document.createElement('div'); wrapper.style.breakInside = 'avoid'; wrapper.style.marginBottom = '1rem'; const image = document.createElement('img'); image.dataset.src = src; image.alt = `Gallery image ${i + 1}`; image.style.width = '100%'; image.style.borderRadius = '8px'; image.style.display = 'block'; image.style.background = '{color1}'; image.style.minHeight = '150px'; image.addEventListener('click', () => { img.src = src; lightbox.style.display = 'flex'; }); wrapper.appendChild(image); container.appendChild(wrapper); }); // Lazy loading const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting && entry.target.dataset.src) { entry.target.src = entry.target.dataset.src; delete entry.target.dataset.src; observer.unobserve(entry.target); } }); }, { rootMargin: '200px' }); container.querySelectorAll('img[data-src]').forEach(img => observer.observe(img)); return { container, lightbox, observer }; } // Usage: createMasonryGallery('#gallery', ['/img/1.jpg', '/img/2.jpg']);''' )) t.append(( "Create an image carousel with touch swipe and keyboard controls", '''function createCarousel(containerSelector, slides) { const container = document.querySelector(containerSelector); if (!container) throw new Error('Carousel container not found'); if (!Array.isArray(slides) || slides.length === 0) throw new Error('Slides array required'); let current = 0; container.innerHTML = ` `; const track = container.querySelector('.track'); const indicators = container.querySelector('.indicators'); slides.forEach((slide, i) => { const div = document.createElement('div'); div.style.minWidth = '100%'; div.innerHTML = slide; track.appendChild(div); const dot = document.createElement('button'); dot.style.width = '10px'; dot.style.height = '10px'; dot.style.borderRadius = '50%'; dot.style.border = 'none'; dot.style.background = i === 0 ? '#fff' : 'rgba(255,255,255,0.4)'; dot.addEventListener('click', () => goTo(i)); indicators.appendChild(dot); }); function goTo(index) { current = ((index % slides.length) + slides.length) % slides.length; track.style.transform = `translateX(-${current * 100}%)`; Array.from(indicators.children).forEach((dot, i) => { dot.style.background = i === current ? '#fff' : 'rgba(255,255,255,0.4)'; }); } container.querySelector('.prev').addEventListener('click', () => goTo(current - 1)); container.querySelector('.next').addEventListener('click', () => goTo(current + 1)); // Touch swipe let startX = 0; container.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; }); container.addEventListener('touchend', (e) => { const diff = startX - e.changedTouches[0].clientX; if (Math.abs(diff) > 50) goTo(current + (diff > 0 ? 1 : -1)); }); // Keyboard container.setAttribute('tabindex', '0'); container.addEventListener('keydown', (e) => { if (e.key === 'ArrowLeft') goTo(current - 1); if (e.key === 'ArrowRight') goTo(current + 1); }); return { goTo, getCurrent: () => current }; } // Usage: createCarousel('#carousel', ['', '']);''' )) t.append(( "Build an infinite scroll image feed with skeleton placeholders", '''function createInfiniteFeed(containerSelector, fetchPage, options = {}) { const container = document.querySelector(containerSelector); if (!container) throw new Error('Feed container not found'); if (typeof fetchPage !== 'function') throw new TypeError('fetchPage must be a function'); const { pageSize = 20, threshold = 300 } = options; let page = 1; let isLoading = false; let hasMore = true; function createSkeletons(count) { const frag = document.createDocumentFragment(); for (let i = 0; i < count; i++) { const div = document.createElement('div'); div.className = 'skeleton'; div.style.cssText = 'height:200px;background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%);background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:8px;'; frag.appendChild(div); } return frag; } // Add shimmer keyframes if not present if (!document.getElementById('skeleton-styles')) { const style = document.createElement('style'); style.id = 'skeleton-styles'; style.textContent = '@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }'; document.head.appendChild(style); } async function loadMore() { if (isLoading || !hasMore) return; isLoading = true; const skeletons = createSkeletons(pageSize); container.appendChild(skeletons); try { const items = await fetchPage(page, pageSize); skeletons.remove(); if (!items || items.length === 0) { hasMore = false; return; } items.forEach(item => container.appendChild(item)); page++; } catch (err) { console.error('Feed load error:', err); skeletons.remove(); } finally { isLoading = false; } } const sentinel = document.createElement('div'); sentinel.style.height = '1px'; container.appendChild(sentinel); const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) loadMore(); }, { rootMargin: `${threshold}px` }); observer.observe(sentinel); loadMore(); return { loadMore, observer }; } // Usage: // createInfiniteFeed('#feed', async (page, size) => { // const res = await fetch(`/api/images?page=${page}&size=${size}`); // const data = await res.json(); // return data.map(url => { const img = document.createElement('img'); img.src = url; return img; }); // });''' )) return t # ── Domain: Games ── def game_templates(): t = [] t.append(( "Create a 2D canvas game loop with delta-time physics", '''class GameEngine { constructor(canvasId, options = {}) { this.canvas = document.getElementById(canvasId); if (!this.canvas) throw new Error('Canvas not found: ' + canvasId); this.ctx = this.canvas.getContext('2d'); this.entities = []; this.lastTime = 0; this.running = false; this.fps = 60; this.canvas.width = options.width || 800; this.canvas.height = options.height || 600; this.canvas.style.background = options.bg || '{color1}'; } addEntity(entity) { if (!entity.update || !entity.draw) { throw new TypeError('Entity must have update(dt) and draw(ctx) methods'); } this.entities.push(entity); return this; } start() { this.running = true; requestAnimationFrame((t) => this.loop(t)); } stop() { this.running = false; } loop(timestamp) { if (!this.running) return; const dt = Math.min((timestamp - this.lastTime) / 1000, 0.05); // Cap delta this.lastTime = timestamp; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); for (const entity of this.entities) { entity.update(dt, this.canvas.width, this.canvas.height); entity.draw(this.ctx); } requestAnimationFrame((t) => this.loop(t)); } } // Usage: // const engine = new GameEngine('game-canvas', { width: 800, height: 600 }); // engine.addEntity({ update(dt, w, h) { this.x += 100 * dt; }, draw(ctx) { ctx.fillRect(this.x, 100, 20, 20); }, x: 0 }); // engine.start();''' )) t.append(( "Implement AABB collision detection for rectangular game entities", '''function checkAABBCollision(a, b) { if (!a || !b) throw new Error('Both entities required for collision check'); return ( a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y ); } function resolveAABBOverlap(a, b) { const overlapX = Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x); const overlapY = Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y); if (overlapX < overlapY) { const dir = a.x < b.x ? -1 : 1; a.x += (overlapX / 2) * dir; b.x -= (overlapX / 2) * dir; } else { const dir = a.y < b.y ? -1 : 1; a.y += (overlapY / 2) * dir; b.y -= (overlapY / 2) * dir; } } class PhysicsWorld { constructor() { this.bodies = []; } add(body) { if (typeof body.x !== 'number' || typeof body.y !== 'number') { throw new TypeError('Body must have numeric x and y properties'); } this.bodies.push(body); } step() { for (let i = 0; i < this.bodies.length; i++) { for (let j = i + 1; j < this.bodies.length; j++) { if (checkAABBCollision(this.bodies[i], this.bodies[j])) { resolveAABBOverlap(this.bodies[i], this.bodies[j]); if (this.bodies[i].onCollision) this.bodies[i].onCollision(this.bodies[j]); if (this.bodies[j].onCollision) this.bodies[j].onCollision(this.bodies[i]); } } } } } // Usage: // const world = new PhysicsWorld(); // world.add({ x: 0, y: 0, width: 32, height: 32, onCollision(other) { console.log('hit!'); } }); // world.step();''' )) t.append(( "Build a sprite animation system with frame clipping and playback controls", '''class SpriteAnimator { constructor(image, frameWidth, frameHeight, frameCount) { if (!(image instanceof HTMLImageElement)) throw new TypeError('Expected HTMLImageElement'); this.image = image; this.frameWidth = frameWidth; this.frameHeight = frameHeight; this.frameCount = frameCount; this.currentFrame = 0; this.elapsed = 0; this.fps = 10; this.playing = true; this.loop = true; } update(dt) { if (!this.playing) return; this.elapsed += dt; const frameDuration = 1 / this.fps; if (this.elapsed >= frameDuration) { this.elapsed -= frameDuration; this.currentFrame++; if (this.currentFrame >= this.frameCount) { if (this.loop) this.currentFrame = 0; else { this.currentFrame = this.frameCount - 1; this.playing = false; } } } } draw(ctx, x, y, options = {}) { const sx = (this.currentFrame * this.frameWidth) % this.image.width; const sy = Math.floor((this.currentFrame * this.frameWidth) / this.image.width) * this.frameHeight; const scale = options.scale || 1; ctx.drawImage( this.image, sx, sy, this.frameWidth, this.frameHeight, x, y, this.frameWidth * scale, this.frameHeight * scale ); } play() { this.playing = true; } pause() { this.playing = false; } reset() { this.currentFrame = 0; this.elapsed = 0; } setFrame(index) { this.currentFrame = Math.max(0, Math.min(index, this.frameCount - 1)); } } // Usage: // const img = new Image(); // img.src = '/sprites/player.png'; // img.onload = () => { // const anim = new SpriteAnimator(img, 32, 32, 8); // // In game loop: anim.update(dt); anim.draw(ctx, 100, 100); // };''' )) t.append(( "Implement a tilemap renderer with camera scrolling and culling", '''class TilemapRenderer { constructor(canvasId, tileSize = 32) { this.canvas = document.getElementById(canvasId); if (!this.canvas) throw new Error('Canvas not found'); this.ctx = this.canvas.getContext('2d'); this.tileSize = tileSize; this.camera = { x: 0, y: 0 }; this.tiles = []; // 2D array of tile IDs this.tileset = new Map(); // ID -> color or image } loadMap(tiles) { if (!Array.isArray(tiles) || !tiles.every(row => Array.isArray(row))) { throw new TypeError('tiles must be a 2D array'); } this.tiles = tiles; } registerTile(id, renderable) { this.tileset.set(id, renderable); } setCamera(x, y) { this.camera.x = x; this.camera.y = y; } render() { const cols = Math.ceil(this.canvas.width / this.tileSize) + 1; const rows = Math.ceil(this.canvas.height / this.tileSize) + 1; const startCol = Math.floor(this.camera.x / this.tileSize); const startRow = Math.floor(this.camera.y / this.tileSize); for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { const tileRow = startRow + r; const tileCol = startCol + c; if (tileRow < 0 || tileRow >= this.tiles.length) continue; if (tileCol < 0 || tileCol >= this.tiles[tileRow].length) continue; const tileId = this.tiles[tileRow][tileCol]; const screenX = c * this.tileSize - (this.camera.x % this.tileSize); const screenY = r * this.tileSize - (this.camera.y % this.tileSize); const renderable = this.tileset.get(tileId); if (typeof renderable === 'string') { this.ctx.fillStyle = renderable; this.ctx.fillRect(screenX, screenY, this.tileSize, this.tileSize); } else if (renderable instanceof HTMLImageElement) { this.ctx.drawImage(renderable, screenX, screenY, this.tileSize, this.tileSize); } } } } } // Usage: // const renderer = new TilemapRenderer('game-canvas', 32); // renderer.loadMap([[0,0,1],[0,1,1],[1,1,1]]); // renderer.registerTile(0, '{color1}'); // renderer.registerTile(1, '{color2}'); // renderer.setCamera(100, 50); // renderer.render();''' )) t.append(( "Create a particle explosion effect on canvas for game feedback", '''class ParticleSystem { constructor() { this.particles = []; } emit(x, y, options = {}) { const { count = {count}, speed = {speed}, life = 1.0, colors = {colors}, size = 4, gravity = 200 } = options; for (let i = 0; i < count; i++) { const angle = (Math.PI * 2 * i) / count + (Math.random() - 0.5) * 0.5; const velocity = speed * (0.5 + Math.random() * 0.5); this.particles.push({ x, y, vx: Math.cos(angle) * velocity, vy: Math.sin(angle) * velocity, life, maxLife: life, color: colors[Math.floor(Math.random() * colors.length)], size: size * (0.5 + Math.random()), gravity }); } } update(dt) { for (let i = this.particles.length - 1; i >= 0; i--) { const p = this.particles[i]; p.x += p.vx * dt; p.y += p.vy * dt; p.vy += p.gravity * dt; p.life -= dt; if (p.life <= 0) this.particles.splice(i, 1); } } draw(ctx) { for (const p of this.particles) { const alpha = Math.max(0, p.life / p.maxLife); ctx.globalAlpha = alpha; ctx.fillStyle = p.color; ctx.beginPath(); ctx.arc(p.x, p.y, p.size * alpha, 0, Math.PI * 2); ctx.fill(); } ctx.globalAlpha = 1; } } // Usage: // const particles = new ParticleSystem(); // particles.emit(400, 300, { count: 30, speed: 150, colors: ['{color1}', '{color2}', '{color3}'] }); // // In game loop: particles.update(dt); particles.draw(ctx);''' )) return t # ── Generator ── def generate(count=1000): rng = random.Random(42) templates = ( threejs_templates() + htmlcss_templates() + playground_templates() + gallery_templates() + game_templates() ) entries = [] tpl_idx = 0 variant = 0 while len(entries) < count: problem_template, solution_template = templates[tpl_idx % len(templates)] tpl_idx += 1 color1 = pick(COLORS, rng) color2 = pick(COLORS, rng) color3 = pick(COLORS, rng) name = pick(NAMES, rng) adj = pick(ADJECTIVES, rng) size = pick(SIZES, rng) speed = pick(SPEEDS, rng) cnt = pick(COUNTS, rng) problem = problem_template.replace('{name}', name).replace('{adj}', adj) solution = (solution_template .replace('{color1}', color1) .replace('{color2}', color2) .replace('{color3}', color3) .replace('{name}', name) .replace('{adj}', adj) .replace('{size}', size) .replace('{speed}', str(speed)) .replace('{count}', str(cnt)) .replace('{colors}', str(picks(COLORS, 3, rng)))) # Determine domain from template source domain = 'threejs' lang = 'javascript' tags = ['frontend'] if problem_template in [p for p, _ in htmlcss_templates()]: domain = 'html-css-js' lang = 'css' if problem_template.startswith('Build a responsive CSS') or 'grid' in problem_template else 'javascript' tags = ['frontend', 'css', 'dom'] elif problem_template in [p for p, _ in playground_templates()]: domain = 'playground' tags = ['frontend', 'ui', 'interactive'] elif problem_template in [p for p, _ in gallery_templates()]: domain = 'gallery' tags = ['frontend', 'images', 'performance'] elif problem_template in [p for p, _ in game_templates()]: domain = 'games' tags = ['frontend', 'canvas', 'game-dev'] if 'three.js' in problem.lower(): domain = 'threejs' tags = ['frontend', 'threejs', 'webgl'] entries.append({ 'problem': problem, 'solution': solution, 'domain': domain, 'language': lang, 'tags': tags, 'variant': variant }) variant += 1 return entries def main(): parser = argparse.ArgumentParser(description='Generate frontend code pattern training pairs') parser.add_argument('--count', type=int, default=1000, help='Number of pairs to generate') parser.add_argument('--output', type=str, default='training-data/code-patterns-frontend-&-creative.jsonl') parser.add_argument('--seed', type=int, default=42, help='Random seed') args = parser.parse_args() random.seed(args.seed) entries = generate(args.count) out_path = Path(args.output) out_path.parent.mkdir(parents=True, exist_ok=True) with open(out_path, 'w', encoding='utf-8') as f: for entry in entries: f.write(json.dumps(entry, ensure_ascii=False) + '\n') print(f'Generated {len(entries)} code pattern pairs → {out_path}') print(f'Size: {out_path.stat().st_size / 1024:.1f} KB') # Print domain distribution from collections import Counter dist = Counter(e['domain'] for e in entries) print('Domain distribution:') for d, c in sorted(dist.items()): print(f' {d}: {c}') if __name__ == '__main__': main()