Skip to content

Native macOS rebuild (Phase 1): Electron → SwiftUI - #70

Open
stefansl wants to merge 23 commits into
mainfrom
native-macos-rebuild
Open

Native macOS rebuild (Phase 1): Electron → SwiftUI#70
stefansl wants to merge 23 commits into
mainfrom
native-macos-rebuild

Conversation

@stefansl

@stefansl stefansl commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Phase 1: Electron → native SwiftUI (macOS)

Rebuilds Image Shrinker as a native macOS app, replacing the Electron 11 stack. macOS-only, deployment target macOS 14, Swift/SwiftUI. Design + plan under docs/superpowers/.

Architecture

  • ImageShrinkerCore — a pure-logic Swift package (fully unit-tested, 14 tests, exercises the real binaries on fixtures):
    • ImageOptimizer protocol + ProcessRunner (shared subprocess helper, concurrent pipe draining to avoid deadlock)
    • JpegOptimizer (cjpeg), PngOptimizer (pngquant), GifOptimizer (gifsicle) — exact legacy args preserved
    • SvgOptimizer — svgo v3 running in JavaScriptCore (no Node; bundle built via build-tools/build-svgo.sh)
    • OutputPathBuilder, OptimizerSettings, OptimizerService (dispatch by extension)
  • SwiftUI app (generated from project.yml via XcodeGen): drop zone + results list, SettingsStore (UserDefaults, legacy keys), notifications, Finder open-file + recent documents (AppModel/AppDelegate), universal binaries bundled into Contents/Resources/bin.

Engines

  • JPEG/PNG/GIF run bundled universal (arm64 + x86_64), self-contained CLI binaries (built from source; see build-tools/fetch-binaries.md). No Rosetta.
  • SVG via svgo in JavaScriptCore — identical output quality, no external binary.

Notable fixes caught in review

  • ProcessRunner pipe-buffer deadlock (concurrent draining).
  • Open-file URLs buffered until the AppDelegate hooks install (cold-launch-via-Finder).
  • Restored the legacy #54 guard: JPEG in-place optimize no longer truncates the original when output == input (temp-copy). Covered by a regression test.

Deliberately deferred (not in this PR)

Code signing, notarization, DMG, and Sparkle auto-update — plus the optional Phase-2 native-lib swaps. TouchBar intentionally dropped.

Verification

  • swift test14/14 pass (real binaries on fixtures).
  • xcodegen generate && xcodebuild … buildBUILD SUCCEEDED; app bundles the binaries, launches, and shrinks real images via the app's own paths/args.
  • Not automated: the GUI drag-and-drop gesture itself (manual).

🤖 Generated with Claude Code

stefansl and others added 23 commits July 7, 2026 09:08
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JWaXeE5W5tCBX41AmYtRxW
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JWaXeE5W5tCBX41AmYtRxW
Read stderr/stdout on background queues started before proc.run()/
waitUntilExit(), joining after exit via DispatchGroup. Prevents a
classic Process/Pipe deadlock when a child writes more than the OS
pipe buffer before exiting. Adds a regression test exercising the
failure path (non-image input) to prove stderr is still captured.
…odel

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JWaXeE5W5tCBX41AmYtRxW
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JWaXeE5W5tCBX41AmYtRxW
Replace the interim x86_64-only cjpeg and pngquant with universal
(arm64 + x86_64), statically-linked, self-contained builds so the app
runs natively on Apple Silicon without Rosetta. gifsicle was already
universal and self-contained (npm-vendored) and is carried over as-is.

- cjpeg: built from mozjpeg @ 08265790 with BUILD_SHARED_LIBS=OFF
  (cjpeg-static target); links only /usr/lib/libSystem.
- pngquant: built from pngquant 3.0.3 (Rust) with PNG_STATIC=1
  LCMS2_STATIC=1 to force static libpng/lcms2 on both cargo targets,
  avoiding a Homebrew dylib dependency on the native arm64 slice;
  links only /usr/lib/{libz,libiconv,libSystem}.

See build-tools/fetch-binaries.md for exact source commits and
rebuild commands. Verified via `swift test` against the new
binaries (all optimizer tests pass) plus lipo/otool checks.
…/bin)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JWaXeE5W5tCBX41AmYtRxW
Rewrite README build/install section for the native SwiftUI/Xcode
workflow (xcodegen, build-svgo.sh, xcodebuild, swift test) and mark
signing/notarization/DMG/auto-update as not yet wired up. Remove the
obsolete Electron sources (main.js, renderer.js, index.html, menu/,
lib/, .eslintrc.json, package.json) now that the app has no runtime
npm dependency; package-lock.json was already gitignored and deleted
from disk.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant