- Remove redundant useMemo wrapper in usePatternValidation
Inline the calculation logic directly in useMemo instead of
useCallback + useMemo pattern for better clarity and efficiency
- Remove unnecessary 'Early return' comment in usePatternRotationUpload
The return statement is self-explanatory
**Problem:**
Store initialization was happening at module load via side effect:
```typescript
useMachineStore.getState()._setupSubscriptions();
```
This caused several issues:
- Executed before app was ready
- Made testing difficult (runs before test setup)
- Hard to control initialization timing
- Could cause issues in different environments
**Solution:**
- Added public `initialize()` method to useMachineStore
- Call initialization from App component's useEffect (proper lifecycle)
- Removed module-level side effect
**Benefits:**
- ✅ Controlled initialization timing
- ✅ Better testability (no side effects on import)
- ✅ Follows React lifecycle patterns
- ✅ No behavioral changes for end users
**Testing:**
- Build tested successfully
- Linter passed
- All TypeScript types validated
Fixes#38🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Split the 570-line useMachineStore into three focused stores for better
separation of concerns and improved re-render optimization:
**New Stores:**
- useMachineUploadStore (128 lines): Pattern upload state and logic
- useMachineCacheStore (194 lines): Pattern caching and resume functionality
- useMachineStore (reduced to ~245 lines): Connection, status, and polling
**Benefits:**
- Components only subscribe to relevant state (reduces unnecessary re-renders)
- Clear separation of concerns (upload, cache, connection)
- Easier to test and maintain individual stores
- 30% reduction in main store size
**Technical Details:**
- Uses dynamic imports to avoid circular dependencies
- Maintains all existing functionality
- Updated FileUpload, PatternCanvas, and App components
- All TypeScript compilation errors resolved
- Build tested successfully
Implements conservative 2-store extraction approach from issue #31.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Separate static stitch grouping (by color/type) from dynamic completion
status to prevent recalculating all groups on every progress update during
active sewing. This dramatically reduces computational overhead during
500ms polling intervals.
Key optimizations:
- Static groups memo: Only recalculates when stitches/colors change
- Dynamic completion: Only checks group boundaries, not full rebuild
- Custom React.memo comparison: Prevents unnecessary re-renders
- Added comments for future optimization paths (virtualization, LOD, Web Workers)
Performance improvement: O(n) every 500ms -> O(g) where g = number of groups
(typically << n for patterns with multiple colors)
Fixes#32🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Store both original unrotated pesData and uploaded rotated pesData in cache
to ensure exact consistency on resume and prevent issues from algorithm changes
between versions. This fixes rotation/position reset issues after page reload.
- Cache original unrotated pattern + rotation angle for editing
- Cache exact uploaded pattern data sent to machine
- Restore original offset after loading cached pattern
- Use cached uploaded data on resume instead of recalculating
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Removed disabled ESLint rules by refactoring state management to follow React best practices. Changes include:
- Replaced setState-in-effect pattern with state-during-render pattern for viewport resets
- Changed from ref-based to state-based storage for initialScale to avoid ref updates during render
- Implemented React-recommended pattern for deriving state from props (similar to getDerivedStateFromProps)
- Properly track pattern changes in state to detect when viewport should reset
This eliminates the need for ESLint disable comments while maintaining the same functionality. The viewport now correctly resets when patterns change, using a pattern that React explicitly recommends for this use case.
Fixes#30🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Moved state synchronization logic from render phase to useEffect hooks to prevent potential infinite loops and unpredictable behavior. Implemented ref-based previous value tracking to detect genuine parent prop changes without causing cascading renders.
Changes:
- Replaced direct setState calls during render with properly structured useEffect hooks
- Added prevOffsetRef and prevRotationRef to track previous prop values
- Documented the "partially controlled" pattern needed for Konva drag interactions
- Added justified ESLint disable comments for legitimate setState-in-effect usage
This fixes a critical React anti-pattern that could cause performance issues and render loops.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Removed all rotation/canvas debug console.log statements
- Added calculateBoundsFromDecodedStitches() utility to eliminate code duplication
- Used calculatePatternCenter() consistently across FileUpload and rotationUtils
- Cleaner code with single source of truth for calculations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The center pattern function was using the old calculation that didn't account for Konva's offsetX/offsetY pivot points. Since the pattern's center is now its pivot point, centering at the origin is simply {x: 0, y: 0}.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Moved KonvaComponents.tsx into PatternCanvas subfolder for better organization and removed the unused RotationHandle component (143 lines) that was replaced by Konva's native Transformer. Updated import paths accordingly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Create two specialized custom hooks for PatternCanvas:
**useCanvasViewport (166 lines)**
- Manages canvas zoom, pan, and container resize
- Handles wheel zoom and button zoom operations
- Tracks container size with ResizeObserver
- Calculates initial scale when pattern changes
- Returns: stagePos, stageScale, containerSize, zoom handlers
**usePatternTransform (179 lines)**
- Manages pattern position, rotation, and transform state
- Handles drag and transform end events
- Syncs local state with global pattern store
- Manages transformer attachment/detachment
- Returns: offsets, rotation, refs, event handlers
Benefits:
- Reduced PatternCanvas from 608 to 388 lines (-220 lines, -36%)
- Better separation of concerns (viewport vs pattern transform)
- Reusable hooks for other canvas components
- Easier to test state management logic in isolation
- Cleaner component with focused responsibility
Combined refactoring impact (Phase 1+2+3):
- Original: 786 lines in single file
- After Phase 3: 388 lines main + hooks + components
- Total reduction: -398 lines (-51%)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Extract three complex inline components into separate files:
- ThreadLegend: Thread color display with metadata (52 lines extracted)
- PatternPositionIndicator: Position/rotation display with locked state (49 lines extracted)
- ZoomControls: Zoom and pan control buttons (41 lines extracted)
Benefits:
- Reduced PatternCanvas.tsx from 730 to 608 lines (-122 lines)
- Cleaner component separation and reusability
- Better testability for individual UI components
- Removed unused icon imports (PlusIcon, MinusIcon, etc.)
- Single responsibility per component
Total refactoring impact (Phase 1+2):
- Before: 786 lines in single file
- After: 608 lines main + 3 focused components
- Reduction: -178 lines of complex inline code
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Extract duplicate code into reusable utilities:
- calculatePatternCenter(): Eliminates 6x duplication of center calculations
- convertPenStitchesToPesFormat(): Eliminates 3x duplication of stitch conversion
- calculateZoomToPoint(): Eliminates 2x duplication of zoom math
Reorganize into subfolder:
- Created src/components/PatternCanvas/ directory
- Moved PatternCanvas.tsx into subfolder
- Added index.ts for clean imports
- All utilities in patternCanvasHelpers.ts
Benefits:
- Reduced ~50+ lines of duplicated code
- Single source of truth for common operations
- Easier to test and maintain
- Better code organization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement comprehensive pattern rotation functionality:
- Use Konva Transformer for native rotation UI with visual handles
- Apply rotation transformation at upload time to stitch coordinates
- Two-layer preview system: original (draggable/rotatable) and uploaded (locked)
- Automatic position compensation for center shifts after rotation
- PEN encoding/decoding with proper bounds calculation from decoded stitches
- Comprehensive unit tests for rotation math and PEN round-trip
- Restore original unrotated pattern on delete
The rotation is applied by transforming stitch coordinates around the pattern's
geometric center, then re-encoding to PEN format. Position adjustments compensate
for center shifts caused by PEN encoder rounding to maintain visual alignment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Replace error button with red error badge in AppHeader
- Add auto-open popover when any error occurs (machine, pairing, or Pyodide)
- Popover auto-closes when errors are cleared
- Respect user dismissals (won't reopen for same error)
- Remove error display from WorkflowStepper (single source of truth)
- Add shortName field to error definitions (max 15 chars)
- Add unit tests to validate error shortName length constraints
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add missing semicolons and adjust line breaks per ESLint rules.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Update the destructive variant to use proper danger colors (light red background with dark red text in light mode, dark red background with light text in dark mode) instead of the previous white background with red text.
This allows semantic use of variant="destructive" throughout the app without custom styling overrides.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Swap destructive and destructive-foreground colors back to correct values. The previous commit accidentally reversed them, causing error states to show white background with red text instead of red background with white text.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Extend the duplicate detection logic from PatternCanvas to ProgressMonitor and PatternInfo. Now all components only show the chart field when it differs from catalogNumber, preventing redundant information display.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fix the Choose File button remaining clickable during the brief window between upload completion and pattern info retrieval. The issue was that labels don't support the disabled attribute, so when using asChild with a label, the disabled state was ignored.
Changes:
- Only use asChild when button is enabled (labels can't be disabled)
- Keep input and button disabled conditions synchronized
- Add uploadProgress check to prevent re-enabling before patternUploaded is true
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace generic key-value skeleton with a proper representation that matches the actual PatternInfo component structure:
- Three-column grid for Size, Stitches, and Colors stats
- Separator line between sections
- Color swatches row with circular placeholders
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhances the Brother color mapping to support exact RGB color matching in
addition to catalog number matching. This follows the logic from the
BrotherColor.FromColor method in the App codebase.
Changes:
- Added optional RGB matching to enhanceThreadWithBrotherColor function
- RGB matching is enabled by default but can be disabled via options
- Enhanced logging to show breakdown of matches (catalog vs RGB)
- Matching priority: catalog number first, then RGB (both exact match only)
Example: A thread with RGB(255,255,255) will now match Brother color "WHITE"
(code 001) even if the catalog number is missing or doesn't match.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit incorporates the Brother thread color mapping from the App codebase
into the web application. When importing PES files, thread colors are now
automatically enhanced with official Brother Embroidery thread information
including proper color names and chart codes.
Changes:
- Added BrotherColor.json database with 56 Brother embroidery thread colors
- Created brotherColors utility module with mapping functions that replicate
the logic from Asura.Core.Models.EmbroideryUtil.GetThreadColorListFromPesx
- Enhanced PES importer client to map thread catalog numbers to Brother colors
- Updated PatternCanvas to avoid showing duplicate chart/catalog information
when they are the same (common for Brother colors)
The mapping logic performs exact color code matching (e.g., "001", "843") and
preserves original thread data when no Brother match is found.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fix bug where total pattern time only included blocks up to current position,
and add comprehensive step-by-step logging for analysis.
Bug Fix:
- Separate total time calculation from elapsed time calculation
- Total time now correctly sums ALL color blocks regardless of current position
- Previously broke early when finding current position, missing remaining blocks
Logging Added:
- Step 1: Log calculation of total time across all blocks
- Step 2: Log calculation of elapsed time based on current stitch position
- Detailed per-block logging showing stitch counts, time conversions, and cumulative values
- Final result summary with total, elapsed, and remaining minutes
This allows proper analysis of the Brother PP1 timing formula:
((stitchCount - 1) * 150ms + 3000ms) / 60000, rounded up to minutes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace fixed height values with flexbox sizing to prevent canvas overflow
when the window height is constrained.
Changes:
- Replace h-[400px] sm:h-[500px] lg:flex-1 with flex-1 on all screen sizes
- Add min-h-0 to CardContent and canvas container for proper flex shrinking
- Canvas now properly respects card boundaries on all screen sizes
The flex-1 min-h-0 pattern ensures the canvas takes available space without
overflowing its parent card, regardless of viewport size.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace the custom connection indicator dots with the shadcn StatusIndicator
component for better visual consistency and state indication.
Changes:
- Install status-indicator component from 8starlabs shadcn registry
- Add getStatusIndicatorState() helper to map MachineStateCategory to StatusIndicator states
- Replace connection indicator divs with StatusIndicator component in AppHeader
- Connection indicator now shows state-dependent colors:
- Green (active): Connected and ready, sewing, or complete
- Yellow (fixing): Connected and waiting for user action
- Red (down): Connected but interrupted or in error state
- Gray (idle): Disconnected
- Remove unused color prop from StatusIndicator component
The StatusIndicator provides animated visual feedback for different machine
states, making it easier for users to understand the current system status at
a glance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace custom scrollable div implementation with shadcn ScrollArea component
for the color blocks list in ProgressMonitor. This improves code maintainability
and provides consistent styling across the application.
Changes:
- Install @radix-ui/react-scroll-area via shadcn CLI
- Add scroll-area.tsx component with proper @/ path alias
- Replace custom scrollable div with ScrollArea wrapper
- Remove manual scroll handling:
- Removed showGradient state and useState import
- Removed colorBlocksScrollRef ref
- Removed handleColorBlocksScroll function
- Removed resize listener useEffect
- Removed gradient overlay div
- Fix ScrollArea height constraint with lg:h-0 for proper flexbox scrolling
- Simplify component structure with 50+ fewer lines of scroll handling code
The ScrollArea component provides better accessibility and consistent scrollbar
styling while eliminating the need for manual scroll position tracking.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Migrated serial number tooltip from native title to shadcn Tooltip
- Shows formatted machine details (serial, MAC, total stitches, service count)
- Better multi-line formatting with proper spacing
- Added tooltip to machine status badge
- Shows state description explaining current status
- Added tooltip to auto-refresh spinner
- Shows "Auto-refreshing machine status"
- Removed redundant title attributes
- Disconnect button already has clear label
- Error button has Popover for details
Benefits:
- Consistent tooltip styling across the app
- Better accessibility with ARIA attributes
- Improved readability with proper formatting
- Better positioning and animations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Exclude machine error code 0xDD (221 decimal) from error popover
- This error code is non-critical and should not be displayed to users
- Maintains all other error codes and messages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added eslint-disable-next-line for react-refresh/only-export-components
- Badge and Button components export utility functions (badgeVariants, buttonVariants)
- These utilities are required by shadcn pattern for variant composition
- Suppressing warning is appropriate since this is intentional design
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Migrated ErrorPopover to use shadcn PopoverContent
- Removed forwardRef wrapper (handled by shadcn internally)
- Replaced custom absolute positioning with PopoverContent component
- Used cn() utility for cleaner className management
- Maintained all styling with info/danger color variants
- Updated AppHeader to use Popover pattern
- Replaced manual state management (showErrorPopover/setErrorPopover)
- Removed refs and click-outside detection useEffect
- Wrapped error button in Popover component with PopoverTrigger
- Simplified code by removing 30+ lines of manual popover handling
Benefits:
- Better keyboard navigation and accessibility (built into Radix UI)
- Automatic focus management and ARIA attributes
- Cleaner, more maintainable code
- Consistent with other shadcn components in the app
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>