From e1040333710943f536c7a00fd06b855a15459e03 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Wed, 24 Sep 2025 08:20:07 -0500 Subject: [PATCH] feat: implement smart tooltip positioning to avoid covering active beads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add collision detection to check for active beads in columns to the left - Position tooltip to the left when safe, above heaven bead when collision detected - Remove fixed left margin since tooltips can now position above when needed - Ensure tooltip never covers beads with direction arrows or highlights 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../components/tutorial/TutorialPlayer.tsx | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/apps/web/src/components/tutorial/TutorialPlayer.tsx b/apps/web/src/components/tutorial/TutorialPlayer.tsx index de757b22..7d0d5d91 100644 --- a/apps/web/src/components/tutorial/TutorialPlayer.tsx +++ b/apps/web/src/components/tutorial/TutorialPlayer.tsx @@ -347,7 +347,7 @@ function TutorialPlayerContent({ return { before, highlighted, after } }, [fullDecomposition, expectedSteps, currentMultiStep]) - // Create overlay for tooltip positioned precisely at topmost bead using AbacusReact's overlay system + // Create overlay for tooltip positioned precisely at topmost bead using smart collision detection const tooltipOverlay = useMemo(() => { if (!currentStepSummary || !currentStepBeads?.length) { return null @@ -359,16 +359,37 @@ function TutorialPlayerContent({ return null } - // Create an overlay that targets the specific bead and positions tooltip outside abacus + // Smart positioning logic: avoid covering active beads + const targetColumnIndex = 4 - topmostBead.placeValue // Convert placeValue to columnIndex (5 columns: 0-4) + + // Check if there are any active beads (with arrows/highlights) in columns to the left + const hasActiveBeadsToLeft = currentStepBeads.some(bead => { + const beadColumnIndex = 4 - bead.placeValue + return beadColumnIndex < targetColumnIndex && bead.direction && bead.direction !== 'none' + }) + + // Determine tooltip position and target + const shouldPositionAbove = hasActiveBeadsToLeft + const tooltipSide = shouldPositionAbove ? 'top' : 'left' + const tooltipTarget = shouldPositionAbove ? { + // Target the heaven bead position for the column + type: 'bead' as const, + columnIndex: targetColumnIndex, + beadType: 'heaven' as const, + beadPosition: 0 // Heaven beads are always at position 0 + } : { + // Target the actual bead + type: 'bead' as const, + columnIndex: targetColumnIndex, + beadType: topmostBead.beadType, + beadPosition: topmostBead.position + } + + // Create an overlay that positions tooltip to avoid covering active beads const overlay: AbacusOverlay = { id: 'bead-tooltip', type: 'tooltip', - target: { - type: 'bead', - columnIndex: 4 - topmostBead.placeValue, // Convert placeValue to columnIndex (5 columns: 0-4) - beadType: topmostBead.beadType, - beadPosition: topmostBead.position - }, + target: tooltipTarget, content: ( @@ -377,7 +398,7 @@ function TutorialPlayerContent({