Complete native rewrite of the web-based SoliCards game as a SwiftUI multiplatform app targeting iOS 17+, iPadOS 17+, and macOS 14+. Three solitaire variants (Klondike, Spider, FreeCell) with full game rules, drag & drop, smart zoom layout, 6 themes, 4 difficulty levels, SwiftData persistence, VoiceOver accessibility, and 57 unit tests. Key features: - MVVM + Protocol-Oriented Strategy architecture - DragGesture with coordinate-space hit-testing (long press + drag) - Smart zoom: cards auto-size to fit screen based on deepest column - Landscape: 30% bigger cards with scrollable overflow (iOS) - macOS: 120pt card cap, 92% height buffer for window resizing - Auto-save, game resume, statistics tracking via SwiftData - Privacy manifest, app icon, String Catalog, zero dependencies Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
79 lines
6.1 KiB
Markdown
79 lines
6.1 KiB
Markdown
# Changelog
|
||
|
||
All notable changes to this project will be documented in this file.
|
||
|
||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||
|
||
## [1.2.0] - 2026-04-14
|
||
|
||
### Fixed
|
||
- **macOS maximized window cutoff** — cards no longer overflow the bottom when the window is maximized. Height calculation uses 92% of available space as buffer for platform chrome and spacing.
|
||
- **macOS card size cap** — maximum card width capped at 120pt on macOS to prevent oversized cards on large displays. Extra space distributed as wider inter-card gaps.
|
||
- **iOS landscape boost scoped to iOS only** — the 30% landscape card size boost and 130% scroll content height no longer apply on macOS, where they caused overflow.
|
||
|
||
## [1.1.0] - 2026-04-14
|
||
|
||
### Changed
|
||
- **Drag and drop rewritten** — replaced `DragGesture` offset-based system with absolute position tracking (`dragPosition: CGPoint`). Cards now follow the finger exactly instead of appearing displaced.
|
||
- **Drop target resolution moved to ViewModel** — `endDrag(at:)` method checks drop targets directly, eliminating the need for a separate global gesture on the board.
|
||
- **Long press + drag** — card dragging now requires a 0.15s long press before drag begins, disambiguating from scroll gestures and tap-to-move.
|
||
- **Gesture priority fixed** — card drag uses `.simultaneousGesture()` instead of `.gesture()` so tap-to-move (`onTapGesture`) is never blocked. Tapping an ace now reliably moves it to the foundation.
|
||
- **Bottom toolbar redesigned (iOS)** — reduced from 9 cramped icons to 4 clear items with labels: New, Undo, Hint, More (menu). Secondary actions (variant, difficulty, auto-complete, sound, rules, stats, settings) consolidated into the "More" menu.
|
||
- **Toolbar contrast fixed** — bottom bar uses `.ultraThinMaterial` background with `.toolbarColorScheme(.dark)` so icons are readable against all game themes.
|
||
- **Top row aligned to tableau columns** — stock at column 0, waste at column 1, gap at column 2, foundations at columns 3–6. No more `Spacer()` pushing elements to opposite edges.
|
||
|
||
### Added
|
||
- **Smart zoom** — card size dynamically adapts to the actual deepest tableau column so the entire board fits on screen without scrolling. Cards shrink as columns grow during play, and expand when columns shorten.
|
||
- **Landscape mode improvements:**
|
||
- 30% larger cards than the base height calculation
|
||
- Extra horizontal space distributed as wider inter-card gaps to fill the screen
|
||
- Vertical scrolling with long-press drag disambiguation (swipe = scroll, hold + drag = move card)
|
||
- Scroll content sized to 130% of viewport to guarantee scrollability
|
||
- Canvas background responds to scroll gestures (`.contentShape(Rectangle())`)
|
||
- Scroll automatically disabled during card drags to prevent interference
|
||
- **Stock pile recycling indicator** — empty stock shows an `arrow.clockwise` icon so users know to tap to recycle the waste pile.
|
||
|
||
### Fixed
|
||
- **Cards no longer displaced during drag** — root cause was overlay positioned with `.offset(translation)` from ZStack origin instead of `.position(location)` at the finger.
|
||
- **Tap-to-move works on iPad** — `.simultaneousGesture()` prevents long press from swallowing taps.
|
||
- **ScrollView no longer fights card drags** — `.scrollDisabled()` activates when a card drag is in progress.
|
||
- **Score bar no longer steals card area** — moved outside `GeometryReader` so card layout measures the actual play area, not the area minus an inaccurate chrome guess.
|
||
- **Static analyzer warnings resolved** — removed unused `freeCellsEmpty` variable, unused `destTableau` binding, fixed `@MainActor` isolation on `HapticManager`.
|
||
|
||
## [1.0.0] - 2026-04-13
|
||
|
||
### Added
|
||
- **Three solitaire variants:** Klondike, Spider (2-deck), and FreeCell with full rule implementations
|
||
- **Four difficulty levels:** Easy, Medium, Hard, Expert — each with distinct draw counts, undo limits, hint availability, and score multipliers
|
||
- **Drag and drop** card movement using SwiftUI DragGesture with coordinate-space hit-testing
|
||
- **Tap to move** cards to the best available destination (foundation preferred)
|
||
- **Undo system** with up to 20 states of history
|
||
- **Hint system** with 5-priority move suggestions (aces first, then foundations, then tableau reveals)
|
||
- **Auto-complete** detection and animated execution when all remaining cards are face-up
|
||
- **Win detection** with animated victory overlay
|
||
- **Six color themes:** Classic Green, Dark Mode, Ocean Blue, Royal Purple, Forest Green, Sunset Orange
|
||
- **12 card back designs** and 3 card face styles (classic, modern, extracted)
|
||
- **Game persistence:** auto-save on every move (debounced 2s), resume on app relaunch
|
||
- **Statistics tracking:** wins, losses, best score, best time, current/best streak per variant per difficulty
|
||
- **User preferences:** theme, card style, card back, sound toggle — all persisted via SwiftData
|
||
- **Sound effects:** card flip, place, error, victory (AVFoundation)
|
||
- **Haptic feedback** on iOS (UIImpactFeedbackGenerator)
|
||
- **Full VoiceOver accessibility:** labels, hints, and traits on all interactive elements
|
||
- **Dynamic Type** support on all text
|
||
- **Reduce Motion** support — animations skipped when user preference is enabled
|
||
- **Localization infrastructure:** String Catalog (Localizable.xcstrings) with 40+ strings, English source
|
||
- **Keyboard shortcuts:** Cmd+Z (undo), Cmd+N (new game), H (hint)
|
||
- **Privacy manifest** (PrivacyInfo.xcprivacy): no tracking, no data collection
|
||
- **App icon** with all required sizes for iOS and macOS
|
||
- **57 unit tests** across 12 test suites covering models, game engine, move validation, auto-complete, and game state
|
||
- **Multiplatform support:** iOS 17+, iPadOS 17+, macOS 14+ from a single target
|
||
|
||
### Architecture
|
||
- MVVM with Protocol-Oriented Strategy pattern
|
||
- `GameRules` protocol with `KlondikeRules`, `SpiderRules`, `FreeCellRules` implementations
|
||
- `@Observable` macro (Observation framework) for property-level UI updates
|
||
- `@MainActor` isolation for thread safety
|
||
- SwiftData for persistence (GameRecord, StatsRecord, PrefsRecord)
|
||
- Zero external dependencies
|