diff --git a/.mcp.json b/.mcp.json index 11633fe..9ea125f 100644 --- a/.mcp.json +++ b/.mcp.json @@ -42,6 +42,10 @@ "PERPLEXITY_TIMEOUT_MS": "600000" } }, + "chrome-devtools": { + "command": "npx", + "args": ["-y", "chrome-devtools-mcp@latest"] + }, "browsermcp": { "type": "stdio", "command": "npx", diff --git a/apps/landing/src/app/lab/generate/page.tsx b/apps/landing/src/app/lab/generate/page.tsx new file mode 100644 index 0000000..a9383f9 --- /dev/null +++ b/apps/landing/src/app/lab/generate/page.tsx @@ -0,0 +1,14 @@ +'use client'; + +import { Section } from '@/components/shared/Section'; +import { GenerateFormPlaceholder } from '@/components/lab/GenerateFormPlaceholder'; + +const GeneratePage = () => { + return ( +
+ +
+ ); +}; + +export default GeneratePage; diff --git a/apps/landing/src/app/lab/images/page.tsx b/apps/landing/src/app/lab/images/page.tsx new file mode 100644 index 0000000..d270fe7 --- /dev/null +++ b/apps/landing/src/app/lab/images/page.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { Section } from '@/components/shared/Section'; + +const ImagesPage = () => { + return ( +
+
+

+ Image Library +

+

+ Browse and manage your generated images +

+
+ +
+
+
🖼️
+

Image Browser

+

Component will be implemented here

+
+
+
+ ); +}; + +export default ImagesPage; diff --git a/apps/landing/src/app/lab/layout.tsx b/apps/landing/src/app/lab/layout.tsx new file mode 100644 index 0000000..38c2cff --- /dev/null +++ b/apps/landing/src/app/lab/layout.tsx @@ -0,0 +1,55 @@ +'use client'; + +/** + * Lab Section Layout + * + * Code Style: + * - Use `const` arrow function components (not function declarations) + * - Use `type` instead of `interface` for type definitions + * - Early returns for conditionals + * - No inline comments (JSDoc headers only) + * - Tailwind classes only + * + * Structure: + * - Layout components: src/components/layout/lab/ + * - Feature components: src/components/lab/ + * - Pages: src/app/lab/{section}/page.tsx + * + * Sub-navigation items: + * - /lab/generate - Image generation + * - /lab/images - Image library browser + * - /lab/live - Live generation testing + * - /lab/upload - File upload interface + */ + +import { ReactNode } from 'react'; +import { usePathname } from 'next/navigation'; +import { ApiKeyWidget } from '@/components/shared/ApiKeyWidget/apikey-widget'; +import { ApiKeyProvider } from '@/components/shared/ApiKeyWidget/apikey-context'; +import { PageProvider } from '@/contexts/page-context'; +import { LabLayout } from '@/components/layout/lab/LabLayout'; + +type LabLayoutWrapperProps = { + children: ReactNode; +}; + +const navItems = [ + { label: 'Generate', href: '/lab/generate' }, + { label: 'Images', href: '/lab/images' }, + { label: 'Live', href: '/lab/live' }, + { label: 'Upload', href: '/lab/upload' }, +]; + +const LabLayoutWrapper = ({ children }: LabLayoutWrapperProps) => { + const pathname = usePathname(); + + return ( + + }> + {children} + + + ); +}; + +export default LabLayoutWrapper; diff --git a/apps/landing/src/app/lab/live/page.tsx b/apps/landing/src/app/lab/live/page.tsx new file mode 100644 index 0000000..eb04de6 --- /dev/null +++ b/apps/landing/src/app/lab/live/page.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { Section } from '@/components/shared/Section'; + +const LivePage = () => { + return ( +
+
+

+ Live Generation +

+

+ Real-time testing and experimentation workspace +

+
+ +
+
+
+

Live Testing Interface

+

Component will be implemented here

+
+
+
+ ); +}; + +export default LivePage; diff --git a/apps/landing/src/app/lab/page.tsx b/apps/landing/src/app/lab/page.tsx new file mode 100644 index 0000000..ea281ad --- /dev/null +++ b/apps/landing/src/app/lab/page.tsx @@ -0,0 +1,16 @@ +'use client'; + +import { useEffect } from 'react'; +import { useRouter } from 'next/navigation'; + +const LabPage = () => { + const router = useRouter(); + + useEffect(() => { + router.replace('/lab/generate'); + }, [router]); + + return null; +}; + +export default LabPage; diff --git a/apps/landing/src/app/lab/upload/page.tsx b/apps/landing/src/app/lab/upload/page.tsx new file mode 100644 index 0000000..1fc4c1c --- /dev/null +++ b/apps/landing/src/app/lab/upload/page.tsx @@ -0,0 +1,28 @@ +'use client'; + +import { Section } from '@/components/shared/Section'; + +const UploadPage = () => { + return ( +
+
+

+ File Upload +

+

+ Upload and manage reference images for generation +

+
+ +
+
+
📤
+

Upload Interface

+

Component will be implemented here

+
+
+
+ ); +}; + +export default UploadPage; diff --git a/apps/landing/src/components/lab/FilterPlaceholder.tsx b/apps/landing/src/components/lab/FilterPlaceholder.tsx new file mode 100644 index 0000000..7932e5d --- /dev/null +++ b/apps/landing/src/components/lab/FilterPlaceholder.tsx @@ -0,0 +1,83 @@ +'use client'; + +/** + * Filter Placeholder Component + * + * Checkbox/radio button group for sidebar filters. + * Supports both single-select (radio) and multi-select (checkbox) modes. + * + * Features: + * - Radio buttons for single selection + * - Checkboxes for multiple selection + * - Option counts (e.g., "All (127)") + * - Accessible keyboard navigation + * - Focus indicators + */ + +import { useState } from 'react'; + +type FilterOption = { + id: string; + label: string; + count?: number; +}; + +type FilterPlaceholderProps = { + options: FilterOption[]; + multiSelect?: boolean; + groupId: string; +}; + +export const FilterPlaceholder = ({ + options, + multiSelect = false, + groupId, +}: FilterPlaceholderProps) => { + const [selected, setSelected] = useState(multiSelect ? [] : [options[0]?.id || '']); + + const handleSelect = (optionId: string) => { + if (multiSelect) { + setSelected((prev) => + prev.includes(optionId) ? prev.filter((id) => id !== optionId) : [...prev, optionId] + ); + } else { + setSelected([optionId]); + } + }; + + const isSelected = (optionId: string) => selected.includes(optionId); + + return ( +
+ {options.map((option) => { + const checked = isSelected(option.id); + const inputId = `${groupId}-${option.id}`; + + return ( + + ); + })} +
+ ); +}; diff --git a/apps/landing/src/components/lab/GenerateFormPlaceholder.tsx b/apps/landing/src/components/lab/GenerateFormPlaceholder.tsx new file mode 100644 index 0000000..7d3bde7 --- /dev/null +++ b/apps/landing/src/components/lab/GenerateFormPlaceholder.tsx @@ -0,0 +1,211 @@ +'use client'; + +/** + * Generate Form Placeholder Component + * + * Main content placeholder for lab generation interface. + * Matches the /demo/tti page visual style with form card and results area. + * + * Features: + * - Header with title and description + * - Prompt textarea (similar to TTI page) + * - Options row with selects and buttons + * - Submit button with gradient styling + * - Results area placeholder + * - Keyboard shortcuts (Ctrl+Enter) + */ + +import { useState, useRef, KeyboardEvent } from 'react'; + +export const GenerateFormPlaceholder = () => { + const [prompt, setPrompt] = useState(''); + const [aspectRatio, setAspectRatio] = useState('1:1'); + const [template, setTemplate] = useState('photorealistic'); + const [generating, setGenerating] = useState(false); + const textareaRef = useRef(null); + + const handleGenerate = () => { + if (!prompt.trim()) return; + setGenerating(true); + setTimeout(() => setGenerating(false), 2000); + }; + + const handleKeyDown = (e: KeyboardEvent) => { + if (e.ctrlKey && e.key === 'Enter') { + e.preventDefault(); + handleGenerate(); + } + }; + + return ( +
+ {/* Page Header */} +
+

+ Generate Workbench +

+

+ Create and experiment with AI-generated images +

+
+ + {/* Info Banner (Placeholder) */} +
+
+
+

Lab Environment

+

+ This is an experimental interface for testing generation features +

+
+
+ + Active +
+
+
+ + {/* Generation Form Card */} +
+ {/* Prompt Textarea */} +
+ +