From 7f8075b76ded4d950f9f6123eaef0c2bf628608e Mon Sep 17 00:00:00 2001 From: Mikkeli Matlock Date: Mon, 9 Feb 2026 21:08:34 +0900 Subject: [PATCH] docs(01): create phase plan Phase 01: Foundation - 2 plan(s) in 2 wave(s) - 2 autonomous, 0 checkpoint-only - Ready for execution Co-Authored-By: Claude Opus 4.6 --- .planning/ROADMAP.md | 9 +- .planning/phases/01-foundation/01-01-PLAN.md | 481 +++++++++++++++++++ .planning/phases/01-foundation/01-02-PLAN.md | 244 ++++++++++ 3 files changed, 730 insertions(+), 4 deletions(-) create mode 100644 .planning/phases/01-foundation/01-01-PLAN.md create mode 100644 .planning/phases/01-foundation/01-02-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 65f88ea..8947a55 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -34,10 +34,11 @@ Decimal phases appear between their surrounding integers in numeric order. 4. Debug logging uses customDebugPrint instead of unconditional print statements 5. App compiles and runs with identical functionality after reorganization -**Plans**: TBD (1-2 plans expected) +**Plans**: 2 plans Plans: -- [ ] 01-01: [To be planned] +- [ ] 01-01-PLAN.md — Infrastructure, Logger, Constants, Models extraction +- [ ] 01-02-PLAN.md — Screen extraction (SpeedometerScreen + OverlaySpeedometer) ### Phase 2: Provider Migration @@ -109,7 +110,7 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 | Phase | Plans Complete | Status | Completed | |-------|----------------|--------|-----------| -| 1. Foundation | 0/TBD | Not started | - | +| 1. Foundation | 0/2 | Planned | - | | 2. Provider Migration | 0/TBD | Not started | - | | 3. Overlay Refactor | 0/TBD | Not started | - | | 4. GPS & Power Optimization | 0/TBD | Not started | - | @@ -127,4 +128,4 @@ Phases execute in numeric order: 1 → 2 → 3 → 4 --- *Roadmap created: 2026-02-09* -*Last updated: 2026-02-09* +*Last updated: 2026-02-09 (Phase 1 planned)* diff --git a/.planning/phases/01-foundation/01-01-PLAN.md b/.planning/phases/01-foundation/01-01-PLAN.md new file mode 100644 index 0000000..29f61b1 --- /dev/null +++ b/.planning/phases/01-foundation/01-01-PLAN.md @@ -0,0 +1,481 @@ +--- +phase: 01-foundation +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - lib/services/logger.dart + - lib/config/gps_constants.dart + - lib/config/timing_constants.dart + - lib/config/overlay_constants.dart + - lib/config/speed_unit.dart + - lib/config/color_themes.dart + - lib/models/processed_gps_data.dart + - lib/models/overlay_message.dart + - lib/models/theme_model.dart + - lib/services/gps_data_manager.dart + - lib/main.dart + - lib/gps_service.dart +autonomous: true + +must_haves: + truths: + - "App compiles with flutter analyze showing zero errors" + - "All existing functionality preserved — speed display, heading, themes, units, overlay all work identically" + - "Logger utility exists with 4 verbosity levels (error, warn, info, debug) guarded by kDebugMode" + - "Behavioral constants centralized in config/ with SCREAMING_SNAKE naming in abstract static classes" + - "ProcessedGpsData model lives in models/ as separate file with immutable fields and copyWith()" + - "OverlayMessage typed class exists with toMap()/fromMap() for overlay IPC serialization" + - "ThemeModel exists with internal palette storage, current palette exposure, and rotate() method" + - "SpeedUnit enum lives in config/speed_unit.dart" + - "ColorTheme/ColorThemes classes live in config/color_themes.dart" + artifacts: + - path: "lib/services/logger.dart" + provides: "Logger utility with error/warn/info/debug levels" + contains: "class Logger" + - path: "lib/config/gps_constants.dart" + provides: "GPS behavioral constants" + contains: "abstract class GpsConfig" + - path: "lib/config/timing_constants.dart" + provides: "Timer and interval constants" + contains: "abstract class TimingConfig" + - path: "lib/config/overlay_constants.dart" + provides: "Overlay sizing and display constants" + contains: "abstract class OverlayConfig" + - path: "lib/config/speed_unit.dart" + provides: "Speed unit enum with conversion" + contains: "enum SpeedUnit" + - path: "lib/config/color_themes.dart" + provides: "Color theme definitions" + contains: "class ColorTheme" + - path: "lib/models/processed_gps_data.dart" + provides: "Immutable GPS data model" + contains: "class ProcessedGpsData" + - path: "lib/models/overlay_message.dart" + provides: "Typed overlay IPC message" + contains: "class OverlayMessage" + - path: "lib/models/theme_model.dart" + provides: "Theme model with palette rotation" + contains: "class ThemeModel" + key_links: + - from: "lib/services/gps_data_manager.dart" + to: "lib/models/processed_gps_data.dart" + via: "import and usage of ProcessedGpsData" + pattern: "import.*models/processed_gps_data" + - from: "lib/services/gps_data_manager.dart" + to: "lib/config/gps_constants.dart" + via: "import and usage of GpsConfig constants" + pattern: "import.*config/gps_constants" + - from: "lib/main.dart" + to: "lib/config/speed_unit.dart" + via: "import SpeedUnit from new location" + pattern: "import.*config/speed_unit" + - from: "lib/main.dart" + to: "lib/config/color_themes.dart" + via: "import ColorThemes from new location" + pattern: "import.*config/color_themes" +--- + + +Create foundational infrastructure (directories, logger, constants) and extract all data models and config files to their proper locations in the new modular structure. + +Purpose: Establishes the architectural skeleton that all subsequent Phase 1 and Phase 2 work depends on. Models, constants, and logger are the lowest-dependency pieces that everything else imports from. + +Output: Logger utility, 3 constants files, 3 model files, 2 moved config files — all with updated imports across the codebase. App compiles and runs identically. + + + +@C:\Users\SKSup\.claude/get-shit-done/workflows/execute-plan.md +@C:\Users\SKSup\.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/01-foundation/01-CONTEXT.md +@.planning/phases/01-foundation/01-RESEARCH.md + +Source files to read before modifying: +@lib/main.dart +@lib/gps_service.dart +@lib/speed_units.dart +@lib/color_themes.dart +@lib/services/gps_data_manager.dart + + + + + + Task 1: Create directory structure, Logger utility, and constants files + + lib/screens/ (directory) + lib/widgets/ (directory) + lib/providers/ (directory) + lib/models/ (directory) + lib/config/ (directory) + lib/services/logger.dart + lib/config/gps_constants.dart + lib/config/timing_constants.dart + lib/config/overlay_constants.dart + + + 1. Create all six directories under lib/: screens/, widgets/, providers/, models/, config/. The services/ directory already exists. + + 2. Create `lib/services/logger.dart` — a Logger utility class with 4 static methods: + - `Logger.error(String message, [String? caller])` + - `Logger.warn(String message, [String? caller])` + - `Logger.info(String message, [String? caller])` + - `Logger.debug(String message, [String? caller])` + + Implementation requirements: + - All methods guarded by `if (kDebugMode)` from `package:flutter/foundation.dart` + - Use `debugPrint()` (not `print()`) to avoid log truncation + - Include ANSI color codes: red for error, yellow for warn, blue for info, gray for debug + - Include ISO 8601 timestamp in output + - Include caller tag in brackets when provided: `[ERROR] 2026-02-09T12:00:00 [GpsDataManager] message` + - Reset ANSI color after each line with `\x1B[0m` + + This replaces the existing `customDebugPrint()` function in main.dart. + + 3. Create `lib/config/gps_constants.dart` — extract GPS behavioral constants from gps_data_manager.dart: + ```dart + abstract class GpsConfig { + static const Duration STALE_DATA_THRESHOLD = Duration(seconds: 4); + static const Duration INITIAL_FIX_TIMEOUT = Duration(seconds: 20); + static const Duration UPDATE_TIMEOUT = Duration(seconds: 2); + static const double VALID_HEADING_MIN = 0.0; + static const double VALID_HEADING_MAX = 360.0; + static const double LOW_SPEED_THRESHOLD = 1.0; + } + ``` + Use SCREAMING_SNAKE naming per CONTEXT.md decisions. Include a comment at the top: `// GPS behavioral constants — SCREAMING_SNAKE per project convention` + + 4. Create `lib/config/timing_constants.dart` — extract timer intervals from main.dart: + ```dart + abstract class TimingConfig { + static const Duration HEARTBEAT_INTERVAL = Duration(seconds: 5); + static const Duration OVERLAY_STATUS_CHECK_INTERVAL = Duration(milliseconds: 1000); + static const Duration TAP_CLOSE_DELAY = Duration(milliseconds: 500); + static const Duration LONG_PRESS_SIGNAL_DELAY = Duration(milliseconds: 50); + } + ``` + + 5. Create `lib/config/overlay_constants.dart` — extract overlay sizing constants from main.dart: + ```dart + abstract class OverlayConfig { + static const double WIDTH_PERCENTAGE = 0.45; + static const double ASPECT_RATIO = 0.6; + static const double BACKGROUND_OPACITY = 0.85; + static const double BORDER_OPACITY = 0.3; + static const double FONT_SIZE_RATIO = 0.2; + static const double UNIT_FONT_RATIO = 0.6; + static const double ICON_SIZE_RATIO = 0.72; + static const double HEADING_FONT_RATIO = 0.4; + static const double DEFAULT_SCREEN_WIDTH = 400.0; + static const double DEFAULT_OVERLAY_WIDTH = 280.0; + static const double DEFAULT_OVERLAY_HEIGHT = 140.0; + } + ``` + + Do NOT update any imports in main.dart or gps_data_manager.dart yet — that happens in Task 2 and Task 3. + + + - Run `flutter analyze` in the project root — should show zero new errors (new files are standalone, not imported yet) + - Verify all 6 directories exist under lib/ + - Verify logger.dart, gps_constants.dart, timing_constants.dart, overlay_constants.dart all exist + + + Directory structure created. Logger utility exists with 4 levels guarded by kDebugMode. Three constants files exist with all behavioral magic numbers extracted using SCREAMING_SNAKE in abstract static classes. + + + + + Task 2: Move config files and extract data models + + lib/config/speed_unit.dart + lib/config/color_themes.dart + lib/models/processed_gps_data.dart + lib/models/overlay_message.dart + lib/models/theme_model.dart + + + 1. Create `lib/config/speed_unit.dart` — copy the SpeedUnit enum from `lib/speed_units.dart` exactly as-is. Same content, new location. Do NOT delete the old file yet. + + 2. Create `lib/config/color_themes.dart` — copy both ColorTheme and ColorThemes classes from `lib/color_themes.dart` exactly as-is. Same content, new location. Do NOT delete the old file yet. + + 3. Create `lib/models/processed_gps_data.dart` — extract the ProcessedGpsData class from `lib/services/gps_data_manager.dart` (lines 6-40) into its own file. The class is already immutable with copyWith(). No changes to the class itself, just move it to its own file. Add the appropriate import for `package:flutter/foundation.dart` if needed (it isn't — this is a pure Dart class, no Flutter imports needed). + + 4. Create `lib/models/overlay_message.dart` — a NEW typed class replacing the unvalidated Map data currently used for overlay IPC. Analyze the current overlay message maps in main.dart to determine fields: + + From `_pushDataToOverlay()` (line 403-411): action, speedText, unitText, headingText, heading, unitIndex, themeIndex + From `_showFloatingWindow()` (line 441-450): adds overlayWidth, overlayHeight + From overlay long press (line 1063-1064): action 'longPressClose' + + Create the class: + ```dart + import 'dart:convert'; + + class OverlayMessage { + final String action; + final String? speedText; + final String? unitText; + final String? headingText; + final double? heading; + final int? unitIndex; + final int? themeIndex; + final double? overlayWidth; + final double? overlayHeight; + + const OverlayMessage({ + required this.action, + this.speedText, + this.unitText, + this.headingText, + this.heading, + this.unitIndex, + this.themeIndex, + this.overlayWidth, + this.overlayHeight, + }); + + // Factory constructors for common message types + factory OverlayMessage.updateDisplay({ + required String speedText, + required String unitText, + required String headingText, + required double heading, + required int unitIndex, + required int themeIndex, + double? overlayWidth, + double? overlayHeight, + }) { + return OverlayMessage( + action: 'updateDisplay', + speedText: speedText, + unitText: unitText, + headingText: headingText, + heading: heading, + unitIndex: unitIndex, + themeIndex: themeIndex, + overlayWidth: overlayWidth, + overlayHeight: overlayHeight, + ); + } + + factory OverlayMessage.longPressClose() { + return const OverlayMessage(action: 'longPressClose'); + } + + factory OverlayMessage.overlayClosed() { + return const OverlayMessage(action: 'overlayClosed'); + } + + Map toMap() { + final map = {'action': action}; + if (speedText != null) map['speedText'] = speedText; + if (unitText != null) map['unitText'] = unitText; + if (headingText != null) map['headingText'] = headingText; + if (heading != null) map['heading'] = heading; + if (unitIndex != null) map['unitIndex'] = unitIndex; + if (themeIndex != null) map['themeIndex'] = themeIndex; + if (overlayWidth != null) map['overlayWidth'] = overlayWidth; + if (overlayHeight != null) map['overlayHeight'] = overlayHeight; + return map; + } + + factory OverlayMessage.fromMap(Map map) { + return OverlayMessage( + action: map['action'] as String? ?? 'unknown', + speedText: map['speedText'] as String?, + unitText: map['unitText'] as String?, + headingText: map['headingText'] as String?, + heading: (map['heading'] as num?)?.toDouble(), + unitIndex: map['unitIndex'] as int?, + themeIndex: map['themeIndex'] as int?, + overlayWidth: (map['overlayWidth'] as num?)?.toDouble(), + overlayHeight: (map['overlayHeight'] as num?)?.toDouble(), + ); + } + + OverlayMessage copyWith({...all fields...}) { ... } + } + ``` + + Include `toMap()` and `fromMap()` because this model crosses the overlay IPC boundary via `FlutterOverlayWindow.shareData()` which requires Map data. + + 5. Create `lib/models/theme_model.dart` — a new ThemeModel that wraps the existing ColorThemes: + ```dart + import '../config/color_themes.dart'; + + class ThemeModel { + final int _currentIndex; + + const ThemeModel({int currentIndex = 0}) : _currentIndex = currentIndex; + + // Current palette access + ColorTheme get current => ColorThemes.getTheme(_currentIndex); + String get currentSetName => current.name; + int get currentIndex => _currentIndex; + + // Rotate to next theme (circular) + ThemeModel rotate() { + return ThemeModel( + currentIndex: ColorThemes.getNextThemeIndex(_currentIndex), + ); + } + + ThemeModel copyWith({int? currentIndex}) { + return ThemeModel( + currentIndex: currentIndex ?? _currentIndex, + ); + } + } + ``` + + ThemeModel delegates palette storage to ColorThemes (which holds the static list). ThemeModel only tracks current selection and provides rotation. This matches the CONTEXT.md mental model: "holds all possible colour palettes, exposing only the current set of colours (with their nametags), informative 'current set name', and a method for rotating themes." The palettes are held via delegation to ColorThemes, not duplicated. + + Do NOT update imports in main.dart or gps_data_manager.dart yet — that is Task 3. + + + - Run `flutter analyze` — zero new errors (new files not imported yet by existing code) + - Verify all 5 model/config files exist in their new locations + - Verify ProcessedGpsData in models/ matches the original in gps_data_manager.dart + + + SpeedUnit and ColorThemes copied to config/. ProcessedGpsData extracted to models/. OverlayMessage typed class created with toMap()/fromMap(). ThemeModel created with palette rotation. + + + + + Task 3: Rewire all imports, replace print() with Logger, delete old files + + lib/services/gps_data_manager.dart + lib/gps_service.dart + lib/main.dart + lib/speed_units.dart (delete) + lib/color_themes.dart (delete) + + + This is the critical wiring task. Every import must be updated in a single pass. The app must compile after this task completes. + + 1. **Update `lib/services/gps_data_manager.dart`:** + - Remove the ProcessedGpsData class definition (lines 6-40) — it now lives in models/ + - Add imports: + ```dart + import '../models/processed_gps_data.dart'; + import '../config/speed_unit.dart'; + import '../config/gps_constants.dart'; + import '../services/logger.dart'; + ``` + - Change import `'../speed_units.dart'` to `'../config/speed_unit.dart'` + - Replace `_staleDataThreshold` constant with `GpsConfig.STALE_DATA_THRESHOLD` + - Replace the Duration(seconds: 20) timeout with `GpsConfig.INITIAL_FIX_TIMEOUT` + - Replace heading validation magic numbers (0, 360) with `GpsConfig.VALID_HEADING_MIN`, `GpsConfig.VALID_HEADING_MAX` + - Replace ALL `print()` calls (there are ~27) with appropriate Logger calls: + - `print('[GpsDataManager] 🚀 ...')` → `Logger.info('Starting GPS manager initialization...', 'GpsDataManager')` + - `print('[GpsDataManager] ❌ ...')` → `Logger.error('GPS service disabled', 'GpsDataManager')` + - `print('[GpsDataManager] ⚠️ ...')` → `Logger.warn('Already initialized, skipping...', 'GpsDataManager')` + - `print('[GpsDataManager] 📡 ...')` → `Logger.debug('Raw GPS update: ...', 'GpsDataManager')` + - `print('[GpsDataManager] ✅ ...')` → `Logger.info('GPS manager initialized successfully', 'GpsDataManager')` + - Use error for failures/exceptions, warn for recoverable issues, info for lifecycle events, debug for data flow + - Remove emoji prefixes from log messages — the Logger's color coding replaces them + + 2. **Update `lib/gps_service.dart`:** + - No import changes needed (it only imports dart:async, geolocator, permission_handler) + - Replace heading validation magic numbers (0, 360) with GpsConfig constants: + ```dart + import 'config/gps_constants.dart'; + ``` + Change `heading >= 0 && heading <= 360` to `heading >= GpsConfig.VALID_HEADING_MIN && heading <= GpsConfig.VALID_HEADING_MAX` + + 3. **Update `lib/main.dart`:** + - Change `import 'speed_units.dart'` to `import 'config/speed_unit.dart'` + - Change `import 'color_themes.dart'` to `import 'config/color_themes.dart'` + - Add imports: + ```dart + import 'config/timing_constants.dart'; + import 'config/overlay_constants.dart'; + import 'models/overlay_message.dart'; + import 'services/logger.dart'; + ``` + - Remove the `customDebugPrint()` function definition (lines 16-20) — replaced by Logger + - Replace ALL `customDebugPrint()` calls with appropriate `Logger.*()` calls: + - Pattern: `customDebugPrint('[Main] emoji message')` → `Logger.debug('message', 'Main')` (or info/warn/error as appropriate) + - Use Logger.error for catch blocks + - Use Logger.warn for unexpected states + - Use Logger.info for lifecycle transitions + - Use Logger.debug for data flow, state changes, gesture events + - Replace ALL remaining `print()` calls (lines 337-373 in _initializeGpsManager and _onGpsDataUpdate) with Logger calls + - Replace magic numbers in timer durations: + - `Duration(seconds: 5)` in heartbeat → `TimingConfig.HEARTBEAT_INTERVAL` + - `Duration(milliseconds: 1000)` in overlay check → `TimingConfig.OVERLAY_STATUS_CHECK_INTERVAL` + - `Duration(milliseconds: 500)` in tap close → `TimingConfig.TAP_CLOSE_DELAY` + - `Duration(milliseconds: 50)` in long press delay → `TimingConfig.LONG_PRESS_SIGNAL_DELAY` + - Replace magic numbers in overlay sizing: + - `0.45` width percentage → `OverlayConfig.WIDTH_PERCENTAGE` + - `0.6` aspect ratio → `OverlayConfig.ASPECT_RATIO` + - `0.85` background opacity → `OverlayConfig.BACKGROUND_OPACITY` + - `0.3` border opacity → `OverlayConfig.BORDER_OPACITY` + - Replace overlay IPC Maps with OverlayMessage typed class: + - In `_pushDataToOverlay()`: replace `FlutterOverlayWindow.shareData({...map...})` with `FlutterOverlayWindow.shareData(OverlayMessage.updateDisplay(...).toMap())` + - In `_showFloatingWindow()`: replace the shareData map with `OverlayMessage.updateDisplay(...).toMap()` + - In overlay `_listenToMainAppMessages()`: parse incoming data with `OverlayMessage.fromMap(data)` + - In overlay long press: replace `FlutterOverlayWindow.shareData({'action': 'longPressClose'})` with `FlutterOverlayWindow.shareData(OverlayMessage.longPressClose().toMap())` + - In `_startListeningToOverlayMessages()`: parse incoming data using OverlayMessage.fromMap() instead of raw Map access + - In overlay constants: replace `_systemScreenWidth = 400` default with `OverlayConfig.DEFAULT_SCREEN_WIDTH`, etc. + + 4. **Delete old files:** + - Delete `lib/speed_units.dart` (moved to config/speed_unit.dart) + - Delete `lib/color_themes.dart` (moved to config/color_themes.dart) + + **IMPORTANT:** Do not change any business logic, UI layout, gesture handling, or widget structure. Only change imports, constant references, Map→OverlayMessage conversions, and print→Logger replacements. The app must behave identically. + + + - Run `flutter analyze` — zero errors + - Verify `lib/speed_units.dart` and `lib/color_themes.dart` no longer exist + - Grep for bare `print(` in lib/ — should find zero occurrences (all replaced with Logger) + - Grep for `customDebugPrint(` in lib/ — should find zero occurrences (all replaced with Logger) + - Grep for `import 'speed_units.dart'` — should find zero occurrences + - Grep for `import 'color_themes.dart'` — should find zero occurrences (only config/color_themes.dart) + - Verify main.dart no longer contains the customDebugPrint function definition + + + All imports rewired to new file locations. All print()/customDebugPrint() replaced with Logger calls. All behavioral magic numbers replaced with named constants. OverlayMessage typed class used for all overlay IPC. Old speed_units.dart and color_themes.dart deleted. App compiles with zero analyzer errors. + + + + + + +After all 3 tasks complete: + +1. `flutter analyze` returns zero errors +2. No bare `print()` calls remain in lib/ (grep confirms) +3. No `customDebugPrint()` calls remain in lib/ (grep confirms) +4. Old files deleted: lib/speed_units.dart, lib/color_themes.dart +5. Directory structure exists: lib/screens/, lib/widgets/, lib/providers/, lib/models/, lib/config/, lib/services/ +6. Files exist in new locations: + - lib/services/logger.dart + - lib/config/gps_constants.dart, timing_constants.dart, overlay_constants.dart + - lib/config/speed_unit.dart, color_themes.dart + - lib/models/processed_gps_data.dart, overlay_message.dart, theme_model.dart +7. gps_data_manager.dart imports ProcessedGpsData from models/ and uses GpsConfig constants +8. main.dart imports SpeedUnit from config/ and uses TimingConfig/OverlayConfig constants +9. Overlay IPC uses OverlayMessage.toMap()/fromMap() instead of raw Maps + +Requirements coverage: ORG-01 (partial — dirs created), ORG-02, ORG-03, ORG-04, ORG-05, ORG-06 + + + +- App compiles cleanly with `flutter analyze` showing zero errors +- All 8 requirements (ORG-01 through ORG-06) addressed by this plan are satisfied +- Zero bare print() or customDebugPrint() calls remain in codebase +- All behavioral constants use named references from config/ files +- OverlayMessage typed class handles all overlay IPC serialization + + + +After completion, create `.planning/phases/01-foundation/01-01-SUMMARY.md` + diff --git a/.planning/phases/01-foundation/01-02-PLAN.md b/.planning/phases/01-foundation/01-02-PLAN.md new file mode 100644 index 0000000..3ca8829 --- /dev/null +++ b/.planning/phases/01-foundation/01-02-PLAN.md @@ -0,0 +1,244 @@ +--- +phase: 01-foundation +plan: 02 +type: execute +wave: 2 +depends_on: ["01-01"] +files_modified: + - lib/screens/speedometer_screen.dart + - lib/screens/overlay_screen.dart + - lib/main.dart +autonomous: true + +must_haves: + truths: + - "App compiles with flutter analyze showing zero errors" + - "main.dart contains only entry points (main, overlayMain), SpeedoApp widget, and imports — under 60 lines" + - "SpeedometerScreen lives in screens/speedometer_screen.dart with all its state and methods intact" + - "OverlaySpeedometer lives in screens/overlay_screen.dart with all its state and methods intact" + - "All existing functionality preserved — speed display, heading, themes, units, overlay, haptics, lifecycle all work identically" + artifacts: + - path: "lib/screens/speedometer_screen.dart" + provides: "Main speedometer screen widget and state" + contains: "class SpeedometerScreen" + - path: "lib/screens/overlay_screen.dart" + provides: "Overlay floating window widget and state" + contains: "class OverlaySpeedometer" + - path: "lib/main.dart" + provides: "App entry points and SpeedoApp widget only" + min_lines: 15 + key_links: + - from: "lib/main.dart" + to: "lib/screens/speedometer_screen.dart" + via: "import and use as MaterialApp home" + pattern: "import.*screens/speedometer_screen" + - from: "lib/main.dart" + to: "lib/screens/overlay_screen.dart" + via: "import and use in overlayMain()" + pattern: "import.*screens/overlay_screen" + - from: "lib/screens/speedometer_screen.dart" + to: "lib/services/gps_data_manager.dart" + via: "GPS data subscription and display" + pattern: "import.*services/gps_data_manager" + - from: "lib/screens/speedometer_screen.dart" + to: "lib/models/overlay_message.dart" + via: "Overlay IPC messaging" + pattern: "import.*models/overlay_message" + - from: "lib/screens/overlay_screen.dart" + to: "lib/config/color_themes.dart" + via: "Theme rendering in overlay" + pattern: "import.*config/color_themes" +--- + + +Extract SpeedometerScreen and OverlaySpeedometer from the monolithic main.dart into dedicated screen files, leaving main.dart as a slim entry point. + +Purpose: Completes the Phase 1 code organization by splitting the 1133-line main.dart into focused, single-responsibility files. This is the final structural change — after this, ORG-07 and ORG-08 are satisfied and the foundation phase is complete. + +Output: speedometer_screen.dart (~780 lines), overlay_screen.dart (~240 lines), slimmed main.dart (~50 lines). + + + +@C:\Users\SKSup\.claude/get-shit-done/workflows/execute-plan.md +@C:\Users\SKSup\.claude/get-shit-done/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/01-foundation/01-CONTEXT.md +@.planning/phases/01-foundation/01-01-SUMMARY.md + +Source files (post Plan 01): +@lib/main.dart + + + + + + Task 1: Extract SpeedometerScreen to screens/speedometer_screen.dart + + lib/screens/speedometer_screen.dart + lib/main.dart + + + 1. Create `lib/screens/speedometer_screen.dart` by extracting the `SpeedometerScreen` widget class and its `_SpeedometerScreenState` class from main.dart. + + **What to move (everything from the SpeedometerScreen class through end of _SpeedometerScreenState):** + - `class SpeedometerScreen extends StatefulWidget` and its state + - All state variables, methods, build methods, layout builders + - The `_getOverlaySize()` method + - All timer management, lifecycle observer, overlay messaging, GPS subscription + - The `_buildSpeedDisplay()`, `_buildPortraitLayout()`, `_buildLandscapeLayout()` methods + + **Imports needed in the new file** (derive from what SpeedometerScreen actually uses): + ```dart + import 'package:flutter/material.dart'; + import 'package:flutter/services.dart'; // HapticFeedback + import 'dart:async'; + import 'dart:math' as math; + import 'package:wakelock_plus/wakelock_plus.dart'; + import 'package:flutter_overlay_window/flutter_overlay_window.dart'; + import 'package:bg_launcher/bg_launcher.dart'; + import '../config/speed_unit.dart'; + import '../config/color_themes.dart'; + import '../config/timing_constants.dart'; + import '../config/overlay_constants.dart'; + import '../models/overlay_message.dart'; + import '../models/processed_gps_data.dart'; + import '../services/gps_data_manager.dart'; + import '../services/logger.dart'; + ``` + + **Do NOT include** in this file: + - `void main()` — stays in main.dart + - `void overlayMain()` — stays in main.dart + - `class SpeedoApp` — stays in main.dart + - `class OverlaySpeedometer` — goes to overlay_screen.dart in Task 2 + + **Critical: preserve ALL business logic, method signatures, and widget structure exactly.** This is a pure file move, not a refactor. Every line of SpeedometerScreen code must appear in the new file unchanged (except import paths). + + 2. Remove SpeedometerScreen class and _SpeedometerScreenState from main.dart. Leave only: imports, main(), overlayMain(), SpeedoApp, and OverlaySpeedometer (OverlaySpeedometer will be moved in Task 2). + + 3. Update main.dart imports to add: + ```dart + import 'screens/speedometer_screen.dart'; + ``` + + 4. Verify main.dart still references SpeedometerScreen correctly in SpeedoApp.build(). + + + - Run `flutter analyze` — zero errors + - Verify SpeedometerScreen class exists in lib/screens/speedometer_screen.dart + - Verify SpeedometerScreen class does NOT exist in lib/main.dart + - Verify main.dart still has SpeedoApp referencing SpeedometerScreen + + + SpeedometerScreen fully extracted to screens/speedometer_screen.dart with all state, methods, and layout builders. main.dart imports it correctly. App compiles. + + + + + Task 2: Extract OverlaySpeedometer to screens/overlay_screen.dart and slim down main.dart + + lib/screens/overlay_screen.dart + lib/main.dart + + + 1. Create `lib/screens/overlay_screen.dart` by extracting the `OverlaySpeedometer` widget class and its `_OverlaySpeedometerState` class from main.dart. + + **What to move:** + - `class OverlaySpeedometer extends StatefulWidget` and its state + - All state variables: _speedText, _unitText, _headingText, _heading, _currentThemeIndex, _systemScreenWidth, _overlayWidth, _overlayHeight + - All methods: _getSystemScreenSize(), _listenToMainAppMessages(), build() + - The entire gesture detection layer and overlay UI + + **Imports needed in the new file:** + ```dart + import 'package:flutter/material.dart'; + import 'dart:math' as math; + import 'package:flutter_overlay_window/flutter_overlay_window.dart'; + import '../config/color_themes.dart'; + import '../config/overlay_constants.dart'; + import '../models/overlay_message.dart'; + import '../services/logger.dart'; + ``` + + **Critical: preserve ALL widget structure, gesture handling, and overlay communication exactly.** Pure file move. + + 2. Remove OverlaySpeedometer class and _OverlaySpeedometerState from main.dart. + + 3. Update main.dart to add import and reference overlay_screen.dart in overlayMain(): + ```dart + import 'screens/overlay_screen.dart'; + ``` + + 4. **Clean up main.dart** to be a slim entry point. After extraction, main.dart should contain ONLY: + - Package imports (flutter/material.dart, flutter/foundation.dart) + - Local imports (screens/speedometer_screen.dart, screens/overlay_screen.dart, services/logger.dart) + - `void main()` function + - `@pragma("vm:entry-point") void overlayMain()` function + - `class SpeedoApp extends StatelessWidget` + + The file should be approximately 40-55 lines. Remove any now-unused imports (dart:async, wakelock_plus, bg_launcher, speed_units, color_themes, gps_data_manager, dart:math, flutter/services.dart — these are all used by SpeedometerScreen which is now in its own file). + + 5. Verify the overlayMain() function references OverlaySpeedometer correctly. + + + - Run `flutter analyze` — zero errors + - Verify OverlaySpeedometer class exists in lib/screens/overlay_screen.dart + - Verify OverlaySpeedometer class does NOT exist in lib/main.dart + - Verify main.dart is under 60 lines + - Verify main.dart contains only: main(), overlayMain(), SpeedoApp class + - Grep for `class ` in main.dart — should find only SpeedoApp + + + OverlaySpeedometer fully extracted to screens/overlay_screen.dart. main.dart slimmed to ~50 lines containing only entry points and SpeedoApp widget. All imports correct. App compiles with zero errors. + + + + + Complete Phase 1 restructure: monolithic main.dart split into modular file structure with extracted models, typed overlay messages, centralized constants, and proper logger. + + 1. Build and run the app on a physical Android device: `flutter run` + 2. Verify speed display works (shows current GPS speed or '--' when stationary) + 3. Tap the speed number to cycle through themes (Yuzuki, Yuuparo, Yuzuki Light, Yuuparo Light) + 4. Tap the unit label to cycle units (km/h, mph, kts) + 5. Tap the compass/navigation area to launch the floating overlay window + 6. Verify overlay shows speed and heading data + 7. Long-press the overlay to close it + 8. Rotate device to verify landscape layout works + 9. Check debug console for Logger output format (colored tags, timestamps, caller names) + + Type "approved" if all functionality works identically to before restructure, or describe any issues. + + + + + +After both tasks complete: + +1. `flutter analyze` returns zero errors +2. main.dart is under 60 lines, containing only entry points and SpeedoApp +3. SpeedometerScreen lives in lib/screens/speedometer_screen.dart +4. OverlaySpeedometer lives in lib/screens/overlay_screen.dart +5. No widget classes remain in main.dart except SpeedoApp +6. All imports resolve correctly (no "Target of URI doesn't exist" errors) +7. Grep for `class SpeedometerScreen` — found only in screens/speedometer_screen.dart +8. Grep for `class OverlaySpeedometer` — found only in screens/overlay_screen.dart + +Requirements coverage: ORG-07 (SpeedometerScreen extracted), ORG-08 (OverlaySpeedometer extracted), ORG-01 (complete — all directories populated) + + + +- App compiles cleanly with `flutter analyze` showing zero errors +- main.dart is a slim entry point under 60 lines +- All screen widgets live in dedicated files under screens/ +- Combined with Plan 01, ALL Phase 1 requirements (ORG-01 through ORG-08) are satisfied +- App runs on physical device with identical behavior to pre-restructure + + + +After completion, create `.planning/phases/01-foundation/01-02-SUMMARY.md` +