mirror of
https://github.com/jhbruhn/respira.git
synced 2026-01-27 10:23:41 +00:00
Add Heroicons and remove button hover lift effect
- Install @heroicons/react package - Replace text symbols with proper heroicons: - Zoom controls: PlusIcon, MinusIcon, ArrowPathIcon - Color block status: CheckCircleIcon, ArrowRightIcon, CircleStackIcon - Resume sewing: PlayIcon - Remove hover:-translate-y-0.5 and active:translate-y-0 from all buttons - Buttons now have stable hover states (color change and shadow only) - Improved UI consistency and accessibility with vector icons 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
cd43a64bc4
commit
bf20c2b378
7 changed files with 42 additions and 24 deletions
10
package-lock.json
generated
10
package-lock.json
generated
|
|
@ -8,6 +8,7 @@
|
|||
"name": "web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"konva": "^10.0.12",
|
||||
"pyodide": "^0.27.4",
|
||||
|
|
@ -831,6 +832,15 @@
|
|||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroicons/react": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
|
||||
"integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">= 16 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"konva": "^10.0.12",
|
||||
"pyodide": "^0.27.4",
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ export function ConfirmDialog({
|
|||
<div className="p-4 px-6 flex gap-3 justify-end border-t border-gray-300">
|
||||
<button
|
||||
onClick={onCancel}
|
||||
className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]"
|
||||
className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]"
|
||||
autoFocus
|
||||
>
|
||||
{cancelText}
|
||||
</button>
|
||||
<button
|
||||
onClick={onConfirm}
|
||||
className={variant === 'danger' ? 'px-6 py-3 bg-red-600 text-white rounded font-semibold text-sm hover:bg-red-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]' : 'px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]'}
|
||||
className={variant === 'danger' ? 'px-6 py-3 bg-red-600 text-white rounded font-semibold text-sm hover:bg-red-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]' : 'px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]'}
|
||||
>
|
||||
{confirmText}
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ export function FileUpload({
|
|||
className="hidden"
|
||||
disabled={!pyodideReady || isLoading}
|
||||
/>
|
||||
<label htmlFor="file-input" className={`inline-block px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm cursor-pointer transition-all ${!pyodideReady || isLoading ? 'opacity-50 cursor-not-allowed grayscale-[0.3]' : 'hover:bg-gray-700 hover:shadow-md hover:-translate-y-0.5 active:translate-y-0'}`}>
|
||||
<label htmlFor="file-input" className={`inline-block px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm cursor-pointer transition-all ${!pyodideReady || isLoading ? 'opacity-50 cursor-not-allowed grayscale-[0.3]' : 'hover:bg-gray-700 hover:shadow-md'}`}>
|
||||
{isLoading ? 'Loading...' : !pyodideReady ? 'Initializing...' : 'Choose PES File'}
|
||||
</label>
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ export function FileUpload({
|
|||
<button
|
||||
onClick={handleUpload}
|
||||
disabled={!isConnected || uploadProgress > 0}
|
||||
className="mt-4 px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]"
|
||||
className="mt-4 px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]"
|
||||
>
|
||||
{uploadProgress > 0
|
||||
? `Uploading... ${uploadProgress.toFixed(0)}%`
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export function MachineConnection({
|
|||
|
||||
{!isConnected ? (
|
||||
<div className="flex gap-3 mt-4 flex-wrap">
|
||||
<button onClick={onConnect} className="px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
<button onClick={onConnect} className="px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
Connect to Machine
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -122,10 +122,10 @@ export function MachineConnection({
|
|||
)}
|
||||
|
||||
<div className="flex gap-3 mt-4 flex-wrap">
|
||||
<button onClick={onRefresh} className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
<button onClick={onRefresh} className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
Refresh Status
|
||||
</button>
|
||||
<button onClick={handleDisconnectClick} className="px-6 py-3 bg-red-600 text-white rounded font-semibold text-sm hover:bg-red-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
<button onClick={handleDisconnectClick} className="px-6 py-3 bg-red-600 text-white rounded font-semibold text-sm hover:bg-red-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
Disconnect
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import { Stage, Layer, Group } from 'react-konva';
|
||||
import Konva from 'konva';
|
||||
import { PlusIcon, MinusIcon, ArrowPathIcon } from '@heroicons/react/24/solid';
|
||||
import type { PesPatternData } from '../utils/pystitchConverter';
|
||||
import type { SewingProgress, MachineInfo } from '../types/machine';
|
||||
import { calculateInitialScale } from '../utils/konvaRenderers';
|
||||
|
|
@ -298,15 +299,15 @@ export function PatternCanvas({ pesData, sewingProgress, machineInfo, initialPat
|
|||
|
||||
{/* Zoom Controls Overlay */}
|
||||
<div className="absolute bottom-5 right-5 flex gap-2 items-center bg-white/95 backdrop-blur-sm px-3 py-2 rounded-lg shadow-lg z-10">
|
||||
<button className="w-8 h-8 p-0 text-lg font-bold border border-gray-300 bg-white rounded cursor-pointer transition-all flex items-center justify-center hover:bg-blue-600 hover:text-white hover:border-blue-600 hover:-translate-y-0.5 hover:shadow-md hover:shadow-blue-600/30 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed" onClick={handleZoomIn} title="Zoom In">
|
||||
+
|
||||
<button className="w-8 h-8 p-1 border border-gray-300 bg-white rounded cursor-pointer transition-all flex items-center justify-center hover:bg-blue-600 hover:text-white hover:border-blue-600 hover:shadow-md hover:shadow-blue-600/30 disabled:opacity-50 disabled:cursor-not-allowed" onClick={handleZoomIn} title="Zoom In">
|
||||
<PlusIcon className="w-5 h-5" />
|
||||
</button>
|
||||
<span className="min-w-[50px] text-center text-[13px] font-semibold text-gray-900 select-none">{Math.round(stageScale * 100)}%</span>
|
||||
<button className="w-8 h-8 p-0 text-lg font-bold border border-gray-300 bg-white rounded cursor-pointer transition-all flex items-center justify-center hover:bg-blue-600 hover:text-white hover:border-blue-600 hover:-translate-y-0.5 hover:shadow-md hover:shadow-blue-600/30 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed" onClick={handleZoomOut} title="Zoom Out">
|
||||
−
|
||||
<button className="w-8 h-8 p-1 border border-gray-300 bg-white rounded cursor-pointer transition-all flex items-center justify-center hover:bg-blue-600 hover:text-white hover:border-blue-600 hover:shadow-md hover:shadow-blue-600/30 disabled:opacity-50 disabled:cursor-not-allowed" onClick={handleZoomOut} title="Zoom Out">
|
||||
<MinusIcon className="w-5 h-5" />
|
||||
</button>
|
||||
<button className="w-8 h-8 p-0 text-xl font-bold border border-gray-300 bg-white rounded cursor-pointer transition-all flex items-center justify-center hover:bg-blue-600 hover:text-white hover:border-blue-600 hover:-translate-y-0.5 hover:shadow-md hover:shadow-blue-600/30 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed ml-1" onClick={handleZoomReset} title="Reset Zoom">
|
||||
⟲
|
||||
<button className="w-8 h-8 p-1 border border-gray-300 bg-white rounded cursor-pointer transition-all flex items-center justify-center hover:bg-blue-600 hover:text-white hover:border-blue-600 hover:shadow-md hover:shadow-blue-600/30 disabled:opacity-50 disabled:cursor-not-allowed ml-1" onClick={handleZoomReset} title="Reset Zoom">
|
||||
<ArrowPathIcon className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { CheckCircleIcon, ArrowRightIcon, CircleStackIcon, PlayIcon } from '@heroicons/react/24/solid';
|
||||
import type { PatternInfo, SewingProgress } from '../types/machine';
|
||||
import { MachineStatus } from '../types/machine';
|
||||
import type { PesPatternData } from '../utils/pystitchConverter';
|
||||
|
|
@ -161,9 +162,13 @@ export function ProgressMonitor({
|
|||
<span className="font-semibold flex-1">
|
||||
Thread {block.colorIndex + 1}
|
||||
</span>
|
||||
<span className={`text-xl font-bold ${isCompleted ? 'text-green-600' : isCurrent ? 'text-blue-600' : 'text-gray-600'}`}>
|
||||
{isCompleted ? '✓' : isCurrent ? '→' : '○'}
|
||||
</span>
|
||||
{isCompleted ? (
|
||||
<CheckCircleIcon className="w-6 h-6 text-green-600" />
|
||||
) : isCurrent ? (
|
||||
<ArrowRightIcon className="w-6 h-6 text-blue-600" />
|
||||
) : (
|
||||
<CircleStackIcon className="w-6 h-6 text-gray-400" />
|
||||
)}
|
||||
<span className="text-sm text-gray-600">
|
||||
{block.stitchCount} stitches
|
||||
</span>
|
||||
|
|
@ -249,12 +254,12 @@ export function ProgressMonitor({
|
|||
Mask trace complete!
|
||||
</div>
|
||||
{canStartSewing(machineStatus) && (
|
||||
<button onClick={onStartSewing} className="px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
<button onClick={onStartSewing} className="px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
Start Sewing
|
||||
</button>
|
||||
)}
|
||||
{canStartMaskTrace(machineStatus) && (
|
||||
<button onClick={onStartMaskTrace} className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
<button onClick={onStartMaskTrace} className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
Trace Again
|
||||
</button>
|
||||
)}
|
||||
|
|
@ -268,7 +273,7 @@ export function ProgressMonitor({
|
|||
Pattern uploaded successfully
|
||||
</div>
|
||||
{canStartMaskTrace(machineStatus) && (
|
||||
<button onClick={onStartMaskTrace} className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
<button onClick={onStartMaskTrace} className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
Start Mask Trace
|
||||
</button>
|
||||
)}
|
||||
|
|
@ -279,12 +284,12 @@ export function ProgressMonitor({
|
|||
{machineStatus === MachineStatus.SEWING_WAIT && (
|
||||
<>
|
||||
{canStartMaskTrace(machineStatus) && (
|
||||
<button onClick={onStartMaskTrace} className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
<button onClick={onStartMaskTrace} className="px-6 py-3 bg-gray-600 text-white rounded font-semibold text-sm hover:bg-gray-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
Start Mask Trace
|
||||
</button>
|
||||
)}
|
||||
{canStartSewing(machineStatus) && (
|
||||
<button onClick={onStartSewing} className="px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
<button onClick={onStartSewing} className="px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
Start Sewing
|
||||
</button>
|
||||
)}
|
||||
|
|
@ -293,8 +298,9 @@ export function ProgressMonitor({
|
|||
|
||||
{/* Resume sewing for interrupted states */}
|
||||
{canResumeSewing(machineStatus) && (
|
||||
<button onClick={onResumeSewing} className="px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
▶️ Resume Sewing
|
||||
<button onClick={onResumeSewing} className="px-6 py-3 bg-blue-600 text-white rounded font-semibold text-sm hover:bg-blue-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3] flex items-center gap-2">
|
||||
<PlayIcon className="w-4 h-4" />
|
||||
Resume Sewing
|
||||
</button>
|
||||
)}
|
||||
|
||||
|
|
@ -321,7 +327,7 @@ export function ProgressMonitor({
|
|||
|
||||
{/* Delete pattern button - ONLY show when safe */}
|
||||
{patternInfo && canDeletePattern(machineStatus) && (
|
||||
<button onClick={onDeletePattern} className="px-6 py-3 bg-red-600 text-white rounded font-semibold text-sm hover:bg-red-700 transition-all hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
<button onClick={onDeletePattern} className="px-6 py-3 bg-red-600 text-white rounded font-semibold text-sm hover:bg-red-700 transition-all hover:shadow-md disabled:opacity-50 disabled:cursor-not-allowed disabled:grayscale-[0.3]">
|
||||
Delete Pattern
|
||||
</button>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue