docs: add app icon implementation plan

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-22 11:56:04 -04:00
parent ba0cbdb01c
commit 6b15b34a26

View File

@@ -0,0 +1,403 @@
# App Icon Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Create an SVG app icon (scissors cutting a film strip) in five color variants, let the user pick one, and generate all platform icon files.
**Architecture:** Hand-craft a single base SVG icon, generate color variants via shell script, use `npx tauri icon` to produce all platform-specific icon files (PNG, ICNS, ICO). Replace the default Tauri branding.
**Tech Stack:** SVG (hand-authored), `rsvg-convert` (SVG→PNG), `npx tauri icon` (multi-platform icon generation), bash (variant generation script)
## Global Constraints
- Canvas: 512×512px SVG viewBox, full-square (no rounded corners, no shadows — Google Play adds dynamically)
- Safe zone: inner 66% (338×338px centered, 87px inset) — all critical artwork within this area
- Color space: sRGB
- Background: solid `#18181b` (zinc-900)
- All five color variants must be generated and previewed before selecting final
- Spec: `docs/superpowers/specs/2026-09-22-app-icon-design.md`
---
### Task 1: Create the Base SVG Icon (Blue Variant)
**Files:**
- Create: `src-tauri/icons/icon.svg`
**Interfaces:**
- Consumes: nothing
- Produces: `src-tauri/icons/icon.svg` — the blue accent variant, used as the template for all other variants. Contains three accent hex values that the variant script substitutes: `#60a5fa` (blade tip), `#2563eb` (blade pivot), `#3b82f6` (cut accent).
- [ ] **Step 1: Create the icons directory**
```bash
mkdir -p src-tauri/icons
```
- [ ] **Step 2: Create the base SVG file**
Create `src-tauri/icons/icon.svg` with the following content. This is the blue accent variant — scissors cutting diagonally across a film strip on a dark background.
```xml
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="blade-grad" gradientUnits="userSpaceOnUse" x1="256" y1="130" x2="256" y2="290">
<stop offset="0%" stop-color="#60a5fa"/>
<stop offset="100%" stop-color="#2563eb"/>
</linearGradient>
</defs>
<!-- Background: solid zinc-900, full square -->
<rect width="512" height="512" fill="#18181b"/>
<!-- Film strip - left half (offset down-left for separation effect) -->
<g transform="translate(-3, 3)">
<polygon points="72,220 265,220 239,292 72,292" fill="#3f3f46"/>
<!-- Sprocket holes: top row -->
<rect x="92" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="130" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="168" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<!-- Sprocket holes: bottom row -->
<rect x="92" y="273" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="130" y="273" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="168" y="273" width="11" height="13" rx="3" fill="#27272a"/>
</g>
<!-- Film strip - right half (offset up-right for separation effect) -->
<g transform="translate(3, -3)">
<polygon points="273,220 440,220 440,292 247,292" fill="#3f3f46"/>
<!-- Sprocket holes: top row -->
<rect x="298" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="345" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="392" y="226" width="11" height="13" rx="3" fill="#27272a"/>
<!-- Sprocket holes: bottom row -->
<rect x="278" y="273" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="330" y="273" width="11" height="13" rx="3" fill="#27272a"/>
<rect x="382" y="273" width="11" height="13" rx="3" fill="#27272a"/>
</g>
<!-- Cut accent line along the diagonal gap -->
<line x1="269" y1="216" x2="243" y2="296" stroke="#3b82f6" stroke-width="2" opacity="0.6"/>
<!-- Scissors (symmetric, rotated 20° clockwise for diagonal cut alignment) -->
<g transform="rotate(20, 256, 256)">
<!-- Blade B cutting portion (rendered first = behind) -->
<polygon points="264,250 172,155 248,262" fill="url(#blade-grad)"/>
<!-- Blade A cutting portion (rendered second = in front, blades cross) -->
<polygon points="248,250 340,155 264,262" fill="url(#blade-grad)"/>
<!-- Handle A (behind at handle end) -->
<polygon points="248,250 194,330 202,336 264,262" fill="#52525b"/>
<!-- Handle B (in front at handle end, crossing) -->
<polygon points="264,250 318,330 310,336 248,262" fill="#52525b"/>
<!-- Ring A (lower-left) -->
<circle cx="192" cy="348" r="20" stroke="#a1a1aa" stroke-width="4" fill="#18181b"/>
<!-- Ring B (lower-right) -->
<circle cx="320" cy="348" r="20" stroke="#a1a1aa" stroke-width="4" fill="#18181b"/>
<!-- Pivot screw -->
<circle cx="256" cy="256" r="7" fill="#a1a1aa"/>
</g>
</svg>
```
Key geometry decisions:
- Film strip spans x=72–440 (extends beyond safe zone for graceful clipping on round masks), y=220–292 (72px tall)
- Cut line from (269, 216) to (243, 296) — gentle diagonal matching the 20° scissors rotation
- Left half polygon: (72,220)→(265,220)→(239,292)→(72,292) — the right edge is the diagonal cut
- Right half polygon: (273,220)→(440,220)→(440,292)→(247,292) — the left edge is the diagonal cut
- Gap between halves: ~8px perpendicular to the cut line
- Scissors: symmetric blades (triangular tapers, 20px wide at pivot → 0 at tip, 130px long), rotated 20° as a group
- Handles: tapered quadrilaterals, ~90px from pivot to ring
- Rings: 20px radius circles with zinc-400 stroke, zinc-900 fill (transparent to background)
- Blade gradient: light blue (`#60a5fa`) at tips, darker (`#2563eb`) near pivot
- [ ] **Step 3: Verify visually in browser**
```bash
open src-tauri/icons/icon.svg
```
Open the SVG in a browser. Verify:
1. Dark background fills the square
2. Film strip is visible as a horizontal band with a diagonal gap
3. Sprocket holes are visible as darker rectangles along the strip edges
4. Scissors blades cross through the gap at a diagonal angle
5. Blade gradient goes from lighter (tips) to darker (pivot)
6. Handles taper from the pivot to the rings
7. Rings are visible as circular outlines
8. Pivot screw is centered
9. All critical elements (scissors, film strip center) are within the inner 66% safe zone
If any element needs adjustment (position, size, proportions), edit the coordinates in `icon.svg` and refresh. This visual tuning is expected — hand-authored SVG coordinates are approximate starting points.
- [ ] **Step 4: Commit**
```bash
git add src-tauri/icons/icon.svg
git commit -m "feat: add base SVG app icon (blue variant)"
```
---
### Task 2: Generate Color Variant SVGs
**Files:**
- Create: `scripts/generate-icon-variants.sh`
- Create: `src-tauri/icons/variants/icon-blue.svg`
- Create: `src-tauri/icons/variants/icon-purple.svg`
- Create: `src-tauri/icons/variants/icon-green.svg`
- Create: `src-tauri/icons/variants/icon-orange.svg`
- Create: `src-tauri/icons/variants/icon-mono.svg`
**Interfaces:**
- Consumes: `src-tauri/icons/icon.svg` — the blue variant template containing hex values `#60a5fa`, `#2563eb`, `#3b82f6`
- Produces: five SVG files in `src-tauri/icons/variants/`, one per color accent. User picks their preferred variant in the next task.
- [ ] **Step 1: Create the variant generation script**
Create `scripts/generate-icon-variants.sh`:
```bash
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
SOURCE="$ROOT_DIR/src-tauri/icons/icon.svg"
OUT_DIR="$ROOT_DIR/src-tauri/icons/variants"
mkdir -p "$OUT_DIR"
# Blue is the base template — copy as-is
cp "$SOURCE" "$OUT_DIR/icon-blue.svg"
# Each variant substitutes three accent hex values:
# #60a5fa = blade tip (lighter)
# #2563eb = blade pivot (darker)
# #3b82f6 = cut edge accent
create_variant() {
local name="$1" tip="$2" pivot="$3" accent="$4"
sed \
-e "s/#60a5fa/$tip/g" \
-e "s/#2563eb/$pivot/g" \
-e "s/#3b82f6/$accent/g" \
"$SOURCE" > "$OUT_DIR/icon-${name}.svg"
echo "Created icon-${name}.svg"
}
create_variant "purple" "#c084fc" "#7c3aed" "#a855f7"
create_variant "green" "#34d399" "#059669" "#10b981"
create_variant "orange" "#fb923c" "#ea580c" "#f97316"
create_variant "mono" "#d4d4d8" "#71717a" "#a1a1aa"
echo "All variants generated in $OUT_DIR"
```
- [ ] **Step 2: Run the script**
```bash
chmod +x scripts/generate-icon-variants.sh
./scripts/generate-icon-variants.sh
```
Expected output:
```
Created icon-purple.svg
Created icon-green.svg
Created icon-orange.svg
Created icon-mono.svg
All variants generated in /path/to/src-tauri/icons/variants
```
- [ ] **Step 3: Verify all five variants exist and differ only in accent colors**
```bash
ls -la src-tauri/icons/variants/
diff <(grep -v '#60a5fa\|#2563eb\|#3b82f6' src-tauri/icons/variants/icon-blue.svg) \
<(grep -v '#c084fc\|#7c3aed\|#a855f7' src-tauri/icons/variants/icon-purple.svg)
```
Expected: `diff` produces no output (files are identical once accent colors are removed).
- [ ] **Step 4: Open all variants for visual comparison**
```bash
open src-tauri/icons/variants/icon-blue.svg \
src-tauri/icons/variants/icon-purple.svg \
src-tauri/icons/variants/icon-green.svg \
src-tauri/icons/variants/icon-orange.svg \
src-tauri/icons/variants/icon-mono.svg
```
Visually confirm each variant renders with the correct accent color on the scissor blades and cut line.
- [ ] **Step 5: Commit**
```bash
git add scripts/generate-icon-variants.sh src-tauri/icons/variants/
git commit -m "feat: add icon color variant generation script and 5 variants"
```
---
### Task 3: User Selects Variant and Generate Platform Icons
**Files:**
- Modify: `src-tauri/icons/icon.svg` (replaced with chosen variant if not blue)
- Create: `src-tauri/icons/32x32.png` (generated)
- Create: `src-tauri/icons/128x128.png` (generated)
- Create: `src-tauri/icons/128x128@2x.png` (generated)
- Create: `src-tauri/icons/icon.icns` (generated)
- Create: `src-tauri/icons/icon.ico` (generated)
- Create: `static/favicon.svg`
**Interfaces:**
- Consumes: the user's chosen variant from `src-tauri/icons/variants/icon-{color}.svg`
- Produces: all platform icon files referenced by `tauri.conf.json`, plus `static/favicon.svg`
- [ ] **Step 1: Present variants to the user and get their choice**
Ask the user which color variant they prefer: blue, purple, green, orange, or mono.
- [ ] **Step 2: Copy the chosen variant to primary locations**
Replace `CHOSEN` with the selected variant name (e.g., `purple`):
```bash
cp src-tauri/icons/variants/icon-CHOSEN.svg src-tauri/icons/icon.svg
cp src-tauri/icons/variants/icon-CHOSEN.svg static/favicon.svg
```
- [ ] **Step 3: Generate a high-res PNG from the SVG for Tauri icon generation**
```bash
rsvg-convert -w 1024 -h 1024 src-tauri/icons/icon.svg -o src-tauri/icons/app-icon-1024.png
```
Verify the PNG was created and is 1024×1024:
```bash
sips -g pixelHeight -g pixelWidth src-tauri/icons/app-icon-1024.png
```
Expected:
```
pixelHeight: 1024
pixelWidth: 1024
```
- [ ] **Step 4: Run Tauri icon generator**
```bash
npx tauri icon src-tauri/icons/app-icon-1024.png -o src-tauri/icons
```
This generates all platform icon files into `src-tauri/icons/`:
- `32x32.png`
- `128x128.png`
- `128x128@2x.png`
- `icon.icns`
- `icon.ico`
- Plus iOS and Android variants if applicable
- [ ] **Step 5: Verify generated icon files exist and match tauri.conf.json references**
```bash
ls -la src-tauri/icons/32x32.png \
src-tauri/icons/128x128.png \
src-tauri/icons/128x128@2x.png \
src-tauri/icons/icon.icns \
src-tauri/icons/icon.ico
```
All five files should exist and be non-empty.
Cross-check with the icon paths in `tauri.conf.json`:
```json
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
]
```
All referenced paths must have corresponding generated files.
- [ ] **Step 6: Generate a 32px favicon PNG fallback**
```bash
rsvg-convert -w 32 -h 32 src-tauri/icons/icon.svg -o static/favicon-32.png
```
- [ ] **Step 7: Clean up the intermediate 1024px source PNG**
```bash
rm src-tauri/icons/app-icon-1024.png
```
- [ ] **Step 8: Commit**
```bash
git add src-tauri/icons/ static/favicon.svg static/favicon-32.png
git commit -m "feat: generate platform icons from chosen variant"
```
---
### Task 4: Update Project References
**Files:**
- Delete: `static/tauri.svg` (replaced by `favicon.svg`)
- Modify: `index.html` (add favicon link)
**Interfaces:**
- Consumes: `static/favicon.svg` and `static/favicon-32.png` from Task 3
- Produces: the app loads the new icon at all touchpoints (window icon, favicon, dock icon)
- [ ] **Step 1: Remove the default Tauri SVG**
```bash
rm static/tauri.svg
```
No source files reference `tauri.svg` — it is only a static asset. No further cleanup needed.
- [ ] **Step 2: Add a favicon link to `index.html`**
The current `index.html` has no favicon `<link>`. Add one inside the `<head>` tag, after the `<title>` element:
```html
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
```
The full `<head>` section should look like:
```html
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Video Clipper</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png" />
</head>
```
- [ ] **Step 3: Verify the app builds**
```bash
npm run build
```
Build should succeed with no missing asset errors.
- [ ] **Step 4: Commit**
```bash
git add -A
git commit -m "chore: replace default Tauri branding with app icon"
```