From bf3e397ddb7724a5a7865846190b28e05d2c0036 Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Wed, 10 Dec 2025 21:56:19 +0100 Subject: [PATCH] Redesign UI for fixed-size window with floating guide overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Convert NextStepGuide to collapsible floating overlay in bottom-right - Implement fixed viewport layout (no page scrolling on desktop) - Make color blocks scrollable with dynamic gradient indicator - Add responsive behavior: scrollable on mobile, fixed on desktop - Increase overlay opacity for better readability - Enable full-height columns that expand to fill available space 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src/App.tsx | 66 ++-- src/components/NextStepGuide.tsx | 496 ++++++++++++++++------------- src/components/PatternCanvas.tsx | 8 +- src/components/ProgressMonitor.tsx | 62 +++- 4 files changed, 367 insertions(+), 265 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 22878e4..c6a3166 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -108,9 +108,9 @@ function App() { const StatusIcon = stateIcons[stateVisual.iconName]; return ( -
-
-
+
+
+
{/* Machine Connection Status - Responsive width column */}
@@ -178,7 +178,7 @@ function App() {
-
+
{/* Global errors */} {machine.error && (
)} -
+
{/* Left Column - Controls */} -
+
{/* Connect Button - Show when disconnected */} {!machine.isConnected && (
@@ -284,22 +284,24 @@ function App() { {/* Progress Monitor - Show when pattern is uploaded */} {machine.isConnected && patternUploaded && ( - +
+ +
)}
{/* Right Column - Pattern Preview */} -
+
{pesData ? ( 0 && machine.uploadProgress < 100} /> ) : ( -
-

Pattern Preview

-
+
+

Pattern Preview

+
{/* Decorative background pattern */}
@@ -354,23 +356,23 @@ function App() {
)} - - {/* Next Step Guide - Below pattern preview */} -
{/* Bluetooth Device Picker (Electron only) */}
+ + {/* Next Step Guide - Fixed floating overlay */} +
); } diff --git a/src/components/NextStepGuide.tsx b/src/components/NextStepGuide.tsx index 0adbcff..fc07e89 100644 --- a/src/components/NextStepGuide.tsx +++ b/src/components/NextStepGuide.tsx @@ -1,4 +1,5 @@ -import { InformationCircleIcon, ExclamationTriangleIcon } from '@heroicons/react/24/solid'; +import { useState, useEffect } from 'react'; +import { InformationCircleIcon, ExclamationTriangleIcon, XMarkIcon } from '@heroicons/react/24/solid'; import { MachineStatus } from '../types/machine'; import { getErrorDetails } from '../utils/errorCodeHelpers'; @@ -21,284 +22,333 @@ export function NextStepGuide({ errorMessage, errorCode }: NextStepGuideProps) { - // Don't show if there's an error - show detailed error guidance instead - if (hasError) { - const errorDetails = getErrorDetails(errorCode); + const [isExpanded, setIsExpanded] = useState(true); - // Check if this is informational (like initialization steps) vs a real error - if (errorDetails?.isInformational) { + // Expand when state changes + useEffect(() => { + setIsExpanded(true); + }, [machineStatus, isConnected, hasPattern, patternUploaded, hasError]); + + // Render guide content based on state + const renderContent = () => { + // Don't show if there's an error - show detailed error guidance instead + if (hasError) { + const errorDetails = getErrorDetails(errorCode); + + // Check if this is informational (like initialization steps) vs a real error + if (errorDetails?.isInformational) { + return ( +
+
+ +
+

+ {errorDetails.title} +

+

+ {errorDetails.description} +

+ {errorDetails.solutions && errorDetails.solutions.length > 0 && ( + <> +

Steps:

+
    + {errorDetails.solutions.map((solution, index) => ( +
  1. {solution}
  2. + ))} +
+ + )} +
+
+
+ ); + } + + // Regular error display for actual errors return ( -
-
- +
+
+
-

- {errorDetails.title} +

+ {errorDetails?.title || 'Error Occurred'}

-

- {errorDetails.description} +

+ {errorDetails?.description || errorMessage || 'An error occurred. Please check the machine and try again.'}

- {errorDetails.solutions && errorDetails.solutions.length > 0 && ( + {errorDetails?.solutions && errorDetails.solutions.length > 0 && ( <> -

Steps:

-
    +

    How to Fix:

    +
      {errorDetails.solutions.map((solution, index) => (
    1. {solution}
    2. ))}
    )} + {errorCode !== undefined && ( +

    + Error Code: 0x{errorCode.toString(16).toUpperCase().padStart(2, '0')} +

    + )}
); } - // Regular error display for actual errors - return ( -
-
- -
-

- {errorDetails?.title || 'Error Occurred'} -

-

- {errorDetails?.description || errorMessage || 'An error occurred. Please check the machine and try again.'} -

- {errorDetails?.solutions && errorDetails.solutions.length > 0 && ( - <> -

How to Fix:

-
    - {errorDetails.solutions.map((solution, index) => ( -
  1. {solution}
  2. - ))} -
- - )} - {errorCode !== undefined && ( -

- Error Code: 0x{errorCode.toString(16).toUpperCase().padStart(2, '0')} -

- )} -
-
-
- ); - } - - // Determine what to show based on current state - if (!isConnected) { - return ( -
-
- -
-

Step 1: Connect to Machine

-

To get started, connect to your Brother embroidery machine via Bluetooth.

-
    -
  • Make sure your machine is powered on
  • -
  • Enable Bluetooth on your machine
  • -
  • Click the "Connect to Machine" button below
  • -
-
-
-
- ); - } - - if (!hasPattern) { - return ( -
-
- -
-

Step 2: Load Your Pattern

-

Choose a PES embroidery file from your computer to preview and upload.

-
    -
  • Click "Choose PES File" in the Pattern File section
  • -
  • Select your embroidery design (.pes file)
  • -
  • Review the pattern preview on the right
  • -
  • You can drag the pattern to adjust its position
  • -
-
-
-
- ); - } - - if (!patternUploaded) { - return ( -
-
- -
-

Step 3: Upload Pattern to Machine

-

Send your pattern to the embroidery machine to prepare for sewing.

-
    -
  • Review the pattern preview to ensure it's positioned correctly
  • -
  • Check the pattern size matches your hoop
  • -
  • Click "Upload to Machine" when ready
  • -
  • Wait for the upload to complete (this may take a minute)
  • -
-
-
-
- ); - } - - // Pattern is uploaded, guide based on machine status - switch (machineStatus) { - case MachineStatus.IDLE: + // Determine what to show based on current state + if (!isConnected) { return ( -
-
- +
+
+
-

Step 4: Start Mask Trace

-

The mask trace helps the machine understand the pattern boundaries.

+

Step 1: Connect to Machine

+

To get started, connect to your Brother embroidery machine via Bluetooth.

    -
  • Click "Start Mask Trace" button in the Sewing Progress section
  • -
  • The machine will trace the pattern outline
  • -
  • This ensures the hoop is positioned correctly
  • +
  • Make sure your machine is powered on
  • +
  • Enable Bluetooth on your machine
  • +
  • Click the "Connect to Machine" button below
); + } - case MachineStatus.MASK_TRACE_LOCK_WAIT: + if (!hasPattern) { return ( -
-
- +
+
+
-

Machine Action Required

-

The machine is ready to trace the pattern outline.

-
    -
  • Press the button on your machine to confirm and start the mask trace
  • -
  • Ensure the hoop is properly attached
  • -
  • Make sure the needle area is clear
  • +

    Step 2: Load Your Pattern

    +

    Choose a PES embroidery file from your computer to preview and upload.

    +
      +
    • Click "Choose PES File" in the Pattern File section
    • +
    • Select your embroidery design (.pes file)
    • +
    • Review the pattern preview on the right
    • +
    • You can drag the pattern to adjust its position
); + } - case MachineStatus.MASK_TRACING: + if (!patternUploaded) { return ( -
-
- +
+
+
-

Mask Trace In Progress

-

The machine is tracing the pattern boundary. Please wait...

-
    -
  • Watch the machine trace the outline
  • -
  • Verify the pattern fits within your hoop
  • -
  • Do not interrupt the machine
  • +

    Step 3: Upload Pattern to Machine

    +

    Send your pattern to the embroidery machine to prepare for sewing.

    +
      +
    • Review the pattern preview to ensure it's positioned correctly
    • +
    • Check the pattern size matches your hoop
    • +
    • Click "Upload to Machine" when ready
    • +
    • Wait for the upload to complete (this may take a minute)
); + } - case MachineStatus.MASK_TRACE_COMPLETE: - case MachineStatus.SEWING_WAIT: - return ( -
-
- -
-

Step 5: Ready to Sew!

-

The machine is ready to begin embroidering your pattern.

-
    -
  • Verify your thread colors are correct
  • -
  • Ensure the fabric is properly hooped
  • -
  • Click "Start Sewing" when ready
  • -
+ // Pattern is uploaded, guide based on machine status + switch (machineStatus) { + case MachineStatus.IDLE: + return ( +
+
+ +
+

Step 4: Start Mask Trace

+

The mask trace helps the machine understand the pattern boundaries.

+
    +
  • Click "Start Mask Trace" button in the Sewing Progress section
  • +
  • The machine will trace the pattern outline
  • +
  • This ensures the hoop is positioned correctly
  • +
+
-
- ); + ); - case MachineStatus.SEWING: - return ( -
-
- -
-

Step 6: Sewing In Progress

-

Your embroidery is being stitched. Monitor the progress below.

-
    -
  • Watch the progress bar and current stitch count
  • -
  • The machine will pause when a color change is needed
  • -
  • Do not leave the machine unattended
  • -
+ case MachineStatus.MASK_TRACE_LOCK_WAIT: + return ( +
+
+ +
+

Machine Action Required

+

The machine is ready to trace the pattern outline.

+
    +
  • Press the button on your machine to confirm and start the mask trace
  • +
  • Ensure the hoop is properly attached
  • +
  • Make sure the needle area is clear
  • +
+
-
- ); + ); - case MachineStatus.COLOR_CHANGE_WAIT: - return ( -
-
- -
-

Thread Change Required

-

The machine needs a different thread color to continue.

-
    -
  • Check the color blocks section to see which thread is needed
  • -
  • Change to the correct thread color
  • -
  • Press the button on your machine to resume sewing
  • -
+ case MachineStatus.MASK_TRACING: + return ( +
+
+ +
+

Mask Trace In Progress

+

The machine is tracing the pattern boundary. Please wait...

+
    +
  • Watch the machine trace the outline
  • +
  • Verify the pattern fits within your hoop
  • +
  • Do not interrupt the machine
  • +
+
-
- ); + ); - case MachineStatus.PAUSE: - case MachineStatus.STOP: - case MachineStatus.SEWING_INTERRUPTION: - return ( -
-
- -
-

Sewing Paused

-

The embroidery has been paused or interrupted.

-
    -
  • Check if everything is okay with the machine
  • -
  • Click "Resume Sewing" when ready to continue
  • -
  • The machine will pick up where it left off
  • -
+ case MachineStatus.MASK_TRACE_COMPLETE: + case MachineStatus.SEWING_WAIT: + return ( +
+
+ +
+

Step 5: Ready to Sew!

+

The machine is ready to begin embroidering your pattern.

+
    +
  • Verify your thread colors are correct
  • +
  • Ensure the fabric is properly hooped
  • +
  • Click "Start Sewing" when ready
  • +
+
-
- ); + ); - case MachineStatus.SEWING_COMPLETE: - return ( -
-
- -
-

Step 7: Embroidery Complete!

-

Your embroidery is finished. Great work!

-
    -
  • Remove the hoop from the machine
  • -
  • Press the Accept button on the machine
  • -
  • Carefully remove your finished embroidery
  • -
  • Trim any jump stitches or loose threads
  • -
  • Click "Delete Pattern" to start a new project
  • -
+ case MachineStatus.SEWING: + return ( +
+
+ +
+

Step 6: Sewing In Progress

+

Your embroidery is being stitched. Monitor the progress below.

+
    +
  • Watch the progress bar and current stitch count
  • +
  • The machine will pause when a color change is needed
  • +
  • Do not leave the machine unattended
  • +
+
-
- ); + ); - default: - return null; - } + case MachineStatus.COLOR_CHANGE_WAIT: + return ( +
+
+ +
+

Thread Change Required

+

The machine needs a different thread color to continue.

+
    +
  • Check the color blocks section to see which thread is needed
  • +
  • Change to the correct thread color
  • +
  • Press the button on your machine to resume sewing
  • +
+
+
+
+ ); + + case MachineStatus.PAUSE: + case MachineStatus.STOP: + case MachineStatus.SEWING_INTERRUPTION: + return ( +
+
+ +
+

Sewing Paused

+

The embroidery has been paused or interrupted.

+
    +
  • Check if everything is okay with the machine
  • +
  • Click "Resume Sewing" when ready to continue
  • +
  • The machine will pick up where it left off
  • +
+
+
+
+ ); + + case MachineStatus.SEWING_COMPLETE: + return ( +
+
+ +
+

Step 7: Embroidery Complete!

+

Your embroidery is finished. Great work!

+
    +
  • Remove the hoop from the machine
  • +
  • Press the Accept button on the machine
  • +
  • Carefully remove your finished embroidery
  • +
  • Trim any jump stitches or loose threads
  • +
  • Click "Delete Pattern" to start a new project
  • +
+
+
+
+ ); + + default: + return null; + } + }; + + // Render floating overlay + return ( + <> + {/* Collapsed state - small info button in bottom-right */} + {!isExpanded && ( +
+ +
+ )} + + {/* Expanded state - floating overlay */} + {isExpanded && ( +
+
+ {/* Close button */} + + + {/* Content */} + {renderContent()} +
+
+ )} + + ); } diff --git a/src/components/PatternCanvas.tsx b/src/components/PatternCanvas.tsx index 0eea74b..86eaf57 100644 --- a/src/components/PatternCanvas.tsx +++ b/src/components/PatternCanvas.tsx @@ -189,8 +189,8 @@ export function PatternCanvas({ pesData, sewingProgress, machineInfo, initialPat const iconColor = pesData ? 'text-teal-600 dark:text-teal-400' : 'text-gray-600 dark:text-gray-400'; return ( -
-
+
+

Pattern Preview

@@ -203,7 +203,7 @@ export function PatternCanvas({ pesData, sewingProgress, machineInfo, initialPat )}
-
+
{containerSize.width > 0 && ( +
Load a PES file to preview the pattern
)} diff --git a/src/components/ProgressMonitor.tsx b/src/components/ProgressMonitor.tsx index 263758f..554614e 100644 --- a/src/components/ProgressMonitor.tsx +++ b/src/components/ProgressMonitor.tsx @@ -1,3 +1,4 @@ +import { useRef, useEffect, useState } from "react"; import { CheckCircleIcon, ArrowRightIcon, @@ -42,6 +43,10 @@ export function ProgressMonitor({ onResumeSewing, isDeleting = false, }: ProgressMonitorProps) { + const currentBlockRef = useRef(null); + const colorBlocksScrollRef = useRef(null); + const [showGradient, setShowGradient] = useState(true); + // State indicators const isMaskTraceComplete = machineStatus === MachineStatus.MASK_TRACE_COMPLETE; @@ -108,6 +113,40 @@ export function ProgressMonitor({ currentStitch >= block.startStitch && currentStitch < block.endStitch, ); + // Auto-scroll to current block + useEffect(() => { + if (currentBlockRef.current) { + currentBlockRef.current.scrollIntoView({ + behavior: "smooth", + block: "nearest", + }); + } + }, [currentBlockIndex]); + + // Handle scroll to detect if at bottom + const handleColorBlocksScroll = () => { + if (colorBlocksScrollRef.current) { + const { scrollTop, scrollHeight, clientHeight } = colorBlocksScrollRef.current; + const isAtBottom = scrollTop + clientHeight >= scrollHeight - 5; // 5px threshold + setShowGradient(!isAtBottom); + } + }; + + // Check initial scroll state and update on resize + useEffect(() => { + const checkScrollable = () => { + if (colorBlocksScrollRef.current) { + const { scrollHeight, clientHeight } = colorBlocksScrollRef.current; + const isScrollable = scrollHeight > clientHeight; + setShowGradient(isScrollable); + } + }; + + checkScrollable(); + window.addEventListener('resize', checkScrollable); + return () => window.removeEventListener('resize', checkScrollable); + }, [colorBlocks]); + const stateIndicatorColors = { idle: "bg-blue-50 dark:bg-blue-900/20 border-blue-600", info: "bg-blue-50 dark:bg-blue-900/20 border-blue-600", @@ -122,7 +161,7 @@ export function ProgressMonitor({ }; return ( -
+
@@ -246,12 +285,17 @@ export function ProgressMonitor({ {/* Color Blocks */} {colorBlocks.length > 0 && ( -
-

+
+

Color Blocks

-
- {colorBlocks.map((block, index) => { +
+
+ {colorBlocks.map((block, index) => { const isCompleted = currentStitch >= block.endStitch; const isCurrent = index === currentBlockIndex; @@ -268,6 +312,7 @@ export function ProgressMonitor({ return (
); })} +
+ {/* Gradient overlay to indicate more content below - only on desktop and when not at bottom */} + {showGradient && ( +
+ )}
)} {/* Action buttons */} -
+
{/* Resume has highest priority when available */} {canResumeSewing(machineStatus) && (