- Add dual-stream calibration: phone sends both raw and cropped preview
frames during calibration so users can see what practice will look like
- Add "Adjust" button to modify existing manual calibration without
resetting to auto-detection first
- Hide calibration quad editor overlay when not in calibration mode
- Fix rotation buttons to update cropped preview immediately
- Add rate limiting (10fps) for cropped preview frames during calibration
- Fix multiple bugs preventing dual-stream mode from working:
- Don't mark calibration as complete during preview mode
- Don't stop detection loop when receiving preview calibration
- Sync refs properly in frame mode change effects
Also includes accumulated formatting and cleanup changes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Apply code formatting across codebase
- Add new vision training boundary frames
- Update model configurations
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Major refactoring of worksheet parsing to use centralized state management:
New architecture:
- WorksheetParsingContext: React context provider for parsing state
- state-machine.ts: Typed reducer with actions for streaming lifecycle
- sse-parser.ts: Shared SSE parsing utility for OpenAI Responses API
- usePartialJsonParser.ts: Progressive JSON extraction during streaming
Streaming UI improvements:
- ParsingProgressOverlay: Dark overlay on photo tile during parsing
- ParsingProgressPanel: Collapsible reasoning text panel
- ProgressiveHighlightOverlay: Problem boxes light up as LLM parses
- New streaming API routes: /parse/stream and /parse-selected/stream
Bug fixes during testing:
- Fix TypeScript error: cast event.response for id access in sse-parser
- Fix reparse reasoning display: preserve "processing" status for reparse
- Fix concurrent parsing: revert previous attachment status when switching
- Fix problem count: track dispatched problems to prevent duplicates
Components updated to use context:
- SummaryClient: Wrapped with WorksheetParsingProvider
- OfflineWorkSection: Uses context instead of local streaming state
- PhotoViewerEditor: Uses context for coordinated parsing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add cancel button to gallery thumbnails when parsing in progress
- Add cancel button to fullscreen PhotoViewerEditor when parsing
- Add cancel button for re-parsing in progress (fullscreen view)
- Track reparsingPhotoId to show correct status per-photo in both views
- Gallery shows "Re-parsing..." badge on specific photo being re-parsed
- DELETE endpoint resets parsing status for immediate retry
Also includes codebase-wide formatting from biome.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove per-problem Exclude/Restore buttons from EditableProblemRow
- Add bulk "Exclude Selected" and "Restore Selected" buttons to selection toolbar
- Add toast notifications for approve success/failure
- Close viewer and refresh page after successful approve to show updated session
- Fix mutation to properly await res.json() before returning
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Teacher observing a student's practice session can now:
- Dock their MyAbacus alongside the observed problem (AbacusDock pattern)
- Manipulate their abacus to sync the student's docked abacus
- See their abacus sync when the student manipulates theirs
Key changes:
- SessionObserverModal: Uses same AbacusDock pattern as ActiveSession
with ResizeObserver for height matching
- MyAbacus: Fixed docked mode to use setDockedValue instead of
setAbacusValue, ensuring read/write use same state source
- MyAbacusContext: Added contextAbacusValue for external abacus control
- useSessionObserver: Sends abacus-control events to student
- useSessionBroadcast: Receives abacus-control events from teacher
The two-way sync flow:
1. Teacher→Student: Teacher moves beads → sendControl(set-abacus-value)
→ student's abacus syncs via onAbacusControl callback
2. Student→Teacher: Student moves beads → broadcasts studentAnswer
→ observer receives → setDockedValue syncs teacher's abacus
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Teachers can now observe students' skill tutorials in real-time:
- Added TutorialObserverModal that reuses SkillTutorialLauncher component
- Student broadcasts tutorial state via WebSocket (useSkillTutorialBroadcast)
- Teacher receives live updates via useClassroomTutorialStates
- Teacher controls work: Start, Skip, Prev, Next, Complete buttons
- Socket server forwards skill-tutorial-state and skill-tutorial-control events
- Added onControl prop to SkillTutorialLauncher and TutorialPlayer
- In observation mode, buttons send control events instead of changing local state
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Student Notes Feature:
- Add notes column to players table with migration
- Create NotesModal component with zoom animation from student tile
- Add notes button on each student card in StudentSelector
- Support viewing and editing notes directly in modal
- Fix modal reopening bug with pointerEvents during animation
- Fix spring animation to start from clicked tile position
BKT & Curriculum Improvements:
- Add configurable BKT thresholds via admin settings
- Add skill anomaly detection API endpoint
- Add next-skill recommendation API endpoint
- Add problem history API endpoint
- Improve skills page with BKT classifications display
- Add skill tutorial integration infrastructure
Dashboard & Session Improvements:
- Enhanced dashboard with notes tab
- Improved session summary display
- Add StartPracticeModal stories
Test Infrastructure:
- Add seedTestStudents.ts script for BKT manual testing
- Add generateTrajectoryData.ts for simulation data
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Create PracticeHelpOverlay component showing interactive abacus with
bead arrows and tooltips (uses same system as TutorialPlayer)
- Extract BeadTooltipContent to shared component for consistency
- Add helpOverlay prop to VerticalProblem for proper positioning
- Position help abacus directly above the term being helped using
bottom: 100% on the term row (overflow visible)
- Dynamically size abacus columns based on max(currentValue, targetValue)
- Add timing configuration in helpTiming.ts (debug vs production)
- Add beadTooltipUtils.ts for tooltip positioning calculations
The help overlay now correctly covers the confirmed terms in the
vertical problem, with the "Adding: +X" badge and interactive abacus
positioned above the term being worked on.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Extract standalone DecompositionContext from TutorialContext
- Create reusable DecompositionDisplay and ReasonTooltip components
- Wire prefix-sum "Get Help" button to progressive help system (L1→L2→L3)
- Sync abacus interactions with decomposition step highlighting
- Add currentStepIndex tracking in PracticeHelpPanel
- Make HelpAbacus interactive at L3 to update decomposition display
- Update documentation linking decomposition components
The progressive help system now:
- L1: Shows coach hint when user clicks "Get Help" after typing prefix sum
- L2: Shows interactive decomposition with hoverable explanations
- L3: Shows visual abacus with arrows, synced with decomposition highlighting
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The problem: When in mastery mode, layout toggles (Problem Numbers and
Cell Borders) appeared to work but had no effect on the worksheet preview.
Root cause: Mastery mode was using hardcoded recommendedScaffolding from
skill definitions, completely overriding user's displayRules choices.
Solution: Merge user's displayRules with skill's recommended scaffolding,
giving user's layout choices (problemNumbers, cellBorders) priority while
still respecting the skill's pedagogical scaffolding recommendations for
other display options.
Changes:
- validation.ts: Override layout options with user choices in mastery mode
- Remove debug logging from OrientationPanel, AdditionWorksheetClient,
preview API route, and typstGenerator
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two critical bugs preventing ten-frames from rendering:
1. **Mastery mode not handled** (typstGenerator.ts:61)
- Code only checked for 'smart' | 'manual' modes
- Mastery mode fell into manual path, tried to use boolean flags that don't exist
- Resulted in all display options being `undefined`
- Fix: Check for both 'smart' OR 'mastery' modes (both use displayRules)
2. **Typst array membership syntax** (already fixed in previous commit)
- Used `(i in array)` which doesn't work in Typst
- Changed to `array.contains(i)`
Added comprehensive unit tests (tenFrames.test.ts):
- Problem analysis tests (regrouping detection)
- Display rule evaluation tests
- Full Typst template generation tests
- Mastery mode specific tests
- All 14 tests now passing
Added debug logging to trace display rules resolution:
- displayRules.ts: Shows rule evaluation per problem
- typstGenerator.ts: Shows enriched problems and Typst data
- Helps diagnose future issues
The issue was that mastery mode (which uses displayRules like smart mode)
was being treated as manual mode (which uses boolean flags), resulting in
undefined display options.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed production runtime error "TypeError: d.value is not a function"
in tutorial component caused by missing context values.
Root cause:
- TutorialContext interface declared activeGroupTargetColumn and
setActiveGroupTargetColumn properties
- State variables were defined with underscore prefixes (_activeGroupTargetColumn)
indicating they were unused
- These values were not included in the context value object
- Components attempting to call setActiveGroupTargetColumn() received
undefined and crashed with "is not a function"
Fix:
- Removed underscore prefixes from state variable declarations
- Added activeGroupTargetColumn and setActiveGroupTargetColumn to
context value object
- Components now receive proper state and setter functions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Recover all changes from stash including:
- Linter/formatter updates across codebase
- Settings permission updates for git checkout
This commit captures the complete state of work that was
stashed during the previous session's git operations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Use CSS light-dark() function to support both color schemes:
Decomposition reasoning tooltips:
- Background: white → dark in dark mode
- Borders: light gray → darker in dark mode
- Text: dark → light in dark mode
- Code blocks and steps: proper contrast in both modes
Tutorial player tooltips:
- Blue background adapts to theme
- Text remains white for good contrast
Fixes unreadable light text on light backgrounds in light mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes production error "Cannot read properties of undefined (reading 'carryBoxes')"
that occurred when users tried to adjust difficulty settings.
Root cause: displayRules was undefined for new users or users with old V1 config
in database. Difficulty adjustment buttons accessed displayRules.carryBoxes without
checking if displayRules existed first.
Changes:
- AdditionWorksheetClient: Initialize displayRules with defaults when missing
- ConfigPanel: Use null-coalescing operators instead of non-null assertions
- ConfigPanel: Add error logging when required fields are missing
- NEW: WorksheetErrorBoundary component to catch all errors in worksheet page
- page.tsx: Wrap client component with error boundary
This ensures users see helpful error messages instead of blank pages,
and never need to open the browser console to understand what went wrong.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add trailing comma in cropToActiveBeads config
- Format console.log call for better readability
- Format postMessage call parameters
- Add approved bash commands for icon testing to local settings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed bug where bead highlights were not showing in the tutorial
because column validation used incorrect minValidColumn calculation
(5 - abacusColumns) instead of checking against actual column range.
Changed from:
if (columnIndex < minValidColumn) return
To:
if (columnIndex < 0 || columnIndex >= abacusColumns) return
This ensures highlights work correctly regardless of abacusColumns value.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Auto-format changes from Biome:
- Multi-line array formatting for COLUMN_LABELS
- Type import statement formatting
- Line wrapping improvements for readability
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change tutorial tooltip z-index from 1000 to 50 so it scrolls under the
app nav bar (z-index 100) along with the abacus. This prevents the coaching
text from appearing layered above the nav while the abacus it points to is
hidden beneath it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add silentErrors prop to TutorialPlayer to allow suppressing "That's not
the highlighted bead" error messages. Used on homepage to provide a less
intrusive demo experience.
Changes:
- Add silentErrors prop to TutorialPlayerProps interface
- Conditionally skip error dispatch when silentErrors is true
- Pass silentErrors={true} from homepage TutorialPlayer
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove references to non-existent highlight.columnIndex property
- Remove references to removed currentStep.errorMessages property
- Use placeValue directly for highlight filtering and calculations
- Add generic error message for incorrect bead clicks
All changes maintain existing functionality while fixing type safety issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added hideTooltip prop to TutorialPlayer to optionally hide guidance panels
- Enhanced coaching bar text for dark mode (brighter yellow with glow effect)
- Applied hideTooltip to homepage tutorial for cleaner presentation
- Updated dark mode header background for better integration
These changes are specific to the homepage dark theme instance while preserving default behavior for all other uses of the tutorial system.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added fill property to ColumnPostStyle and ReckoningBarStyle interfaces in abacus-react to enable high-contrast colors in dark mode. Updated TutorialPlayer to set fill colors for column posts (30% white) and reckoning bar (40% white) when in dark theme mode.
This improves visibility of the abacus frame elements in dark mode on the homepage tutorial.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed the dark mode styling to use the correct AbacusReact customStyles API:
Previous (incorrect):
- Used nested `frame` object that doesn't exist in the API
- `frame.column`, `frame.reckoningBar`, `frame.border`
Corrected (per AbacusReact.tsx interface):
- `columnPosts` - Global styling for all column dividers
- `reckoningBar` - Horizontal middle bar styling
Changes:
- Column dividers: rgba(255, 255, 255, 0.2) with 2px stroke
- Reckoning bar: rgba(255, 255, 255, 0.25) with 3px stroke
These properties are at the root level of customStyles, not nested
under a `frame` object. The styling will now properly apply to the
abacus frame elements in dark mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhanced the dark mode theme support for the tutorial player:
Coaching Bar:
- Updated instruction text color to use yellow.300 for dark mode instead of
hardcoded yellow.900
- Ensures coaching instructions are readable against dark backgrounds
Abacus Frame:
- Added custom frame styling for dark mode using customStyles prop
- Column dividers: rgba(255, 255, 255, 0.15) with 2px stroke
- Reckoning bar: rgba(255, 255, 255, 0.2) with 3px stroke
- Outer border: rgba(255, 255, 255, 0.15) with 2px stroke
- Provides subtle, elegant appearance that blends with dark theme
The frame styling is automatically applied when theme="dark" and does not
affect light mode or other tutorial instances.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed a critical bug where tooltip overlays were referencing invalid column
indices when using fewer than 5 columns. The issue occurred because column
index calculations assumed a 5-column layout (0-4), but when using
abacusColumns={2}, the valid indices should be 0-1.
Changes:
- Updated targetColumnIndex calculation to use (abacusColumns - 1) - placeValue
instead of hardcoded 4 - placeValue
- Fixed hasActiveBeadsToLeft logic to use abacusColumns for padding and
column index conversions
- All column index calculations now properly account for the actual number
of columns
This resolves the "Cannot read properties of undefined (reading 'heavenActive')"
error that occurred when using fewer than 5 columns on the homepage tutorial.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix runtime error when abacusColumns < 5 by filtering all bead highlights
to only include columns that actually exist.
Changes:
- Filter highlightBeads prop to only include valid place values
- Filter stepBeadHighlights to only include valid place values
- Filter customStyles column highlights to only include valid columns
- Add abacusColumns to dependencies of relevant useMemo/useCallback
This prevents accessing undefined column states when rendering with
fewer than 5 columns (e.g., abacusColumns={2} for simple demos).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add `theme` and `abacusColumns` props to TutorialPlayer for better customization:
- theme: 'light' | 'dark' controls all color schemes
- abacusColumns: number controls abacus column count (default 5)
Updated homepage to use:
- abacusColumns={2} for simpler 2+3 demo
- theme="dark" for cohesive integration with dark page design
- Vertical layout with "What You'll Learn" below tutorial
Dark theme styling:
- Transparent dark backgrounds for all containers
- Muted text colors (gray.200-gray.400)
- Subtle borders and shadows
- Removed bright yellow/amber gradients
All changes maintain backward compatibility - defaults to light theme with 5 columns.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add `hideNavigation` prop to TutorialPlayer component that hides
the header and footer navigation controls, allowing the tutorial
content to be embedded cleanly without navigation chrome.
Perfect for single-step tutorial demos like the homepage.
Changes:
- Add hideNavigation prop to TutorialPlayerProps
- Wrap header section in conditional rendering
- Wrap navigation footer in conditional rendering
- Update homepage to use hideNavigation={true}
- Adjust minHeight when navigation is hidden
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change <p> tag to <div> tag to fix HTML nesting violation. The
<p> tag was containing <DecompositionWithReasons> which renders
a <div>, causing a hydration error. In HTML, <p> cannot contain
block-level elements like <div>.
Fixed: apps/web/src/components/tutorial/TutorialPlayer.tsx:1386
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
- player-ownership.ts imported drizzle-orm and @/db at top level
- When RoomMemoryPairsProvider imported client-safe utilities, Webpack bundled ALL imports including database code
- This caused hydration error: "The 'original' argument must be of type Function"
- Node.js util.promisify was being called in browser context
**Solution:**
1. Created player-ownership.client.ts with ONLY client-safe utilities
- No database imports
- Safe to import from 'use client' components
- Contains: buildPlayerOwnershipFromRoomData(), buildPlayerMetadata(), helper functions
2. Updated player-ownership.ts to re-export client utilities and add server-only functions
- Re-exports everything from .client.ts
- Adds buildPlayerOwnershipMap() (async, database-backed)
- Safe to import from server components/API routes
3. Updated RoomMemoryPairsProvider to import from .client.ts
**Result:**
- No more hydration errors on /arcade/room
- Client bundle doesn't include database code
- Server code can still use both client and server utilities
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Run Biome formatter on all files to ensure consistent code style:
- Single quotes for JS/TS
- Double quotes for JSX
- 2-space indentation
- 100 character line width
- Semicolons as needed
- ES5 trailing commas
This is the result of running: npx @biomejs/biome format . --write
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update test imports to use centralized abacus-react provider
- Ensure test compatibility with consolidated context management
- Maintain test coverage for tutorial celebration functionality
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove duplicate AbacusDisplayContext in favor of centralized abacus-react provider
- Update all components to use useAbacusDisplay and useAbacusConfig hooks from @soroban/abacus-react
- Create ClientProviders component to centralize provider setup
- Simplify context management across the application
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Restore @soroban/core and @soroban/client dependencies in apps/web
- Correct workspace configuration to include all existing packages
- Disable problematic example-server.ts to fix TypeScript build
- Update Dockerfile to copy all required package.json files
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add "More details" disclosure styling for expandable tooltip content
- Add typography clamps with max-width for better text wrapping
- Add summary paragraph styling with proper spacing and line-height
- Improve dev warning styling to be less visually disruptive
- Add visual quieting for chips in details sections
- Add proper spacing and visual hierarchy for tooltip content
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace Tooltip with HoverCard for better interactive content support
- Add "More details" disclosure pattern to emphasize summary over details
- Enhance provenance integration with flexible chip label matching
- Add performance optimizations with useMemo for expensive computations
- Improve accessibility by removing incorrect role attributes
- Add development-time validation warnings for pedagogical accuracy
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove manual tooltip open/close state management from DecompositionWithReasons
to let HoverCard handle its own timing. This fixes the issue where tooltips
were closing immediately on mouse out. Simplify hover handlers to only manage
visual highlighting while HoverCard manages tooltip display timing.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive tests for the two-level column highlighting system to verify proper mapping between terms and columns in complement groups.
- Test individual term column mapping using termPlace
- Test group target column mapping using rhsPlace
- Verify complement group detection and highlighting behavior
- Ensure fallback logic works correctly
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add hover event handlers to TermSpan components to enable individual term highlighting within complement groups. Previously, only segment-level hover worked, preventing two-level highlighting.
- Add onMouseEnter/onMouseLeave handlers to TermSpan components
- Individual terms now trigger their own addActiveTerm/removeActiveTerm calls
- Maintain segment-level tooltip functionality through event bubbling
- Add cursor pointer styling to indicate interactivity
- Enhanced debug logging for term hover tracking
This fixes the core issue where "(100 - 90 - 5)" terms couldn't be individually hovered.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update TutorialPlayer to use enhanced column mapping for proper two-level highlighting. Group highlights use rhsPlace (target column) while individual highlights use termPlace (specific term column).
- Group highlighting: getColumnFromTermIndex(termIndex, true) for rhsPlace
- Individual highlighting: getColumnFromTermIndex(termIndex, false) for termPlace
- Blue glow for group target column where complement operation net effect lands
- Orange glow for individual term column being hovered
- Enhanced debug logging to track column mapping behavior
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update getColumnFromTermIndex to support both group and individual column mapping modes. This enables proper two-level highlighting where complement groups show both target column (rhsPlace) and individual term columns (termPlace).
- Add useGroupColumn parameter to getColumnFromTermIndex function
- Group highlighting uses rhsPlace (target column where net effect goes)
- Individual highlighting uses termPlace with rhsPlace fallback
- Update getTermIndicesFromColumn with same logic
- Add activeGroupTargetColumn state (unused but prepared for future)
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Replace obtrusive bead overlays with subtle backgroundGlow effect
- Remove conflicting column post highlighting to avoid double effects
- Use refined blur (4px) and opacity (0.25) for gentle visual feedback
- Maintain numeral highlighting for additional context
- Preserve full bead interactivity with non-interfering glow layer
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Document comprehensive architecture for implementing bidirectional
highlighting between mathematical terms and abacus columns:
- 4-phase implementation plan with context bridge and integration
- Detailed code examples and data flow architecture
- Testing strategy with unit, integration, and E2E approaches
- Performance considerations and migration strategy
- Leverages existing provenance system for term-to-column mapping
Plan enables students to see clear connections between abstract
mathematical decompositions and concrete bead manipulations
through coordinated visual highlighting.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>