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>
- Fixed CSS variable format by wrapping HSL values in hsl() function
- Removed double hsl() wrapping in @theme section (now uses var() directly)
- Switched dark mode card background to OKLCH for better perceptual uniformity
- Improved dark mode border visibility (17.5% → 37.5% lightness)
- Improved dark mode input visibility (17.5% → 47.5% lightness)
- Changed app background from gray-300 to gray-100 for cleaner appearance
- Enhanced PatternCanvasPlaceholder to match PatternCanvas styling with icon and description
These changes ensure shadcn components use colors correctly and improve overall dark mode readability.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Migrated ConfirmDialog to use shadcn AlertDialog component
- Replaced custom modal structure with AlertDialogContent, AlertDialogHeader, AlertDialogFooter
- Used AlertDialogAction and AlertDialogCancel for buttons
- Maintained danger/warning variant support with border-top styling
- Simplified code by removing manual escape key handling (built into AlertDialog)
- Migrated BluetoothDevicePicker to use shadcn Dialog component
- Replaced custom modal structure with DialogContent, DialogHeader, DialogFooter
- Used shadcn Button components with outline variant for device selection
- Maintained scanning state UI with loading spinner in DialogDescription
- Simplified code by removing manual escape key handling (built into Dialog)
- Disabled close button for better UX during device selection
Both migrations maintain the same external API and functionality while significantly reducing code complexity.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Applied prettier auto-formatting to all component and utility files
- Fixed semicolons, commas, and indentation throughout codebase
- No functional changes, only code style improvements
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Migrated PatternCanvas component to use shadcn Card components (Card, CardHeader, CardTitle, CardDescription, CardContent)
- Replaced custom zoom control buttons with shadcn Button component using outline variant and icon size
- Renamed PatternPreviewPlaceholder to PatternCanvasPlaceholder for consistency
- Updated all imports and references in App.tsx
- Maintained all existing functionality including Konva canvas rendering, zoom controls, and pattern positioning
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrated PatternPreviewPlaceholder component to use shadcn Card, CardHeader, and CardTitle components for consistent styling with other card-based components.
Changes:
- Replaced outer div with shadcn Card component
- Migrated header to use CardHeader and CardTitle
- Wrapped content in CardContent
- Applied consistent Card padding (p-0 gap-0)
- Maintained header border separator styling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrated ProgressMonitor component to use shadcn Card, Button, and Progress components. Removed redundant state visual indicator section that duplicated information already shown by progress bars, color blocks, and action buttons. Changed active thread block styling from vibrant accent colors to muted grays for better visual hierarchy.
Changes:
- Replaced outer div with shadcn Card component
- Migrated header to use CardHeader with CardTitle and CardDescription
- Replaced custom progress bars with shadcn Progress components
- Migrated all action buttons (Resume Sewing, Start Sewing, Start Mask Trace) to shadcn Button
- Removed redundant state visual indicator section (Ready/Active/Complete status)
- Removed unused imports (getStateVisualInfo and related icons)
- Applied consistent Card padding (p-0 gap-0, explicit CardHeader and CardContent padding)
- Changed active thread block from accent colors to muted grays (border, background, icon, progress bar)
- Removed violet border override on active color swatch
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added shadcn Tooltip component for interactive pattern information. Wrapped all PatternInfo stat boxes and color swatches in tooltips with detailed metadata and explanations. Migrated PatternSummaryCard to use CardHeader/CardTitle/CardDescription for better semantic structure. Fixed Card component spacing issues across all cards.
Changes:
- Installed and added shadcn Tooltip component
- Added tooltips to Size, Stitches, and Colors stat boxes with explanatory text
- Wrapped color swatches in Tooltips with detailed thread information
- Added Separator between pattern stats and colors sections
- Migrated PatternSummaryCard to use CardHeader with semantic title/description
- Fixed Card gap-0 on all cards (FileUpload, PatternSummaryCard, ConnectionPrompt)
- Added explicit padding to PatternSummaryCard: CardHeader (p-4 pb-3) and CardContent (px-4 pt-0 pb-4)
- Updated components.json to use src/ paths instead of @/ aliases to fix shadcn install location
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrated ConnectionPrompt to use shadcn/ui Card and CardContent components for consistency with other card-based components (FileUpload, PatternSummaryCard). Maintains the same visual appearance while using the unified Card system.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrated FileUpload component to use shadcn/ui Card, Button, Alert, and Progress components. Updated dark mode CSS variables to use media query approach for automatic system theme detection. Fixed Card component padding and button styling for better visual consistency.
Changes:
- Replaced custom div with shadcn Card and CardContent components
- Migrated buttons to shadcn Button component with outline variant for Choose File
- Replaced custom alerts with shadcn Alert components
- Replaced custom progress bars with shadcn Progress component
- Fixed Card padding by adding p-0 to Card and rounded-lg to CardContent
- Changed dark mode from .dark class to @media (prefers-color-scheme: dark)
- Fixed primary-foreground color in dark mode for proper white text contrast
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The src/assets directory only contained a react.svg file that was not referenced anywhere in the codebase. This appears to be leftover boilerplate from the project initialization.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The pyodideLoader was superseded by the worker-based pattern converter (client.ts and worker.ts) which runs Pyodide in a background thread. This legacy code is no longer referenced anywhere in the codebase.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Define semantic color theme in App.css using @theme directive
- Primary (blue), Success (green), Warning (amber), Danger (red)
- Info (cyan), Accent (purple), Secondary (orange), Tertiary (teal)
- Semantic colors reference Tailwind color variables via var()
- Media query-based dark mode for canvas colors
- Migrate all 16 components from direct Tailwind colors to semantic names
- Create cssVariables.ts utility for Konva canvas color access
- Update KonvaComponents to use CSS variables dynamically
- Replace @apply with CSS variables in index.css for v4 compatibility
- Remove unused designTokens.ts file
- Improve light mode contrast with gray-300 app background
- Adjust canvas and info box backgrounds to gray-200
Benefits:
- Easy theme customization by updating App.css @theme block
- Consistent semantic naming across all components
- Proper dark mode support via media queries
- No visual regressions, all colors maintained
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Auto-formatted by linter:
- Single quotes → double quotes
- Line wrapping for better readability
No logic changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This hook was replaced by useMachineStore (Zustand) and is no longer
used anywhere in the codebase. All functionality has been migrated to
the centralized machine store.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implemented proper time calculation matching the Brother app algorithm:
- 150ms per stitch + 3000ms startup time per color block
- Calculate total and elapsed time by summing across color blocks
- This fixes the "999 seconds" issue by calculating time accurately
Created timeCalculation utility with:
- convertStitchesToMinutes: Convert stitches to minutes using PP1 formula
- calculatePatternTime: Calculate total/elapsed time per color blocks
Updated ProgressMonitor to show:
- Total Time (calculated from all color blocks)
- Elapsed Time / Total Time (based on current stitch position)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When the machine reports 0 total stitches in patternInfo, fall back to
using the PEN data stitch count (penStitches.stitches.length) for UI
display. This ensures progress percentage and stitch counts display
correctly even when the machine hasn't fully initialized pattern info.
Updated ProgressMonitor to use derived totalStitches value that prefers
patternInfo.totalStitches but falls back to PEN data when needed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Removed redundant patternUploaded state from PatternStore and replaced
it with a derived selector usePatternUploaded() in MachineStore that
computes it from patternInfo !== null.
This eliminates duplicate state, removes the need for synchronization
logic, and ensures a single source of truth for pattern upload status.
Updated all components (App, LeftSidebar, FileUpload, PatternCanvas,
WorkflowStepper) to use the derived selector.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Extracted App.tsx (469 lines → 91 lines) into 5 new components:
- ErrorPopover: Displays error details with solutions (84 lines)
- AppHeader: Machine status, workflow stepper, errors (207 lines)
- ConnectionPrompt: Connect button or browser warning (67 lines)
- LeftSidebar: Conditional rendering of controls (42 lines)
- PatternPreviewPlaceholder: Empty state for preview (46 lines)
This improves code organization, maintainability, and reusability.
Each component now has a single, clear responsibility.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Created shared PatternInfo component for displaying pattern statistics
(size, stitch count, colors) used in both FileUpload and PatternSummaryCard.
Reduces code duplication and ensures consistency across the UI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Shows actual machine stitch count (including lock stitches) in both
FileUpload and PatternSummaryCard components, with original PES count
in lighter gray when they differ.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Detects if the browser supports Web Bluetooth API and displays an
informative warning when unsupported. Provides users with clear
options: use a compatible browser or download the desktop app.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Detects if the browser supports Web Bluetooth API and displays an
informative warning when unsupported. Provides users with clear
options: use a compatible browser or download the desktop app.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The application now properly detects when a Brother PP1 machine is not paired with the operating system. Previously, the connection would appear successful but commands would fail silently.
Changes:
- Enhanced sendCommand() to check for empty or invalid responses during initial connection
- Updated connect() to validate the connection with a test command (getMachineState)
- Properly disconnect and clean up when pairing is not established
- Throw BluetoothPairingError with helpful instructions for the user
When the machine is not paired, users now see a clear error message instructing them to:
1. Long-press the Bluetooth button on the machine
2. Pair via the operating system's Bluetooth settings
3. Try connecting again
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The COLOR_END and CUT commands must be on the same stitch, not separate stitches.
This was causing the machine to execute an extra stitch with the new color before
the jump to the new position.
Changes:
- Combine COLOR_END (X flag) and CUT (Y flag) into single stitch at old position
- Machine now correctly pauses after cut, before jumping to new color section
- Update all color change tests to expect combined COLOR_END+CUT stitch
The correct sequence is now:
1. Finishing lock stitches (old color)
2. COLOR_END+CUT stitch (old color) ← Machine pauses here
3. Jump to new position (new color)
4. Starting lock stitches (new color)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The encoder now adds 8 lock stitches at the very beginning of every pattern,
matching the behavior of the original C# PesxToPen.cs code (Nuihajime_TomeDataPlus
is called when counter <= 2).
Key changes:
- Find first non-MOVE stitch for lock stitch placement
- Add 8 starting lock stitches before main encoding loop
- Calculate forward-looking direction for optimal knot hiding
- Update all 30 tests to account for starting lock stitches
Also added tests to verify:
- DATA_END flag is automatically added to last stitch
- Starting lock stitches are correctly placed at pattern start
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added tests to verify the encoder correctly handles pattern termination:
- Test that DATA_END flag is added to last stitch even without END flag
- Test that DATA_END flag is added when input has explicit END flag
This ensures patterns are always properly terminated in PEN format,
regardless of whether the input stitches have an END marker.
All 29 tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Renamed and restructured PEN parsing to match encoder pattern:
Changes:
- Renamed pen/parser.ts → pen/decoder.ts (consistent with encoder.ts)
- Created pen/types.ts for PEN-specific type definitions
- Moved types out of machine.ts (they're format-specific, not machine-specific)
- Unified decoder with encoder test helpers (encoder.test.ts now uses decoder)
New structure:
- decodePenStitch() - decode single 4-byte stitch
- decodeAllPenStitches() - decode all stitches from bytes
- decodePenData() - full decode with color blocks and bounds
- getStitchColor() - helper to get color for a stitch index
Type definitions:
- DecodedPenStitch - individual stitch with coordinates and flags
- PenColorBlock - color block (stitch range for one thread)
- DecodedPenData - complete decoded pattern data
Backward compatibility:
- Added compatibility aliases (isJump, flags, startStitch, endStitch)
- Maintains API compatibility with existing UI code
Also added dist-electron to eslint ignore list.
All tests passing (27/27), build successful, lint clean.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Moved embroidery format-related code from utils to new formats folder:
Structure:
- src/formats/pen/ - PEN format encoding and parsing
- encoder.ts (was utils/penEncoder.ts)
- encoder.test.ts (was utils/penEncoder.test.ts)
- parser.ts (was utils/penParser.ts)
- PEN constants moved inline to encoder.ts
- src/formats/import/ - Pattern import/conversion (currently PES)
- worker.ts (was workers/patternConverter.worker.ts)
- client.ts (was utils/patternConverterClient.ts)
- pesImporter.ts (was utils/pystitchConverter.ts)
- pyodideLoader.ts (was utils/pyodideLoader.ts)
- constants.ts (PyStitch/pyembroidery constants)
Benefits:
- Better separation of concerns
- PEN encoder is co-located with PEN parser
- Import logic is in one place and extensible for other formats
- Removed utils/embroideryConstants.ts - split into appropriate locations
- Updated all 18 import references across the codebase
All tests passing, build successful.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhanced penEncoder test suite with byte-level sequence verification:
- Added helper functions to decode PEN bytes for detailed assertions
- Replaced length-only tests with precise operation order verification
- Added tests for color change sequences (same position and with jump)
- Added test for color change followed by explicit JUMP command
- Added test for long jump sequences with lock stitches
- Verified exact placement of lock stitches, cuts, jumps, and color markers
All 27 tests passing with comprehensive coverage of PEN format encoding.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Extract PEN encoding logic into separate testable module (penEncoder.ts)
- Implement 24 comprehensive tests covering:
- Position encoding and coordinate shifting
- Lock stitch direction calculation (forward/backward)
- Lock stitch generation with rotation
- Full PEN encoding with color changes, jumps, and bounds
- Edge cases (empty arrays, single stitches, TRIM flags)
- Setup vitest for testing
- Refactor pattern converter worker to use extracted penEncoder module
- Fix bounds calculation to include non-MOVE stitches (not just STITCH)
- Remove duplicate function definitions from worker
- Add test scripts: npm run test, npm run test:ui, npm run test:run
All tests passing (24/24) and build successful.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>