Files
gui-video-clipper/docs/superpowers/specs/2026-09-22-build-cleanup-release-packaging-design.md
cottongin d46973a719 v0.1.1: fix .app bundle PATH for pyenv/nvm, detect yt-dlp pot-provider plugin
- fix_path_env() now adds ~/.pyenv/shims (with PYENV_ROOT) before
  Homebrew paths so pyenv-managed yt-dlp (with pip-installed plugins)
  takes priority over Homebrew's bare yt-dlp binary
- Also handles ~/.nvm, ~/.deno/bin, ~/.cargo/bin for broader coverage
- check_pot_plugin uses lightweight 'yt-dlp -v' instead of network
  simulation for reliable plugin detection
- Bump version to 0.1.1

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-22 14:09:23 -04:00

4.8 KiB
Raw Blame 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