Skip to content

Commit d7c66a3

Browse files
authored
Merge pull request #2 from kevinkiklee/feat/playwright-e2e
feat: i18n, e2e tests, component refactoring, and cleanup
2 parents 4b2737c + 514d3dd commit d7c66a3

253 files changed

Lines changed: 13450 additions & 12382 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,14 @@ jobs:
2626
- run: npm run lint
2727
- run: npm test
2828
- run: npm run build
29+
30+
# Integration tests (Playwright)
31+
- run: npx playwright install --with-deps chromium firefox
32+
- run: npx playwright test
33+
34+
- uses: actions/upload-artifact@v4
35+
if: failure()
36+
with:
37+
name: playwright-report
38+
path: playwright-report/
39+
retention-days: 7

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ build
2424
# Build artifacts
2525
*.tsbuildinfo
2626

27+
# Playwright
28+
playwright-report/
29+
test-results/
30+
2731

2832
# Source examples (not published)
2933
examples/

CLAUDE.md

Lines changed: 121 additions & 12 deletions
Large diffs are not rendered by default.

GEMINI.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ Every tool features a `LearnPanel` (right sidebar) defined in `content.ts` and `
2828
- **Interactive Challenges**: 3-5 multiple-choice questions with persistence to `localStorage`.
2929
- **Tooltips**: Use `InfoTooltip` on control labels for quick definitions.
3030

31-
### 4. Layout Shell (`src/components/shared/ToolPageShell.tsx`)
32-
Standard three-column layout (Controls | Main Visualizer | LearnPanel). Provides consistent header actions (Share, Export, Info).
31+
### 4. Layout
32+
Each tool manages its own layout (Controls | Main Visualizer | LearnPanel). Consistent header actions (Share, Export, Info) via `ToolActions` component.
3333

3434
## Engineering Standards
3535

README.md

Lines changed: 98 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,139 @@
11
# PhotoTools
22

3-
Free photography calculators, simulators, and references. No sign-up required — your photos never leave your browser.
3+
Free photography calculators, simulators, and references. No sign-up, no uploads — everything runs in your browser.
44

55
**[www.phototools.io](https://www.phototools.io)**
66

7-
## Tools
8-
9-
### Live in Production
7+
---
108

11-
- **FOV Simulator** — Compare field of view across up to four lens and sensor combinations, overlaid on real-world scenes. Plan lens purchases and understand how focal length and sensor size affect what you capture.
12-
- **Color Scheme Generator** — Build harmonious color palettes using complementary, analogous, triadic, split-complementary, and tetradic relationships. Pick colors from uploaded photos or the color wheel to plan wardrobe, props, and set design.
13-
- **Star Trail Calculator** — Calculate maximum exposure for pinpoint stars using the 500 Rule or the more accurate NPF Rule. Plan star trail stacking sessions with frame count and total duration estimates. Animated canvas preview with sky gradient, star glow, and terrain.
14-
- **White Balance Visualizer** — See how color temperature (1000K–12000K) shifts the look of different scenes. Compare presets from Candle to Blue Sky, or upload your own photo to preview white balance changes via WebGL.
15-
- **Sensor Size Comparison** — Visually compare camera sensor sizes from medium format down to smartphone. Overlay, side-by-side, and pixel density modes with per-sensor resolution and popular camera model data.
16-
- **EXIF Viewer** — View EXIF metadata and histogram for any photo. Extracts camera, lens, exposure settings, GPS coordinates, and software info. Includes a privacy warning when location data is detected. 100% client-side — nothing is uploaded.
9+
## What is PhotoTools?
1710

18-
### In Development
11+
PhotoTools is a collection of interactive tools that help photographers understand and visualize core concepts — field of view, depth of field, exposure, color theory, white balance, and more. Each tool includes an educational Learn panel with beginner and in-depth explanations, key factors, pro tips, and interactive quiz challenges.
1912

20-
- **Exposure Triangle Simulator** — Interactively adjust aperture, shutter speed, and ISO to see real-time effects on exposure, depth of field blur, motion blur, and noise through a WebGL preview.
21-
- **Depth of Field Calculator** — Calculate near focus, far focus, hyperfocal distance, and total depth of field for any lens, aperture, and subject distance. Includes an interactive DoF diagram.
22-
- **Hyperfocal Distance Simulator** — Learn where to focus for maximum sharpness from foreground to infinity. Includes a depth of field diagram and a mini reference table for quick field use.
23-
- **Shutter Speed Visualizer** — Find the minimum safe shutter speed for sharp handheld shots based on focal length, crop factor, and image stabilization stops.
24-
- **Perspective Compression Simulator** — See how focal length affects background compression and apparent distance between subjects.
25-
- **ND Filter Calculator** — Calculate the resulting shutter speed after attaching any ND filter (1–10 stops).
13+
All image processing happens client-side. Photos you load into the EXIF Viewer, Histogram, Frame Studio, or any other tool never leave your device.
2614

27-
### Reference
15+
## Tools
2816

29-
- **Glossary** — 50+ photography terms with clear definitions, linked to relevant tools for hands-on learning.
17+
### Visualizers
3018

31-
Each tool includes an educational **Learn panel** with beginner and deeper explanations, key factors, pro tips, and interactive challenges with try-again and reset functionality.
19+
| Tool | Description |
20+
|------|-------------|
21+
| **[FOV Simulator](https://www.phototools.io/fov-simulator)** | Compare field of view across up to four lens/sensor combinations overlaid on real-world scenes. Plan lens purchases and understand how focal length and sensor size affect framing. |
22+
| **[Color Scheme Generator](https://www.phototools.io/color-scheme-generator)** | Build harmonious color palettes using complementary, analogous, triadic, split-complementary, and tetradic relationships. Pick colors from uploaded photos or the color wheel. |
23+
| **[Star Trail Calculator](https://www.phototools.io/star-trail-calculator)** | Calculate maximum exposure for pinpoint stars (500 Rule and NPF Rule). Plan star trail stacking sessions with frame count and duration estimates. Animated sky preview. |
24+
| **[White Balance Visualizer](https://www.phototools.io/white-balance-visualizer)** | See how color temperature (1000K–12000K) shifts the look of scenes. Compare presets from Candle to Blue Sky, or upload your own photo for a live WebGL preview. |
25+
| **[Sensor Size Comparison](https://www.phototools.io/sensor-size-comparison)** | Visually compare sensor sizes from medium format to smartphone. Overlay, side-by-side, and pixel density modes with resolution data and popular camera models. |
3226

33-
## Tech Stack
27+
### File Tools
3428

35-
- Next.js 16 (App Router, Turbopack)
36-
- React 19 + TypeScript 6
37-
- Vitest + Testing Library (235 tests across 18 files)
38-
- CSS Modules + CSS custom properties (design tokens)
39-
- Canvas API + WebGL2 + GLSL shaders
40-
- Vercel (deployment)
29+
| Tool | Description |
30+
|------|-------------|
31+
| **[Frame Studio](https://www.phototools.io/frame-studio)** | Crop, frame, and compose photos with aspect ratio presets and composition grid overlays (rule of thirds, golden ratio, diagonal). |
32+
| **[EXIF Viewer](https://www.phototools.io/exif-viewer)** | View EXIF metadata and histogram for any photo. Extracts camera, lens, exposure, GPS, and software info. Privacy warning when location data is detected. 100% client-side. |
4133

42-
## Development
34+
### Reference
4335

44-
```bash
45-
npm install
46-
npm run dev
47-
```
36+
| Tool | Description |
37+
|------|-------------|
38+
| **[Glossary](https://www.phototools.io/learn/glossary)** | 50+ photography terms with clear definitions, linked to relevant tools for hands-on learning. |
4839

49-
Dev server runs at `http://localhost:3000`. All tools are visible in development regardless of production status.
40+
### In Development
5041

51-
## Tool Visibility
42+
These tools are functional in the dev environment but not yet published to production:
5243

53-
Each tool in `src/lib/data/tools.ts` has separate `dev` and `prod` status fields with three states:
44+
- **Exposure Triangle Simulator** — Adjust aperture, shutter speed, and ISO to see real-time effects on exposure, depth of field blur, motion blur, and noise (WebGL).
45+
- **Depth of Field Calculator** — Calculate near/far focus, hyperfocal distance, and total DoF with an interactive diagram.
46+
- **Hyperfocal Distance Simulator** — Learn where to focus for maximum foreground-to-infinity sharpness.
47+
- **Shutter Speed Visualizer** — Find the minimum safe shutter speed for handheld shooting based on focal length, crop factor, and stabilization.
48+
- **Perspective Compression Simulator** — See how focal length affects background compression and apparent subject distance.
49+
- **ND Filter Calculator** — Calculate resulting shutter speed after attaching any ND filter (1–10 stops).
5450

55-
- **`live`** — fully accessible, appears in nav, homepage, and footer
56-
- **`draft`** — appears in nav/homepage as "Coming Soon" (disabled); still reachable by direct URL with a draft banner
57-
- **`disabled`** — hidden from all menus, not shown anywhere in production
51+
## Tech Stack
5852

59-
Helper functions: `getLiveTools()` (live only), `getVisibleTools()` (live + draft), `getAllTools()` (everything).
53+
| Layer | Technology |
54+
|-------|-----------|
55+
| Framework | Next.js 16 (App Router, Turbopack) |
56+
| UI | React 19, TypeScript 6, CSS Modules |
57+
| Rendering | Canvas API, WebGL2 + GLSL shaders |
58+
| i18n | next-intl |
59+
| Unit tests | Vitest + Testing Library (353 tests) |
60+
| E2E tests | Playwright (Chromium + Firefox) |
61+
| Deployment | Vercel (auto-deploy from `main`) |
62+
| Analytics | Vercel Speed Insights, Google Analytics |
63+
| Ads/Consent | Google AdSense, CookieYes |
6064

61-
## Testing
65+
## Getting Started
6266

6367
```bash
64-
npm test # single run (235 tests)
65-
npm run test:watch # watch mode
68+
git clone https://github.com/kevinkiklee/phototools.io.git
69+
cd phototools.io
70+
npm install
71+
npm run dev
6672
```
6773

68-
## Build & Lint
74+
The dev server starts at `http://localhost:3000`. In development, all tools are visible regardless of production status — draft tools show a banner.
6975

70-
```bash
71-
npm run build
72-
npm run lint
73-
```
76+
## Scripts
77+
78+
| Command | Description |
79+
|---------|-------------|
80+
| `npm run dev` | Start dev server (Turbopack) |
81+
| `npm run build` | Production build |
82+
| `npm run start` | Serve production build locally |
83+
| `npm run lint` | Run ESLint |
84+
| `npm test` | Run Vitest unit tests |
85+
| `npm run test:watch` | Run tests in watch mode |
86+
| `npm run test:e2e` | Run Playwright e2e tests (requires build) |
7487

7588
## Project Structure
7689

7790
```
7891
src/
79-
app/ Routes (homepage, tools, learn/glossary)
80-
[slug]/ Each tool at top-level URL (e.g. /fov-simulator)
81-
page.tsx Route entry point
82-
_components/ Tool-specific UI components (co-located)
92+
app/ App Router routes
93+
[slug]/ Each tool at a top-level URL (e.g. /fov-simulator)
94+
page.tsx Route entry point
95+
_components/ Tool-specific UI (co-located, private folder)
96+
api/contact/ Contact form API route (Resend)
97+
learn/glossary/ Photography glossary page
8398
components/
84-
layout/ Nav (mega-menu), Footer, ThemeProvider, ThemeToggle
85-
shared/ Reusable components (ToolPageShell, LearnPanel, ControlPanel, ToolIcon, etc.)
99+
layout/ Nav (mega-menu), Footer, ThemeProvider, ThemeToggle
100+
shared/ Reusable UI — LearnPanel, ControlPanel, ToolActions,
101+
FileDropZone, ShareModal, AdUnit, MobileAdBanner, etc.
86102
lib/
87-
math/ Pure calculation modules with co-located tests
88-
data/ Tool registry, education content, sensors, glossary, etc.
89-
data/education/ Per-tool educational content, challenge definitions, types
90-
utils/ Query sync, export helpers
91-
types.ts Shared TypeScript types
92-
public/ Images, icons, manifest, sitemap, robots.txt
103+
math/ Pure calculation modules with co-located tests
104+
(fov, dof, exposure, color, histogram, compression,
105+
startrail, diffraction, frame, grid)
106+
data/ Tool registry, education content, sensors, focal lengths,
107+
scenes, glossary, camera settings, ND filters, white balance
108+
data/education/ Per-tool education skeletons and challenge definitions
109+
i18n/ Message loader and translation JSON files (next-intl)
110+
utils/ URL query sync, canvas export helpers
111+
types.ts Shared TypeScript types
112+
ads.ts Ad configuration and feature flags
113+
e2e/ Playwright e2e test specs
114+
public/ Static assets (scene images, sample photos)
93115
```
94116

117+
## Architecture Highlights
118+
119+
- **Tool registry** — All tools defined in `src/lib/data/tools.ts` with separate `dev`/`prod` status fields (`live`, `draft`, `disabled`). Helper functions: `getLiveTools()`, `getVisibleTools()`, `getAllTools()`.
120+
- **Education system** — Each tool has a LearnPanel sidebar with structured content: beginner/deeper explanations, key factors, pro tips, and progressive quiz challenges persisted to localStorage.
121+
- **Pure math layer** — All calculation logic lives in `src/lib/math/` as pure functions with thorough unit tests, decoupled from UI.
122+
- **Privacy-first** — All image processing is client-side. No photos are uploaded to any server. EXIF Viewer warns users when GPS data is detected.
123+
- **No desktop scroll** — On desktop, the application fits within the viewport (100vh). Individual panels scroll internally. Mobile pages scroll naturally.
124+
- **URL state sync** — Tool parameters are synced to URL query strings for shareability and SEO.
125+
- **Consent-first ads** — CookieYes consent banner loads before AdSense. Ads are blocked until the user grants consent.
126+
95127
## Deployment
96128

97-
Push to `main` triggers:
98-
1. GitHub Actions CI (audit, lint, test, build)
99-
2. Vercel auto-deploy to production
129+
Pushing to `main` triggers:
130+
131+
1. **GitHub Actions CI**`npm ci``npm audit``npm run lint``npm test``npm run build` → Playwright e2e
132+
2. **Vercel auto-deploy** — production deployment to [www.phototools.io](https://www.phototools.io)
133+
134+
## Contributing
100135

101-
Live at [www.phototools.io](https://www.phototools.io).
136+
See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for the guide on adding new tools, and [docs/SETUP.md](docs/SETUP.md) for detailed setup instructions.
102137

103138
## License
104139

__tests__/integration.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { SENSORS } from '@/lib/data/sensors'
1010
import { FOCAL_LENGTHS } from '@/lib/data/focalLengths'
1111
import { TOOLS, getToolBySlug, getLiveTools } from '@/lib/data/tools'
1212
import { GLOSSARY } from '@/lib/data/glossary'
13-
import { getAllEducation } from '@/lib/data/education'
13+
import { getAllSkeletons } from '@/lib/data/education'
1414

1515
describe('FOV calculations with real sensor data', () => {
1616
it('all sensors produce valid FOV at all focal lengths', () => {
@@ -201,15 +201,15 @@ describe('Education and tool registry alignment', () => {
201201
it('every education entry has a matching tool or known sub-feature', () => {
202202
const toolSlugs = new Set(TOOLS.map((t) => t.slug))
203203
const knownSubFeatures = new Set(['histogram'])
204-
for (const edu of getAllEducation()) {
204+
for (const edu of getAllSkeletons()) {
205205
expect(toolSlugs.has(edu.slug) || knownSubFeatures.has(edu.slug)).toBe(true)
206206
}
207207
})
208208

209209
it('every live tool has education content', () => {
210210
const liveTools = getLiveTools()
211211
for (const tool of liveTools) {
212-
const edu = getAllEducation().find((e) => e.slug === tool.slug)
212+
const edu = getAllSkeletons().find((e) => e.slug === tool.slug)
213213
expect(edu).toBeDefined()
214214
}
215215
})

docs/CONTRIBUTING.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Dev server runs at `http://localhost:3000`.
2828
app/ Routes (homepage, tool pages, learn/glossary)
2929
components/
3030
layout/ Nav (mega-menu), Footer, ThemeProvider, ThemeToggle
31-
shared/ ToolPageShell, LearnPanel, InfoTooltip, ShareModal, ToolActions, FileDropZone, DraftBanner, Toast
31+
shared/ LearnPanel, InfoTooltip, ShareModal, ToolActions, FileDropZone, DraftBanner
3232
tools/ One directory per tool + shared/
3333
lib/
3434
math/ Pure calculation modules with co-located tests
@@ -43,13 +43,13 @@ public/ Static assets (images, icons, manifest)
4343

4444
1. **Math module** (if the tool needs calculations): create `lib/math/yourtool.ts` with pure functions and `lib/math/yourtool.test.ts` with tests.
4545
2. **Component**: create `components/tools/your-tool/YourTool.tsx` (add `'use client'` at the top if it uses state, effects, or event handlers) and `YourTool.module.css` for styles.
46-
3. **Route**: create `app/tools/your-tool/page.tsx`. Import and render the component inside `ToolPageShell`.
46+
3. **Route**: create `app/your-tool/page.tsx` with a co-located `_components/` directory for tool-specific UI.
4747
4. **Registry**: add an entry to the `TOOLS` array in `lib/data/tools.ts`:
4848
```ts
4949
{ slug: 'your-tool', name: 'Your Tool', description: '...', status: 'draft', category: 'calculator' }
5050
```
5151
Set `status: 'live'` when the tool is ready for production. In development, all tools are visible regardless of status.
52-
5. **Education content**: add a `ToolEducation` entry in `lib/data/education/` with beginner/deeper explanations, tips, tooltips, and challenges. The `LearnPanel` renders automatically via `ToolPageShell`.
52+
5. **Education content**: add a `ToolEducation` entry in `lib/data/education/` with beginner/deeper explanations, tips, tooltips, and challenges. Include `LearnPanel` in the tool's layout.
5353
6. **Verify**: run `npm test && npm run build`.
5454

5555
## Adding a Glossary Term

docs/SETUP.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ phototools.io/
4848
│ └── glossary/page.tsx # Photography glossary
4949
├── components/
5050
│ ├── layout/ # Nav (mega-menu), Footer, ThemeProvider, ThemeToggle
51-
│ ├── shared/ # ToolPageShell, LearnPanel, InfoTooltip, ShareModal, ToolActions, FileDropZone, DraftBanner, Toast
51+
│ ├── shared/ # LearnPanel, InfoTooltip, ShareModal, ToolActions, FileDropZone, DraftBanner
5252
│ └── tools/ # One directory per tool + shared/
5353
│ ├── fov-simulator/
5454
│ ├── dof-calculator/
@@ -93,7 +93,7 @@ Calculation logic lives in `lib/math/` as pure functions with no React dependenc
9393
### Components
9494

9595
- `components/layout/` — site-wide layout (Nav, Footer, theme)
96-
- `components/shared/` — reusable across tools (ToolPageShell wraps every tool page)
96+
- `components/shared/` — reusable across tools (LearnPanel, ControlPanel, ToolActions, etc.)
9797
- `components/tools/` — tool-specific UI, one directory per tool
9898

9999
### Styling
@@ -104,7 +104,7 @@ CSS Modules for component scoping. Design tokens (colors, spacing, typography) d
104104

105105
1. **Math module** (if needed): create `lib/math/yourtool.ts` with pure calculation functions and `lib/math/yourtool.test.ts` with tests.
106106
2. **Component**: create `components/tools/your-tool/YourTool.tsx` (with `'use client'` if interactive) and `YourTool.module.css`.
107-
3. **Route**: create `app/tools/your-tool/page.tsx` that wraps the component in `ToolPageShell`.
107+
3. **Route**: create `app/your-tool/page.tsx` with a co-located `_components/` directory for tool-specific UI.
108108
4. **Registry**: add the tool to the `TOOLS` array in `lib/data/tools.ts` with `status: 'draft'`. Change to `'live'` when ready.
109109
5. **Test**: run `npm test` to verify. Run `npm run build` to confirm the build passes.
110110

docs/screenshot.png

-9.41 MB
Binary file not shown.

next.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import type { NextConfig } from 'next'
2+
import createNextIntlPlugin from 'next-intl/plugin'
3+
4+
const withNextIntl = createNextIntlPlugin('./src/lib/i18n/request.ts')
25

36
const nextConfig: NextConfig = {
47
experimental: {
@@ -68,4 +71,4 @@ const nextConfig: NextConfig = {
6871
},
6972
}
7073

71-
export default nextConfig
74+
export default withNextIntl(nextConfig)

0 commit comments

Comments
 (0)