Skip to content

Commit 3b5b620

Browse files
Filip-Bumbuclaude
andcommitted
Add 7 new templates, category filtering, and featured landing grid
- Extend Template interface with category and featured fields - Add 7 new templates: Python Basics, Python Data, Snake Game, TypeScript Playground, Contact Form, React Quiz, Bar Chart - Landing page shows 8 featured templates instead of all 28 - TemplateGallery modal now has category tabs (All, React, Interactive, Layouts, Data & APIs, Languages) - Update descriptions and icons for all existing templates - Replace em-dashes with regular dashes across all source files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a63dae8 commit 3b5b620

File tree

9 files changed

+2104
-46
lines changed

9 files changed

+2104
-46
lines changed

frontend/src/components/HTMLPreview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export default function HTMLPreview({ html, css, js, files, isOpen, onClose, hid
131131
if (bodyMatch) {
132132
bodyContent = bodyMatch[1]
133133
}
134-
// Strip local script tags (e.g. <script src="/src/main.tsx">) the bundler handles these
134+
// Strip local script tags (e.g. <script src="/src/main.tsx">) - the bundler handles these
135135
bodyContent = bodyContent.replace(/<script[^>]*\ssrc=["']\/[^"']*["'][^>]*>\s*<\/script>/gi, '')
136136

137137
const importMap = generateImportMap(bundleImports)

frontend/src/components/TemplateGallery.tsx

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { useState } from 'react'
12
import { Link } from 'react-router-dom'
2-
import { TEMPLATES } from '../lib/templates'
3+
import { TEMPLATES, Template } from '../lib/templates'
34
import { encodeWorkspace } from '../lib/hash'
45
import { ArrowRight, FileCode, BarChart3, Sparkles, Plug, File, Atom, X, Palette, Timer, Type, Layout, Cloud, Gamepad2 } from 'lucide-react'
56

@@ -18,36 +19,55 @@ const iconMap: Record<string, React.ReactNode> = {
1819
weather: <Cloud className="w-5 h-5" />,
1920
}
2021

22+
const CATEGORIES = [
23+
{ id: 'all', label: 'All' },
24+
{ id: 'react', label: 'React' },
25+
{ id: 'interactive', label: 'Interactive' },
26+
{ id: 'layout', label: 'Layouts' },
27+
{ id: 'data', label: 'Data & APIs' },
28+
{ id: 'languages', label: 'Languages' },
29+
] as const
30+
31+
function filterTemplates(category: string): Template[] {
32+
if (category === 'all') return TEMPLATES
33+
if (category === 'languages') return TEMPLATES.filter(t => t.category === 'python' || t.category === 'typescript')
34+
return TEMPLATES.filter(t => t.category === category || (category === 'all' && true))
35+
}
36+
2137
interface TemplateGalleryProps {
2238
isOpen: boolean
2339
onClose: () => void
2440
onSelect?: (workspace: unknown) => void
2541
}
2642

2743
export default function TemplateGallery({ isOpen, onClose }: TemplateGalleryProps) {
44+
const [activeCategory, setActiveCategory] = useState('all')
45+
2846
if (!isOpen) return null
29-
47+
48+
const filtered = filterTemplates(activeCategory)
49+
3050
return (
3151
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
3252
{/* Backdrop */}
33-
<div
53+
<div
3454
className="absolute inset-0 bg-black/80 backdrop-blur-sm animate-[fadeIn_0.1s_ease-out]"
3555
onClick={onClose}
3656
/>
37-
57+
3858
{/* Modal */}
3959
<div className="relative w-full max-w-4xl max-h-[80vh] overflow-hidden bg-[#0A0A0F] border border-purple-500/30 chamfer">
4060
{/* Scanlines */}
4161
<div className="absolute inset-0 pointer-events-none opacity-30 scanlines" />
42-
62+
4363
{/* Header */}
4464
<div className="flex items-center justify-between p-6 border-b border-purple-500/20">
4565
<div>
4666
<h2 className="text-xl font-bold uppercase tracking-wider neon-text-purple">
4767
Select Template
4868
</h2>
4969
<p className="text-sm text-gray-500 mt-1 font-mono">
50-
{'>'} Choose a starting point for your project
70+
{'>'} {filtered.length} template{filtered.length !== 1 ? 's' : ''} available
5171
</p>
5272
</div>
5373
<button
@@ -57,10 +77,33 @@ export default function TemplateGallery({ isOpen, onClose }: TemplateGalleryProp
5777
<X className="w-5 h-5" />
5878
</button>
5979
</div>
60-
80+
81+
{/* Category tabs */}
82+
<div className="flex items-center gap-1 px-6 py-3 border-b border-purple-500/10 overflow-x-auto">
83+
{CATEGORIES.map((cat) => {
84+
const count = cat.id === 'all' ? TEMPLATES.length : filterTemplates(cat.id).length
85+
return (
86+
<button
87+
key={cat.id}
88+
onClick={() => setActiveCategory(cat.id)}
89+
className={`flex items-center gap-1.5 px-3 py-1.5 rounded text-sm font-mono whitespace-nowrap transition ${
90+
activeCategory === cat.id
91+
? 'bg-purple-500/20 text-purple-400 border border-purple-500/30'
92+
: 'text-gray-500 hover:text-gray-300 hover:bg-white/5 border border-transparent'
93+
}`}
94+
>
95+
{cat.label}
96+
<span className={`text-xs ${activeCategory === cat.id ? 'text-purple-400/70' : 'text-gray-600'}`}>
97+
{count}
98+
</span>
99+
</button>
100+
)
101+
})}
102+
</div>
103+
61104
{/* Grid */}
62-
<div className="p-6 overflow-y-auto max-h-[60vh] grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
63-
{TEMPLATES.map((template) => (
105+
<div className="p-6 overflow-y-auto max-h-[55vh] grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
106+
{filtered.map((template) => (
64107
<Link
65108
key={template.id}
66109
to={`/ide#${encodeWorkspace(template.workspace)}`}

frontend/src/components/UpgradeModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* UpgradeModal shown when user hits a plan limit
2+
* UpgradeModal - shown when user hits a plan limit
33
* Nudges them to upgrade with context about what they tried to do
44
*/
55
import { Link } from 'react-router-dom'

0 commit comments

Comments
 (0)