Commit graph

232 commits

Author SHA1 Message Date
Jan-Henrik Bruhn
957a3f07b8
Merge pull request #57 from jhbruhn/fix/memoize-pattern-canvas-calculations
fix: Add memoization to PatternCanvas expensive computations
2025-12-27 17:02:11 +01:00
Jan-Henrik Bruhn
65275c0557 fix: Add memoization to PatternCanvas expensive computations
Extract inline calculations to useMemo hooks to prevent unnecessary
recalculations on every render. Memoized displayPattern selection
and pattern dimensions calculation improve performance with large patterns.

Fixes #34

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 16:56:26 +01:00
Jan-Henrik Bruhn
89bc55b822
Merge pull request #56 from jhbruhn/refactor/extract-sub-components
refactor: Extract sub-components from large components
2025-12-27 16:43:19 +01:00
Jan-Henrik Bruhn
095c879ea3 fix: Address Copilot review feedback
- Simplify StepCircle cursor logic to use isComplete || isCurrent
- Fix UploadButton to use boundsFits prop instead of !!boundsError
- Remove XSS vulnerability by parsing markdown safely without dangerouslySetInnerHTML
- Move ColorBlock type to shared types.ts file to reduce coupling
- Rename useDisplayFilename to getDisplayFilename and move to utils (not a hook)
- Improve threadMetadata JSDoc with detailed examples
- Make WorkflowStep interface properties readonly for full immutability
- Fix PyodideProgress redundant negation logic

All issues from Copilot review resolved.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 16:41:58 +01:00
Jan-Henrik Bruhn
681ce223c3 refactor: Extract FileUpload sub-components and utilities
- Move FileUpload into dedicated folder with sub-components
- Extract FileSelector, PyodideProgress, UploadButton, UploadProgress, BoundsValidator
- Create useDisplayFilename hook for filename priority logic
- Reduce FileUpload.tsx from 391 to 261 lines (33% reduction)

Part of #33: Extract sub-components from large components

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 16:27:17 +01:00
Jan-Henrik Bruhn
dade72453e refactor: Extract ProgressMonitor sub-components and utilities
- Move ProgressMonitor into dedicated folder with sub-components
- Extract ProgressStats, ProgressSection, ColorBlockList, ColorBlockItem, ProgressActions
- Create threadMetadata utility for formatting thread metadata
- Reduce ProgressMonitor.tsx from 389 to 178 lines (54% reduction)

Part of #33: Extract sub-components from large components

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 16:22:12 +01:00
Jan-Henrik Bruhn
2de8cd12ff refactor: Extract WorkflowStepper sub-components and utilities
- Move WorkflowStepper into dedicated folder with sub-components
- Extract StepCircle, StepLabel, and StepPopover components
- Create workflowSteps constant for step definitions
- Extract getCurrentStep logic to workflowStepCalculation utility
- Extract getGuideContent logic to workflowGuideContent utility
- Reduce WorkflowStepper.tsx from 487 to 140 lines (71% reduction)

Part of #33: Extract sub-components from large components

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 13:22:14 +01:00
Jan-Henrik Bruhn
ec19426dd1
Merge pull request #55 from jhbruhn/feature/40-comprehensive-hooks-library
feature: Create comprehensive custom hooks library (Issue #40)
2025-12-27 13:05:19 +01:00
Jan-Henrik Bruhn
d98a19bb4b fix: Address GitHub Copilot review feedback
Resolved all 7 issues identified in PR review:

1. @testing-library/dom peer dependency already explicitly listed
2. Removed invalid eslint-disable comments (replaced with correct rule)
3. Fixed unstable callbacks in useMachinePolling using refs to prevent unnecessary re-renders
4. Fixed useAutoScroll options dependency with useMemo for stability
5. Fixed stale closure in BluetoothDevicePicker using functional setState
6. Fixed memory leak in useBluetoothDeviceListener by preventing re-registration of IPC listeners
7. Added proper eslint-disable for intentional setState in effect with detailed comment

All tests passing (91/91), build successful, linter clean.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 13:04:03 +01:00
Jan-Henrik Bruhn
eff8e15179 fix: Resolve TypeScript strict mode errors in hook tests
- Add type assertions to useErrorPopoverState test rerender calls
- Use non-null assertions for callback invocations in useBluetoothDeviceListener tests
- Fix type inference issues with union types (number | undefined, string | null)
- All 91 tests passing with proper TypeScript compliance

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 12:50:47 +01:00
Jan-Henrik Bruhn
f2b01c59e1 feature: Add comprehensive test suite for custom hooks
Add tests for all hooks with @testing-library/react:
- utility hooks: usePrevious, useAutoScroll, useClickOutside
- domain hooks: useErrorPopoverState, useMachinePolling
- platform hooks: useBluetoothDeviceListener

Changes:
- Install @testing-library/react and jsdom for React hook testing
- Configure vitest to use jsdom environment for React testing
- Add 91 tests covering all hook functionality
- Test state management, effects, event listeners, and async behavior
- Verify proper cleanup and edge cases

All tests passing. Coverage includes error states, polling intervals,
click-outside detection, auto-scroll behavior, and IPC integration.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 12:40:47 +01:00
Jan-Henrik Bruhn
0db0bcd40a fix: Resolve TypeScript import paths and type errors in hooks refactor
- Fix import paths in domain hooks (useErrorPopoverState, useMachinePolling)
- Fix import path in platform hooks (useBluetoothDeviceListener)
- Correct RefObject type signatures in useAutoScroll and useClickOutside
- Add proper type parameters to hook usages in components
- Fix useRef initialization in useMachinePolling
- Add type guard for undefined in useErrorPopoverState

All TypeScript build errors resolved. Build and tests passing.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 12:29:01 +01:00
Jan-Henrik Bruhn
e1aadc9e1f feature: Create comprehensive custom hooks library (WIP)
- Extract 5 new custom hooks:
  * useAutoScroll - Auto-scroll element into view
  * useClickOutside - Detect outside clicks with exclusions
  * useMachinePolling - Dynamic machine status polling
  * useErrorPopoverState - Error popover state management
  * useBluetoothDeviceListener - Bluetooth device discovery

- Reorganize all hooks into categorized folders:
  * utility/ - Generic reusable patterns
  * domain/ - Business logic for embroidery/patterns
  * ui/ - Library integration (Konva)
  * platform/ - Electron/Pyodide specific

- Create barrel exports for clean imports (@/hooks)

- Update components to use new hooks:
  * AppHeader uses useErrorPopoverState
  * ProgressMonitor uses useAutoScroll
  * FileUpload, PatternCanvas use barrel exports

Part 1: Hooks extraction and reorganization
Still TODO: Update remaining components, add tests, add documentation

Related to #40

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 12:19:12 +01:00
Jan-Henrik Bruhn
2372278081
Merge pull request #54 from jhbruhn/refactor/36-improve-useeffect-with-useprevious
Some checks are pending
Build, Test, and Lint / Build, Test, and Lint (push) Waiting to run
Draft Release / Draft Release (push) Waiting to run
Draft Release / Build Web App (push) Blocked by required conditions
Draft Release / Build Release - macos-latest (push) Blocked by required conditions
Draft Release / Build Release - ubuntu-latest (push) Blocked by required conditions
Draft Release / Build Release - windows-latest (push) Blocked by required conditions
Draft Release / Upload to GitHub Release (push) Blocked by required conditions
refactor: Improve useEffect patterns in AppHeader with usePrevious hook
2025-12-27 12:00:58 +01:00
Jan-Henrik Bruhn
705815a8fc fix: Provide initial value to useRef in usePrevious hook
TypeScript requires an initial value argument when calling useRef.
Changed useRef<T>() to useRef<T | undefined>(undefined) to fix
build error and properly type the ref for the first render.

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 11:59:42 +01:00
Jan-Henrik Bruhn
757e0cdd73 fix: Address PR review feedback for usePrevious implementation
- Update usePrevious hook to use useEffect pattern instead of mutating
  refs during render (addresses Concurrent Mode compatibility)
- Add wasManuallyDismissed flag to properly track dismissal of all error
  types (machineError, machineErrorMessage, and pyodideError)
- Add proper eslint-disable comment with explanation for ref access
- Update handlePopoverOpenChange to handle dismissal of all error types

These changes address all feedback from PR review #54

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 11:55:32 +01:00
Jan-Henrik Bruhn
7baf03e4f7 refactor: Improve useEffect patterns in AppHeader with usePrevious hook
Replace manual ref tracking with usePrevious custom hook for cleaner,
more maintainable code. Simplifies error state change detection by
eliminating manual ref updates and reducing complexity.

Resolves #36

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 11:46:45 +01:00
Jan-Henrik Bruhn
03ba6c77e9
Merge pull request #53 from jhbruhn/refactor/39-separate-fileupload-business-logic
refactor: Extract business logic from FileUpload into custom hooks
2025-12-27 11:41:09 +01:00
Jan-Henrik Bruhn
e63a96b024 fix: Address PR review comments
- 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
2025-12-27 11:39:48 +01:00
Jan-Henrik Bruhn
c905c4f5f7 refactor: Extract business logic from FileUpload into custom hooks
**Problem:**
FileUpload component mixed UI and business logic making it:
- Hard to test business logic independently
- Difficult to reuse logic elsewhere
- Component had too many responsibilities (550+ lines)
- Harder to understand and maintain

**Solution:**
Extracted business logic into three focused custom hooks:

1. **useFileUpload** (84 lines)
   - File selection and conversion
   - Pyodide initialization handling
   - Error handling

2. **usePatternRotationUpload** (145 lines)
   - Rotation transformation logic
   - PEN encoding/decoding
   - Center shift calculation
   - Upload orchestration

3. **usePatternValidation** (105 lines)
   - Bounds checking logic
   - Rotated pattern validation
   - Error message generation

**Impact:**
- FileUpload component reduced from 550 → 350 lines (36% smaller)
- Business logic now testable in isolation
- Clear separation of concerns
- Logic can be reused in other components
- Improved maintainability

**Technical Details:**
- All hooks fully typed with TypeScript
- Proper dependency management with useCallback/useMemo
- No behavioral changes
- Build tested successfully
- Linter passed

Fixes #39

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-27 11:36:04 +01:00
Jan-Henrik Bruhn
91bc0285e0
Merge pull request #52 from jhbruhn/fix/38-store-initialization-side-effects
Some checks are pending
Build, Test, and Lint / Build, Test, and Lint (push) Waiting to run
Draft Release / Draft Release (push) Waiting to run
Draft Release / Build Web App (push) Blocked by required conditions
Draft Release / Build Release - macos-latest (push) Blocked by required conditions
Draft Release / Build Release - ubuntu-latest (push) Blocked by required conditions
Draft Release / Build Release - windows-latest (push) Blocked by required conditions
Draft Release / Upload to GitHub Release (push) Blocked by required conditions
fix: Remove module-level side effect from useMachineStore initialization
2025-12-26 23:30:24 +01:00
Jan-Henrik Bruhn
e44bea11c1 fix: Remove module-level side effect from useMachineStore initialization
**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>
2025-12-26 23:29:08 +01:00
Jan-Henrik Bruhn
851d37c1d2
Merge pull request #51 from jhbruhn/refactor/31-split-machine-store
feature: Refactor useMachineStore into focused stores
2025-12-26 23:23:39 +01:00
Jan-Henrik Bruhn
2b5e925e72 feature: Refactor useMachineStore into focused stores
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>
2025-12-26 23:21:25 +01:00
Jan-Henrik Bruhn
48918194a1
Merge pull request #50 from jhbruhn/fix/32-optimize-stitch-rendering
fix: Optimize stitch rendering performance for large patterns
2025-12-26 22:59:08 +01:00
Jan-Henrik Bruhn
381d8e672d fix: Optimize stitch rendering performance for large patterns
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>
2025-12-26 22:57:40 +01:00
Jan-Henrik Bruhn
a3cb6a4e5c
Merge pull request #49 from jhbruhn/fix/48-cached-pattern-disappears-on-reconnect
fix: Store original and uploaded pattern data to prevent rotation inconsistencies
2025-12-26 22:50:57 +01:00
Jan-Henrik Bruhn
03ffe7f71f fix: formatting 2025-12-26 22:50:02 +01:00
Jan-Henrik Bruhn
2b5e1d763b fix: Store original and uploaded pattern data to prevent rotation inconsistencies
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>
2025-12-26 22:48:25 +01:00
Jan-Henrik Bruhn
3ec9dda235
Merge pull request #47 from jhbruhn/fix/30-eslint-hook-rules-useCanvasViewport
fix: Refactor useCanvasViewport to eliminate ESLint hook warnings
2025-12-26 21:55:07 +01:00
Jan-Henrik Bruhn
e8f5c9085c fix: Refactor useCanvasViewport to eliminate ESLint hook warnings
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>
2025-12-26 21:49:58 +01:00
Jan-Henrik Bruhn
446f1b0639
Merge pull request #29 from jhbruhn/fix/state-during-render-usePatternTransform
fix: Resolve state-during-render anti-pattern in usePatternTransform
2025-12-26 21:34:54 +01:00
Jan-Henrik Bruhn
47e32ef83d fix: Resolve state-during-render anti-pattern in usePatternTransform
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>
2025-12-26 21:33:32 +01:00
Jan-Henrik Bruhn
7fd31d209c
Merge pull request #28 from jhbruhn/feature/pattern-rotation
Some checks failed
Build, Test, and Lint / Build, Test, and Lint (push) Has been cancelled
Draft Release / Draft Release (push) Has been cancelled
Draft Release / Build Web App (push) Has been cancelled
Draft Release / Build Release - macos-latest (push) Has been cancelled
Draft Release / Build Release - ubuntu-latest (push) Has been cancelled
Draft Release / Build Release - windows-latest (push) Has been cancelled
Draft Release / Upload to GitHub Release (push) Has been cancelled
Feature: pattern rotation
2025-12-25 21:55:07 +01:00
Jan-Henrik Bruhn
213be4670c
Merge pull request #27 from jhbruhn/fix/error-badge-auto-popup
feature: Add error badge with auto-opening popover for machine errors
2025-12-25 21:54:38 +01:00
Jan-Henrik Bruhn
786464c4c6 fix: TypeScript build errors in WorkflowStepper and errorCodeHelpers
- Remove unreachable error type check in WorkflowStepper icon selection
- Remove error code display block that accessed non-existent properties
- Add missing shortName property to fallback error info objects

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-25 21:53:28 +01:00
Jan-Henrik Bruhn
e92c9f9616 fix: Remove debug logs and refactor with utility functions
- 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>
2025-12-25 21:49:46 +01:00
Jan-Henrik Bruhn
271229c200 fix: Correct center pattern button calculation for Konva pivot points
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>
2025-12-25 21:49:33 +01:00
Jan-Henrik Bruhn
a97439da7b fix: Refactor KonvaComponents and remove unused rotation code
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>
2025-12-25 21:37:34 +01:00
Jan-Henrik Bruhn
469c08d45b feature: Create unified PatternLayer component to eliminate duplication
Unified three separate layer implementations into single PatternLayer component:

**PatternLayer.tsx (156 lines)**
- Handles both interactive (original) and locked (uploaded) pattern rendering
- Props-driven configuration for dragging, rotation, transformer
- Includes CurrentPosition indicator for uploaded patterns
- Memoized stitches and center calculations for performance
- Single source of truth for pattern rendering logic

**Benefits:**
- Eliminated ~140 lines of duplicated layer code
- Reduced PatternCanvas from 388 to 271 lines (-117 lines, -30%)
- Consistent rendering behavior across pattern states
- Easier to maintain and test pattern rendering
- Cleaner component composition in PatternCanvas

**Removed from PatternCanvas:**
- Original pattern layer implementation (74 lines)
- Uploaded pattern layer implementation (33 lines)
- Current position layer implementation (26 lines)
- Duplicate Group, Transformer, Stitches setup
- Redundant center calculations and stitch conversions

**Complete refactoring impact (All Phases):**
- Original: 786 lines in single monolithic file
- After Phase 4: 271 lines main + hooks + components
- **Total reduction: -515 lines (-65%)**

File structure now:
- PatternCanvas.tsx: 271 lines (orchestration)
- PatternLayer.tsx: 156 lines (rendering)
- useCanvasViewport.ts: 166 lines (viewport)
- usePatternTransform.ts: 179 lines (transform)
- 3 display components + 3 utilities

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-25 21:29:24 +01:00
Jan-Henrik Bruhn
99d32f9029 feature: Extract canvas state management into custom React hooks
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>
2025-12-25 21:24:46 +01:00
Jan-Henrik Bruhn
b008fd3aa8 feature: Extract PatternCanvas display components into reusable modules
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>
2025-12-25 21:19:20 +01:00
Jan-Henrik Bruhn
07275fa75a feature: Refactor PatternCanvas with utility functions and folder structure
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>
2025-12-25 21:15:05 +01:00
Jan-Henrik Bruhn
d813c22df5 feature: Add pattern rotation with Konva Transformer
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>
2025-12-25 21:06:48 +01:00
Jan-Henrik Bruhn
dabfa3b35a feature: Add error badge with auto-opening popover for machine errors
- 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>
2025-12-25 16:48:47 +01:00
Jan-Henrik Bruhn
ea879640a2
Merge pull request #26 from jhbruhn/fix/skeleton-fixes
Some checks failed
Build, Test, and Lint / Build, Test, and Lint (push) Has been cancelled
Draft Release / Draft Release (push) Has been cancelled
Draft Release / Build Web App (push) Has been cancelled
Draft Release / Build Release - macos-latest (push) Has been cancelled
Draft Release / Build Release - ubuntu-latest (push) Has been cancelled
Draft Release / Build Release - windows-latest (push) Has been cancelled
Draft Release / Upload to GitHub Release (push) Has been cancelled
Fix: skeleton fixes and upload button disable logic
2025-12-22 12:06:00 +01:00
Jan-Henrik Bruhn
943f117191 style: Apply linter auto-fixes
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>
2025-12-22 12:03:49 +01:00
Jan-Henrik Bruhn
5aa4e15922 fix: Improve destructive Alert variant styling
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>
2025-12-22 12:00:54 +01:00
Jan-Henrik Bruhn
4593b66356 fix: Restore correct destructive variant colors
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>
2025-12-22 11:59:02 +01:00
Jan-Henrik Bruhn
469d9860de fix: Apply chart/catalogNumber duplicate detection to all components
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>
2025-12-22 11:54:30 +01:00