diff --git a/src/DockableApp.jsx b/src/DockableApp.jsx index b131bd46..b15ee685 100644 --- a/src/DockableApp.jsx +++ b/src/DockableApp.jsx @@ -32,7 +32,7 @@ import { DXLocalTime, } from './components'; -import { loadLayout, saveLayout } from './store/layoutStore.js'; +import { resetLayout, loadLayout, saveLayout } from './store/layoutStore.js'; import { DockableLayoutProvider } from './contexts'; import { useRig } from './contexts/RigContext.jsx'; import { calculateBearing, calculateDistance, formatDistance } from './utils/geo.js'; @@ -175,6 +175,12 @@ export const DockableApp = ({ return next; }); }, []); + const handleResetLayout = useCallback(() => { + if (confirm('Reset panel layout to default? This will undo any customizations.')) { + const defaultLayout = resetLayout(); + setModel(Model.fromJson(defaultLayout)); + } + }, []); const [showDXLocalTime, setShowDXLocalTime] = useState(false); const [showDxccSelect, setShowDxccSelect] = useState(false); @@ -417,7 +423,7 @@ export const DockableApp = ({ 'on-air': { name: 'On Air', icon: 'πŸ”΄' }, 'id-timer': { name: 'ID Timer', icon: 'πŸ“’' }, keybindings: { name: 'Keyboard Shortcuts', icon: '⌨️' }, - 'lock-layout': { name: 'Lock Layout', icon: 'πŸ”’' }, + layout: { name: 'Layout', icon: 'πŸ“' }, }; }, [isLocalInstall]); @@ -946,19 +952,34 @@ export const DockableApp = ({ content = ; break; - case 'lock-layout': + case 'layout': content = ( - +
+ + +
); break; diff --git a/src/store/layoutStore.js b/src/store/layoutStore.js index 2ad23ee6..d1777ae3 100644 --- a/src/store/layoutStore.js +++ b/src/store/layoutStore.js @@ -26,9 +26,9 @@ export const DEFAULT_LAYOUT = { children: [ { type: 'tab', - name: 'Lock Layout', - component: 'lock-layout', - id: 'lock-layout-tab', + name: 'Layout', + component: 'layout', + id: 'layout-tab', enableClose: false, }, ], @@ -119,7 +119,7 @@ export const PANEL_DEFINITIONS = { 'world-map': { name: 'World Map', icon: 'πŸ—ΊοΈ', description: 'Interactive world map' }, 'rig-control': { name: 'Rig Control', icon: 'πŸ“»', description: 'Transceiver control and feedback' }, 'on-air': { name: 'On Air', icon: 'πŸ”΄', description: 'Large TX status indicator' }, - 'lock-layout': { name: 'Lock the Layout', icon: 'πŸ”’', description: 'Lock the layout' }, + layout: { name: 'Layout', icon: 'πŸ“', description: 'Layout controls' }, }; // Load layout from localStorage @@ -135,6 +135,20 @@ export const loadLayout = () => { if (parsed.borders.length === 0) { parsed.borders = DEFAULT_LAYOUT.borders; saveLayout(parsed); + } else { + // Migrate lock-layout β†’ layout rename + let migrated = false; + for (const border of parsed.borders) { + for (const child of border.children || []) { + if (child.component === 'lock-layout') { + child.component = 'layout'; + child.id = 'layout-tab'; + child.name = 'Layout'; + migrated = true; + } + } + } + if (migrated) saveLayout(parsed); } return parsed; } diff --git a/src/styles/main.css b/src/styles/main.css index eb012b85..1fcacca0 100644 --- a/src/styles/main.css +++ b/src/styles/main.css @@ -363,9 +363,12 @@ body::before { /* ============================================ LOCK LAYOUT PANEL ============================================ */ +.panel-layout-reset-button, .panel-layout-lock-button { + width: 90%; display: flex; align-items: center; + justify-content: center; gap: 4px; color: var(--text-muted); background: var(--bg-tertiary); @@ -378,6 +381,11 @@ body::before { margin: 1em auto; } +.panel-layout-reset-button:disabled { + cursor: not-allowed; + opacity: 0.5; +} + .panel-layout-lock-button.locked { color: var(--accent-amber); background: rgba(255, 170, 0, 0.15);