+ {containerSize.width > 0 && (
+
{
+ if (stageRef.current) {
+ stageRef.current.container().style.cursor = "grabbing";
+ }
+ }}
+ onDragEnd={() => {
+ if (stageRef.current) {
+ stageRef.current.container().style.cursor = "grab";
+ }
+ }}
+ ref={(node) => {
+ stageRef.current = node;
+ if (node) {
+ node.container().style.cursor = "grab";
+ }
+ }}
+ >
+ {/* Background layer: grid, origin, hoop */}
+
+ {pesData && (
+ <>
+
+
+ {machineInfo && }
+ >
+ )}
+
- {/* Pattern layer: draggable stitches and bounds */}
-
- {pesData && (
- {
- const stage = e.target.getStage();
- if (stage && !patternUploaded && !isUploading)
- stage.container().style.cursor = "move";
- }}
- onMouseLeave={(e) => {
- const stage = e.target.getStage();
- if (stage && !patternUploaded && !isUploading)
- stage.container().style.cursor = "grab";
- }}
- >
- {
- // Convert PEN stitch format {x, y, flags, isJump} to PES format [x, y, cmd, colorIndex]
- const cmd = s.isJump ? 0x10 : 0; // MOVE flag if jump
- const colorIndex =
- pesData.penStitches.colorBlocks.find(
- (b) => i >= b.startStitch && i <= b.endStitch,
- )?.colorIndex ?? 0;
- return [s.x, s.y, cmd, colorIndex];
- },
- )}
- pesData={pesData}
- currentStitchIndex={sewingProgress?.currentStitch || 0}
- showProgress={patternUploaded || isUploading}
- />
-
-
- )}
-
-
- {/* Current position layer */}
-
- {pesData &&
- pesData.penStitches &&
- sewingProgress &&
- sewingProgress.currentStitch > 0 && (
-
-
+ {pesData && (
+ {
+ const stage = e.target.getStage();
+ if (stage && !patternUploaded && !isUploading)
+ stage.container().style.cursor = "move";
+ }}
+ onMouseLeave={(e) => {
+ const stage = e.target.getStage();
+ if (stage && !patternUploaded && !isUploading)
+ stage.container().style.cursor = "grab";
+ }}
+ >
+ {
- const cmd = s.isJump ? 0x10 : 0;
+ // Convert PEN stitch format {x, y, flags, isJump} to PES format [x, y, cmd, colorIndex]
+ const cmd = s.isJump ? 0x10 : 0; // MOVE flag if jump
const colorIndex =
pesData.penStitches.colorBlocks.find(
(b) => i >= b.startStitch && i <= b.endStitch,
@@ -379,138 +362,175 @@ export function PatternCanvas() {
return [s.x, s.y, cmd, colorIndex];
},
)}
+ pesData={pesData}
+ currentStitchIndex={sewingProgress?.currentStitch || 0}
+ showProgress={patternUploaded || isUploading}
/>
+
)}
-
-
- )}
+
- {/* Placeholder overlay when no pattern is loaded */}
- {!pesData && (
-
- Load a PES file to preview the pattern
-
- )}
+ {/* Current position layer */}
+
+ {pesData &&
+ pesData.penStitches &&
+ sewingProgress &&
+ sewingProgress.currentStitch > 0 && (
+
+ {
+ const cmd = s.isJump ? 0x10 : 0;
+ const colorIndex =
+ pesData.penStitches.colorBlocks.find(
+ (b) => i >= b.startStitch && i <= b.endStitch,
+ )?.colorIndex ?? 0;
+ return [s.x, s.y, cmd, colorIndex];
+ },
+ )}
+ />
+
+ )}
+
+
+ )}
- {/* Pattern info overlays */}
- {pesData && (
- <>
- {/* Thread Legend Overlay */}
-
-
- Colors
-
- {pesData.uniqueColors.map((color, idx) => {
- // Primary metadata: brand and catalog number
- const primaryMetadata = [
- color.brand,
- color.catalogNumber ? `#${color.catalogNumber}` : null,
- ]
- .filter(Boolean)
- .join(" ");
+ {/* Placeholder overlay when no pattern is loaded */}
+ {!pesData && (
+
+ Load a PES file to preview the pattern
+
+ )}
- // Secondary metadata: chart and description
- const secondaryMetadata = [color.chart, color.description]
- .filter(Boolean)
- .join(" ");
+ {/* Pattern info overlays */}
+ {pesData && (
+ <>
+ {/* Thread Legend Overlay */}
+
+
+ Colors
+
+ {pesData.uniqueColors.map((color, idx) => {
+ // Primary metadata: brand and catalog number
+ const primaryMetadata = [
+ color.brand,
+ color.catalogNumber ? `#${color.catalogNumber}` : null,
+ ]
+ .filter(Boolean)
+ .join(" ");
- return (
-
+ // Secondary metadata: chart and description
+ const secondaryMetadata = [color.chart, color.description]
+ .filter(Boolean)
+ .join(" ");
+
+ return (
-
-
- Color {idx + 1}
-
- {(primaryMetadata || secondaryMetadata) && (
-
- {primaryMetadata}
- {primaryMetadata && secondaryMetadata && (
-
•
- )}
- {secondaryMetadata}
+ key={idx}
+ className="flex items-start gap-1.5 sm:gap-2 mb-1 sm:mb-1.5 last:mb-0"
+ >
+
+
+
+ Color {idx + 1}
- )}
+ {(primaryMetadata || secondaryMetadata) && (
+
+ {primaryMetadata}
+ {primaryMetadata && secondaryMetadata && (
+ •
+ )}
+ {secondaryMetadata}
+
+ )}
+
-
- );
- })}
-
+ );
+ })}
+
- {/* Pattern Offset Indicator */}
-
-
-
- Pattern Position:
+ {/* Pattern Offset Indicator */}
+
+
+
+ Pattern Position:
+
+ {patternUploaded && (
+
+
+ LOCKED
+
+ )}
+
+
+ X: {(localPatternOffset.x / 10).toFixed(1)}mm, Y:{" "}
+ {(localPatternOffset.y / 10).toFixed(1)}mm
+
+
+ {patternUploaded
+ ? "Pattern locked • Drag background to pan"
+ : "Drag pattern to move • Drag background to pan"}
- {patternUploaded && (
-
-
- LOCKED
-
- )}
-
- X: {(localPatternOffset.x / 10).toFixed(1)}mm, Y:{" "}
- {(localPatternOffset.y / 10).toFixed(1)}mm
-
-
- {patternUploaded
- ? "Pattern locked • Drag background to pan"
- : "Drag pattern to move • Drag background to pan"}
-
-
- {/* Zoom Controls Overlay */}
-
-
-
-
- {Math.round(stageScale * 100)}%
-
-
-
-
- >
- )}
-
-
+ {/* Zoom Controls Overlay */}
+
+
+
+
+ {Math.round(stageScale * 100)}%
+
+
+
+
+ >
+ )}
+
+
+
);
}
diff --git a/src/components/PatternCanvasPlaceholder.tsx b/src/components/PatternCanvasPlaceholder.tsx
new file mode 100644
index 0000000..d2ebf1e
--- /dev/null
+++ b/src/components/PatternCanvasPlaceholder.tsx
@@ -0,0 +1,75 @@
+import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
+
+export function PatternCanvasPlaceholder() {
+ return (
+
+
+ Pattern Preview
+
+
+
+ {/* Decorative background pattern */}
+
+
+
+
+
+ No Pattern Loaded
+
+
+ Connect to your machine and choose a PES embroidery file to see
+ your design preview
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/PatternPreviewPlaceholder.tsx b/src/components/PatternPreviewPlaceholder.tsx
deleted file mode 100644
index 6e4cb01..0000000
--- a/src/components/PatternPreviewPlaceholder.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
-
-export function PatternPreviewPlaceholder() {
- return (
-
-
- Pattern Preview
-
-
-
- {/* Decorative background pattern */}
-
-
-
-
-
- No Pattern Loaded
-
-
- Connect to your machine and choose a PES embroidery file to see your
- design preview
-
-
-
-
-
-
- );
-}