Commit Graph

3215 Commits

Author SHA1 Message Date
semantic-release-bot 41aa7ff33f chore(abacus-react): release v2.20.0 [skip ci]
# [2.20.0](https://github.com/antialias/soroban-abacus-flashcards/compare/abacus-react-v2.19.0...abacus-react-v2.20.0) (2026-01-02)

### Bug Fixes

* **vision:** clear config when switching camera sources ([ff59612](ff59612e7b))
* **vision:** hide flip camera button when only one camera available ([7a9185e](7a9185eadb))
* **vision:** include remote camera in isVisionSetupComplete check ([a8fb77e](a8fb77e8e3))
* **vision:** remote camera persistence and UI bugs ([d90d263](d90d263b2a))

### Features

* **vision:** add activeCameraSource tracking and simplify calibration UI ([1be6151](1be6151bae))
* **vision:** add CV-based bead detection and fix remote camera connection ([005140a](005140a1e7))
* **vision:** add TensorFlow.js column classifier model and improve detection ([5d0ac65](5d0ac65bdd))
* **vision:** broadcast vision frames to observers (Phase 5) ([b3b769c](b3b769c0e2))
* **vision:** disable auto-detection with feature flag ([a5025f0](a5025f01bc))
* **vision:** integrate vision feed into docked abacus ([d8c7645](d8c764595d))
2026-01-02 00:02:33 +00:00
Thomas Hallock 1be6151bae feat(vision): add activeCameraSource tracking and simplify calibration UI
- Add explicit activeCameraSource field to VisionConfig to track which
  camera is in use (local vs phone), fixing button visibility bugs when
  switching between camera sources
- Simplify calibration UI by removing the confusing "Auto/Manual" mode
  toggle, replacing with a cleaner crop status indicator
- Remove calibration requirement from isVisionSetupComplete for local
  camera since auto-crop runs continuously when markers are detected
- Update DockedVisionFeed to use activeCameraSource instead of inferring
  from which configs are set

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 18:01:12 -06:00
Thomas Hallock 70b363ce88 refactor(vision): combine setup modal into single draggable experience
- Merge VisionSetupModal and AbacusVisionBridge into unified UI
- Remove two-step configuration process (no more "Configure Camera" button)
- Add vision control props to AbacusVisionBridge:
  - showVisionControls, isVisionEnabled, isVisionSetupComplete
  - onToggleVision, onClearSettings callbacks
- Add Enable/Disable Vision and Clear Settings buttons to bridge footer
- Simplify VisionSetupModal from ~257 to ~93 lines
- Modal is now draggable via framer-motion (built into AbacusVisionBridge)

User experience: Open modal → immediately see camera feed and all controls
in one place. Drag modal anywhere. Configure, enable/disable, close.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 17:30:46 -06:00
Thomas Hallock d90d263b2a fix(vision): remote camera persistence and UI bugs
- Fix camera source switching: clear remoteCameraSessionId in context when
  switching to local camera so DockedVisionFeed uses the correct source
- Fix modal drag during calibration: disable framer-motion drag when
  calibration overlay is active to allow handle dragging
- Fix initial camera source: pass initialCameraSource prop to
  AbacusVisionBridge so it shows phone camera when reconfiguring remote
- Extend session TTL from 10 to 60 minutes for remote camera sessions
- Add localStorage persistence for remote camera session IDs
- Add auto-reconnect logic for both desktop and phone hooks
- Add comprehensive tests for session-manager, useRemoteCameraDesktop,
  and useRemoteCameraPhone hooks
- Guard test setup.ts for node environment (HTMLImageElement check)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 17:23:27 -06:00
Thomas Hallock 43524d8238 test: add unit tests for vision broadcast feature
- VisionIndicator.test.tsx: tests for rendering, status indicator, click behavior, accessibility
- ObserverVisionFeed.test.tsx: tests for image display, detected value, live/stale indicator
- useSessionBroadcast.vision.test.ts: tests for sendVisionFrame socket emission
- useSessionObserver.vision.test.ts: tests for visionFrame receiving and cleanup
- MyAbacusContext.vision.test.tsx: tests for vision config state and callbacks

Also fixes:
- useSessionObserver: clear visionFrame and transitionState on stopObserving
- test/setup.ts: add canvas Image mock to prevent jsdom errors with data URIs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 16:08:51 -06:00
Thomas Hallock a5025f01bc feat(vision): disable auto-detection with feature flag
- Add ENABLE_AUTO_DETECTION flag (set to false) in DockedVisionFeed
- Conditionally import detection modules for tree-shaking when disabled
- Guard all detection processing, loops, and value handlers
- Hide detection overlay when auto-detection is disabled
- Remove vision toggle button from ActiveSession (no longer needed)
- Clean up unused imports and code
- Format fixes from biome

The camera feed still works for observation mode, but the ML/CV
bead detection is disabled until accuracy is improved.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 15:55:50 -06:00
Thomas Hallock a8fb77e8e3 fix(vision): include remote camera in isVisionSetupComplete check
The isVisionSetupComplete flag was only checking for local camera
setup (cameraDeviceId + calibration), which caused remote camera
mode to be treated as "not configured" even when connected.

Now considers vision setup complete if either:
- Local camera: has camera device AND calibration
- Remote camera: has remote session ID (phone handles calibration)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 15:36:27 -06:00
Thomas Hallock e80ef04f45 chore(vision): clean up debug console.log statements
Remove unnecessary debug logging from vision components
that was used during development.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 15:31:06 -06:00
Thomas Hallock b3b769c0e2 feat(vision): broadcast vision frames to observers (Phase 5)
Wire up the vision broadcast pipeline:

1. DockedVisionFeed captures rectified frames from canvas and emits
   them at 5fps via the context's emitVisionFrame callback

2. PracticeClient wires setVisionFrameCallback to call sendVisionFrame
   from useSessionBroadcast, connecting the context to the socket

3. useSessionBroadcast sends VisionFrameEvent to the session channel
   with imageData, detectedValue, and confidence

4. socket-server relays vision-frame events to observers

5. useSessionObserver receives and stores visionFrame for display

6. SessionObserverModal shows ObserverVisionFeed when visionFrame
   is available, replacing the interactive AbacusDock with the
   student's live camera feed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 15:28:59 -06:00
Thomas Hallock ff59612e7b fix(vision): clear config when switching camera sources
When switching between local and phone camera, clear the other
source's configuration to prevent stale data.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 15:13:20 -06:00
Thomas Hallock d8c764595d feat(vision): integrate vision feed into docked abacus
- Add vision state management to MyAbacusContext (camera, calibration,
  remote session, enabled state)
- Add VisionIndicator component showing vision status on dock header
- Add VisionSetupModal for configuring camera and calibration
- Add DockedVisionFeed component that replaces SVG abacus when vision
  is enabled, with:
  - Continuous ArUco marker detection for auto-calibration
  - OpenCV perspective correction via VisionCameraFeed
  - Real-time bead detection and value display
  - Support for both local camera and remote phone camera
- Wire AbacusVisionBridge to save config to context via
  onConfigurationChange callback
- Update MyAbacus to conditionally render DockedVisionFeed vs
  AbacusReact based on vision state

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 15:05:58 -06:00
Thomas Hallock 005140a1e7 feat(vision): add CV-based bead detection and fix remote camera connection
- Add beadDetector.ts with intensity-profile-based bead detection (CV approach)
- Integrate CV pipeline for both local camera and remote phone camera feeds
- Add processImageFrame() to frameProcessor for remote camera image processing
- Fix React 18 Strict Mode duplicate session creation in RemoteCameraQRCode
- Add debug logging to remote camera hooks for connection troubleshooting
- Add VisionStatusIndicator for remote camera feed in AbacusVisionBridge

The duplicate session bug was caused by React 18 Strict Mode double-mounting
components and running effects twice with fresh state, which called
createSession() twice and created two different sessions - phone joined
one, desktop subscribed to the other.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 11:29:02 -06:00
Thomas Hallock 5d0ac65bdd feat(vision): add TensorFlow.js column classifier model and improve detection
- Add trained CNN model for abacus column digit classification
  - model.json: TensorFlow.js layers model (fixed for Keras 3 compatibility)
  - group1-shard1of1.bin: quantized model weights (~2.2MB)

- Improve detection performance and stability
  - Throttle inference to 5fps (was running every animation frame)
  - Lower stability threshold: 3 consecutive frames (was 10)
  - Lower confidence threshold: 50% (was 70%)

- Clean up debug logging from development

Note: Model trained on synthetic data, accuracy on real images is limited.
Future work: retrain on real abacus photos for better accuracy.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 22:59:40 -06:00
Thomas Hallock 7a9185eadb fix(vision): hide flip camera button when only one camera available
Only show camera controls section when there's something to display:
- Flip button: only if multiple cameras
- Torch button: only if torch available
- Whole section: only if either button would be shown

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 22:35:52 -06:00
semantic-release-bot da97ad0675 chore(abacus-react): release v2.19.0 [skip ci]
# [2.19.0](https://github.com/antialias/soroban-abacus-flashcards/compare/abacus-react-v2.18.0...abacus-react-v2.19.0) (2026-01-01)

### Features

* **vision:** add physical abacus column setting and fix remote flash toggle ([b206eb3](b206eb3071))
* **vision:** improve remote camera calibration and UX ([8846cec](8846cece93))
2026-01-01 04:35:16 +00:00
Thomas Hallock b206eb3071 feat(vision): add physical abacus column setting and fix remote flash toggle
Physical Abacus Columns Setting:
- Add physicalAbacusColumns to AbacusDisplayConfig (default: 4)
- Add database column with migration 0054
- Add slider UI in AbacusDisplayDropdown (range 1-21)
- Update AbacusVisionBridge to use setting instead of calculating from problem

Remote Camera Flash Toggle Fix:
- Add socket events for torch sync (set-torch, torch-state)
- Phone reports torch state to desktop on change/connection
- Desktop can control phone's torch remotely
- Add torch button in AbacusVisionBridge for phone camera mode
- Both local and remote flash toggles now work correctly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 22:33:58 -06:00
Thomas Hallock 8846cece93 feat(vision): improve remote camera calibration and UX
- Fix remote camera autocrop rotation by swapping ArUco corners for phone camera
  (detectMarkers assumes Desk View orientation which is 180° rotated)
- Add rotate left/right buttons to CalibrationOverlay for manual calibration
- Fix mode switching bug: switching to auto mode now clears desktop calibration
  on phone via new 'remote-camera:clear-calibration' socket event
- Add copy button to QR code URL with visual feedback
- Fix text selection spanning into video feed with userSelect: none
- Add flip camera and torch controls to local camera UI
- Add session persistence for remote camera reconnection
- Fix 4:3 aspect ratio for cropped abacus output

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 22:04:06 -06:00
semantic-release-bot 937223e318 chore(abacus-react): release v2.18.0 [skip ci]
# [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/abacus-react-v2.17.0...abacus-react-v2.18.0) (2026-01-01)

### Bug Fixes

* allow teacher-parents to enroll their children in other classrooms ([52df7f4](52df7f4697))
* **bkt:** handle missing helpLevelUsed in legacy data causing NaN ([b300ed9](b300ed9f5c))
* **camera:** handle race condition in camera initialization ([2a24700](2a24700e6c))
* **classroom:** auto-transition tutorial→session observation + fix NaN display ([962a52d](962a52d756))
* **classroom:** broadcast digit-by-digit answer and correct phase indicator ([fb73e85](fb73e85f2d))
* **dashboard:** compute skill stats from session results in curriculum API ([11d4846](11d48465d7))
* **db:** add missing is_paused column to session_plans ([9d8b5e1](9d8b5e1148))
* **db:** add missing journal entries for migrations 0041-0042 ([398603c](398603c75a))
* **docker:** add canvas native deps for jsdom/vitest ([5f51bc1](5f51bc1871))
* **docker:** override canvas with mock package for Alpine/musl ([8be1995](8be19958af))
* **docker:** skip canvas native build (optional jsdom dep) ([d717f44](d717f44fcc))
* **observer:** seed results panel with full session history ([aab7469](aab7469d9e))
* only show session stats when there are actual problems ([62aefad](62aefad676))
* **practice:** allow teachers to create student profiles ([5fee129](5fee1297e1))
* **practice:** always show add student FAB button ([a658414](a6584143eb))
* **practice:** real-time progress in observer modal + numeric answer comparison ([c0e63ff](c0e63ff68b))
* **practice:** show active sessions for teacher's own children ([ece3197](ece319738b))
* **practice:** use Next.js Link for student tiles + fix session observer z-index ([6def610](6def610877))
* **seed:** accurate BKT simulation for developing classifications ([d5e4c85](d5e4c858db))
* **share:** use getShareUrl for correct production URLs ([98a69f1](98a69f1f80))
* **vision:** fix manual calibration overlay not showing on remote camera ([44dcb01](44dcb01473))
* **vision:** fix remote camera calibration coordinate system ([e52f94e](e52f94e4b4))
* **vision:** swap corners diagonally for webcam orientation ([dd8efe3](dd8efe379d))

### Features

* API authorization audit + teacher enrollment UI + share codes ([d6e369f](d6e369f9dc))
* **camera:** auto-start camera when opening camera modal ([f3bb0ae](f3bb0aee4f))
* **camera:** fullscreen modal with edge-to-edge preview ([db17c96](db17c96168))
* **chart:** add grouped structure to chart hover tooltip ([594e22c](594e22c428))
* **chart:** improve skill classification visual hierarchy with colors and patterns ([c9518a6](c9518a6b99))
* **classroom:** add active sessions API endpoint ([07f6bb7](07f6bb7f9c))
* **classroom:** add real-time enrollment/unenrollment reactivity ([a0693e9](a0693e9084))
* **classroom:** add session broadcast and active session indicators ([9636f7f](9636f7f44a))
* **classroom:** add unified add-student modal with two-column layout ([dca696a](dca696a29f))
* **classroom:** add unified TeacherClassroomCard with auto-enrollment ([4d6adf3](4d6adf359e))
* **classroom:** complete reactivity fixes (Steps 7-11) ([2015494](2015494c0e))
* **classroom:** consolidate filter pill to single-row design ([78a63e3](78a63e35e3))
* **classroom:** implement enrollment system (Phase 4) ([1952a41](1952a412ed))
* **classroom:** implement entry prompts system ([de39ab5](de39ab52cc))
* **classroom:** implement real-time enrollment updates ([bbe0500](bbe0500fe9))
* **classroom:** implement real-time presence with WebSocket (Phase 6) ([629bfcf](629bfcfc03))
* **classroom:** implement real-time session observation (Step 3) ([2feb684](2feb6844a4))
* **classroom:** implement real-time skill tutorial observation ([4b73879](4b7387905d))
* **classroom:** implement teacher classroom dashboard (Phase 3) ([2202716](2202716f56))
* **classroom:** implement teacher-initiated pause and fix manual pause ([ccea0f8](ccea0f86ac))
* **classroom:** implement two-way abacus sync for session observation (Step 5) ([2f7002e](2f7002e575))
* **classroom:** improve enrollment reactivity and UX ([77336be](77336bea5b))
* **classroom:** integrate create student form into unified add-student modal ([da92289](da92289ed1))
* **classroom:** integrate Enter Classroom into StudentActionMenu ([2f1b9df](2f1b9df9d9))
* **dashboard:** add skill progress chart with trend analysis and timing awareness ([1fc8949](1fc8949b06))
* enable parents to observe children's practice sessions ([7b82995](7b82995664))
* **family:** implement parent-to-parent family code sharing (Phase 2) ([0284227](02842270c9))
* improve session summary header and add practice type badges ([518fe15](518fe153c9))
* **observer:** add live active session item to history list ([91d6d6a](91d6d6a1b6))
* **observer:** add live results panel and session progress indicator ([8527f89](8527f892e2))
* **observer:** implement shareable session observation links ([3ac7b46](3ac7b460ec))
* **practice:** add auto-rotation for captured documents ([ff79a28](ff79a28c65))
* **practice:** add document adjustment UI and auto-capture ([473b7db](473b7dbd7c))
* **practice:** add document scanning with multi-quad tracking ([5f4f1fd](5f4f1fde33))
* **practice:** add fixed filter bar, sticky headers, and shared EmojiPicker ([0e03561](0e0356113d))
* **practice:** add intervention system and improve skill chart hierarchy ([bf5b99a](bf5b99afe9))
* **practice:** add mini start practice banner to QuickLook modal ([d1176da](d1176da9aa))
* **practice:** add Needs Attention to unified compact layout ([8727782](8727782e45))
* **practice:** add photo attachments for practice sessions ([9b85311](9b853116ec))
* **practice:** add photo editing with rotation persistence and auto-detect ([156a0df](156a0dfe96))
* **practice:** add smooth fullscreen transition from QuickLook to dashboard ([cb8b0df](cb8b0dff67))
* **practice:** add student organization with filtering and archiving ([538718a](538718a814))
* **practice:** add StudentActionMenu to dashboard + fix z-index layering ([bf262e7](bf262e7d53))
* **practice:** compact single-student categories and UI improvements ([0e7f326](0e7f3265fe))
* **practice:** implement measurement-based compact layout ([1656b93](1656b9324f))
* **practice:** implement retry wrong problems system ([474c4da](474c4da05a))
* **practice:** parent session observation + relationship UI + error boundaries ([07484fd](07484fdfac))
* **practice:** polish unified student list with keyboard nav and mobile UX ([0ba1551](0ba1551fea))
* **seed:** add category field to all mock student profiles ([f883fbf](f883fbfe23))
* **session-summary:** redesign ProblemToReview with BKT integration and animations ([430c46a](430c46adb9))
* **storybook:** add TeacherClassroomCard stories ([a5e5788](a5e5788fa9))
* **vision:** add AbacusVisionBridge for physical soroban detection ([47088e4](47088e4850))
* **vision:** add ArUco marker auto-calibration for abacus detection ([9e9a06f](9e9a06f2e4))
* **vision:** add remote phone camera support for abacus detection ([8e4975d](8e4975d395))

### Performance Improvements

* reduce practice page dev bundle from 47MB to 115KB ([fd1df93](fd1df93a8f))
2026-01-01 02:42:45 +00:00
Thomas Hallock e52f94e4b4 fix(vision): fix remote camera calibration coordinate system
- Fix hook dependency issues in AbacusVisionBridge by using destructured
  stable function references instead of remoteCamera object
- Add proper container dimension tracking for remote camera calibration
  overlay to fix coordinate mismatch
- Add rotate180 option to perspectiveTransform to support both Desk View
  (camera pointing down, needs 180° rotation) and phone cameras (no rotation)
- Phone camera cropping now uses direct mapping without rotation

The main issues fixed:
1. Hook dependencies were causing effects to run repeatedly when navigating
   to remote camera URL in a different window
2. CalibrationOverlay was using hardcoded fallback dimensions instead of
   actual container dimensions for remote camera
3. Perspective transform was applying 180° rotation which is wrong for
   phone cameras held at normal angles

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 20:41:25 -06:00
Thomas Hallock 957ff71cf1 chore: polish vision and practice components
- CalibrationOverlay: formatting fixes
- useDeskViewCamera: add SSR guard and focusMode setting
- ActiveSession: integrate vision mode changes
- DocumentAdjuster/PhotoViewerEditor: component updates
- SummaryClient: updates for practice summary
- Drizzle meta: formatting consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 19:07:49 -06:00
Thomas Hallock 44dcb01473 fix(vision): fix manual calibration overlay not showing on remote camera
- Re-run container dimension effect when isCalibrating changes
- Add height checks in addition to width checks for overlay visibility
- Add delayed dimension update to handle layout settling

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 19:01:42 -06:00
Thomas Hallock 8e4975d395 feat(vision): add remote phone camera support for abacus detection
Enable using a phone as a remote camera source for abacus vision detection.
The phone handles calibration and perspective correction locally, then
streams cropped frames to the desktop via Socket.IO.

Features:
- QR code generation for easy phone connection
- Auto-calibration with ArUco markers on phone
- Manual calibration option
- Proper OpenCV perspective transform (not bounding box crop)
- Real-time frame streaming with frame rate display
- LAN address support via NEXT_PUBLIC_LAN_HOST env var

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 18:59:24 -06:00
Thomas Hallock 9e9a06f2e4 feat(vision): add ArUco marker auto-calibration for abacus detection
- Add js-aruco2 library for pure JavaScript marker detection
- Create /create/vision-markers page for downloading printable marker PDFs
- Add auto/manual calibration mode toggle to AbacusVisionBridge
- Implement automatic quad detection from 4 corner markers (IDs 0-3)
- Fix 180° rotation for Desk View camera orientation
- Add loading state for marker library initialization

🤖 Generated with [Claude Code](https://claude.ai/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-31 18:16:48 -06:00
Thomas Hallock dd8efe379d fix(vision): swap corners diagonally for webcam orientation
Webcam images are oriented such that screen positions need to be
swapped diagonally to get the correct physical orientation in the
rectified output.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 17:25:19 -06:00
Thomas Hallock 47088e4850 feat(vision): add AbacusVisionBridge for physical soroban detection
Implements camera-based detection system for physical abacus:
- VisionCameraFeed: Camera display with Desk View auto-detection
- CalibrationOverlay: Interactive quad corner calibration with proper
  letterbox handling and bounds clamping
- Perspective transform using OpenCV.js for rectification
- Live rectified preview during calibration
- Hooks: useAbacusVision, useDeskViewCamera, useCameraCalibration,
  useFrameStability

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 17:20:19 -06:00
Thomas Hallock 156a0dfe96 feat(practice): add photo editing with rotation persistence and auto-detect
- Add PhotoViewerEditor component for unified viewing/editing of session photos
- Persist rotation (0/90/180/270) in database when saving edited photos
- Always show cropping UI even when no quad detected (with fallback corners)
- Add "Auto-detect edges" button to re-run document detection
- Button shows detection status: "Edges detected" / "Auto-detect edges" / "No edges found"
- Fix: clicking Done/Back in edit mode closes viewer when opened via pencil icon
- Fix: pressing Escape in edit mode closes viewer when opened via pencil icon
- Show loading screen instead of view mode flash when opening editor
- Add original file preservation for re-editing photos
- Add corners column to practice_attachments for crop coordinates
- Add rotation column to practice_attachments schema

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 13:34:19 -06:00
Thomas Hallock 8be19958af fix(docker): override canvas with mock package for Alpine/musl
canvas is an optional peer dep of jsdom that fails to compile on Alpine
due to missing <cstdint> includes in canvas 3.x. Override it with
canvas-mock which provides a stub implementation with no native deps.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 12:39:30 -06:00
Thomas Hallock d717f44fcc fix(docker): skip canvas native build (optional jsdom dep)
canvas is an optional dependency of jsdom used for testing. It fails to
compile on Alpine/musl due to missing <cstdint> includes in canvas 3.x.
Since canvas is not required for production or even for tests, configure
pnpm to skip its native build.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 12:28:01 -06:00
Thomas Hallock 5f51bc1871 fix(docker): add canvas native deps for jsdom/vitest
canvas is an optional dependency of jsdom (used by vitest) and requires
native libraries (cairo, pango, pixman) to build on Alpine. Add these
dependencies to the base stage.

Also fix legacy ENV format warnings.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 12:23:32 -06:00
Thomas Hallock 473b7dbd7c feat(practice): add document adjustment UI and auto-capture
Adds an interstitial adjustment UI between camera capture and session
report, allowing users to fine-tune document cropping and rotation.

New features:
- DocumentAdjuster component with draggable corner handles
- Live preview of cropped/rotated result
- 90° rotation controls (clockwise/counter-clockwise)
- Auto-capture: automatically enters adjustment mode when detection
  is locked (stable for 5+ frames with >50% stability)

New hook exports:
- cv: OpenCV reference for external use
- getBestQuadCorners(): get current detected quad corners
- captureSourceFrame(): capture video frame as canvas

Flow: Camera → Auto-capture on lock → Adjust corners/rotation → Confirm → Upload

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-31 12:09:35 -06:00
Thomas Hallock ff79a28c65 feat(practice): add auto-rotation for captured documents
Analyzes cropped document orientation and auto-rotates:
- Detects text line direction via horizontal vs vertical edge analysis
- Rotates 90° if text appears sideways (vertical edges dominate)
- Checks content density top vs bottom to detect upside-down
- Rotates 180° if content is heavier at bottom (likely inverted)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-31 12:01:41 -06:00
Thomas Hallock 5f4f1fde33 feat(practice): add document scanning with multi-quad tracking
Adds real-time document detection to the camera capture on session
summary pages. Uses OpenCV.js for edge detection and perspective
correction.

Key features:
- Multi-quad tracking: detects ALL quadrilaterals, not just largest
- Scores quads by stability over time (filters transient detections)
- Visual feedback: yellow (detecting) → green (stable) → bright green (locked)
- Auto-crops and deskews captured documents
- Falls back to raw photo if no document detected

Technical details:
- OpenCV.js (~8MB) lazy-loaded only when camera opens
- Tracks quads across frames by matching corner positions
- Filters by area (15-95% of frame) and document aspect ratios
- Locks on after 5 frames with 50%+ stability

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-31 11:57:50 -06:00
Thomas Hallock 9124a3182e refactor(summary): restructure session report with two-column desktop layout
Major refactoring of the session summary page to match the redesign plan:

Layout changes:
- Desktop (≥1200px): Two-column layout with Hero+Evidence left, Skills+Review right
- Extract individual components from monolithic SessionSummary
- Add ScrollspyNav fixed at bottom for mobile/tablet navigation
- Add scrollspy section attributes for navigation tracking

New components extracted:
- SessionHero: Celebration header, stats, practice type badges, trend indicator
- SkillsPanel: Skills breakdown by category with human-readable names
- ProblemsToReviewPanel: Auto-pause timing + problems needing attention
- TrendIndicator: Comparison to previous session accuracy
- ScrollspyNav: Fixed bottom navigation with dot indicator
- OfflineWorkSection: Photo upload zone with lightbox
- PhotoLightbox: Full-screen photo viewer with navigation

Utilities added:
- skillDisplay.ts: Human-readable skill name resolution
- trends.ts: Session trend calculations

Known issues documented for follow-up:
- Mobile section order needs adjustment (Evidence before Skills)
- Tablet layout not yet implemented (Skills+Review side-by-side)
- ScrollspyNav visibility breakpoint needs tuning

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 10:52:29 -06:00
Thomas Hallock 2d3b319e57 refactor: remove redundant Practice Again buttons from summary page
The sticky practice banner in the sub-nav already provides the "Start Practice"
action, making the bottom buttons redundant:
- Remove Practice Again button from SessionSummary
- Remove Start Practice button for photo-only sessions
- Remove Start Practice button for no-session empty state
- Remove unused onPracticeAgain prop from SessionSummary interface

Also simplifies header logic since SessionSummary handles its own header
for sessions with problems.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 06:32:10 -06:00
Thomas Hallock 518fe153c9 feat: improve session summary header and add practice type badges
- Show celebration header only when just completed a session (?completed=1)
- Show session date as header when viewing historical sessions
- Add practice type badges (e.g., "Use Abacus", "Visualize") to main stats
- Pass justCompleted flag through page -> client -> SessionSummary

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 06:13:49 -06:00
Thomas Hallock 6d36da47b3 refactor: centralize practice types in constants file
Create single source of truth for practice types in src/constants/practiceTypes.ts
with IDs, labels, descriptions, and icons. This makes it easy to add new
practice types app-wide.

Updated:
- SessionPartType now aliases PracticeTypeId from constants
- OfflineSessionModal uses PRACTICE_TYPES from constants
- offline-sessions API uses isValidPracticeTypeId for validation
- practiceTheme uses PracticeTypeId with default fallback for new types

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 04:21:34 -06:00
Thomas Hallock 62aefad676 fix: only show session stats when there are actual problems
Photo-only sessions (offline practice with just photo uploads) now show
only the photos section with upload controls, not misleading 0% accuracy
and empty stats. Sessions with problems still show full stats.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 21:33:22 -06:00
Thomas Hallock ce2cb331e8 refactor: inline photo upload on session summary, remove modal
- Remove SessionPhotoGallery modal in favor of inline upload
- Add drag-and-drop support to Practice Photos section
- Upload photos immediately on file selection or camera capture
- Fullscreen camera modal using Radix Dialog
- Photos appear in grid directly on session summary page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 21:30:59 -06:00
Thomas Hallock 2a24700e6c fix(camera): handle race condition in camera initialization
Add cancelled flag to prevent setting state after unmount and properly
cleanup streams that were acquired after component unmounted (e.g. in
React Strict Mode double-mounting).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 21:20:39 -06:00
Thomas Hallock db17c96168 feat(camera): fullscreen modal with edge-to-edge preview
- Use Radix Dialog for proper modal management
- Camera preview fills entire viewport
- Floating close button (top-right) and capture button (bottom-center)
- iOS-style capture button design
- Works fullscreen on both mobile and desktop

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 21:17:06 -06:00
Thomas Hallock 310672ceb9 refactor(camera): simplify - camera always starts on mount
Removed the concept of an "unstarted" camera state. When the camera
modal opens, the camera starts immediately. Just show "Starting..."
while initializing, then the capture button once ready.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 21:13:12 -06:00
Thomas Hallock f3bb0aee4f feat(camera): auto-start camera when opening camera modal
Camera now starts immediately when clicking the Camera button,
eliminating the extra "Start Camera" click.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 21:10:29 -06:00
Thomas Hallock 9b853116ec feat(practice): add photo attachments for practice sessions
Adds ability to upload, view, and manage photos for practice sessions:

- New database schema for practice attachments with file metadata
- Photo upload zone component with drag & drop and camera capture
- Offline session modal for logging practice done away from the app
- Session photo gallery with lightbox viewer and upload capability
- Photo indicators on session list cards
- Inline photo thumbnails on session summary page
- APIs for creating offline sessions, uploading photos, and serving files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 20:33:18 -06:00
Thomas Hallock 1656b9324f feat(practice): implement measurement-based compact layout
Replace count-based heuristic with actual width measurements to determine
which single-student sections flow together on the same row.

Key changes:
- Add useMeasuredCompactLayout hook that uses useLayoutEffect to measure
  before paint (no flash of wrong layout)
- Hidden measurement container measures actual item widths
- ResizeObserver triggers re-measurement on container resize
- Group items by measured fit into rows
- Storybook stories demonstrate all layout scenarios including
  interactive resize, various widths, and mixed compact/full sections

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 12:39:37 -06:00
Thomas Hallock 8727782e45 feat(practice): add Needs Attention to unified compact layout
- Integrate "Needs Attention" section into unified compacting system
- Single attention student flows with other compact sections
- Multiple attention students get full sticky header
- Add comprehensive Storybook stories for Needs Attention scenarios:
  - NeedsAttentionSingle, NeedsAttentionMultiple
  - NeedsAttentionWithCompactBuckets, NeedsAttentionFullThenCompact
  - NeedsAttentionRealistic, NeedsAttentionDarkMode
- Update GroupedStudentsDemo to use unified renderItems array

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 12:11:44 -06:00
Thomas Hallock 0e7f3265fe feat(practice): compact single-student categories and UI improvements
- Add compact display for single-student categories so they flow
  together on the same row instead of each taking a full row
- Hide "Present" (in-classroom) filter segment when no students present
- Update in-classroom empty state with bulk-prompt instructions
- Fix classroomId ReferenceError in ViewEmptyState component
- Add Storybook stories for GroupedCategories demonstrating
  compact/full rendering patterns
- Add compact mode to StudentSelector component

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 11:59:32 -06:00
Thomas Hallock 78a63e35e3 feat(classroom): consolidate filter pill to single-row design
- Move classroom name to first segment of compound chip (replaces "Enrolled" label)
- Move add student "+" button to prefix position on the chip
- Integrate settings gear into classroom name segment
- Remove two-row card layout, now matches other filter pill styling
- Add settingsTrigger prop to TeacherCompoundChip for popover integration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 10:04:51 -06:00
Thomas Hallock da92289ed1 feat(classroom): integrate create student form into unified add-student modal
- Integrate create student form directly into AddStudentToClassroomModal
  instead of opening a separate modal
- Left column shows choose mode (create button + family code) or create form
- Clicking "Cancel" in create mode returns to choose view
- User stays in one modal throughout the entire flow
- Hide "Link existing child" option in AddStudentModal when in classroom mode
  (this functionality is now in the unified modal)
- Remove unused onCreateStudent prop from PracticeClient

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 09:42:45 -06:00
Thomas Hallock dca696a29f feat(classroom): add unified add-student modal with two-column layout
Consolidates three add-student flows into a single discoverable modal:
- Left column "Add Now": Create student button + family code input
- Right column "Invite Parents": QR code + copy code/link buttons

Also refactors TeacherClassroomCard:
- Moves classroom name from header to first filter segment
- Removes ShareCodePanel from header (now in unified modal)
- Adds classroomName prop to TeacherCompoundChip for label override

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 09:03:26 -06:00
Thomas Hallock a5e5788fa9 feat(storybook): add TeacherClassroomCard stories
Add Storybook stories for the TeacherClassroomCard component with
14 variants covering:
- Basic usage (enrolled, in-classroom, active views)
- View count variations (empty, large, none present)
- Custom settings (expiry time, long names)
- Dark theme variants
- Responsive width testing

Also exports TeacherClassroomCard and TeacherClassroomCardProps
from StudentFilterBar.tsx for use in stories.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 08:05:45 -06:00