diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 0000000..b775de0
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,18 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(dotnet build:*)",
+ "Bash(dotnet test:*)",
+ "Bash(dotnet clean:*)",
+ "Bash(Remove-Item -Recurse -Force CodeSoupCafe.Mauiobj,CodeSoupCafe.Mauibin -ErrorAction SilentlyContinue)",
+ "Bash(Remove-Item -Recurse -Force obj,bin -ErrorAction SilentlyContinue)",
+ "Bash(dir:*)",
+ "Bash(dotnet pack:*)",
+ "Bash(dotnet restore:*)",
+ "Bash(dotnet sln:*)",
+ "Bash(findstr:*)",
+ "Bash(Get-ChildItem -Recurse -Filter \"*carousel*\")",
+ "Bash(Select-Object FullName)"
+ ]
+ }
+}
diff --git a/.github/workflows/dotnet-desktop.yml b/.github/workflows/dotnet-desktop.yml
index fac0a1a..166f7ed 100644
--- a/.github/workflows/dotnet-desktop.yml
+++ b/.github/workflows/dotnet-desktop.yml
@@ -15,21 +15,32 @@ jobs:
runs-on: windows-latest # Or macos-latest for iOS/Mac Catalyst builds
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v4
- - name: Setup .NET SDK
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: '10.0.x'
+ - name: Setup .NET SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: "10.0.x"
- - name: Install MAUI Workloads
- run: dotnet workload install maui
+ - name: Install MAUI Workloads
+ run: dotnet workload install maui
- - name: Restore NuGet packages
- run: dotnet restore LunaDraw.csproj
+ - name: Cache NuGet packages
+ uses: actions/cache@v4
+ with:
+ path: ~/.nuget/packages
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json', '**/*.csproj') }}
+ restore-keys: |
+ ${{ runner.os }}-nuget-
- - name: Build MAUI App (Windows)
- run: dotnet build LunaDraw.csproj -c Release -f net10.0-windows10.0.19041.0
+ # - name: Remove Local Source for CI
+ # run: dotnet nuget remove source LocalNuGetPackages --configfile nuget.config
- - name: Run Unit Tests
- run: dotnet test tests/LunaDraw.Tests/LunaDraw.Tests.csproj
+ - name: Restore NuGet packages
+ run: dotnet restore LunaDraw.csproj --configfile nuget.config
+
+ - name: Build MAUI App (Windows)
+ run: dotnet build LunaDraw.csproj -c Release -f net10.0-windows10.0.19041.0
+
+ - name: Run Unit Tests
+ run: dotnet test tests/LunaDraw.Tests/LunaDraw.Tests.csproj
diff --git a/App.xaml b/App.xaml
index fc81ac9..9e45f6d 100644
--- a/App.xaml
+++ b/App.xaml
@@ -12,6 +12,9 @@
+
+
+
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..39231a1
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,233 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+LunaDraw is a child-centric drawing application (ages 3-8) built with .NET MAUI targeting Windows, Android, iOS, and MacCatalyst. The app features 24+ magical brush effects, shape tools, stamps, undo/redo, layer management, and "Movie Mode" time-lapse replay.
+
+**Important Context:**
+
+- The codebase is heavily AI-generated ("vibe-coded") and can be fragile in places
+- Some canvas functionality was migrated from a working app in `\Legacy\SurfaceBurnCalc`
+- The app is in active development with missing features tracked in `Documentation/MissingFeatures.md`
+
+## Build & Development Commands
+
+### Building
+
+```bash
+# Build the project (Windows target)
+dotnet build LunaDraw.csproj -f net10.0-windows10.0.19041.0
+
+# Build for specific platform
+dotnet build LunaDraw.csproj -f net10.0-android36.0
+dotnet build LunaDraw.csproj -f net10.0-ios26.0
+dotnet build LunaDraw.csproj -f net10.0-maccatalyst26.0
+```
+
+### Testing
+
+```bash
+# Run all tests
+dotnet test tests/LunaDraw.Tests/LunaDraw.Tests.csproj
+
+# Run specific test
+dotnet test tests/LunaDraw.Tests/LunaDraw.Tests.csproj --filter "FullyQualifiedName~TestMethodName"
+```
+
+### Running
+
+- Use Visual Studio 2022 or VS Code with C# Dev Kit and .NET MAUI extensions
+- Select target framework (e.g., `net10.0-windows10.0.19041.0`)
+- Build and Run through IDE
+
+## Architecture
+
+### Core Technologies
+
+- **.NET MAUI** - Cross-platform UI framework
+- **SkiaSharp** - All vector graphics and rendering (primary graphics engine)
+- **ReactiveUI** - MVVM framework and state management using observables
+- **CommunityToolkit.Maui** - Extended MAUI controls and utilities
+
+### Architectural Patterns
+
+#### MVVM with ReactiveUI
+
+- ViewModels inherit from `ReactiveObject` for property change notifications
+- Use `this.RaiseAndSetIfChanged(ref field, value)` for reactive properties
+- Leverage reactive subscriptions for messaging and state changes
+
+#### Messaging & Communication
+
+- **MessageBus** (ReactiveUI's `IMessageBus`): Use sparingly for loosely-coupled broadcast messages between disconnected components
+- **Reactive Observables**: Preferred approach for component communication where possible
+- **Command/Event Pattern**: Fallback when reactive approaches don't fit
+- MessageBus is instance-based (injected via DI), NOT static for testability
+
+#### Dependency Injection
+
+Services registered in `MauiProgram.cs`:
+
+- **Core State**: `IMessageBus`, `NavigationModel`, `SelectionObserver`, `ILayerFacade`
+- **Logic Services**: `ICanvasInputHandler`, `ClipboardMemento`, `IBitmapCache`, `IPreferencesFacade`, `IDrawingStorageMomento`
+- **ViewModels**: Singleton or Transient as appropriate
+- **Pages**: Typically Transient
+
+### Key Architectural Components
+
+#### Drawing Model
+
+- **IDrawableElement**: Base interface for all drawable objects (paths, shapes, stamps, images)
+
+ - Supports selection, transforms, visibility, layering (ZIndex), opacity, fill/stroke
+ - Each element has `Bounds`, `TransformMatrix`, and methods: `Draw()`, `HitTest()`, `Clone()`, `Translate()`, `Transform()`
+ - Concrete implementations: `DrawablePath`, `DrawableEllipse`, `DrawableRectangle`, `DrawableLine`, `DrawableImage`, `DrawableStamps`, `DrawableGroup`
+
+- **Layer**: Container for drawable elements with ReactiveUI observable collections
+
+ - Uses **QuadTree spatial indexing** (`QuadTreeMemento`) for efficient spatial queries and rendering
+ - No bitmap tiling - renders directly from vector elements
+ - Auto-assigns ZIndex to new elements to maintain draw order
+ - Supports masking modes, visibility, locking
+
+- **ILayerFacade**: Abstraction for layer management operations
+ - Manages `ObservableCollection` and current layer state
+ - Integrates with `HistoryMemento` for undo/redo
+ - Methods: `AddLayer()`, `RemoveLayer()`, `MoveLayer()`, `MoveElementsToLayer()`, `SaveState()`
+
+#### Tool System
+
+- **IDrawingTool**: Interface for all drawing/editing tools
+- Tool implementations: `FreehandTool`, `EraserTool`, `EraserBrushTool`, `FillTool`, `SelectTool`, `LineTool`, `RectangleTool`, `EllipseTool`, `ShapeTool`
+- Tools receive `ToolContext` with canvas state, navigation, layers, and brush settings
+- Input handling delegated to `CanvasInputHandler` which dispatches to active tool
+
+#### View & Viewport Management
+
+- **NavigationModel**: Manages pan/zoom transformations via `ViewMatrix` (SKMatrix)
+- **CanvasInputHandler**: Central touch/mouse input processor
+ - Handles multi-touch gestures (pan, zoom, rotate) on canvas and selection
+ - Delegates single-touch drawing to active tool
+ - Right-click switches to Select tool
+ - Applies smoothing to gestures for fluid interaction
+- **MainPage**: Primary page hosting `SKCanvasView` for rendering
+ - Subscribes to `CanvasInvalidateMessage` to trigger redraws
+ - Manages context menus and flyout panels (brushes, shapes, settings)
+
+#### State Management & History
+
+- **HistoryMemento**: Undo/redo stack for layer states
+- **ClipboardMemento**: Copy/paste buffer for drawable elements
+- **DrawingStorageMomento**: Serialization/deserialization of drawings to file
+- **QuadTreeMemento**: Generic spatial partitioning for efficient hit-testing and culling
+
+## Code Quality & Testing Standards
+
+### Testing with xUnit, Moq
+
+- **Test Format**: Arrange-Act-Assert (AAA)
+ - If no `// Arrange` needed, start with `// Act`
+- **Naming**: `Should_When_Returns` format
+ - Example: `Should_Set_Sliding_Issued_At_Time_When_Valid_Credentials_Expired_Or_Invalid_Returns_Logout`
+- **Test Instances**: Use class name for instance, NOT 'sut' or arbitrary names
+ - Mocks: `mockClassName` (e.g., `mockLayerFacade`)
+- **Assertions**: One assertion per line
+- **Test Types**: Prefer `[Theory]`, `[InlineData]`, `[MemberData]` over multiple `[Fact]` methods. Include negative test cases
+
+### Bug Fixing Workflow
+
+1. **Write test first** to validate the bug exists
+2. Implement the fix
+3. Run test to confirm bug elimination
+
+### Code Style Rules (from .clinerules)
+
+- **NO underscores** in names
+- **NO regions**
+- **NO abbreviations** in variable names or otherwise (use full descriptive names)
+- **NO legacy or duplicate code** - refactor to clean state, remove obsolete code
+- **Static extensions**: Use ONLY for reusable logic (see `Logic/Extensions/`)
+
+### SOLID & Design Principles
+
+Ensure adherence to:
+
+- Single Responsibility Principle (SRP)
+- Open/Closed Principle (OCP)
+- Liskov Substitution Principle (LSP)
+- Interface Segregation Principle (ISP)
+- Dependency Inversion Principle (DIP)
+- DRY (Don't Repeat Yourself)
+- Low Coupling / High Cohesion
+- Separation of Concerns & Modularity
+
+## Project Structure
+
+```
+LunaDraw/
+├── Components/ # Reusable UI components and controls
+│ ├── Carousel/ # Gallery carousel implementation
+│ ├── *FlyoutPanel.xaml # Brush, shape, settings panels
+│ └── *.cs # Custom controls (BrushPreview, ShapePreview, etc.)
+├── Converters/ # XAML value converters
+├── Documentation/ # Architecture, features, missing features
+├── Logic/ # Core business logic (non-UI)
+│ ├── Constants/ # App-wide constants
+│ ├── Extensions/ # Static extension methods (SkiaSharp, Preferences)
+│ ├── Handlers/ # Input handling (CanvasInputHandler)
+│ ├── Messages/ # MessageBus message types
+│ ├── Models/ # Domain models (IDrawableElement, Layer, ToolContext, etc.)
+│ ├── Tools/ # IDrawingTool implementations
+│ ├── Utils/ # Utilities (LayerFacade, Mementos, BitmapCache, etc.)
+│ └── ViewModels/ # ReactiveUI ViewModels
+├── Pages/ # MAUI pages (MainPage)
+├── Platforms/ # Platform-specific code
+├── Resources/ # Images, fonts, splash, raw assets
+├── tests/ # Unit tests
+│ └── LunaDraw.Tests/
+└── MauiProgram.cs # DI registration and app configuration
+```
+
+## Important Technical Notes
+
+### SkiaSharp Rendering
+
+- All graphics rendered via SkiaSharp (`SKCanvas`, `SKPaint`, `SKPath`)
+- `MainPage.OnCanvasViewPaintSurface`: Main rendering loop
+ - Applies `NavigationModel.ViewMatrix` for pan/zoom
+ - Iterates layers, uses QuadTree to cull off-screen elements
+ - Elements sorted by ZIndex before drawing
+
+### Brush Effects
+
+- 24+ brush effects with custom shaders and blending modes
+- Examples: Glow/Neon (additive blending, bloom), Star Sparkles, Rainbow, Fireworks, Crayon, Spray, Ribbon
+- Brush settings stored in `ToolbarViewModel` and passed via `ToolContext`
+
+### Movie Mode (Time-Lapse)
+
+- Records drawing process in background
+- Playback animates creation of drawing
+
+### Child-Friendly UX Requirements
+
+- Large touch targets (min 2cm x 2cm)
+- Multi-sensory feedback (sounds, animations)
+- Icon-driven, minimal text
+- Visual/audio guidance over explicit instructions
+
+## Legacy & Migration Notes
+
+- Canvas functionality migrated from `\Legacy\SurfaceBurnCalc` (previous working app)
+- Current branch `reactive-carousel-v2` is refactoring carousel infrastructure from SBC to MAUI
+- Code is fragile in places due to AI generation - test thoroughly
+
+## Additional Resources
+
+- **README.md**: Project overview, features, screenshots, setup
+- **Documentation/ArchitectureDesign.md**: Detailed architecture and design requirements
+- **Documentation/Features.md**: Feature specifications
+- **Documentation/MissingFeatures.md**: Pending features and known issues
+- **.clinerules/**: Coding standards and SPARC methodology guidelines
diff --git a/Components/AdvancedSettingsPopup.xaml b/Components/AdvancedSettingsPopup.xaml
index 6765595..9dda415 100644
--- a/Components/AdvancedSettingsPopup.xaml
+++ b/Components/AdvancedSettingsPopup.xaml
@@ -7,7 +7,7 @@
x:Class="LunaDraw.Components.AdvancedSettingsPopup"
x:DataType="viewModels:MainViewModel"
CanBeDismissedByTappingOutsideOfPopup="True"
- BackgroundColor="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray900}}">
+ BackgroundColor="{AppThemeBinding Light={StaticResource PopupBackgroundLight}, Dark={StaticResource PopupBackgroundDark}}">
-
@@ -20,7 +20,7 @@
HorizontalTextAlignment="Center"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Components/DrawingGalleryPopup.xaml.cs b/Components/DrawingGalleryPopup.xaml.cs
new file mode 100644
index 0000000..3c48592
--- /dev/null
+++ b/Components/DrawingGalleryPopup.xaml.cs
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2025 CodeSoupCafe LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+using CommunityToolkit.Maui.Views;
+using LunaDraw.Logic.ViewModels;
+
+namespace LunaDraw.Components;
+
+public partial class DrawingGalleryPopup : Popup
+{
+ private readonly DrawingGalleryPopupViewModel viewModel;
+
+ public DrawingGalleryPopup(DrawingGalleryPopupViewModel viewModel)
+ {
+ this.viewModel = viewModel;
+ InitializeComponent();
+ BindingContext = viewModel;
+
+ viewModel.DrawingItems.CollectionChanged += (s, e) =>
+ {
+ GalleryView.ItemsSource = null;
+ GalleryView.ItemsSource = viewModel.DrawingItems;
+ };
+
+ viewModel.RequestClose += OnRequestClose;
+ }
+
+ private void OnDrawingItemTapped(object? sender, EventArgs e)
+ {
+ if (sender is Grid grid && grid.BindingContext is DrawingItemViewModel item)
+ {
+ viewModel.OpenDrawingCommand.Execute(item).Subscribe();
+ }
+ }
+
+ private void OnThumbnailImageLoaded(object? sender, EventArgs e)
+ {
+ }
+
+ private void OnThumbnailImagePropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ }
+
+ private async void OnRequestClose(object? sender, EventArgs e)
+ {
+ await this.CloseAsync();
+ }
+
+ protected override void OnHandlerChanged()
+ {
+ base.OnHandlerChanged();
+
+ // Unsubscribe when handler is removed to prevent memory leaks
+ if (Handler == null && BindingContext is DrawingGalleryPopupViewModel vm)
+ {
+ vm.RequestClose -= OnRequestClose;
+
+ if (vm is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
+ }
+ }
+}
diff --git a/Components/SettingsFlyoutPanel.xaml b/Components/SettingsFlyoutPanel.xaml
index 13767b2..ec6953f 100644
--- a/Components/SettingsFlyoutPanel.xaml
+++ b/Components/SettingsFlyoutPanel.xaml
@@ -3,10 +3,10 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:Maui.ColorPicker;assembly=Maui.ColorPicker"
x:Class="LunaDraw.Components.SettingsFlyoutPanel">
-