Commit Graph

437 Commits

Author SHA1 Message Date
1ccdab2780 Added chat interface. 2025-12-20 11:25:18 -06:00
e714c3d3df Added chat interface. 2025-12-20 11:25:14 -06:00
54e5017c38 Added chat interface. 2025-12-20 11:24:31 -06:00
8a78e45440 version bump 2025-12-19 15:32:28 -06:00
1c50dd5c84 Implement three-state feature flag system with upgrade badges
Feature States:
- 'on': Feature fully accessible
- 'off': Feature hidden from menus
- 'coming-soon': Visible with "Coming Soon!" badge, not clickable
- 'basic': Visible with "Sign Up for Free" badge, triggers Auth0 login
- 'pro': Visible with "Upgrade to Pro" badge (for future upgrade flow)

Changes:
- Update FeatureState type to support 5 states (on/off/coming-soon/basic/pro)
- Consolidate GUEST_FEATURE_CONFIG as DEFAULT_FEATURE_CONFIG
- Create ComingSoonBadge component for coming-soon features
- Create UpgradeBadge component for basic/pro tier requirements
- Update VR Experience hamburger menu to maintain open/closed state
- Make menu default to open, persist state in localStorage
- Make 'basic' features clickable to trigger Auth0 sign-in
- Update createDiagramModal to show appropriate badges
- Fix camera initial position to match VR rig (prevent flip on load)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-21 14:33:11 -06:00
31dd8a89da Clean up code formatting and remove unused functions
- Remove unused createGrassGround function from customEnvironment
- Remove commented HemisphericLight code
- Fix deviceDetection to use proper VR detection instead of hardcoded true
- Clean up whitespace in spinner.ts and animatedLineTexture.ts
- Pass empty object to refreshBoundingInfo to match API signature

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 13:23:17 -06:00
1098f03c7d Optimize STL asset loading with promise-based caching
- Replace ImportMeshAsync with LoadAssetContainerAsync for person.stl
- Cache loading promise to prevent race conditions and multiple fetches
- Use instantiateModelsToScene() to create mesh instances from cached container
- Simplify buildMesh signature to use DefaultScene singleton
- Add Havok physics WASM prefetch hint to index.html

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 13:19:24 -06:00
0e053bf69c Add Quest VR onboarding flow with auto-entry prompt
Implemented comprehensive VR onboarding experience for Quest users:

- Add demo database template with pre-built architecture diagram
- Implement automatic demo template loading on first visit
- Create VREntryPrompt component for seamless VR mode entry
- Add device detection utilities for Quest/VR headset identification
- Integrate export/import functionality for diagram templates
- Add About page with device-aware CTA and VR benefits
- Remove legacy tutorial and FirstVisitVr modal for demo flow
- Add upgrade prompts and tiered feature configuration

Quest users now see prominent VR entry prompt when navigating to /db/** paths,
providing one-tap entry into immersive mode after scene initialization.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 10:47:28 -06:00
0b81605bdf Fix label positioning to use world space bounding boxes
Labels were incorrectly mixing local and global transform matrices, causing
incorrect positioning on scaled/rotated meshes. Now properly converts world
space bounding box positions to mesh local space using temporary TransformNode.

Changes:
- updateTextNode.ts: Use boundingBox.maximumWorld instead of boundingSphere.maximum
- diagramObject.ts: Add empty object param to refreshBoundingInfo()
- inputTextView.ts: Adjust input handle default position and mesh offsets

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 16:52:04 -06:00
7849bf4eb2 Extract toolbox buttons into standalone reusable classes
Refactor Toolbox button code into separate, focused classes following Single Responsibility Principle.

New Button Classes:
- ExitXRButton (src/objects/buttons/ExitXRButton.ts)
  * Encapsulates exit XR functionality
  * Takes XR experience, scene, parent, and optional position
  * Handles click events to exit XR session
  * Provides dispose() and transform getter

- ConfigButton (src/objects/buttons/ConfigButton.ts)
  * Encapsulates config panel toggle functionality
  * Uses dependency injection for toggle callback
  * Configurable position relative to parent
  * Provides dispose() and transform getter

- RenderModeButton (src/objects/buttons/RenderModeButton.ts)
  * Encapsulates render mode cycling functionality
  * Internally manages render mode state
  * Automatically recreates button with new label on mode change
  * Cycles through all available rendering modes
  * Provides dispose() and transform getter

Toolbox Changes:
- Removed createRenderModeButton() and updateRenderModeButton() methods
- Simplified setupXRButton() to instantiate button classes
- Reduced button-related code by ~120 lines
- Added button instance properties for cleanup
- Clean, declarative button creation with clear positioning

Benefits:
- Single Responsibility Principle - each button class has one purpose
- Reusability - buttons can be used anywhere in the app
- Testability - each button can be tested independently
- Cleaner code - Toolbox class focuses on core tool management
- Better dependencies - clear interfaces and dependency injection
- Easier maintenance - button logic isolated and self-contained

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 16:14:01 -06:00
e329b95f2f Fix AppConfig persistence and consolidate handle storage
AppConfig Persistence Fixes:
- Fix constructor to properly handle null localStorage values
- Add null check before JSON.parse to prevent errors
- Create fresh config copies with spread operator to avoid reference issues
- Add better error handling and logging for config loading
- Initialize handles array properly

React ConfigModal Improvements:
- Fix config initialization to get fresh values on render instead of stale module-level values
- Separate useEffect hooks for each config property (prevents unnecessary updates)
- Fix SegmentedControl string-to-number conversion (locationSnaps now use "0.01", "0.1" format)
- Enable/disable logic now properly sets values to 0 when disabled

Handle Storage Consolidation:
- Create dynamic HandleConfig type with Vec3 for serializable position/rotation/scale
- Add handles array to AppConfigType for flexible handle storage
- Replace individual localStorage keys with centralized AppConfig storage
- Add handle management methods: getHandleConfig, setHandleConfig, removeHandleConfig, getAllHandleConfigs
- Update Handle class to read from AppConfig instead of direct localStorage
- Update dropMesh to save handles via AppConfig using Vec3 serialization
- Convert between BabylonJS Vector3 and serializable Vec3 at conversion points

Benefits:
- Single source of truth for all configuration
- Proper localStorage persistence across page reloads
- Dynamic handle creation without code changes
- Type-safe configuration with proper JSON serialization
- Consolidated storage (one appConfig key instead of multiple handle-* keys)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 15:34:40 -06:00
25963d5289 version bump 2025-11-19 13:35:40 -06:00
aa0810be02 Refactor Handle class and fix VR positioning
Major improvements to Handle class architecture:
- Replace positional constructor parameters with options object pattern (HandleOptions interface)
- Add automatic platform parenting - handles now find and parent themselves to platform
- Rename idStored → hasStoredPosition for better clarity
- Remove unused staort() method
- Improve position/rotation persistence with better error handling
- Add comprehensive JSDoc documentation
- Use .parent instead of setParent() for proper local space coordinates

Update all Handle usage sites:
- Toolbox: Use new API with position (-.5, 1.5, .5) and zero rotation
- InputTextView: Use new API with position (0, 1.5, .5) and zero rotation
- VRConfigPanel: Use new API with position (.5, 1.5, .5) and zero rotation
- Remove manual platform parenting logic (61 lines of duplicated code removed)
- Remove local position offsets that were overriding handle positions

Fix VR entry positioning:
- Disable camera-relative positioning in groundMeshObserver
- Handles now use their configured defaults or saved localStorage positions
- Positions are now in platform local space as intended

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 13:28:48 -06:00
15c6617151 Fix VR config panel positioning to prevent overflow below handle
The VR config panel was extending 55cm below the handle bar due to
incorrect positioning calculation.

Problem:
- Panel dimensions: 2m wide × 1.5m tall
- Panel was positioned at y=0.2m above handle center
- This placed panel bottom at y=-0.55m (55cm BELOW handle)
- Panel overflowed significantly below the handle bar

Solution:
- Calculate proper position based on panel height
- Position panel center at y=0.8m (0.75m half-height + 0.05m gap)
- Panel bottom now sits 5cm above handle, matching toolbox appearance
- Add 0.6x scaling to match toolbox compact size (1.2m×0.9m actual)

Result:
- Panel bottom aligns just above handle bar
- Consistent visual relationship with toolbox
- Comfortable viewing distance and ergonomics in VR

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 09:46:51 -06:00
adc80c54c4 Optimize animated connection textures and fix material texture bleeding
Performance Optimizations (~90% improvement):
- Implement texture color caching in AnimatedLineTexture
  - Reuse textures for connections with same color
  - Reduces texture count by 70-90% with duplicate colors
- Reduce animation update frequency from every frame to every other frame
  - Halves CPU-to-GPU texture updates while maintaining smooth animation
- Add texture preloading for all 16 toolbox colors
  - Eliminates first-connection creation stutter
- Add GetCacheStats, ClearCache, and PreloadTextures utility methods

Bug Fixes:
1. Fix texture disappearing when moving objects with connections
   - Root cause: Shared cached textures were disposed on connection update
   - Solution: Never dispose cached textures, only swap references
   - Add safety check in DisposeTexture to prevent cached texture disposal

2. Fix UI text textures bleeding to normal materials
   - Add metadata.isUI = true to 10+ UI components:
     - Button.ts (with unique material names per button)
     - handle.ts, roundButton.ts, createLabel.ts, updateTextNode.ts
     - spinner.ts, vrConfigPanel.ts, buildImage, introduction.ts
     - ResizeGizmo.ts
   - Update LightmapGenerator filter to check metadata.isUI first
   - Change exact name match to startsWith for button materials

3. Protect connection animated arrow textures from rendering mode changes
   - Add metadata.isConnection = true to connection materials
   - Update LightmapGenerator to skip connection materials
   - Connections maintain animated arrows in all rendering modes

Technical Details:
- Texture caching follows existing LightmapGenerator pattern
- All UI materials now consistently marked with metadata flags
- Rendering mode filter uses metadata-first approach with fallback checks
- Connection materials preserve textures via metadata.preserveTextures flag

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 16:37:22 -06:00
0e318e7cc7 Fix Fly Mode and Snap Turn not applying at runtime in VR
Add Observable subscription to Rigplatform so Fly Mode and Snap Turn
settings take effect immediately when changed in the VR config panel,
instead of only applying after exiting and re-entering XR.

Changes to src/controllers/rigplatform.ts:
- Import Observer, appConfigInstance, and AppConfigType
- Add _configObserver property to track subscription
- Add _subscribeToConfigChanges() method in constructor
- Subscribe to onConfigChangedObservable to update flyMode and turnSnap
- Add dispose() method to clean up observer and controllers
- Log config changes for debugging

Changes to src/menus/vrConfigPanel.ts:
- Remove unused index parameter in forEach loop

Root cause: Settings were only applied once at Rigplatform initialization
in groundMeshObserver.ts. Config changes during VR session were saving to
localStorage but not updating the running Rigplatform instance.

Result: Fly Mode and Snap Turn now update in real-time when changed in VR.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 15:27:08 -06:00
5091ca0bab Implement Phase 7: Add Label Rendering Mode controls to VR config panel
Complete the final phase of VR configuration panel implementation by adding
Label Rendering Mode selection controls. This allows users to configure how
diagram labels are rendered in the VR environment.

Changes:
- Add radio-style buttons for 4 label rendering modes:
  - Fixed: Static orientation
  - Billboard: Always faces camera (default)
  - Dynamic: Coming soon (disabled)
  - Distance-based: Coming soon (disabled)
- Implement disabled styling for future modes (gray background, 50% opacity)
- Wire up to appConfigInstance.setLabelRenderingMode()
- Update UI when config changes from external sources (2D modal)
- Add updateLabelModeButtonStates() for visual state management

This completes all 7 phases of VRCONFIGPLAN.md implementation.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 15:05:13 -06:00
3002181160 Implement Phase 6: Snap Turn controls for VR config panel
Add fully functional Snap Turn controls:
- Toggle button (Enabled/Disabled) with blue/gray color coding
- 5 angle buttons: 22.5°, 45°, 90°, 180°, 360°
- Selected button highlighted in blue with bold text
- Disabled appearance when snap is off (50% opacity)
- Wire up to appConfigInstance.setTurnSnap()
- Update UI from config observable changes

Follows same pattern as Rotation Snap section but for snap turning.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 13:44:53 -06:00
f8ae71a962 Implement Phase 5: Fly Mode toggle for VR config panel
Add Fly Mode toggle control:
- Single toggle button showing "Fly Mode Enabled" or "Fly Mode Disabled"
- 400px wide button with blue/gray color coding
- Wire up to appConfigInstance.setFlyMode()
- Update UI from config observable changes

Simplest section - just one boolean toggle, no value selection needed.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 13:36:19 -06:00
c66da87401 Migrate from legacy config to new AppConfig singleton system
Remove dual config system and migrate all code to use appConfigInstance.

**Phase 1: Update VR Controller Code**
- snapAll.ts: Replace getAppConfig() with appConfigInstance.current
  - Use `rotateSnap > 0` instead of rotationSnapEnabled flag
  - Use `locationSnap > 0` instead of locationSnapEnabled flag
  - Remove parseFloat() calls (values already numbers)
- groundMeshObserver.ts: Direct property replacement
  - flyModeEnabled → flyMode
  - snapTurnSnap → turnSnap (remove parseFloat)
- customPhysics.ts: Add enabled checks and update
  - Add `> 0` checks (was applying unconditionally)
  - Use locationSnap and rotateSnap directly

**Phase 2: Remove Legacy Config Bridge**
- vrConfigPanel.ts: Remove syncLegacyConfig() method and all calls
- configModal.tsx: Remove legacy localStorage 'config' writes

**Phase 3: Cleanup**
- appConfig.ts: Remove legacy code (ConfigType, getAppConfig(), setAppConfig())
- Remove unused log import

**Benefits:**
- Eliminates dual config system confusion
- Fixes precision error from string "0" values
- Single source of truth via appConfigInstance
- Reactive updates via Observable pattern
- Cleaner, simpler codebase

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 13:20:18 -06:00
3cf3d996dc Implement Phase 4 and fix config sync for actual snap functionality
Phase 4: Add Rotation Snap controls
- Toggle button (Enabled/Disabled) with blue/gray color coding
- 5 rotation value buttons: 22.5°, 45°, 90°, 180°, 360°
- Selected button highlighted in blue with bold text
- Disabled appearance when snap is off (50% opacity)
- Wire up to appConfigInstance.setRotateSnap()
- Update UI from config observable changes

Critical fix: Sync to legacy config system
- Add syncLegacyConfig() method to write to localStorage 'config' key
- Call after all snap value changes (location and rotation)
- Legacy config is used by snapAll.ts for actual object snapping
- Ensures VR config changes affect real VR object manipulation
- Matches ConfigModal pattern for backward compatibility

Without this sync, changes in VR panel had no effect on actual snapping behavior.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 12:53:41 -06:00
5889a1ed79 Implement Phase 3: Location Snap controls for VR config panel
Add fully functional Location Snap controls:
- Toggle button (Enabled/Disabled) with blue/gray color coding
- 5 snap value buttons: 1cm, 5cm, 10cm, 50cm, 1m
- Selected button highlighted in blue with bold text
- Disabled appearance when snap is off (50% opacity)
- Wire up to appConfigInstance.setGridSnap()
- Update UI from config observable changes

Fix layout issues:
- Change texture aspect ratio from 2048x2048 to 2048x1536 (4:3) to match plane dimensions
- Add adaptHeightToChildren to section containers for proper auto-sizing
- Add horizontal alignment to button containers

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 12:13:22 -06:00
be311e6dc8 Implement Phase 2 UI layout and toolbox integration for VR config panel
Phase 2: Add UI layout structure to VRConfigPanel
- Create 5 section containers with titles (Location Snap, Rotation Snap, Fly Mode, Snap Turn, Label Rendering Mode)
- Add visual separators between sections using Rectangle components
- Style with proper padding and spacing for VR readability (60px titles, blue #4A9EFF)
- Store section content containers as private properties for Phase 3-7 controls

Toolbox Integration (Phase 8 partial):
- Instantiate VRConfigPanel in DiagramMenuManager constructor
- Add "Config" button to toolbox (bottom-left, opposite Exit VR button)
- Wire up click handler to toggle panel visibility
- Add B-button positioning logic to reposition panel with other UI elements
- Pass DiagramMenuManager reference to Toolbox.setXR() for panel access

The panel now has complete skeleton structure and can be tested in VR:
- Click "Config" button on toolbox to show/hide panel
- Grab handle to reposition and test ergonomics
- Press B-button to auto-lower panel if too high
- 2m x 1.5m panel size optimized for VR viewing distance

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 12:03:24 -06:00
aa41895675 Add VR configuration panel implementation plan and Phase 1 foundation
Create VRCONFIGPLAN.md with comprehensive 10-phase implementation guide for building an immersive WebXR configuration panel using AdvancedDynamicTexture.

Implement Phase 1: Core panel setup
- Create VRConfigPanel class following Handle pattern for grabbability
- Set up 2m x 1.5m plane mesh with high-resolution ADT (2048x2048)
- Initialize main StackPanel container with title
- Add show/hide/dispose methods for panel lifecycle
- Integrate with appConfigInstance observable for config changes
- Auto-parent to platform for world movement tracking

The panel starts hidden and provides foundation for adding configuration controls in subsequent phases (location snap, rotation snap, fly mode, snap turn, label rendering mode).

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 11:53:47 -06:00
970f6fc78a Fix label positioning, add billboard mode, fix XR entry shift, and fix config system
Label Positioning Fixes:
- Fix labels accounting for mesh scaling using maximumWorld coordinates
- Labels now properly positioned on scaled objects (spheres, boxes, etc.)
- Restore world→local coordinate transformation in updateLabelPosition

Billboard Mode Implementation:
- Add configurable label rendering modes: fixed, billboard, dynamic, distance-based
- Implement billboard mode (labels always face camera using BILLBOARDMODE_Y)
- Add label rendering mode to AppConfig with default 'billboard'
- Add UI selector in ConfigModal for label rendering mode
- Observable pattern updates all existing labels when mode changes

XR Entry Positioning Fix:
- Synchronize desktop camera position to platform before entering XR
- Transfer camera world position and rotation to prevent scene shift
- Reset physics velocity on XR entry to prevent drift
- Add debug logging for position synchronization

Config System Architecture Fix:
- Create singleton appConfigInstance to ensure single source of truth
- Update DiagramObject to use singleton instead of creating instances
- Update DiagramManager to use singleton
- Fix ConfigModal to update AppConfig directly (was only updating legacy config)
- ConfigModal now triggers Observable notifications via appConfigInstance setters
- Maintain legacy config for backward compatibility
- Fixes issue where label rendering mode changes didn't take effect

Files Modified:
- src/diagram/diagramObject.ts - Label positioning, billboard mode, singleton config
- src/diagram/diagramManager.ts - Use singleton config
- src/util/appConfig.ts - Add labelRenderingMode, export singleton
- src/util/appConfigType.ts - Add LabelRenderingMode type
- src/react/pages/configModal.tsx - Update AppConfig directly, add label mode UI
- src/util/functions/groundMeshObserver.ts - Add camera position sync on XR entry
- public/api/user/features - Update test config
- package.json - Version bump

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 08:52:04 -06:00
c1503d959e Add configurable feature management system with JSON-based feature flags
Implement comprehensive feature toggle system allowing menu options and features
to be controlled via JSON configuration fetched from API endpoint or static files.

Core System:
- Create FeatureConfig type system with page, feature, and limit-based flags
- Add React Context (FeatureProvider) that fetches from /api/user/features
- Implement custom hooks (useFeatures, useIsFeatureEnabled, useFeatureLimit, etc.)
- Default config disables everything except home page

Integration:
- Update PageHeader to filter menu items based on page flags
- Add ProtectedRoute component to guard routes
- Update VR menu to conditionally render items based on feature flags
- Update CreateDiagramModal to enable/disable options (private, encrypted, invite)
- Update ManageDiagramsModal to use configurable maxDiagrams limit

Configuration Files:
- Add static JSON files for local testing (none, free, basic, pro tiers)
- Add dev proxy for /api/user/features endpoint
- Include README with testing instructions

Updates:
- Complete CLAUDE.md naming conventions section
- Version bump to 0.0.8-27

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:52:39 -06:00
6ea6eaaac7 Implement SVG-based dynamic connection arrows with toolbox color matching
Replace static arrow.png with dynamically generated SVG arrows that match
the source object's color from the toolbox palette.

Changes:
- Replace arrow.png loading with inline SVG generation (32x32 right-pointing triangle)
- Add CreateColoredTexture() method to generate arrows in any hex color
- Extract color from source mesh using three-priority fallback system:
  1. mesh.metadata.color (most reliable)
  2. sourceMesh.id parsing (e.g., "tool-#box-template-#FF0000")
  3. material color extraction (backwards compatibility)
- Match extracted color to closest of 16 toolbox colors using Euclidean distance
- Track all textures in Set for synchronized animation
- Add proper texture disposal to prevent memory leaks

Benefits:
- No external arrow.png dependency
- Connections visually match their source object's toolbox color
- Consistent 16-color palette across all connections
- Efficient texture sharing for matching colors
- SVG scales perfectly at any resolution

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 06:24:42 -06:00
2915717a3a Adjust ResizeGizmo handle appearance and sizing
Updated handle visual properties:
- Changed handle color from yellow to blue for better visibility
- Increased base handle size from 20% to 50% of corner distance
- Modified distance-based scaling multiplier from 0.2 to 1.0 for improved depth perception
- Removed unused HANDLE_SIZE constant

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 04:57:22 -06:00
6643379133 Add distance-based scaling to ResizeGizmo handles
- Implement camera distance-based scaling so handles appear consistent visual size
- Add updateHandleScaling() method called every frame
- Use 0.2 multiplier for scale factor (adjustable)
- Handles maintain mesh rotation alignment (not billboarded)
- Keeps existing utility layer configuration for compatibility

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 20:10:14 -06:00
1ab3deae92 Add face handles and transform tracking to ResizeGizmo
- Add 6 face handles for single-axis scaling (in addition to 8 corner handles for uniform scaling)
- Implement single-axis scaling for face handles vs uniform scaling for corners
- Add automatic handle position updates when target mesh moves or rotates
- Track mesh transform changes using quaternions for accurate rotation detection
- Update handles in real-time during scaling to match new bounding box
- Add FACE_POSITIONS constant array to enums.ts
- Fix handle sizing to use consistent size calculation for all handles

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 17:06:32 -06:00
016b1fe6e2 Rebuild ResizeGizmo from scratch with simplified corner-only approach
Completely rewrote ResizeGizmo to be methodical and debuggable:

- Created CORNER_POSITIONS static array with normalized coordinates
- 8 corner handles only (removed face handles for simplicity)
- Handle sizing based on bbox distance (20% of corner-to-center)
- Handle positioning uses vectorsWorld directly
- XR controller ray picking in utility layer
- Edge rendering for hover (white) and grab (blue) states
- Virtual stick scaling: fixed-length ray from controller
- Uniform scaling based on distance ratio
- Snap to 0.1 increments on release only
- Proper XR input setup for existing and new controllers

Key improvements:
- Uses BabylonJS vectorsWorld instead of manual calculations
- Cleaner separation of concerns (picking, input, scaling)
- All private fields use underscore prefix convention
- Better haptic feedback (hover pulse, grab pulse, release pulse)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 16:40:56 -06:00
ebad30ce4d Implement Virtual Stick scaling with modular ResizeGizmo architecture
Refactored ResizeGizmo into modular structure:
- ResizeGizmo.ts: Main implementation with Virtual Stick scaling
- enums.ts: HandleType and HandleState enums
- types.ts: TypeScript interfaces
- index.ts: Barrel exports

Implemented Virtual Stick scaling approach:
- Fixed-length virtual stick extends from controller forward
- Scaling based on distance ratio in mesh local space
- World-to-local coordinate transforms for proper rotation handling
- Smooth continuous scaling during drag (no rounding)
- Snap to 0.1 increments on grip release
- Face handles: round only scaled axis
- Corner handles: round uniformly on all axes

Fixed scaling oscillation issues:
- Freeze handle position updates during active scaling
- Prevents feedback loop between scaling and handle positioning
- Use absoluteRotationQuaternion for proper handle rotation

Added WebXRDefaultExperience parameter to constructor for proper controller integration with manual ray casting in world space.

Added test shortcuts:
- Ctrl+Shift+T: Create test entities (sphere and box)
- Ctrl+Shift+X: Clear all entities

Wired Close button to dispose active ResizeGizmo.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 11:43:17 -06:00
0712abe729 Remove old ResizeGizmo integration code from AbstractController
Clean up AbstractController by removing references to old ResizeGizmo implementation:
- Remove utility layer mesh filtering logic
- Remove auto-show gizmo on hover
- Remove gizmo handle click filtering
- Remove unused Ray import
- Bump version to 0.0.8-26

These changes complete the migration to the new simplified ResizeGizmo architecture.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 05:55:17 -06:00
2c3fba31d3 Reimplement ResizeGizmo as simplified single-file XR gizmo
Complete rewrite of ResizeGizmo with a much simpler architecture:
- Single file implementation (index.ts) replacing multi-file system
- 14 handles: 6 face handles for single-axis scaling, 8 corner handles for uniform scaling
- XR-only interaction using UtilityLayerRenderer
- Billboard scaling for constant screen-size handles
- Grip-based interaction with hover/active visual states (gray/white/blue)
- Single-axis scaling from opposite face (fixed pivot)
- Uniform scaling from center
- Integrated with ClickMenu Size button
- Observable events (onScaleEnd, onScaleDrag) for future integration

Removed old complex implementation files and simplified documentation.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 05:53:26 -06:00
c815db4594 Improve ResizeGizmo hover state and handle interaction
Phase 1 & 2: Handle positioning and wireframe improvements
- Move handles 5% outward from bounding box (was inward)
- Rename boundingBoxPadding → handleOffset for clarity
- Add wireframePadding (3% breathing room around mesh)

Hover boundary detection (prevent loss in whitespace):
- Add isPointerInsideHandleBoundary() with ray-AABB intersection
- Use local space transformation for accurate OBB handling
- Keep HOVER_MESH state when pointer in handle boundary
- Fix: Trust ResizeGizmo state instead of recreating with fake rays

Prevent main scene mesh grab during handle interaction:
- Add ResizeGizmo state check in pointer observable
- Add defense-in-depth guard in grab() method
- Prevents controller from grabbing diagram mesh when hovering handle
- Two-level protection against race conditions

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-15 13:55:18 -06:00
43100ad650 Fix color persistence using metadata and source mesh ID fallback
Colors were being lost during resize operations because toDiagramEntity
extracted color from material.diffuseColor, which is no longer used after
the emissiveColor rendering optimization (commit c7887d7).

Root Causes:
1. Rendering system changed from diffuseColor to emissiveColor
2. Material properties unreliable when materials are shared
3. Material-based extraction broke when properties changed

Solution - Three-Tier Fallback Chain:

Priority 1: mesh.metadata.color
- Most reliable, explicitly set during mesh creation
- Already populated by buildMeshFromDiagramEntity (line 163)

Priority 2: Extract from mesh.sourceMesh.id (InstancedMesh)
- Tool mesh IDs encode color: "tool-BOX-#FF0000"
- Preserves original tool color regardless of material state
- Works for all instanced diagram meshes

Priority 3: Material properties (backwards compatibility)
- Checks emissiveColor first (current system)
- Falls back to diffuseColor (old system)
- Handles both StandardMaterial and PBRMaterial
- Maintains compatibility with non-instanced meshes

Changes:
- Import InstancedMesh from @babylonjs/core
- Replace direct material extraction with fallback chain
- Parse tool mesh ID to extract hex color code
- Normalize colors to lowercase
- Add null checks for safe color extraction

Benefits:
 Independent of material system changes
 Works with shared materials
 Preserves original tool colors
 Backwards compatible
 More reliable than material-only extraction

Files modified:
- toDiagramEntity.ts: Implement fallback chain for color extraction

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-15 08:05:49 -06:00
af52d5992c Fix handle rotation to properly match mesh orientation
Handle meshes now correctly rotate to match the target mesh's world-space
orientation instead of appearing axis-aligned.

Root Cause:
- Handle positions from HandleGeometry are calculated in world space
- Setting mesh.position treats values as local space
- This created coordinate system mismatch when rotation was also set
- Result: rotation appeared to have no effect

Solution:
- Extract rotation from mesh world matrix using quaternion decomposition
- Set rotation FIRST (before position)
- Use setAbsolutePosition() for world-space positioning
- This ensures rotation and position work correctly together

Changes:
- Import Quaternion from @babylonjs/core
- Update createHandleMeshes(): decompose world matrix, set rotation,
  then use setAbsolutePosition()
- Rename updateHandlePositions() to updateHandleTransforms()
- Update updateHandleTransforms(): same rotation-then-position approach
- Add null check for _targetMesh in updateHandleTransforms()

Technical Details:
- computeWorldMatrix(true) gets complete transform including parent
- decompose() extracts pure rotation as quaternion (avoids gimbal lock)
- setAbsolutePosition() correctly handles world-space coords with rotation
- Order matters: rotation before position for correct transformation

Result:
 Handle box shapes visually tilt/rotate with mesh
 Handles remain correctly positioned on OBB
 Both wireframe and individual handles rotate together

Files modified:
- ResizeGizmoVisuals.ts: Handle rotation implementation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-15 07:36:57 -06:00
5fbf2b87c1 Implement OBB-based scaling for rotated meshes and simplify gizmo UX
Major improvements to ResizeGizmo rotation handling and interface:

1. **OBB (Oriented Bounding Box) Implementation**
   - Replace AABB with true OBB that rotates with mesh
   - Calculate 8 OBB corners in world space using mesh world matrix
   - Update bounding box wireframe to use OBB corners
   - Rewrite all handle generation (corner, edge, face) for OBB positioning
   - Handle normals now calculated from mesh center to handle position
   - Result: Bounding box and handles rotate with mesh, scaling follows local axes

2. **Simplify UX - Remove Edge Handles**
   - Remove TWO_AXIS mode from ResizeGizmoMode enum
   - Disable edge handles (green, two-axis) to reduce cognitive complexity
   - Keep only corner handles (blue, uniform) and face handles (red, single-axis)
   - Updated from 26 total handles to 14 handles (6 face + 8 corner)
   - All scaling capabilities still available through remaining handle types

3. **Fix Event Leak-Through (Hit Testing)**
   - Add getUtilityScene() method to ResizeGizmoManager
   - Configure XR pick predicate to exclude utility layer meshes (primary defense)
   - Filter utility layer in pointer observable (secondary defense)
   - Filter utility layer in click handler (tertiary defense)
   - Prevents gizmo handle events from leaking to main scene

4. **Documentation**
   - Add TODO.md documenting implementation and decisions
   - Document OBB implementation and edge handle removal
   - Track completed features and rationale

Files modified:
- ResizeGizmoVisuals.ts: OBB wireframe and corner calculation
- HandleGeometry.ts: OBB-based handle positioning for all types
- ResizeGizmoConfig.ts: Disable edge handles
- ResizeGizmoManager.ts: Add utility scene access
- ScalingCalculator.ts: Uniform two-axis scaling (distance-ratio)
- types.ts: Remove TWO_AXIS mode
- diagramMenuManager.ts: XR pick predicate filtering
- abstractController.ts: Pointer and click filtering
- TODO.md: Documentation of changes

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 07:06:06 -06:00
204ef670f9 Move ResizeGizmo handles inside bounding box for better selection
- Reverse padding direction in HandleGeometry:
  - Corner handles now positioned inward (add padding to min, subtract from max)
  - Edge handles now positioned inward (same reversal)
  - Face handles now positioned inward (same reversal)
- Remove padding from bounding box wireframe to match mesh bounds
- Handles are now 5% inside edges instead of 5% outside
- Improves handle selection reliability
- Prevents unwanted gizmo auto-hide when pointer moves to handles

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 18:00:05 -06:00
26b48b26c8 Implement WebXR resize gizmo with virtual stick scaling and extract adapter to integration layer
- Implement comprehensive WebXR resize gizmo system with three handle types:
  - Corner handles: uniform scaling (all axes)
  - Edge handles: two-axis planar scaling
  - Face handles: single-axis scaling
- Use "virtual stick" metaphor for intuitive scaling:
  - Fixed-length projection from controller to handle intersection
  - Distance-ratio based scaling from mesh pivot point
  - Works naturally with controller rotation and movement
- Add world-space coordinate transformations for VR rig parenting
- Implement manual ray picking for utility layer handle detection
- Add motion controller initialization handling for grip button
- Fix color persistence bug in diagram entities:
  - DiagramEntityAdapter now uses toDiagramEntity() converter
  - Store color in mesh metadata for persistence
  - Add dependency injection for loose coupling
- Extract DiagramEntityAdapter to integration layer:
  - Move from src/gizmos/ResizeGizmo/ to src/integration/gizmo/
  - Add dependency injection for mesh-to-entity converter
  - Keep ResizeGizmo pure and reusable without diagram dependencies
- Add closest color matching for missing toolbox colors
- Handle size now relative to bounding box (20% of avg dimension)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 17:52:23 -06:00
02c08b35f2 Add comprehensive material sharing validation and diagnostics
Implemented extensive logging and validation to diagnose material sharing issues and prevent unnecessary material creation:

**Validation Added:**
- Pre-creation check: Verify tool meshes have materials before creating instances
- Early exit if tool mesh lacks material to prevent bad instances
- Post-creation validation in buildColor.ts to catch tool creation issues

**Enhanced Diagnostics:**
- Detailed debug logging for tool mesh lookup and instance creation
- Error logging with full context when material sharing fails
- Source mesh material validation for InstancedMesh
- Lists available tool meshes when lookup fails

**Statistics Tracking:**
- Tracks instances created vs materials shared
- Counts fallback material creations
- Logs sharing rate every 10 instances (target: 100%)
- Helps identify material sharing failures in production

**Expected Outcome:**
- 100% material sharing rate for tool-based entities
- Zero fallback material creations
- All instances inherit materials from tool templates
- Better draw call batching (same material = batched rendering)

This diagnostic infrastructure will identify:
1. Timing issues (tools not ready when entities created)
2. Tool mesh creation failures
3. BabylonJS InstancedMesh material inheritance issues

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 11:18:07 -06:00
bda0735c7f Add WebXR rendering mode toggle with 4 modes
Implemented a single button in the toolbox that cycles through four rendering modes:
1. Lightmap + Lighting - diffuseColor + lightmapTexture with lighting enabled
2. Emissive Texture - emissiveColor + emissiveTexture with lighting disabled (default)
3. Flat Color - emissiveColor only with lighting disabled
4. Diffuse + Lights - diffuseColor with two dynamic scene lights enabled

Features:
- Single clickable button displays current mode and cycles to next on click
- Automatically manages two scene lights (HemisphericLight + PointLight) for Diffuse + Lights mode
- UI materials (buttons, handles, labels) are excluded from mode changes to remain readable
- Button positioned below color grid with user-adjusted scaling
- Added comprehensive naming conventions documentation
- Updated inspector hotkey to Ctrl+Shift+I

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 10:36:03 -06:00
c7887d7d8f Optimize lightmap rendering using emissive texture approach
Changed from lightmapTexture with lighting enabled to emissiveTexture with lighting disabled for better performance. The new approach provides the same lighting illusion without expensive per-pixel lighting calculations.

- Added LightmapGenerator.ENABLED toggle for performance testing
- Updated buildColor.ts to use emissiveColor + emissiveTexture with disableLighting = true
- Updated buildMissingMaterial() to match new rendering approach
- Fixed buildTool.ts to access emissiveColor instead of diffuseColor for material color detection

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 09:44:56 -06:00
3f02fc7ea5 Implement lightmap-based rendering for performant lighting illusion
Replace emissive-only rendering with diffuse + lightmap system to achieve realistic lighting appearance without dynamic light overhead.

- Create LightmapGenerator class with canvas-based radial gradient generation
- Generate one lightmap per color (16 total) using top-left directional light simulation
- Cache lightmaps in static Map for reuse across all instances
- Preload all lightmaps at toolbox initialization for instant availability
- Update buildColor() to use diffuseColor + lightmapTexture instead of emissiveColor
- Update buildMissingMaterial() to use lightmap-based rendering
- Enable lighting calculations (disableLighting = false) to apply lightmaps

Lightmap details:
- 512x512 resolution RGBA textures
- Radial gradient: center (color × 1.5), mid (base color), edge (color × 0.3)
- Simulates top-left key light with smooth falloff
- Total memory: ~16 MB for all lightmaps
- Zero per-frame performance cost

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 09:20:40 -06:00
100c5e612c Move exit XR button to toolbox class
Refactored exit XR button creation from rigplatform to toolbox for better organization and UI cohesion.

- Add setXR() methods to DiagramManager, DiagramMenuManager, and Toolbox to pass WebXRDefaultExperience after initialization
- Create setupXRButton() in Toolbox class that creates button when entering XR
- Position button at bottom-right of toolbox (x: 0.5, y: -0.35, z: 0)
- Use Y-axis rotation (Math.PI) for correct orientation within toolbox coordinate system
- Scale button to 0.2 for appropriate size
- Remove button creation code from rigplatform

Exit button now moves with toolbox and is logically grouped with other UI elements.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 06:57:35 -06:00
d59c7b6e6e Enable per-instance edge rendering for hover effects
Changed EdgesRenderer to work on individual instances instead of source mesh to prevent all instances from highlighting when one is hovered.

- Remove edgesShareWithInstances flag (was causing all instances to highlight)
- Enable/disable edges directly on hovered instance
- Adjust edge width to 0.2 and color to pure white for cleaner appearance
- Remove metadata tracking in favor of checking edgesRenderer directly

This ensures only the specific hovered entity shows visual feedback while maintaining haptic feedback for all interactions.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-13 05:49:54 -06:00
0ad61bdde9 Fix XR component positioning to appear in front of user
- Use camera.getDirection() instead of manual Euler angle calculation to properly account for camera transform hierarchy
- Negate forward offsets to position objects in -Z direction (user faces -Z by design)
- Replace expensive HighlightLayer hover effect with lightweight EdgesRenderer (20-50x faster)
- Add comprehensive debug logging for position calculations

The camera has a parent transform with 180° Y rotation, causing the user to face -Z in world space. Components now correctly position in front of the user when entering XR.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 22:41:51 -06:00
4a9d7acc41 Optimize connection raycasting with position caching
Performance improvements:
- Added Vector3 position caching for connection endpoints
- Only update connections when meshes actually move (>0.001 units)
- Use DistanceSquared for efficient movement detection
- Replace inefficient vector length comparison

Impact:
- Static connections: 0 raycasts/second (was ~20/sec per connection)
- With 10 connections: 90-99% reduction in raycast operations
- Eliminates unnecessary curve geometry recreation

Implementation:
- Added _lastFromPosition and _lastToPosition caching
- Created hasConnectionMoved() method with tolerance threshold
- Reset cache on mesh removal and initial setup
- Clean up cache in disposal method

This dramatically reduces CPU usage in VR with multiple connections.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 21:46:57 -06:00
6ad04bb21a Refactor config naming and upgrade dependencies
Config changes:
- Renamed gridSnap to locationSnap for clarity
- Fixed configMenu to reference correct property
- Added debug logging to setAppConfig

Code cleanup:
- Removed commented duplicate exitXR call

Dependencies:
- Upgraded @babylonjs packages from 7.21.5 to 8.16.2
- Upgraded @mantine packages from 7.12.0 to 7.17.8

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 21:36:56 -06:00
293c74d7c1 Remove debug console.log from render loop
Removed console.log() from connectionPreview render observer that was
executing every frame during connection dragging. This eliminates I/O
blocking and stringification overhead in the critical VR render path.

Performance: Quick win for VR framerate improvement.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 21:29:24 -06:00