From e8ca84882b4a3b5700b47510749ca18312e7a9b0 Mon Sep 17 00:00:00 2001 From: bwees Date: Sun, 29 Mar 2026 13:45:42 -0500 Subject: [PATCH] feat: Replace monitor view selector with eye icon popup and persist selection Replaces the always-visible view mode button panel with a single eye icon that opens a popup with layout options. View selection is now persisted in localStorage so it survives tab switches. Co-Authored-By: Claude Opus 4.6 --- apps/web/src/routes/Monitor/index.tsx | 128 ++++++++++++++++---------- 1 file changed, 77 insertions(+), 51 deletions(-) diff --git a/apps/web/src/routes/Monitor/index.tsx b/apps/web/src/routes/Monitor/index.tsx index fa9221d1a..9a8160347 100644 --- a/apps/web/src/routes/Monitor/index.tsx +++ b/apps/web/src/routes/Monitor/index.tsx @@ -1,7 +1,7 @@ import React, { useState, useMemo, useEffect, useCallback, useRef } from 'react' import { useNavigate } from 'react-router-dom' import { useTranslation } from 'react-i18next' -import { Camera, Terminal, Maximize2, Clock, FileText, Gauge, Columns3, PictureInPicture, ArrowLeftRight, RotateCcw, RotateCw, Square, ChevronDown, GripVertical, BarChart3, Wrench, ActivitySquare, ClipboardList, Move } from 'lucide-react' +import { Camera, Terminal, Maximize2, Clock, FileText, Gauge, Columns3, PictureInPicture, ArrowLeftRight, RotateCcw, RotateCw, Square, ChevronDown, GripVertical, BarChart3, Wrench, ActivitySquare, ClipboardList, Move, Eye } from 'lucide-react' import Hls from 'hls.js' import { Button } from '@/components/ui/button' import { Progress } from '@/components/ui/progress' @@ -250,10 +250,24 @@ interface VisualizerCameraViewProps { function VisualizerCameraView({ machinePosition, processedLines }: VisualizerCameraViewProps) { const { t } = useTranslation() - const [viewMode, setViewMode] = useState('side-by-side') + const [viewMode, setViewMode] = useState(() => { + const saved = localStorage.getItem('monitor.viewMode') + if (saved && ['side-by-side', 'visual-only', 'camera-only', 'pip-visual', 'pip-camera'].includes(saved)) { + return saved as ViewMode + } + return 'side-by-side' + }) + const [viewMenuOpen, setViewMenuOpen] = useState(false) + const setViewModePersist = useCallback((mode: ViewMode | ((prev: ViewMode) => ViewMode)) => { + setViewMode(prev => { + const next = typeof mode === 'function' ? mode(prev) : mode + localStorage.setItem('monitor.viewMode', next) + return next + }) + }, []) const { data: settings } = useGetSettingsQuery() const dispatch = useAppDispatch() - + // Get shared machine state for positions const workPosition = useWorkPosition() const connectedPort = useConnectedPort() // Use Redux state instead of settings @@ -451,9 +465,9 @@ function VisualizerCameraView({ machinePosition, processedLines }: VisualizerCam const handleSwapPiP = () => { if (viewMode === 'pip-visual') { - setViewMode('pip-camera') + setViewModePersist('pip-camera') } else if (viewMode === 'pip-camera') { - setViewMode('pip-visual') + setViewModePersist('pip-visual') } } @@ -520,55 +534,67 @@ function VisualizerCameraView({ machinePosition, processedLines }: VisualizerCam )} - {/* View mode controls */} -
+ {/* View mode controls - eye icon toggle with popup */} +
- - -
- - {(viewMode === 'pip-visual' || viewMode === 'pip-camera') && ( - + {viewMenuOpen && ( +
+ + + +
+ + {(viewMode === 'pip-visual' || viewMode === 'pip-camera') && ( + + )} +
)}