Commit Graph

3492 Commits

Author SHA1 Message Date
Thomas Hallock f8861e6f8d fix(scripts): import classroom functions directly to fix build
The barrel export from @/lib/classroom pulls in all manager modules,
some of which have transitive dependencies on React components with
styled-system imports. Import directly from the specific manager files
(classroom-manager.ts and enrollment-manager.ts) to avoid the
dependency chain that breaks esbuild bundling for the seed script.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 11:38:21 -06:00
Thomas Hallock c587caaaae fix(scoreboard): use student's enrolled classroom for leaderboard
The scoreboard was incorrectly using the teacher's classroom instead of
the student's enrolled classroom. Now uses useEnrolledClassrooms hook
to get the student's classroom membership for the leaderboard display.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 11:34:12 -06:00
Thomas Hallock e419725037 feat(seeding): add classroom enrollment for test students
- Create classroom for current user if they don't have one
- Enroll all seeded test students in the teacher's classroom
- Enables testing of classroom leaderboard features

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 11:27:30 -06:00
Thomas Hallock d258c267f5 fix(scoreboard): handle ISO string dates in formatRelativeTime
Dates serialized through JSON API come back as ISO strings, not Date objects.
Updated formatRelativeTime to handle string | number | Date inputs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 11:16:13 -06:00
Thomas Hallock 71efc2c096 chore: update local Claude settings permissions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:50:18 -06:00
Thomas Hallock 12c95c92fa data(vision): add boundary frame training data
Captured frames for boundary detector training

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:49:58 -06:00
Thomas Hallock 9fa95eca89 chore: update tsconfig, plans, lockfile, and templates
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:49:34 -06:00
Thomas Hallock ba7cdb7044 feat(core): update schema, hooks, and socket server
- Update scanner settings schema
- Update remote camera hooks
- Update socket server and vision session API

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:49:24 -06:00
Thomas Hallock 534192f60f chore(models): update boundary detector and column classifier models
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:49:19 -06:00
Thomas Hallock 4f812d4ad0 feat(arcade): update matching game and manifest schema
Updates to game break configuration and manifest validation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:49:13 -06:00
Thomas Hallock b197d8f035 feat(practice): update practice session components and APIs
Updates to session plans, attempt history, and game break configuration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:49:08 -06:00
Thomas Hallock f45dc8b7f3 feat(vision): update vision training components and APIs
Updates to boundary detection, sync status, and data panel components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:49:03 -06:00
Thomas Hallock 7e3932ae5f feat(queries): add game results query keys for scoreboard
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:48:57 -06:00
Thomas Hallock c182756b80 feat(seeding): add game results generation to test student profiles
- Add gameHistory configs to all 21 test student profiles
- Implement generateGameResults function to create scoreboard data
- Each profile has appropriate game history matching their characteristics:
  - Struggling students get low scores (35-45)
  - Developing students get medium scores (55-75)
  - Strong students get high scores (78-95)
- Add accuracyMultiplier to TuningAdjustment interface (fixes TS error)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:48:51 -06:00
Thomas Hallock dda041e014 fix(scoreboard): use valid gray.800 token instead of gray.850
gray.850 is not a valid Panda CSS token, causing the value to be
output as a literal string instead of resolving to a hex color.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:48:44 -06:00
Thomas Hallock 17b1749342 feat(dashboard): add scoreboard tab with game results tracking
Add complete game results tracking and scoreboard system:

- Database schema: Add game_results table for storing game outcomes
- API endpoints: Add routes for saving results, player history, and
  classroom leaderboards
- React Query hooks: Add usePlayerGameHistory, usePlayerClassroomRank,
  useSaveGameResult
- ScoreboardTab: New dashboard tab showing personal bests, recent games,
  and classroom rankings
- GameBreakResultsScreen: Interstitial showing results after game breaks
- Integration: Save game results when matching game completes, display
  results before returning to practice

Includes Storybook stories and unit tests for both components.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 10:26:49 -06:00
Thomas Hallock 706dbce6f5 feat(practice): add attempt history panel with result editing
Add AttemptHistoryPanel component for browse mode that displays all
attempts (original + retries) for each problem slot. Teachers/parents
can now:

- Mark incorrect attempts as correct (for typo fixes)
- Exclude attempts from progress tracking
- Re-include previously excluded attempts

Technical changes:
- New ResultWithGlobalIndex type for tracking result indices
- Extended SlotResultSource with 'teacher-corrected' and 'teacher-excluded'
- New PATCH API endpoint for editing individual results
- updateSessionPlanResults() function in session-planner
- Query invalidation on result edits

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 07:19:04 -06:00
Thomas Hallock 94be27bc06 fix(practice): use findMatchedPrefixIndex in handleSubmit for docked abacus
When abacus is docked, handleSubmit was using indexOf() to check if the
user's answer matched a prefix sum. This failed when an intermediate
prefix sum equaled the final answer (e.g., 65-34-15+33-18=31 where
prefixSums=[65,31,16,49,31]). indexOf(31) returns 1, triggering help
mode instead of accepting the correct answer.

Now uses findMatchedPrefixIndex() which correctly checks if the answer
equals the final answer FIRST before looking for intermediate matches.

Adds regression tests for this edge case.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 20:44:12 -06:00
Thomas Hallock b4b1090281 feat(practice): add game break preconfiguration UI (Phases 1-3)
Phase 1 - Foundation:
- Extended PracticeBreakConfig type with suggestedConfig, lockedFields, difficultyPresets
- Added PracticeBreakOptions and PracticeBreakGameConfig types
- Extended GameBreakSettings with gameConfig field

Phase 2 - Game Integration:
- Added practiceBreakConfig to matching, memory-quiz, card-sorting manifests
- Implemented getInitialStateForPracticeBreak in game validators
- Set practiceBreakReady: false until full integration complete

Phase 3 - Teacher Configuration UI:
- Added GameBreakDifficultyPresets component (Easy/Medium/Hard presets)
- Added GameBreakCustomConfig component (per-field customization)
- Extended StartPracticeModalContext with game config state
- Integrated presets and customize into GameBreakSettings

Test Coverage:
- 23 context tests for game config state
- 8 component tests for GameBreakDifficultyPresets
- 12 component tests for GameBreakCustomConfig
- 7 Storybook stories for manual testing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:06:39 -06:00
Thomas Hallock 6eddd75536 fix(vision): add JPG support to boundary detector sync
The boundary detector saves images as either PNG or JPG depending on the
camera source format, but the sync system only looked for PNG files. This
caused 3000+ JPG boundary frames on production to be invisible to sync.

Changes:
- SSH find pattern now includes *.png and *.jpg for boundary-detector
- countLocalData counts both PNG and JPG for boundary-detector
- listLocalFiles lists both extensions for boundary-detector
- rsync progress regex matches both extensions

Column classifier remains PNG-only as intended.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:50:51 -06:00
Thomas Hallock a8df139a00 fix(arcade): add missing practiceBreakReady field to game manifests
Add practiceBreakReady: false to all game manifests that were missing it.
This field is required by the GameManifest type after it was added for
practice session game break support.

Affected games:
- card-sorting
- complement-race
- know-your-world
- memory-quiz
- rithmomachia
- yjs-demo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:39:37 -06:00
Thomas Hallock ed93c36f52 feat(matching): add responsive compact layout for practice game breaks
- Add GameLayoutContext to control StandardGameLayout behavior
- Container mode uses 100% height (no viewport sizing) for practice breaks
- SetupPhase detects compact mode and hides verbose descriptions/hints
- Add data attributes to all SetupPhase elements for debugging
- Add Storybook stories for matching game in practice context

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:21:43 -06:00
Thomas Hallock 32d23d026d feat(practice): improve game break duration selector as time continuum
- Add fill indicator track that animates to selected position
- Use dramatic typography progression (font-size and weight increase)
- Position track at bottom as baseline, not through text
- Export GameInfo type from context for consistent typing
- Combine label and toggle into single clickable element
- Add gaming-inspired gradient backgrounds and glow effects

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 16:27:05 -06:00
Thomas Hallock 54ca5e4f02 feat(practice): add test coverage and storybook for game break UI modes
- Add unit tests for single-game scenario (hasSingleGame, singleGame, auto-selection)
- Add GameBreakSettings tests for single-game UI mode
- Add initialExpanded and practiceApprovedGamesOverride props for Storybook
- Update stories to properly demonstrate both single-game and multi-game UI
- Increase expanded panel maxHeight from 520px to 620px for multi-game UI

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 16:04:14 -06:00
Thomas Hallock a12df6b332 feat(arcade): add shortName field to game manifest
Replace hacky .replace(' Battle', '').replace(' Lightning', '') calls
with a proper shortName field that games can define for compact UI spaces.

- Add optional shortName to GameManifestSchema
- Update matching game with shortName: 'Matching Pairs'
- Use shortName || displayName in GameBreakSettings

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:49:04 -06:00
Thomas Hallock b58a0eeca9 feat(practice): add game break indicators and simplify single-game UI
Game Break Progress Indicators:
- Add game break countdown badge to practice nav (shows "3 until 🎮")
- Display specific game icon when a game is selected (not random)
- Show game icon and name in game break HUD header

Practice-Approved Games:
- Add practiceBreakReady flag to game manifest schema
- Games must opt-in via manifest AND be whitelisted
- Currently only Matching Pairs is practice-break ready

StartPracticeModal Refactoring:
- Extract state management to StartPracticeModalContext
- Create sub-components: DurationSelector, PracticeModesSelector,
  GameBreakSettings, MaxTermsSelector, TutorialCTA, RemediationCTA,
  StartButton, ErrorDisplay, SessionConfigSummary
- Add comprehensive unit tests for context and sub-components

Single-Game Mode UI:
- Simplified GameBreakSettings when only one game available
- Shows game icon + name inline with compact duration selector
- Removes unnecessary selection mode toggle and dropdown
- Adds "More games coming soon!" teaser

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:46:00 -06:00
Thomas Hallock 93a25c1e7b feat(vision): enhance quad detection with Hough lines and multi-strategy preprocessing
- Add Hough line detection for improved edge finding with finger occlusion
- Implement multi-strategy preprocessing (standard, enhanced, adaptive, multi)
- Add configurable parameters for Canny thresholds, adaptive threshold, morph gradient
- Refactor useDocumentDetection hook with cleaner API
- Add OpenCV type definitions and async loading improvements
- Add loader test pages for debugging OpenCV initialization
- Add quad-test page for interactive detection testing
- Add document detection research notes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 11:16:06 -06:00
Thomas Hallock bc02ba281d feat(scanner): add camera controls, auto presets, and persisted settings
- Add torch/flashlight toggle (when available)
- Add camera flip button (when multiple cameras)
- Add lighting preset selector with auto-detection based on frame analysis
- Add experimental finger occlusion mode for better edge detection
- Persist scanner settings per-user in database
- Add responsive ScannerControlsDrawer with compact mobile layout
- Hide sublabels on small screens, show on tablets+

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 11:12:30 -06:00
Thomas Hallock fb0abcef27 feat(vision): add modular quad detection with opencv-react
- Add quad-test page for testing quadrilateral detection
- Add modular quadDetector.ts with createQuadDetector(cv) factory
- Add quadTracker.ts for temporal stability tracking
- Add OpenCV types and lazy loader modules
- Add opencv-react and @techstark/opencv-js dependencies
- Add useOpenCV and useQuadDetection hooks
- Add various loader test pages for debugging OpenCV loading

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 11:12:30 -06:00
github-actions[bot] 102c18f44c 🎨 Update template examples and crop mark gallery
Auto-generated fresh SVG examples and unified gallery from latest templates.
Includes comprehensive crop mark demonstrations with before/after comparisons.

Files updated:
- packages/templates/gallery-unified.html

🤖 Generated with GitHub Actions

Co-Authored-By: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-11 02:48:08 +00:00
Thomas Hallock b96f8e6d2f feat(vision): add nav sync indicator and useSyncStatus hook
- Add NavSyncIndicator component for compact sync status in nav bar
  - Shows sync availability, new items count, syncing progress
  - Dropdown with sync actions and history
  - States: loading, unavailable, in-sync, new-available, syncing, error
- Add useSyncStatus hook for managing sync state
  - Fetches sync status from API
  - Handles sync execution with SSE progress
  - Tracks sync history

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 18:21:23 -06:00
Thomas Hallock 9ec139bdfa feat(vision): add multi-selection and bulk actions to unified data panel
- Add multi-selection support with click to toggle, shift+click for range
- Add bulk delete for selected items (both model types)
- Add bulk reclassify for column-classifier (move images to different digit)
- Show bulk selection panel when 2+ items selected with action buttons
- Single click now also opens detail panel for the clicked item
- Add selection indicators on grid items (checkmark badge)
- Add hover actions on grid items (view details, delete buttons)
- Update grid items to show focused vs selected states with different colors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 18:20:02 -06:00
Thomas Hallock 5239e3f3cf fix(vision): fix sync status API endpoint and enable for both models
- Fix sync status fetch URL from non-existent /api/vision-training/sync/status
  to the correct /api/vision-training/sync endpoint
- Add modelType query parameter to both GET (status) and POST (sync) requests
- Enable sync feature for both boundary-detector and column-classifier
  (was previously restricted to column-classifier only)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 18:19:07 -06:00
Thomas Hallock ff5e0afc64 feat(vision): unify data panels with inline model testers
- Create unified data panel architecture replacing separate BoundaryDataPanel and ColumnClassifierDataPanel
- Add MiniBoundaryTester with SVG overlay showing ground truth vs predicted corners
- Add MiniColumnTester with prediction badge overlay showing digit and correct/wrong status
- Support preloading images in full test page via ?image= query param
- Fix overflow on detail panel to allow scrolling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 14:00:09 -06:00
Thomas Hallock bc95a2f34b feat(vision): add sync history tracking with tombstone pruning
Track all training data sync operations with detailed metrics:
- Add vision_training_sync_history table with SQLite schema
- Record sync start/end times, file counts, tombstone stats
- Prune tombstone entries for files that no longer exist on remote
- Show sync history indicator in ColumnClassifierDataPanel

New files:
- vision-training-sync-history.ts - DB schema for sync history
- sync/history/route.ts - API to query sync history
- SyncHistoryIndicator.tsx - UI component showing recent syncs

The tombstone pruning ensures local tombstone files don't grow unbounded -
after each sync, entries for files deleted on remote are removed since
there's no longer a risk of re-syncing them.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 11:59:13 -06:00
Thomas Hallock 2635bc565b fix(vision): consolidate training data deletion with tombstone recording
All training data deletions now go through shared utility functions that
ensure deletions are recorded to tombstone files (preventing re-sync from
production).

- Add trainingDataDeletion.ts with deleteColumnClassifierSample() and
  deleteBoundaryDetectorSample() functions
- Update bulk delete endpoint to use shared utility (was missing tombstone)
- Update single-file delete endpoint to use shared utility
- Update boundary-samples delete to use shared utility (was missing tombstone)
- Update sync route to use shared readTombstone() function
- Add comprehensive unit tests (17 tests covering validation, deletion,
  tombstone recording, and edge cases)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 11:31:54 -06:00
Thomas Hallock 391ae20157 fix(vision): support JPEG images and fix null safety in session detail
- Image route now tries both .png and .jpg extensions (passive captures are JPEG)
- Session detail page guards against undefined result/config values
- Prevents "Cannot read properties of undefined" errors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 10:34:00 -06:00
Thomas Hallock cf45b86a28 feat(vision): add filtering and passive capture for boundary training data
Major changes:
- Add passive boundary capture during practice sessions via DockedVisionFeed
- Create BoundaryFrameFilters component with capture type, session, player,
  and time range filtering using TimelineRangeSelector
- Add sessionId/playerId metadata to boundary frame annotations
- Update boundary-samples API to store and return session/player context
- Improve boundary detector training pipeline with marker masking
- Add preprocessing pipeline preview in training data browser
- Update model sharding (2 shards instead of 1)

Files added:
- BoundaryFrameFilters.tsx - Filter UI component
- usePassiveBoundaryCapture.ts - Hook for passive training data collection
- saveBoundarySample.ts - Shared utility for saving boundary samples
- preview-augmentation/route.ts - API for preprocessing pipeline preview
- preview-masked/route.ts - API for marker masking preview
- marker_masking.py, pipeline_preview.py - Python training utilities

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 10:25:56 -06:00
Thomas Hallock f19e8b1dfa feat(vision): add path-based URL routing for vision training
Implement shared nav bar and path-based model selection for vision training pages.

Changes:
- Add useModelType() hook to get model type from URL path
- Add VisionTrainingNav component with model selector dropdown and tab links
- Add [model]/layout.tsx with fixed nav and --nav-height CSS variable
- Move pages under [model]/ directory structure:
  - /vision-training/[model] - Data hub
  - /vision-training/[model]/train - Training wizard
  - /vision-training/[model]/test - Model tester
  - /vision-training/[model]/sessions - Sessions list
  - /vision-training/[model]/sessions/[id] - Session detail
- Remove Model Card from training wizard (model selection now via URL/nav)
- Update TrainingWizard to use modelType from URL (always defined, not null)
- Remove localStorage restoration of model type
- Add registry.tsx for model metadata

URL is now single source of truth for model type. Browser history and
deep linking work correctly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 08:07:21 -06:00
Thomas Hallock 7ee42a1b56 chore: update pnpm-lock.yaml and add __pycache__ to gitignore
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 19:19:00 -06:00
Thomas Hallock f78eae1148 feat(vision): add boundary detector for marker-free calibration
- Add boundary detector ML model infrastructure (MobileNetV2-based)
- Add training script for boundary detector (train_model.py)
- Add useBoundaryDetector hook for browser inference
- Add BoundaryCameraTester for real-time camera testing
- Add BoundaryImageTester for static image testing
- Add sync API support for boundary detector training data
- Add model type selector on test page (column classifier vs boundary detector)
- Add marker inpainting for training data preprocessing
- Update training wizard to support both model types

The boundary detector aims to detect abacus corners without ArUco markers,
using ML to predict corner positions from raw camera frames. Currently
requires more training data for accurate predictions.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 19:15:00 -06:00
Thomas Hallock 07908218b5 fix(vision): remove data augmentation option from training UI
Data augmentation doesn't work well for abacus column recognition,
so remove the option entirely. Training now always runs without
augmentation (--no-augmentation flag always passed to Python script).

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 13:35:53 -06:00
Thomas Hallock aa10d549b5 fix(vision): resolve 6 issues from practice session testing
1. VisionSetupModal too tall - constrain modal height and video aspect ratio
2. Modal not draggable after setup - add drag constraint ref to motion.div
3. Remote camera URL changes on reload - prevent race condition by checking
   localStorage before rendering QR code component
4. "Waiting for remote connection" stuck - add 15s timeout with retry/settings buttons
5. Auto-undocking on problem progression - remove incorrect isDockedByUser=false
   from unregisterDock() which fired during React re-renders
6. Re-docking goes to fullscreen - check dock existence instead of visibility

Also includes updated ML model weights (non-quantized export).

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 12:59:15 -06:00
Thomas Hallock 49d05d9f21 feat(vision): re-trained model without quantization
Model exported without --quantize_uint8 flag, which was corrupting
weights. Size increased from ~556KB to ~2.2MB but predictions should
now be correct in the browser.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 11:02:16 -06:00
Thomas Hallock a8c0c5d921 fix(vision): remove quantization from model export - corrupts weights!
The --quantize_uint8 flag in tensorflowjs_converter corrupts model
weights, causing completely wrong predictions in the browser even
though Python testing works fine.

This was documented in .claude/CLAUDE.md but the training script
still used quantization. Model size increases (556KB → 2.2MB) but
predictions are now correct.

Removed quantization from all 3 export paths:
- SavedModel → GraphModel conversion
- Keras → LayersModel fallback
- Direct Python API fallback

After this fix, re-run training via /vision-training/train to get
a working model.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 10:58:28 -06:00
Thomas Hallock c7ae52212c feat(vision): update column classifier model with more training data
Re-trained model with additional training samples for improved
digit detection accuracy.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 10:52:58 -06:00
Thomas Hallock 25bdc99b63 fix(vision): improve dock sizing and mirror hint UX
Dock sizing:
- Larger on desktop: up to 240px wide, 300px tall (md breakpoint)
- Smaller on mobile: 140-180px wide, 180-240px tall (base)

Mirror hint:
- Once shown, stays visible until dismissed or mirror enabled
- No more flashing when stability temporarily drops
- Added dismiss (✕) button to hide hint for rest of session
- Clicking "Try Mirror" still enables mirror mode

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 10:34:22 -06:00
Thomas Hallock 24d08bb0f5 fix(vision): constrain docked abacus size for responsive layout
Add explicit height constraint alongside width for the docked abacus.
Both vision-enabled and vision-disabled modes now share the same
container size constraints:

- Width: clamp(140px, 16vw, 200px) - about 2/3 of previous
- Height: clamp(180px, 24vh, 260px) - prevents going under nav

Also reduced container max-width from 900px to 780px to match.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 10:32:08 -06:00
Thomas Hallock 3adc427b9d fix(vision): unify docked abacus layout with status bar
Use the same flex column layout for both vision-enabled and digital
abacus modes. The status bar is now at the bottom (not overlapping the
abacus columns) with VisionIndicator on left and undock button on right.

This matches the DockedVisionFeed layout for consistency.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 10:28:54 -06:00
Thomas Hallock e685f10aff fix(vision): move undock button to vision status bar
Move the undock button into the DockedVisionFeed status bar alongside
the disable-vision button. This prevents the buttons from overlapping.

When vision is enabled, the dock-controls overlay is now hidden since
all controls are in the unified status bar.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 10:23:48 -06:00