From 99d1a0ab31234a9e1253eb29415ce5e95600fe18 Mon Sep 17 00:00:00 2001 From: cottongin Date: Tue, 22 Sep 2026 12:42:12 -0400 Subject: [PATCH] docs: add v0.1.0 release polish spec and implementation plan Co-authored-by: Cursor --- .../plans/2026-09-22-v0.1-release-polish.md | 763 ++++++++++++++++++ .../2026-09-22-v0.1-release-polish-design.md | 225 ++++++ 2 files changed, 988 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-22-v0.1-release-polish.md create mode 100644 docs/superpowers/specs/2026-09-22-v0.1-release-polish-design.md diff --git a/docs/superpowers/plans/2026-09-22-v0.1-release-polish.md b/docs/superpowers/plans/2026-09-22-v0.1-release-polish.md new file mode 100644 index 0000000..d6c83e4 --- /dev/null +++ b/docs/superpowers/plans/2026-09-22-v0.1-release-polish.md @@ -0,0 +1,763 @@ +# v0.1.0 Release Polish 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:** Prepare GUI Video Clipper for its initial tagged v0.1.0 release — versioning, branding, About dialog, LICENSE, README, history scrub, and push to remote. + +**Architecture:** All changes are config/metadata/UI polish — no backend feature work. A `VERSION` file is the single source of truth, synced to manifests via a bump script. An `AboutDialog.svelte` component exposes version/author/license info. `git-filter-repo` scrubs leftover personal paths from history before the first push. + +**Tech Stack:** Svelte 5, Tauri v2, Rust, shell scripting, git-filter-repo + +## Global Constraints + +- Official title: "GUI Video Clipper" +- App identifier: `xyz.cottongin.gui-video-clipper` +- Author: cottongin +- License: MIT, copyright 2026 +- Remote: `git@code.cottongin.xyz:cottongin/gui-video-clipper.git` +- Version: `0.1.0` +- No new npm or crate dependencies +- The string `REDACTED_USERNAME` must not appear anywhere in the final repo (files or git history) + +--- + +### Task 1: VERSION file and bump script + +**Files:** +- Create: `VERSION` +- Create: `scripts/bump-version.sh` + +**Interfaces:** +- Consumes: Nothing +- Produces: `VERSION` file containing `0.1.0`. `scripts/bump-version.sh` that reads `VERSION` (or accepts a version argument) and patches `package.json`, `src-tauri/Cargo.toml`, and `src-tauri/tauri.conf.json`. + +- [ ] **Step 1: Create the VERSION file** + +Create `VERSION` at the project root with exactly this content (no trailing newline): + +``` +0.1.0 +``` + +- [ ] **Step 2: Create the bump script** + +Create `scripts/bump-version.sh`: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +VERSION_FILE="$PROJECT_ROOT/VERSION" + +if [ $# -ge 1 ]; then + echo "$1" > "$VERSION_FILE" +fi + +if [ ! -f "$VERSION_FILE" ]; then + echo "ERROR: VERSION file not found at $VERSION_FILE" >&2 + exit 1 +fi + +VERSION=$(cat "$VERSION_FILE") + +# Sanitize: strip leading v/V, trim whitespace +VERSION=$(echo "$VERSION" | sed 's/^[vV]//' | tr -d '[:space:]') + +# Validate semver (X.Y.Z) +if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "ERROR: '$VERSION' is not valid semver (expected X.Y.Z)" >&2 + exit 1 +fi + +echo "Bumping to version $VERSION" + +# 1. package.json +cd "$PROJECT_ROOT" +npm pkg set "version=$VERSION" --json 2>/dev/null || npm pkg set "version=$VERSION" +echo " ✓ package.json" + +# 2. src-tauri/Cargo.toml — update [package] version (first version = line) +sed -i '' "s/^version = \".*\"/version = \"$VERSION\"/" "$PROJECT_ROOT/src-tauri/Cargo.toml" +echo " ✓ src-tauri/Cargo.toml" + +# 3. src-tauri/tauri.conf.json +node -e " + const fs = require('fs'); + const path = '$PROJECT_ROOT/src-tauri/tauri.conf.json'; + const conf = JSON.parse(fs.readFileSync(path, 'utf8')); + conf.version = '$VERSION'; + fs.writeFileSync(path, JSON.stringify(conf, null, 2) + '\n'); +" +echo " ✓ src-tauri/tauri.conf.json" + +# 4. Sync Cargo.lock +cd "$PROJECT_ROOT/src-tauri" +cargo generate-lockfile 2>/dev/null +echo " ✓ src-tauri/Cargo.lock" + +echo "" +echo "Done — all manifests set to $VERSION" +``` + +- [ ] **Step 3: Make the script executable** + +Run: `chmod +x scripts/bump-version.sh` + +- [ ] **Step 4: Test the bump script** + +Run: `./scripts/bump-version.sh` + +Expected output: +``` +Bumping to version 0.1.0 + ✓ package.json + ✓ src-tauri/Cargo.toml + ✓ src-tauri/tauri.conf.json + ✓ src-tauri/Cargo.lock + +Done — all manifests set to 0.1.0 +``` + +Verify the files were updated: + +Run: `grep '"version"' package.json src-tauri/tauri.conf.json && grep '^version' src-tauri/Cargo.toml` + +Expected: all three show `0.1.0`. + +- [ ] **Step 5: Test the sanitization (leading v, trailing newline)** + +Run: `echo "v0.1.0 " > VERSION && ./scripts/bump-version.sh` + +Expected: script prints `Bumping to version 0.1.0` (strips the `v` and whitespace) and completes successfully. + +Reset: `echo -n "0.1.0" > VERSION` + +- [ ] **Step 6: Commit** + +```bash +git add VERSION scripts/bump-version.sh +git commit -m "chore: add VERSION file and bump-version.sh script" +``` + +--- + +### Task 2: Metadata cleanup — config files and Rust crate rename + +**Files:** +- Modify: `src-tauri/tauri.conf.json` +- Modify: `src-tauri/Cargo.toml` +- Modify: `src-tauri/src/main.rs` +- Modify: `index.html` +- Modify: `src/lib/components/SetupWizard.svelte` + +**Interfaces:** +- Consumes: Nothing (standalone metadata changes) +- Produces: All placeholder values replaced with real project metadata. The Rust lib crate is renamed from `tauri_app_lib` to `gui_video_clipper_lib`. + +- [ ] **Step 1: Update `src-tauri/tauri.conf.json`** + +Replace the following values: + +- `"productName": "Video Clipper"` → `"productName": "GUI Video Clipper"` +- `"identifier": "xyz.cottongin.gui-video-clipper"` → `"identifier": "xyz.cottongin.gui-video-clipper"` +- `"title": "Video Clipper"` → `"title": "GUI Video Clipper"` + +- [ ] **Step 2: Update `src-tauri/Cargo.toml`** + +Replace the following values in the `[package]` section: + +- `name = "tauri-app"` → `name = "gui-video-clipper"` +- `description = "A Tauri App"` → `description = "A macOS GUI app for clipping online videos"` +- `authors = ["you"]` → `authors = ["cottongin"]` + +In the `[lib]` section: + +- `name = "tauri_app_lib"` → `name = "gui_video_clipper_lib"` + +- [ ] **Step 3: Update `src-tauri/src/main.rs`** + +Replace `tauri_app_lib::run()` with `gui_video_clipper_lib::run()`. + +The full file should be: + +```rust +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + gui_video_clipper_lib::run() +} +``` + +- [ ] **Step 4: Update `index.html`** + +Replace `Video Clipper` with `GUI Video Clipper`. + +- [ ] **Step 5: Update `src/lib/components/SetupWizard.svelte`** + +Replace the string `Video Clipper requires` with `GUI Video Clipper requires` (line 82). + +- [ ] **Step 6: Sync Cargo.lock after crate rename** + +Run: `cd src-tauri && cargo generate-lockfile` + +This updates the `Cargo.lock` entry from `tauri-app` to `gui-video-clipper`. + +- [ ] **Step 7: Verify the build compiles** + +Run: `cd src-tauri && cargo check` + +Expected: compiles without errors. The lib name change is picked up via `Cargo.toml` and `main.rs`. + +- [ ] **Step 8: Commit** + +```bash +git add src-tauri/tauri.conf.json src-tauri/Cargo.toml src-tauri/Cargo.lock src-tauri/src/main.rs index.html src/lib/components/SetupWizard.svelte +git commit -m "chore: replace placeholder metadata with real project values" +``` + +--- + +### Task 3: Path scrubbing in plan docs + +**Files:** +- Modify: `docs/superpowers/plans/2026-09-21-video-clipper.md` +- Modify: `docs/superpowers/plans/2026-09-22-paged-teleprompter-subtitles.md` +- Modify: `docs/superpowers/plans/2026-09-22-extended-caption-styling.md` + +**Interfaces:** +- Consumes: Nothing +- Produces: All absolute paths containing the personal username are replaced with relative equivalents. + +- [ ] **Step 1: Replace absolute paths in all three plan files** + +In each of the three files, replace all occurrences of `` (with trailing slash) with an empty string — this leaves just the command after `cd ... &&` becomes just the command. + +Also replace any remaining occurrences of `` (without trailing slash) with `.` (current directory). + +The specific patterns to replace in order (longer match first): +1. `` → `` (empty — removes the cd entirely, leaving just the command) +2. `cd ` → `cd .` (standalone cd references) +3. `` → `` (path prefixes before filenames) +4. `` → `.` (any remaining) + +- [ ] **Step 2: Verify no personal paths remain** + +Run: `grep -r "REDACTED_USERNAME" docs/superpowers/plans/` + +Expected: no output (no matches). + +- [ ] **Step 3: Commit** + +```bash +git add docs/superpowers/plans/ +git commit -m "chore: replace absolute paths with relative equivalents in plan docs" +``` + +--- + +### Task 4: LICENSE file + +**Files:** +- Create: `LICENSE` + +**Interfaces:** +- Consumes: Nothing +- Produces: MIT license file at project root. + +- [ ] **Step 1: Create the LICENSE file** + +Create `LICENSE` at the project root with this exact content: + +``` +MIT License + +Copyright (c) 2026 cottongin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +- [ ] **Step 2: Commit** + +```bash +git add LICENSE +git commit -m "chore: add MIT license" +``` + +--- + +### Task 5: README rewrite + +**Files:** +- Modify: `README.md` + +**Interfaces:** +- Consumes: Nothing +- Produces: Full README replacing the stock Tauri template. + +- [ ] **Step 1: Rewrite README.md** + +Replace the entire contents of `README.md` with: + +```markdown +# GUI Video Clipper + +A macOS desktop app for clipping segments from online videos. Paste a URL, scrub the timeline, mark your clips, and export — without manually downloading the video first. + +## Features + +- **URL Input** — Paste a YouTube (or other supported) URL and start working immediately +- **Video Preview** — Scrub, seek, and frame-step through the video with full transport controls +- **Mark In/Out** — Set clip boundaries with keyboard shortcuts (I/O) or transport buttons +- **Timeline** — Visual timeline with waveform display and thumbnail strip +- **Clip Export** — Export clips in lossless (stream copy) or precise (re-encode) modes +- **Captions** — Download subtitles, preview with karaoke-style highlighting, burn into exports +- **Setup Wizard** — Automatically detects and installs required dependencies (ffmpeg, yt-dlp) + +## Prerequisites + +- **macOS** (primary target platform) +- **Node.js** (v18+) +- **Rust** toolchain ([rustup.rs](https://rustup.rs)) + +ffmpeg and yt-dlp are detected (and can be installed) automatically by the built-in setup wizard on first launch. + +## Build & Run + +```bash +# Install frontend dependencies +npm install + +# Development (hot-reload) +npm run tauri dev + +# Production build +npm run tauri build +``` + +## Architecture + +GUI Video Clipper is built with [Tauri v2](https://v2.tauri.app/) (Rust backend) and [Svelte 5](https://svelte.dev/) (TypeScript frontend). + +### Backend (Rust) + +- **Video Resolver** — Resolves URLs via yt-dlp, extracts metadata and stream URLs +- **Download Manager** — Manages preview and high-quality downloads with progress tracking +- **Media Server** — Local HTTP server for streaming video to the frontend player +- **Waveform Generator** — Extracts audio waveform data at multiple zoom tiers via ffmpeg +- **Thumbnail Extractor** — Generates timeline thumbnail strips from keyframes +- **Export Pipeline** — Handles clip extraction with ffmpeg (lossless and precise modes) +- **Subtitle Downloader** — Fetches and converts subtitles for preview and burn-in + +### Frontend (Svelte 5 + TypeScript) + +- **Timeline** — Canvas-based timeline with waveform, thumbnails, and clip region rendering +- **Transport Controls** — Play/pause, seek, frame-step, shuttle (J/K/L), keyboard shortcuts +- **Video Player** — HTML5 video element connected to the local media server +- **Export Dialog** — Configure and execute clip exports with progress tracking +- **Caption Settings** — Font, color, shadow, and positioning controls for subtitle burn-in + +## Contributing + +1. Fork the repository +2. Create a feature branch (`git checkout -b feat/my-feature`) +3. Make your changes +4. Run checks: `npm run check` +5. Commit with a descriptive message +6. Open a pull request + +## License + +[MIT](LICENSE) +``` + +- [ ] **Step 2: Commit** + +```bash +git add README.md +git commit -m "docs: rewrite README with full project documentation" +``` + +--- + +### Task 6: About dialog + +**Files:** +- Create: `src/lib/components/AboutDialog.svelte` +- Modify: `src/App.svelte` +- Modify: `src/lib/components/StatusBar.svelte` + +**Interfaces:** +- Consumes: `getVersion()` from `@tauri-apps/api/app`. Existing `showAboutDialog` state from `App.svelte` (added in this task). `onClose` callback prop (same pattern as `PreferencesPanel`). +- Produces: `AboutDialog.svelte` component with `{ onClose: () => void }` props. Clickable version label in `StatusBar` that calls `onOpenAbout` callback. Toolbar info button and `⌘/` shortcut in `App.svelte`. + +- [ ] **Step 1: Create `AboutDialog.svelte`** + +Create `src/lib/components/AboutDialog.svelte`: + +```svelte + + + + + +``` + +- [ ] **Step 2: Add `showAboutDialog` state and import to `App.svelte`** + +In `src/App.svelte`, add the import at the top of the `