chore: install openagent opencode

Signed-off-by: Dmytro Stanchiev <git@dmytros.dev>
This commit is contained in:
2026-04-07 11:31:26 -04:00
parent b4c03ff25e
commit c2263602c4
204 changed files with 38010 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
<!-- Context: ui/scroll-linked-animations | Priority: critical | Version: 1.0 | Updated: 2026-02-15 -->
# Concept: Scroll-Linked Animations
**Purpose**: Sync image sequences to scroll position for cinematic product reveals
**Last Updated**: 2026-01-07
---
## Core Idea
Map scroll position to video frames. As user scrolls, play through image sequence like a scrubbing timeline. Creates illusion of 3D animation controlled by scroll.
**Formula**: `scrollProgress (0→1) → frameIndex (0→N) → canvas.drawImage()`
---
## Essential Parts
1. **Image sequence** - 60-150 WebP frames from video/3D render
2. **Sticky canvas** - Fixed HTML5 canvas, always visible while scrolling
3. **Scroll tracker** - Framer Motion `useScroll` hook
4. **Preloader** - Load all frames upfront (prevents flicker)
5. **Background match** - Page BG = image BG (hides edges)
---
## Minimal Example
```tsx
const { scrollYProgress } = useScroll({ target: containerRef })
const frameIndex = useTransform(scrollYProgress, [0, 1], [0, 119])
useEffect(() => {
ctx.drawImage(images[Math.round(frameIndex)], 0, 0)
}, [frameIndex])
```
**Why canvas?** 10x faster than swapping `<img src>`. DOM updates are slow.
---
## Related
- examples/scrollytelling-headphone.md - Full code
- guides/building-scrollytelling-pages.md - Implementation
- lookup/scroll-animation-prompts.md - Generate sequences
---
## Reference
[Apple AirPods Pro](https://www.apple.com/airpods-pro/) - Production example

View File

@@ -0,0 +1,265 @@
<!-- Context: ui/scrollytelling-headphone | Priority: high | Version: 1.0 | Updated: 2026-02-15 -->
---
description: "Full Next.js implementation of scroll-linked image sequence animation"
---
# Example: Scrollytelling Headphone Animation
**Purpose**: Full Next.js implementation of scroll-linked image sequence animation
**Last Updated**: 2026-01-07
---
## Overview
Complete working example of "Zenith X" headphone scrollytelling page using Next.js 14, Framer Motion, and Canvas.
**Tech Stack**: Next.js 14 (App Router) + Framer Motion + Canvas + Tailwind CSS
---
## File Structure
```
app/
├── page.tsx
├── components/
│ └── HeadphoneScroll.tsx
└── globals.css
public/
└── frames/
└── frame_0001.webp through frame_0120.webp
```
---
## 1. globals.css
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply bg-[#050505] text-white antialiased;
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
}
}
```
---
## 2. app/page.tsx
```tsx
import HeadphoneScroll from './components/HeadphoneScroll'
export default function Home() {
return (
<main className="bg-[#050505]">
<HeadphoneScroll />
</main>
)
}
```
---
## 3. components/HeadphoneScroll.tsx
```tsx
'use client'
import { useEffect, useRef, useState } from 'react'
import { motion, useScroll, useTransform } from 'framer-motion'
const FRAME_COUNT = 120
export default function HeadphoneScroll() {
const containerRef = useRef<HTMLDivElement>(null)
const canvasRef = useRef<HTMLCanvasElement>(null)
const [images, setImages] = useState<HTMLImageElement[]>([])
const [loading, setLoading] = useState(true)
// Track scroll progress (0 to 1)
const { scrollYProgress } = useScroll({
target: containerRef,
offset: ['start start', 'end end']
})
// Map scroll progress to frame index
const frameIndex = useTransform(scrollYProgress, [0, 1], [0, FRAME_COUNT - 1])
const [currentFrame, setCurrentFrame] = useState(0)
// Update current frame
useEffect(() => {
return frameIndex.on('change', (latest) => {
setCurrentFrame(Math.round(latest))
})
}, [frameIndex])
// Preload all images
useEffect(() => {
const loadImages = async () => {
const promises = Array.from({ length: FRAME_COUNT }, (_, i) => {
return new Promise<HTMLImageElement>((resolve) => {
const img = new Image()
const frameNum = String(i + 1).padStart(4, '0')
img.src = `/frames/frame_${frameNum}.webp`
img.onload = () => resolve(img)
})
})
const loaded = await Promise.all(promises)
setImages(loaded)
setLoading(false)
}
loadImages()
}, [])
// Render current frame to canvas
useEffect(() => {
if (!canvasRef.current || !images.length) return
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
if (!ctx) return
const img = images[currentFrame]
// Set canvas size
canvas.width = window.innerWidth
canvas.height = window.innerHeight
// Clear and draw centered
ctx.clearRect(0, 0, canvas.width, canvas.height)
const scale = Math.min(
canvas.width / img.width,
canvas.height / img.height
)
const x = (canvas.width - img.width * scale) / 2
const y = (canvas.height - img.height * scale) / 2
ctx.drawImage(img, x, y, img.width * scale, img.height * scale)
}, [currentFrame, images])
// Text overlay opacity transforms
const title = useTransform(scrollYProgress, [0, 0.1, 0.2], [1, 1, 0])
const text1 = useTransform(scrollYProgress, [0.25, 0.3, 0.4], [0, 1, 0])
const text2 = useTransform(scrollYProgress, [0.55, 0.6, 0.7], [0, 1, 0])
const cta = useTransform(scrollYProgress, [0.85, 0.9, 1], [0, 1, 1])
if (loading) {
return (
<div className="fixed inset-0 flex items-center justify-center bg-[#050505]">
<div className="h-12 w-12 animate-spin rounded-full border-4 border-white/20 border-t-white" />
</div>
)
}
return (
<div ref={containerRef} className="relative h-[400vh]">
{/* Sticky Canvas */}
<canvas
ref={canvasRef}
className="sticky top-0 h-screen w-full"
style={{ willChange: 'transform' }}
/>
{/* Text Overlays */}
<motion.div
style={{ opacity: title }}
className="pointer-events-none fixed inset-0 flex items-center justify-center"
>
<div className="text-center">
<h1 className="text-7xl font-bold tracking-tight text-white/90">
Zenith X
</h1>
<p className="mt-4 text-xl text-white/60">Pure Sound.</p>
</div>
</motion.div>
<motion.div
style={{ opacity: text1 }}
className="pointer-events-none fixed inset-y-0 left-20 flex items-center"
>
<p className="text-4xl font-bold tracking-tight text-white/90">
Precision Engineering.
</p>
</motion.div>
<motion.div
style={{ opacity: text2 }}
className="pointer-events-none fixed inset-y-0 right-20 flex items-center"
>
<p className="text-4xl font-bold tracking-tight text-white/90">
Titanium Drivers.
</p>
</motion.div>
<motion.div
style={{ opacity: cta }}
className="pointer-events-none fixed inset-0 flex items-center justify-center"
>
<div className="text-center">
<h2 className="text-6xl font-bold tracking-tight text-white/90">
Hear Everything.
</h2>
<button className="pointer-events-auto mt-8 rounded-full bg-white px-8 py-3 text-lg font-semibold text-black transition hover:bg-white/90">
Pre-Order Now
</button>
</div>
</motion.div>
</div>
)
}
```
---
## Key Implementation Details
**Line 15-18**: `useScroll` tracks scroll progress from container start to end
**Line 21**: `useTransform` maps 0-1 scroll to 0-119 frame index
**Line 32-46**: Preload all 120 frames using Promise.all
**Line 49-70**: Draw current frame to canvas, scaled and centered
**Line 73-76**: Text opacity transforms for fade in/out at specific scroll positions
---
## Usage
1. Install dependencies: `bun --bun install framer-motion`
2. Place 120 WebP frames in `/public/frames/`
3. Copy code into respective files
4. Run: `bun --bun run dev`
---
## Customization
**Change frame count**: Update `FRAME_COUNT` constant (line 7)
**Adjust scroll length**: Change `h-[400vh]` to `h-[300vh]` or `h-[500vh]` (line 120)
**Modify text timing**: Update transform ranges in lines 73-76
**Change colors**: Update `bg-[#050505]` to match your image background
---
## Related
- concepts/scroll-linked-animations.md - Understanding the technique
- guides/scrollytelling-setup.md - Getting started
- lookup/scroll-animation-prompts.md - Generating image sequences
---
## References
- [Framer Motion Docs](https://www.framer.com/motion/)
- [Next.js App Router](https://nextjs.org/docs/app)

View File

@@ -0,0 +1,273 @@
<!-- Context: ui/building-scrollytelling-pages | Priority: high | Version: 1.0 | Updated: 2026-02-15 -->
---
description: "Step-by-step implementation of scroll-linked image sequence animations"
---
# Guide: Building Scrollytelling Pages
**Purpose**: Step-by-step implementation of scroll-linked image sequence animations
**Last Updated**: 2026-01-07
---
## Prerequisites
- Next.js 14+ project with App Router
- Framer Motion installed (`bun --bun i framer-motion`)
- Tailwind CSS configured
- Image sequence ready (60-240 WebP frames)
---
## Step 1: Generate Image Sequences
Use nano banana or AI image tools to create start/end frames, then generate interpolation:
**Start frame prompt**:
```
Ultra-premium product photography of [product] on matte black surface,
minimalistic studio shoot, deep black background with subtle gradient,
soft rim lighting, cinematic, high contrast, luxury aesthetic, sharp focus,
no clutter, DSLR 85mm f/1.8, photorealistic
```
**End frame prompt**:
```
Exploded technical diagram of same [product], every component separated
and floating in alignment, against deep black studio background, visible
internal structure, hyper-realistic, studio rim lighting, cinematic,
high contrast, no labels, photorealistic
```
**Generate video**: Use AI video tools (Runway, Pika) to interpolate between frames.
**Export frames**: Use ffmpeg or ezgif to split video into 120+ WebP images.
```bash
ffmpeg -i animation.mp4 -vf fps=30 frame_%04d.webp
```
---
## Step 2: Project Structure
```
app/
├── page.tsx # Main landing page
├── components/
│ └── HeadphoneScroll.tsx # Scroll animation component
└── globals.css # Dark theme, Inter font
public/
└── frames/
├── frame_0001.webp # 120+ frames
├── frame_0002.webp
└── ...
```
---
## Step 3: Setup globals.css
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply bg-[#050505] text-white antialiased;
font-family: 'Inter', -apple-system, sans-serif;
}
}
```
---
## Step 4: Create Scroll Component
**Key patterns**:
- Container with `h-[400vh]` for long scroll
- Canvas with `sticky top-0` stays fixed
- `useScroll` tracks scroll progress (0-1)
- `useTransform` maps progress to frame index
- `useEffect` preloads all images
**Core logic**:
```tsx
const { scrollYProgress } = useScroll({ target: containerRef })
const frameIndex = useTransform(scrollYProgress, [0, 1], [0, 119])
```
---
## Step 5: Implement Preloader
Always preload images before starting animation:
```tsx
useEffect(() => {
const loadImages = async () => {
const promises = Array.from({ length: 120 }, (_, i) => {
return new Promise((resolve) => {
const img = new Image()
img.src = `/frames/frame_${String(i + 1).padStart(4, '0')}.webp`
img.onload = () => resolve(img)
})
})
const loaded = await Promise.all(promises)
setImages(loaded)
setLoading(false)
}
loadImages()
}, [])
```
---
## Step 6: Canvas Rendering
Draw current frame to canvas on every scroll update:
```tsx
useEffect(() => {
if (!canvasRef.current || !images.length) return
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
const img = images[Math.round(currentFrame)]
// Scale canvas to window
canvas.width = window.innerWidth
canvas.height = window.innerHeight
// Draw centered
ctx.drawImage(img,
(canvas.width - img.width) / 2,
(canvas.height - img.height) / 2
)
}, [currentFrame, images])
```
---
## Step 7: Add Text Overlays
Fade text in/out at specific scroll positions:
```tsx
<motion.div
style={{
opacity: useTransform(scrollYProgress,
[0.25, 0.30, 0.35], // Fade in 25-30%, out 35%
[0, 1, 0]
)
}}
className="absolute left-20 text-4xl font-bold"
>
Precision Engineering.
</motion.div>
```
---
## Step 8: Match Backgrounds
**CRITICAL**: Page background MUST match image background exactly.
1. Open first frame in image editor
2. Use eyedropper tool on background (e.g., `#050505`)
3. Set page background to exact same color in globals.css
4. Test: Image edges should be invisible
---
## Step 9: Optimize Performance
```tsx
// Add GPU hint
<canvas
ref={canvasRef}
className="sticky top-0 h-screen w-full"
style={{ willChange: 'transform' }}
/>
// Throttle redraws on mobile
useEffect(() => {
let rafId
const render = () => {
// Draw logic here
rafId = requestAnimationFrame(render)
}
render()
return () => cancelAnimationFrame(rafId)
}, [])
```
---
## Step 10: Add Loading State
Show spinner while frames load:
```tsx
{loading && (
<div className="fixed inset-0 flex items-center justify-center bg-[#050505]">
<div className="animate-spin h-12 w-12 border-4 border-white/20 border-t-white rounded-full" />
</div>
)}
```
---
## Common Issues & Fixes
### Images not loading
- Check file paths match exactly (case-sensitive)
- Verify all frames exist in `/public/frames/`
- Open browser console for 404 errors
### Stuttering animation
- Ensure all images preloaded before starting
- Use WebP (not PNG/JPEG)
- Check canvas size isn't too large
### Visible image edges
- Background colors don't match exactly
- Use eyedropper tool, not guessing
- Check for gradients in image background
### Mobile performance
- Reduce frame count (use every 2nd frame)
- Debounce with requestAnimationFrame
- Consider disabling on small screens
---
## Testing Checklist
- [ ] All frames load without 404s
- [ ] Animation smooth from 0-100% scroll
- [ ] Text fades in/out at correct positions
- [ ] Background seamlessly blends with images
- [ ] Loading spinner shows before animation
- [ ] Works on mobile (or gracefully disabled)
- [ ] No console errors
---
## Related
- concepts/scroll-linked-animations.md - Understanding the technique
- examples/headphone-scrollytelling.md - Full code example
- lookup/animation-image-prompts.md - Prompts for frame generation
---
## References
- [Next.js Image Optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images)
- [Framer Motion useScroll](https://www.framer.com/motion/use-scroll/)

View File

@@ -0,0 +1,215 @@
<!-- Context: ui/scroll-animation-prompts | Priority: high | Version: 1.0 | Updated: 2026-02-15 -->
---
description: "AI prompts for generating start/end frames and video sequences for scrollytelling"
---
# Lookup: Scroll Animation Image Generation Prompts
**Purpose**: AI prompts for generating start/end frames and video sequences for scrollytelling
**Last Updated**: 2026-01-07
---
## Overview
Use these prompts with nano banana, Runway, Pika, or other AI tools to create image sequences for scroll animations.
**Workflow**: Start frame → End frame → Video interpolation → Frame extraction
---
## Start Frame Prompts
### Product Hero Shot
```
Ultra-premium product photography of [PRODUCT NAME] placed on a matte black
surface, minimalistic studio photoshoot. Deep black background (#050505) with
subtle gradient falloff, soft rim lighting outlining edges, controlled
reflections on smooth textures. Cinematic lighting, high contrast, luxury tech
aesthetic, sharp focus, shallow depth of field. No clutter, no text, no logos
emphasized. Shot with professional DSLR, 85mm lens, f/1.8, ultra-high
resolution, photorealistic, Apple-level product shoot, dramatic mood, modern
and elegant.
```
**Variables**: Replace [PRODUCT NAME] with your product
**Output**: Starting position, fully assembled, hero angle
---
### Variations by Product Type
| Product Type | Additional Details |
|--------------|-------------------|
| **Headphones** | "over-ear headphones with leather cushions and metal headband" |
| **Smartphone** | "smartphone with edge-to-edge OLED display, aluminum frame" |
| **Watch** | "luxury smartwatch with titanium case and sapphire crystal" |
| **Laptop** | "thin laptop with aluminum unibody, open at 45 degrees" |
| **Camera** | "mirrorless camera with prime lens attached, side profile" |
---
## End Frame Prompts
### Exploded Technical View
```
Exploded technical diagram view of [PRODUCT NAME], every component precisely
separated and floating in perfect alignment, suspended in mid-air against deep
black studio background (#050505). Visible internal structure including
[INTERNAL COMPONENTS], all parts evenly spaced showing assembly order.
Hyper-realistic product visualization, ultra-sharp focus, studio rim lighting
identical to hero shot, soft highlights tracing each component, controlled
reflections on matte and metal surfaces. Cinematic lighting, high contrast,
luxury engineering aesthetic, no labels, no annotations, no text.
Photorealistic, ultra-high resolution, Apple-style industrial design render,
dramatic and clean.
```
**Variables**:
- [PRODUCT NAME]: Your product
- [INTERNAL COMPONENTS]: Specific parts to show
---
### Internal Components by Product
| Product | Internal Components Examples |
|---------|------------------------------|
| **Headphones** | "copper wiring, titanium drivers, magnets, circuit boards, padding layers, metal frame" |
| **Smartphone** | "battery, logic board, cameras, OLED panel, antenna bands, frame" |
| **Watch** | "watch movement, gears, battery, sensors, display, crown mechanism" |
| **Laptop** | "keyboard assembly, trackpad, battery cells, logic board, cooling fans, display panel" |
| **Camera** | "sensor, shutter mechanism, lens elements, mirror assembly, circuit boards" |
---
## Video Interpolation Prompts
### For Runway/Pika
```
Smoothly transition from fully assembled [PRODUCT] to exploded view.
Components separate slowly and precisely, maintaining perfect alignment.
Camera stays locked, product rotates slightly clockwise. Cinematic motion,
professional product animation, 4-5 seconds duration, 30fps.
```
**Settings**:
- Duration: 4-5 seconds
- FPS: 30 (yields 120-150 frames)
- Camera: Static or slow orbit
- Motion: Smooth, controlled separation
---
## Frame Extraction
### Using ffmpeg
```bash
# Extract as WebP (best for web)
ffmpeg -i animation.mp4 -vf fps=30 frame_%04d.webp
# Extract as PNG (higher quality, larger)
ffmpeg -i animation.mp4 -vf fps=30 frame_%04d.png
# Extract with quality control
ffmpeg -i animation.mp4 -vf fps=30 -quality 90 frame_%04d.webp
```
### Using ezgif.com
1. Upload MP4 video
2. Choose "Video to GIF" → "Split to frames"
3. Select WebP format
4. Download all frames as ZIP
5. Rename: `frame_0001.webp`, `frame_0002.webp`, etc.
---
## Background Color Matching
**CRITICAL**: Page background MUST match image background exactly
### Recommended Dark Backgrounds
| Color Code | Usage |
|------------|-------|
| `#050505` | Pure black with slight lift (recommended) |
| `#0a0a0a` | Slightly lighter, less harsh |
| `#000000` | True black (only if images are true black) |
| `#1a1a1a` | Dark gray (for lighter renders) |
**Pro tip**: Use eyedropper tool on first frame background, use exact hex in CSS
---
## Alternative Animation Styles
### Rotation (360° spin)
**Start**: Front view
**End**: Front view (after 360° rotation)
**Prompt**: "Rotate product 360 degrees on turntable, maintain lighting"
### Zoom In (Feature reveal)
**Start**: Full product view
**End**: Close-up of key feature
**Prompt**: "Smooth camera push-in focusing on [FEATURE], maintain focus"
### Morph (Color/style change)
**Start**: Product in color A
**End**: Product in color B
**Prompt**: "Seamlessly morph product color from [A] to [B], maintain form"
---
## Quality Settings
### For High-End Results
- **Resolution**: 1920x1080 minimum (4K for high-DPI)
- **Format**: WebP (compression) or PNG (quality)
- **Frame count**: 90-150 frames (3-5 seconds at 30fps)
- **Total size**: Target <50MB for all frames combined
### Optimization Tips
1. Use WebP format (70% smaller than PNG)
2. Compress with quality 85-90
3. Resize images to max 2000px width
4. Use consistent aspect ratio (16:9 or 1:1)
---
## Testing Checklist
- [ ] Background color matches exactly (no visible edges)
- [ ] All frames same dimensions
- [ ] Smooth motion (no jumps between frames)
- [ ] Consistent lighting across sequence
- [ ] File names sequential (`frame_0001` to `frame_0120`)
- [ ] Total file size reasonable (<50MB)
---
## Related
- concepts/scroll-linked-animations.md - Understanding the technique
- examples/scrollytelling-headphone.md - Full implementation
- guides/scrollytelling-setup.md - Setup instructions
---
## Tool References
- [Runway ML](https://runwayml.com) - AI video generation
- [Pika Labs](https://pika.art) - AI video interpolation
- [ezgif](https://ezgif.com/split-video) - Frame extraction
- [FFmpeg](https://ffmpeg.org) - Video processing

View File

@@ -0,0 +1,95 @@
<!-- Context: ui/navigation | Priority: critical | Version: 1.0 | Updated: 2026-02-15 -->
---
description: "Advanced web UI patterns - scroll animations, visual effects, and interactive design"
---
# Web Design Patterns
**Purpose**: Advanced web UI patterns - scroll animations, visual effects, and interactive design
**Last Updated**: 2026-01-31
---
## Quick Navigation
### Concepts
| File | Description | Priority |
|------|-------------|----------|
| [navigation.md](concepts/navigation.md) | Concepts navigation | high |
| [scroll-linked-animations.md](concepts/scroll-linked-animations.md) | Scroll-synced image sequences (scrollytelling) | high |
### Examples
| File | Description | Priority |
|------|-------------|----------|
| [navigation.md](examples/navigation.md) | Examples navigation | high |
| [scrollytelling-headphone.md](examples/scrollytelling-headphone.md) | Full Next.js scroll animation example | high |
### Guides
| File | Description | Priority |
|------|-------------|----------|
| [navigation.md](guides/navigation.md) | Guides navigation | high |
| [building-scrollytelling-pages.md](guides/building-scrollytelling-pages.md) | Complete implementation guide | high |
| [premium-dark-ui-visual-reference.md](guides/premium-dark-ui-visual-reference.md) | Visual reference for premium dark UI | medium |
### Lookup
| File | Description | Priority |
|------|-------------|----------|
| [navigation.md](lookup/navigation.md) | Lookup navigation | high |
| [scroll-animation-prompts.md](lookup/scroll-animation-prompts.md) | AI prompts for generating animation sequences | medium |
### Errors
| File | Description | Priority |
|------|-------------|----------|
| _(No error files yet)_ | | |
---
## Loading Strategy
**For scroll animation work**:
1. Load `concepts/scroll-linked-animations.md` (understand technique)
2. Load `lookup/scroll-animation-prompts.md` (generate image sequences)
3. Load `examples/scrollytelling-headphone.md` (see full code)
4. Reference `guides/building-scrollytelling-pages.md` (step-by-step)
**For premium dark UI design**:
1. Load `guides/premium-dark-ui-visual-reference.md` (visual patterns and implementation)
---
## Scope
This subcategory covers:
- ✅ Scroll-linked animations (scrollytelling)
- ✅ Canvas-based rendering
- ✅ Framer Motion patterns
- ✅ Image sequence generation
- ✅ Premium dark UI design system
- ✅ Glassmorphism patterns
- ⏳ CSS animations (future)
- ⏳ SVG animations (future)
- ⏳ WebGL effects (future)
---
## Related Categories
- `ui/web/` - Core web UI patterns (parent directory)
- `ui/web/animation-patterns.md` - CSS animations and transitions
- `development/` - General development patterns
---
## Used By
**Agents**: frontend-specialist, design-specialist, animation-expert
## Statistics
- Concepts: 1 + navigation
- Examples: 1 + navigation
- Guides: 6 + navigation
- Lookup: 1 + navigation
- Errors: 0
- **Total**: 13 files