# PROGRESS.md — eng-pad Implementation Progress This file tracks completed work and decisions. Updated after every step. See PROJECT_PLAN.md for the full step list. ## Completed ### Phase 0: Project Documents (2026-03-24) - [x] 0.1: Created DESIGN.md — full technical design covering architecture, data model, coordinate system, rendering, input handling, and source tree. - [x] 0.2: Created PROJECT_PLAN.md — discrete steps grouped by phase with checkboxes and file references. - [x] 0.3: Created PROGRESS.md — this file. - [x] 0.4: Updated CLAUDE.md — build commands, source tree, project doc pointers. ### Phase 1: Project Skeleton + Data Layer (2026-03-24) - [x] 1.1: Generated Android project — Gradle 8.14.2, AGP 8.10.1, Kotlin 2.1.20 - [x] 1.2: Version catalog with Compose BOM 2026.03.00, Room 2.8.4, Navigation 2.9.7, Lifecycle 2.10.0 - [x] 1.3: Lint configured — warningsAsErrors, AGP version check suppressed (AGP 9.x needs Gradle 9.x) - [x] 1.4: Room entities: Notebook, Page, Stroke, PageSize enum - [x] 1.5: Converters: FloatArray ↔ ByteArray (packed little-endian) - [x] 1.6: DAOs: NotebookDao, PageDao, StrokeDao - [x] 1.7: EngPadDatabase (Room, version 1) - [x] 1.8: NotebookRepository, PageRepository - [x] 1.9: Unit tests: StrokeBlobTest (6 tests), PageSizeTest (4 tests) — all pass - Foojay resolver added for automatic JDK toolchain download - compileSdk/targetSdk bumped to 36 (required by latest androidx dependencies) ### Phase 2: Notebook List Screen (2026-03-24) - [x] 2.1: MainActivity with Compose NavHost (three routes: notebooks, pages, editor) - [x] 2.2: NotebookListViewModel with StateFlow, create/delete operations - [x] 2.3: NotebookListScreen — lazy list, create dialog (title + page size radio), long-press delete with confirmation, empty state - [x] 2.4: Auto-create page 1 in NotebookRepository.create() - [x] 2.5: Navigation wired — tap notebook → pages stub, editor stub - EngPadTheme: high-contrast light color scheme (black on white for e-ink) ### Phase 3: Canvas — Basic Drawing (2026-03-24) - [x] 3.1–3.4: PadCanvasView — stylus input with historical points, Path/Paint stroke rendering, direct path drawing (no backing bitmap), 60pt grid drawn in screen space with pixel-snapped positions, Matrix coordinate transform (canonical ↔ screen) - [x] 3.5: CanvasState — tool enum (PEN_FINE, PEN_MEDIUM, ERASER, SELECT, BOX), zoom/pan state, pen width constants (4.49pt, 5.91pt) - [x] 3.6: EditorViewModel — loads strokes from Room, saves on completion - [x] 3.7: EditorScreen + Toolbar — Compose wrapper with AndroidView, FilterChip toolbar for pen size and eraser selection - [x] 3.8: NavGraph updated — pages route auto-navigates to first page's editor, page size passed through route params - Used KTX Canvas extensions (withMatrix, withScale, createBitmap) per lint - ClickableViewAccessibility suppressed on PadCanvasView (drawing view) ### Phase 4: Zoom and Pan (2026-03-24) - [x] 4.1: ScaleGestureDetector with focal-point zoom (dynamic min–4×) - [x] 4.2: Finger drag for pan with multi-pointer tracking, pan clamped so page always fills viewport - [x] 4.3: Input routing by tool type already in place from Phase 3 - Zoom/pan state managed locally in PadCanvasView for responsiveness, synced to ViewModel on gesture end - Minimum zoom computed dynamically from page/view dimensions ### Phase 5: Eraser (2026-03-24) - [x] 5.1–5.3: Stroke-level eraser with bounding box hit test (42pt radius), processes historical touch points for thorough erasing, deletes from view + Room DB ### Phase 6: Undo/Redo (2026-03-24) - [x] 6.1: UndoableAction interface, AddStrokeAction, DeleteStrokeAction - [x] 6.2: UndoManager with undo/redo stacks (depth 50), StateFlow for canUndo/canRedo - [x] 6.3: EditorViewModel rewired — stroke add/delete go through UndoManager, visual callbacks sync canvas view without full reload - [x] 6.4: 9 unit tests for UndoManager (perform, undo, redo, depth limit, clear, no-ops) - Toolbar now has undo/redo buttons with enabled state ### Phase 7: Selection — Move, Copy, Delete (2026-03-24) - [x] 7.1–7.2: Rectangle selection with dashed rect and blue highlight - [x] 7.3: Delete, drag-to-move, copy operations with toolbar buttons - [x] 7.4: Full undo integration — DeleteMultipleStrokesAction, MoveStrokesAction, CopyStrokesAction - Drag existing selection to move, tap outside to deselect ### Phase 8: Multi-Page Navigation (2026-03-24) - [x] 8.1: PageListScreen — adaptive grid of page cards with correct aspect ratio - [x] 8.2: Add new page via FAB - [x] 8.3: NavGraph refactored — pages route loads notebook metadata then shows PageListScreen, tap page navigates to editor with page size ### Phase 9: PDF Export (2026-03-24) - [x] 9.1: PdfExporter — creates PdfDocument, scales canonical 300 DPI coords to 72 DPI PDF points (×0.24), renders strokes without grid - [x] 9.2: Share via Intent.ACTION_SEND + FileProvider (configured in Phase 1) - [x] 9.3: PDF button in editor toolbar, exports current page ### Phase 10: Polish (2026-03-24) - [x] 10.1: High-contrast e-ink theme — expanded color scheme with proper container, variant, and outline colors for readability - [x] 10.2: Auto-save already in place since Phase 3 (strokes save on completion) - [x] 10.3: Empty states and delete confirmations already in place from Phase 2 - [ ] 10.4: Performance profiling — requires on-device testing ### On-Device Testing — Daylight DC-1 (2026-03-24) Tested on DC-1, identified and fixed rendering issues: - **Grid**: original grid drawn in canonical space had uneven spacing due to sub-pixel positioning when scaled. Fixed by drawing grid in screen space with pixel-snapped line positions. Now uniform squares. - **Stroke quality**: original backing bitmap at 1/4 resolution caused blurry strokes. Fixed by removing backing bitmap entirely and drawing paths directly. Anti-aliasing disabled on all paint for crisp e-ink lines. - **Viewport**: dark gray background was visible around page edges. Fixed by clamping pan to page edges, computing dynamic min zoom, and white background. - **Box tool**: works correctly on device. - **PDF export**: correctly elides grid — confirmed on device. - **Line snap**: increased threshold to 60pt (~5mm) and switched to max-distance tracking to handle EMR stylus hand tremor. ### Code Audit & Bug Fixes (2026-03-24) Critical/high-priority fixes applied: - **MoveStrokesAction.undo()**: was offsetting by (0,0) instead of restoring original positions. Fixed to write back the pre-move point data. - **Stroke commit flicker**: backing bitmap was fully redrawn on each new stroke. Fixed with incremental rendering — new strokes draw directly onto the existing bitmap without clearing. - **Selection state leak**: view-side selection wasn't cleared on page navigation. Added `onPageNavigated` callback. - **Initial page fallback**: if `initialPageId` isn't found in DB, fall back to first page instead of showing blank canvas. - **Arrow head Paint allocation**: preallocated reusable Paint object instead of allocating per drawArrowHeads() call. Known issues still to address: - CopyStrokesAction uses `takeLast(n)` to find new stroke IDs — fragile - No mutex guarding concurrent page navigation + paste operations - Grid redrawn every frame (could be cached) ## Backup Design (Not Yet Implemented) Notebook backup as a zip file: - Export: `notebook_title.engpad.zip` containing: - `notebook.json` — notebook metadata (title, page_size, page count) - `pages/` directory with one JSON per page: - `pages/001.json` — page metadata + strokes array - Each stroke: `{penSize, color, style, points: [x0,y0,x1,y1,...]}` - Points stored as JSON float arrays (portable, human-readable) - Import: parse zip, create notebook + pages + strokes in DB - Could be shared via Android share intents like PDF/JPG export - Consider: export all notebooks as a single zip for full backup, or individual notebook export for sharing Alternative: SQLite `VACUUM INTO` for raw DB backup (simpler but not portable or human-readable). ## Status All implementation phases complete. Remaining work: - Test on Supernote Manta - Verify line snap behavior on device - Performance profiling with heavily annotated pages - Implement notebook backup/restore ## Decisions & Deviations - **Language**: Kotlin (chosen over Java for better Compose/coroutine support). - **Storage**: Room/SQLite with packed float BLOBs for stroke points. - **Palm rejection**: Hardware only (EMR digitizer handles it). - **Pressure sensitivity**: None — four pen sizes (0.38, 0.5, 0.6, 0.7mm). - **Coordinate system**: 300 DPI canonical points (scaled to 72 DPI for PDF export). - **UI framework**: Compose for chrome, custom View for canvas (Compose Canvas lacks MotionEvent access needed for stylus input).