From 0e504c3069abf24806e925301b522cd5566eaf04 Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Wed, 17 Dec 2025 00:09:14 +0100 Subject: [PATCH 1/4] fix: Use decoded penStitches for progress monitor color blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src/components/ProgressMonitor.tsx | 46 +++++++++++------------------- 1 file changed, 16 insertions(+), 30 deletions(-) diff --git a/src/components/ProgressMonitor.tsx b/src/components/ProgressMonitor.tsx index bc1a874..0f1d3ea 100644 --- a/src/components/ProgressMonitor.tsx +++ b/src/components/ProgressMonitor.tsx @@ -60,9 +60,9 @@ export function ProgressMonitor() { ? ((sewingProgress?.currentStitch || 0) / patternInfo.totalStitches) * 100 : 0; - // Calculate color block information from pesData + // Calculate color block information from decoded penStitches const colorBlocks = useMemo(() => { - if (!pesData) return []; + if (!pesData || !pesData.penStitches) return []; const blocks: Array<{ colorIndex: number; @@ -76,34 +76,20 @@ export function ProgressMonitor() { threadChart: string | null; }> = []; - let currentColorIndex = pesData.stitches[0]?.[3] ?? 0; - let blockStartStitch = 0; - - for (let i = 0; i < pesData.stitches.length; i++) { - const stitchColorIndex = pesData.stitches[i][3]; - - // When color changes, save the previous block - if ( - stitchColorIndex !== currentColorIndex || - i === pesData.stitches.length - 1 - ) { - const endStitch = i === pesData.stitches.length - 1 ? i + 1 : i; - const thread = pesData.threads[currentColorIndex]; - blocks.push({ - colorIndex: currentColorIndex, - threadHex: thread?.hex || "#000000", - threadCatalogNumber: thread?.catalogNumber ?? null, - threadBrand: thread?.brand ?? null, - threadDescription: thread?.description ?? null, - threadChart: thread?.chart ?? null, - startStitch: blockStartStitch, - endStitch: endStitch, - stitchCount: endStitch - blockStartStitch, - }); - - currentColorIndex = stitchColorIndex; - blockStartStitch = i; - } + // Use the pre-computed color blocks from decoded PEN data + for (const penBlock of pesData.penStitches.colorBlocks) { + const thread = pesData.threads[penBlock.colorIndex]; + blocks.push({ + colorIndex: penBlock.colorIndex, + threadHex: thread?.hex || "#000000", + threadCatalogNumber: thread?.catalogNumber ?? null, + threadBrand: thread?.brand ?? null, + threadDescription: thread?.description ?? null, + threadChart: thread?.chart ?? null, + startStitch: penBlock.startStitchIndex, + endStitch: penBlock.endStitchIndex, + stitchCount: penBlock.endStitchIndex - penBlock.startStitchIndex, + }); } return blocks; From a6d9d266f85b55d637789a7e2ef05c302ede9d16 Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Wed, 17 Dec 2025 00:21:02 +0100 Subject: [PATCH 2/4] fix: Display PEN stitch count with PES count in parentheses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/components/FileUpload.tsx | 10 +++++++++- src/components/PatternSummaryCard.tsx | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/components/FileUpload.tsx b/src/components/FileUpload.tsx index 3ed4a91..f83a912 100644 --- a/src/components/FileUpload.tsx +++ b/src/components/FileUpload.tsx @@ -211,7 +211,15 @@ export function FileUpload() {
Stitches - {pesData.stitchCount.toLocaleString()} + {pesData.penStitches?.stitches.length.toLocaleString() || pesData.stitchCount.toLocaleString()} + {pesData.penStitches && pesData.penStitches.stitches.length !== pesData.stitchCount && ( + + ({pesData.stitchCount.toLocaleString()}) + + )}
diff --git a/src/components/PatternSummaryCard.tsx b/src/components/PatternSummaryCard.tsx index 9abc0ca..8a62357 100644 --- a/src/components/PatternSummaryCard.tsx +++ b/src/components/PatternSummaryCard.tsx @@ -55,7 +55,15 @@ export function PatternSummaryCard() {
Stitches - {pesData.stitchCount.toLocaleString()} + {pesData.penStitches?.stitches.length.toLocaleString() || pesData.stitchCount.toLocaleString()} + {pesData.penStitches && pesData.penStitches.stitches.length !== pesData.stitchCount && ( + + ({pesData.stitchCount.toLocaleString()}) + + )}
From e07d6b9a6f3fbefdfc93a925490088b88e8c470f Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Wed, 17 Dec 2025 00:23:56 +0100 Subject: [PATCH 3/4] refactor: Extract PatternInfo component to eliminate duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/components/FileUpload.tsx | 70 +------------------- src/components/PatternInfo.tsx | 92 +++++++++++++++++++++++++++ src/components/PatternSummaryCard.tsx | 72 +-------------------- 3 files changed, 96 insertions(+), 138 deletions(-) create mode 100644 src/components/PatternInfo.tsx diff --git a/src/components/FileUpload.tsx b/src/components/FileUpload.tsx index f83a912..201166a 100644 --- a/src/components/FileUpload.tsx +++ b/src/components/FileUpload.tsx @@ -6,6 +6,7 @@ import { useUIStore } from '../stores/useUIStore'; import { convertPesToPen, type PesPatternData } from '../formats/import/pesImporter'; import { canUploadPattern, getMachineStateCategory } from '../utils/machineStateHelpers'; import { PatternInfoSkeleton } from './SkeletonLoader'; +import { PatternInfo } from './PatternInfo'; import { ArrowUpTrayIcon, CheckCircleIcon, DocumentTextIcon, FolderOpenIcon } from '@heroicons/react/24/solid'; import { createFileService } from '../platform'; import type { IFileService } from '../platform/interfaces/IFileService'; @@ -200,74 +201,7 @@ export function FileUpload() { {!isLoading && pesData && (
-
-
- Size - - {((pesData.bounds.maxX - pesData.bounds.minX) / 10).toFixed(1)} x{' '} - {((pesData.bounds.maxY - pesData.bounds.minY) / 10).toFixed(1)} mm - -
-
- Stitches - - {pesData.penStitches?.stitches.length.toLocaleString() || pesData.stitchCount.toLocaleString()} - {pesData.penStitches && pesData.penStitches.stitches.length !== pesData.stitchCount && ( - - ({pesData.stitchCount.toLocaleString()}) - - )} - -
-
- Colors / Blocks - - {pesData.uniqueColors.length} / {pesData.threads.length} - -
-
- -
- Colors: -
- {pesData.uniqueColors.slice(0, 8).map((color, idx) => { - // Primary metadata: brand and catalog number - const primaryMetadata = [ - color.brand, - color.catalogNumber ? `#${color.catalogNumber}` : null - ].filter(Boolean).join(" "); - - // Secondary metadata: chart and description - const secondaryMetadata = [ - color.chart, - color.description - ].filter(Boolean).join(" "); - - const metadata = [primaryMetadata, secondaryMetadata].filter(Boolean).join(" • "); - - const tooltipText = metadata - ? `Color ${idx + 1}: ${color.hex} - ${metadata}` - : `Color ${idx + 1}: ${color.hex}`; - - return ( -
- ); - })} - {pesData.uniqueColors.length > 8 && ( -
- +{pesData.uniqueColors.length - 8} -
- )} -
-
+
)} diff --git a/src/components/PatternInfo.tsx b/src/components/PatternInfo.tsx new file mode 100644 index 0000000..efff798 --- /dev/null +++ b/src/components/PatternInfo.tsx @@ -0,0 +1,92 @@ +import type { PesPatternData } from '../formats/import/pesImporter'; + +interface PatternInfoProps { + pesData: PesPatternData; + showThreadBlocks?: boolean; +} + +export function PatternInfo({ pesData, showThreadBlocks = false }: PatternInfoProps) { + return ( + <> +
+
+ Size + + {((pesData.bounds.maxX - pesData.bounds.minX) / 10).toFixed(1)} x{' '} + {((pesData.bounds.maxY - pesData.bounds.minY) / 10).toFixed(1)} mm + +
+
+ Stitches + + {pesData.penStitches?.stitches.length.toLocaleString() || pesData.stitchCount.toLocaleString()} + {pesData.penStitches && pesData.penStitches.stitches.length !== pesData.stitchCount && ( + + ({pesData.stitchCount.toLocaleString()}) + + )} + +
+
+ + {showThreadBlocks ? 'Colors / Blocks' : 'Colors'} + + + {showThreadBlocks + ? `${pesData.uniqueColors.length} / ${pesData.threads.length}` + : pesData.uniqueColors.length + } + +
+
+ +
+ Colors: +
+ {pesData.uniqueColors.slice(0, 8).map((color, idx) => { + // Primary metadata: brand and catalog number + const primaryMetadata = [ + color.brand, + color.catalogNumber ? `#${color.catalogNumber}` : null + ].filter(Boolean).join(" "); + + // Secondary metadata: chart and description + const secondaryMetadata = [ + color.chart, + color.description + ].filter(Boolean).join(" "); + + const metadata = [primaryMetadata, secondaryMetadata].filter(Boolean).join(" • "); + + // Show which thread blocks use this color in PatternSummaryCard + const threadNumbers = color.threadIndices.map(i => i + 1).join(", "); + const tooltipText = showThreadBlocks + ? (metadata + ? `Color ${idx + 1}: ${color.hex} - ${metadata}` + : `Color ${idx + 1}: ${color.hex}`) + : (metadata + ? `Color ${idx + 1}: ${color.hex}\n${metadata}\nUsed in thread blocks: ${threadNumbers}` + : `Color ${idx + 1}: ${color.hex}\nUsed in thread blocks: ${threadNumbers}`); + + return ( +
+ ); + })} + {pesData.uniqueColors.length > 8 && ( +
+ +{pesData.uniqueColors.length - 8} +
+ )} +
+
+ + ); +} diff --git a/src/components/PatternSummaryCard.tsx b/src/components/PatternSummaryCard.tsx index 8a62357..a094f35 100644 --- a/src/components/PatternSummaryCard.tsx +++ b/src/components/PatternSummaryCard.tsx @@ -2,6 +2,7 @@ import { useShallow } from 'zustand/react/shallow'; import { useMachineStore } from '../stores/useMachineStore'; import { usePatternStore } from '../stores/usePatternStore'; import { canDeletePattern } from '../utils/machineStateHelpers'; +import { PatternInfo } from './PatternInfo'; import { DocumentTextIcon, TrashIcon } from '@heroicons/react/24/solid'; export function PatternSummaryCard() { @@ -44,76 +45,7 @@ export function PatternSummaryCard() {
-
-
- Size - - {((pesData.bounds.maxX - pesData.bounds.minX) / 10).toFixed(1)} x{' '} - {((pesData.bounds.maxY - pesData.bounds.minY) / 10).toFixed(1)} mm - -
-
- Stitches - - {pesData.penStitches?.stitches.length.toLocaleString() || pesData.stitchCount.toLocaleString()} - {pesData.penStitches && pesData.penStitches.stitches.length !== pesData.stitchCount && ( - - ({pesData.stitchCount.toLocaleString()}) - - )} - -
-
- Colors - - {pesData.uniqueColors.length} - -
-
- -
- Colors: -
- {pesData.uniqueColors.slice(0, 8).map((color, idx) => { - // Primary metadata: brand and catalog number - const primaryMetadata = [ - color.brand, - color.catalogNumber ? `#${color.catalogNumber}` : null - ].filter(Boolean).join(" "); - - // Secondary metadata: chart and description - const secondaryMetadata = [ - color.chart, - color.description - ].filter(Boolean).join(" "); - - const metadata = [primaryMetadata, secondaryMetadata].filter(Boolean).join(" • "); - - // Show which thread blocks use this color - const threadNumbers = color.threadIndices.map(i => i + 1).join(", "); - const tooltipText = metadata - ? `Color ${idx + 1}: ${color.hex}\n${metadata}\nUsed in thread blocks: ${threadNumbers}` - : `Color ${idx + 1}: ${color.hex}\nUsed in thread blocks: ${threadNumbers}`; - - return ( -
- ); - })} - {pesData.uniqueColors.length > 8 && ( -
- +{pesData.uniqueColors.length - 8} -
- )} -
-
+ {canDelete && (