Files
gui-video-clipper/docs/superpowers/specs/2026-09-22-build-cleanup-release-packaging-design.md

113 lines
4.8 KiB
Markdown
Raw Normal View History

# Build Cleanup & Release Packaging
**Date:** 2026-09-22
**Status:** Approved
## Overview
Resolve all build warnings and errors across the frontend (svelte-check, Vite) and backend (cargo), fix the failing test, and implement a release build script that produces a distributable `GUI Video Clipper.app` bundle.
## Scope
Two workstreams:
1. **Warning/Error Cleanup** — zero warnings from `npm run check`, `cargo build`, and `npm test`
2. **Release Build Packaging** — `scripts/build-release.sh` that produces a signed `.app` in `dist/release/`
## 1. TypeScript Errors (4)
All in `vite.config.ts`:
| Error | Root Cause | Fix |
|-------|-----------|-----|
| Cannot find `node:path` | Missing `@types/node` | `npm install -D @types/node`, add `"types": ["node"]` to `tsconfig.json` |
| Cannot find `node:process` | Same | Same |
| Cannot find `node:url` | Same | Same |
| `test` not in `UserConfigExport` | `defineConfig` from `vite` doesn't know vitest's `test` field | Import `defineConfig` from `vitest/config` instead |
## 2. Svelte A11y Warnings (19)
### 2a. Labels not associated with controls (9 in CaptionSettingsPanel.svelte)
Labels are siblings of their controls rather than wrapping them. Fix by either:
- Wrapping the `<input>`/`<select>` inside the `<label>` element, or
- Adding `id` to the control and `for` to the label
Prefer the wrapping approach where layout allows.
### 2b. Dialog divs missing tabindex (4 files)
Add `tabindex="-1"` to `<div role="dialog">` in `PreferencesPanel`, `ExportDialog`, `AboutDialog`, and `<div role="listbox">` in `ClipList`. Makes them programmatically focusable without entering the tab order.
### 2c. Click handlers without key handlers (4 files)
Dialog overlay `onclick={onClose}` divs need corresponding `onkeydown` handlers. Add Escape key handling on the overlay. For `ClipList`'s listbox container, add an `onkeydown` handler mirroring the container click logic.
### 2d. `<video>` without `<track>` (1 in VideoPlayer.svelte)
Add an empty `<track kind="captions" default />` to the `<video>` element. Our custom caption overlay handles actual rendering.
### 2e. Resize handle (1 in App.svelte)
The resize handle `<div>` with `role="separator"` and `onmousedown` needs:
- `tabindex="0"` for keyboard focusability
- `onkeydown` handler for arrow key resizing
- `aria-valuenow`, `aria-valuemin`, `aria-valuemax` attributes
## 3. Rust Dead Code Warnings (6)
Add `#[allow(dead_code)]` with explanatory comments:
| Warning | Location | Annotation Rationale |
|---------|----------|---------------------|
| `ExportEvent::Error` unused | `commands/export.rs:23` | Reserved for future error-channel reporting |
| `DownloadEvent::Error` unused | `commands/video.rs:23` | Reserved for future error-channel reporting |
| `sanitize_vtt_for_ffmpeg` unused | `clip_exporter.rs:10` | Scaffolded for caption burn-in pipeline |
| `opacity_to_ass_back_colour` unused | `clip_exporter.rs:204` | Scaffolded for caption burn-in pipeline |
| `caption_style_to_force_style` unused | `clip_exporter.rs:210` | Scaffolded for caption burn-in pipeline |
| `SubtitleFormat.ext`/`.url` unread | `video_resolver.rs:8-9` | Fields required for serde deserialization |
## 4. Test Fix
**File:** `tests/lib/stores/clips.test.ts`
**Test:** "updates selected clip start when I is pressed"
The test calls `markInPoint(5)` on a clip spanning 10–20. With `EDIT_TOLERANCE` of 0.5s, time 5 is outside the clip boundary (needs ≥ 9.5). The test was written before the tolerance check existed.
**Fix:** Change `markInPoint(5)` to `markInPoint(12)` (inside the clip, within tolerance). Update the assertion to `expect(getClips()[0].startTime).toBe(12)`. This verifies the edit actually changes the start time (unlike `markInPoint(10)` which would pass trivially since start is already 10).
After fix: 31/31 tests pass.
## 5. Release Build Script
### 5a. `scripts/build-release.sh`
A shell script that:
1. Reads `VERSION` from the project root
2. Runs `npm install`
3. Runs `npx tauri build` (builds frontend + Rust + bundles .app and .dmg)
4. Ad-hoc code signs the `.app`: `codesign --force --deep -s - <path>`
5. Copies `.app` (and `.dmg` if built) to `dist/release/`
6. Prints summary: version, output path, bundle size
### 5b. Configuration changes
- `tauri.conf.json`: Change `"targets": "all"` to `"targets": ["app", "dmg"]`
- `.gitignore`: Add `dist/`
- `package.json`: Add `"release": "bash scripts/build-release.sh"`
### 5c. README update
Add a "Release Build" section documenting `npm run release` and its outputs.
## Verification
After all changes:
- `npm run check` exits 0 with 0 errors, 0 warnings
- `cargo build 2>&1 | grep warning` returns nothing
- `npm test` shows 31/31 pass
- `npm run release` produces `dist/release/GUI Video Clipper.app`
- The `.app` launches without the Gatekeeper right-click workaround