@solithix/optimize — interactive CLI for optimizing GIFs, videos, and images for the web. Built with Ink (React for terminals) + TypeScript. Published as npx @solithix/optimize.
npm run build # tsc → dist/
npm run dev # tsc --watch
npm run start # node ./bin/optimize-media.jsNo test framework.
bin/optimize-media.js → src/cli.tsx → src/app.tsx (step state machine)
deps → files → options → processing → summary
Each step is a standalone component in src/components/. App manages transitions via useState<AppStep> + callback props.
src/
├── app.tsx # Step state machine, ErrorBoundary wrapper
├── cli.tsx # meow arg parsing, Ink render, SIGINT cleanup
├── components/
│ ├── Banner.tsx # ASCII logo + typewriter subtitle
│ ├── DependencyCheck.tsx # Verifies ffmpeg availability
│ ├── ErrorBoundary.tsx # React error boundary
│ ├── FileBrowser.tsx # Directory browser with file scanning
│ ├── FileSelector.tsx # Multi-select file picker
│ ├── OptionsWizard.tsx # Quality/format/resolution config
│ ├── ProgressView.tsx # Per-file optimization progress
│ ├── Summary.tsx # Results table with size savings
│ └── ui/ # Branded primitives (Divider, FadeIn, TypeWriter, etc.)
├── hooks/ # Animation hooks (useElapsed, useFadeIn, usePulse, useStagger, useTypewriter, useFrameLoop)
├── optimizers/
│ ├── video.ts # ffmpeg via fluent-ffmpeg (H.264/VP9, CRF, resolution scaling, poster extraction)
│ ├── image.ts # sharp (WebP/JPEG/PNG, resize, srcset, metadata strip)
│ └── gif.ts # ffmpeg (3 paths: to-video, 2-pass palette optimize, direct GIF palette)
└── utils/
├── ffmpeg.ts # Singleton ffmpeg init, runFfmpeg() wrapper, process tracking + cleanup
├── files.ts # scanFiles(), getOutputPath() (web-safe filenames), ensureDir()
├── detect.ts # Extension-based media type detection + formatBytes()
├── presets.ts # VideoPreset, ImagePreset, GifPreset, OutputConfig, CRF/resolution maps
├── theme.ts # Brand colors (#60A5FA, #38BDF8) via chalk
└── icons.ts # Unicode symbols (no emojis)
- ESM-only —
"type": "module", all local imports use.jsextensions (NodeNext resolution) - JSX —
react-jsxtransform, noReactimport required - Filenames — output files are sanitized: lowercase, spaces→hyphens, non-alphanumeric stripped
- Process cleanup — ffmpeg processes tracked in a
Set, killed on SIGINT/SIGTERM - UI — Unicode symbols only, no emojis. Brand colors from
theme.ts - No co-author lines — do not add
Co-Authored-Byto commits