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);