From faa2e41be42883904ed7d3e5fe58c466da40563d Mon Sep 17 00:00:00 2001 From: Jaime Idolpx Date: Thu, 11 Jun 2026 17:08:27 -0400 Subject: [PATCH] Refactor config structure: rename 'iec.devices' to 'devices.iec' across the codebase - Updated references in AGENTS.md, README.md, and various component files (DevicesPage, GeneralPage, IECPage, MediaManager, SearchOverlay, StatusPage) to reflect the new config structure. - Adjusted settings handling in settings.ts to accommodate the new structure. - Modified initial config in config.json to match the updated schema. --- AGENTS.md | 10 +- README.md | 2 +- src/app/components/DevicesPage.tsx | 26 ++--- src/app/components/GeneralPage.tsx | 24 ++--- src/app/components/IECPage.tsx | 70 ++++++------- src/app/components/MediaManager.tsx | 10 +- src/app/components/SearchOverlay.tsx | 8 +- src/app/components/StatusPage.tsx | 8 +- src/app/settings.ts | 16 ++- src/imports/config.json | 144 ++++++++++++--------------- 10 files changed, 149 insertions(+), 169 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f150116..69ab718 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -72,7 +72,7 @@ src/ package.json imports/ logo.svg - config.json # Bundled fallback (full merged config including iec.devices) + config.json # Bundled fallback (full merged config including devices.iec) main.tsx styles/ public/ @@ -81,7 +81,7 @@ public/ service-worker.js # PWA service worker files/ .sys/ - config.json # Runtime config on device (no iec.devices) + config.json # Runtime config on device (no devices.iec) devices.json # Runtime device list: { "iec": { "devices": { ... } } } webdav3.py # Dev Python WebDAV + WebSocket server (port 80, serves files/) index.html @@ -155,7 +155,7 @@ Grid of app cards grouped by category, each navigates to a stub `AppPage` unless 35. **Rescan Bus via WebSocket** — DevicesPage "Rescan Bus" button sends `"iec scan"` via shared WS 36. **WiFi Scan via WebSocket** — NetworkPage "Scan" button sends `"scan"` via shared WS before opening overlay 37. **WiFi connect via WebSocket** — WiFiScanOverlay "Connect" sends `"connect "` or `"connect "`; NetworkPage known-network click sends same command, marks network `enabled: 1`, moves it to top of list -38. **Split config / devices storage** — `settings.ts` loads `/.sys/config.json` and `/.sys/devices.json` in parallel; merges with one-level deep merge (so `iec.devices` from devices.json is merged into `iec` from config.json); saves with split: `iec.devices` → `devices.json`, everything else (including remaining `iec` bus settings) → `config.json`; `beforeunload` flush also split across both files +38. **Split config / devices storage** — `settings.ts` loads `/.sys/config.json` and `/.sys/devices.json` in parallel; merges with one-level deep merge (so `devices.iec` from devices.json is merged into `iec` from config.json); saves with split: `devices.iec` → `devices.json`, everything else (including remaining `iec` bus settings) → `config.json`; `beforeunload` flush also split across both files 39. **SerialConsolePage** — xterm.js terminal (`@xterm/xterm` + `@xterm/addon-fit`) over shared `useWs()`; line-buffered input: printable chars echoed locally, `\r` sends buffer, `\x7f` backspaces, `\x03` clears; echo suppression via `echoQueue` ref; tiled icon background; lazy-loaded 40. **LazyLoader component** — `ui/lazy-loader.tsx`: animated progress bar with staged percentage steps (30 → 60 → 80 → 92%) for Suspense fallbacks; replaces inline `PageLoader` in `App.tsx` 41. **MediaViewerEditor tiled background** — icon tile overlay (`z-index: -1`) inside a `z-index: 0` stacking context; sub-components (HexEditor, ConfigEditor, CodeEditor) use transparent backgrounds so the tile shows through @@ -184,12 +184,12 @@ Config is split across two files on the device: | File | Contents | |---|---| -| `/.sys/config.json` | `general`, `host`, `hardware`, `wifi`, `network`, `bluetooth`, `modem`, `cassette`, `boip`, `iec` (bus settings only — no `iec.devices`) | +| `/.sys/config.json` | `general`, `host`, `hardware`, `wifi`, `network`, `bluetooth`, `modem`, `cassette`, `boip`, `iec` (bus settings only — no `devices.iec`) | | `/.sys/devices.json` | `{ "iec": { "devices": { "printer": {...}, "drive": {...}, "network": {...}, "other": {...}, "meatloaf": {...} } } }` | The `useSettings()` hook in `settings.ts` loads both files on mount, merges them into a single `config` object, and splits them back on save. All page components receive the unified `config` / `setConfig` props from `App.tsx`. -The bundled `src/imports/config.json` is used as the initial UI state while the server fetch is in flight. It contains the full merged shape (including `iec.devices`) so the UI renders immediately without waiting. +The bundled `src/imports/config.json` is used as the initial UI state while the server fetch is in flight. It contains the full merged shape (including `devices.iec`) so the UI renders immediately without waiting. ### media_set format diff --git a/README.md b/README.md index 7dc924b..3b962bd 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Device settings are stored in two separate files on the device (LittleFS erase-b | File | Contents | |---|---| | `/.sys/config.json` | General, host, hardware, WiFi, network, Bluetooth, modem, cassette, BOIP, IEC bus settings | -| `/.sys/devices.json` | IEC device list (`iec.devices`) | +| `/.sys/devices.json` | IEC device list (`devices.iec`) | `useSettings()` loads both files in parallel and merges them; saves split them back automatically. A bundled fallback (`src/imports/config.json`) is used as the initial UI state while the device is being contacted. diff --git a/src/app/components/DevicesPage.tsx b/src/app/components/DevicesPage.tsx index 3078fe0..351c805 100644 --- a/src/app/components/DevicesPage.tsx +++ b/src/app/components/DevicesPage.tsx @@ -106,13 +106,13 @@ export default function DevicesPage({ config, setConfig, openDeviceId, onClearOp const [physicalDevices, setPhysicalDevices] = useState([]); const [actionDevice, setActionDevice] = useState(null); - const hardware = config.hardware || {}; - const cassette = config.cassette || {}; + const cassette = config.devices?.cassette || {}; + const userport = config.devices?.userport || {}; // Virtual devices from config (used for detail overlay navigation) const devices: Device[] = []; - if (config.iec?.devices) { - Object.entries(config.iec.devices).forEach(([num, device]: [string, any]) => { + if (config.devices?.iec) { + Object.entries(config.devices.iec).forEach(([num, device]: [string, any]) => { const type = device.type as Device['type']; const name = device.name || ( type === 'drive' ? `Drive ${num}` : @@ -249,7 +249,7 @@ export default function DevicesPage({ config, setConfig, openDeviceId, onClearOp const toggleDeviceEnabled = (device: DisplayDevice, e: React.MouseEvent) => { e.stopPropagation(); const newConfig = JSON.parse(JSON.stringify(config)); - newConfig.iec.devices[device.number].enabled = device.enabled ? 0 : 1; + newConfig.devices.iec[device.number].enabled = device.enabled ? 0 : 1; setConfig(newConfig); toast.success(`Device #${device.number} ${device.enabled ? 'disabled' : 'enabled'}`); }; @@ -457,7 +457,7 @@ export default function DevicesPage({ config, setConfig, openDeviceId, onClearOp
updateSetting(['general', 'devicename'], e.target.value)} + onChange={(e) => updateSetting(['preferences', 'devicename'], e.target.value)} className="w-full px-3 py-2 border border-neutral-300 rounded-lg" />
@@ -60,7 +60,7 @@ export default function GeneralPage({ config, setConfig }: GeneralPageProps) { updateSetting(['general', 'language'], e.target.value)} + onChange={(e) => updateSetting(['preferences', 'language'], e.target.value)} className="w-full px-3 py-2 border border-neutral-300 rounded-lg" > @@ -87,7 +87,7 @@ export default function GeneralPage({ config, setConfig }: GeneralPageProps) { {!tzError && timezones ? ( updateSetting(['general', 'timezone'], e.target.value)} + onChange={(e) => updateSetting(['preferences', 'timezone'], e.target.value)} placeholder={timezones === null && !tzError ? 'Loading…' : 'America/Los_Angeles'} disabled={timezones === null && !tzError} className="w-full px-3 py-2 border border-neutral-300 rounded-lg disabled:opacity-50" @@ -114,7 +114,7 @@ export default function GeneralPage({ config, setConfig }: GeneralPageProps) { updateSetting(['general', 'country'], e.target.value)} + onChange={(e) => updateSetting(['preferences', 'country'], e.target.value)} placeholder="US" className="w-full px-3 py-2 border border-neutral-300 rounded-lg" /> @@ -123,7 +123,7 @@ export default function GeneralPage({ config, setConfig }: GeneralPageProps) {
@@ -60,14 +60,14 @@ export default function IECPage({ config, setConfig }: IECPageProps) {
@@ -76,14 +76,14 @@ export default function IECPage({ config, setConfig }: IECPageProps) {
@@ -94,8 +94,8 @@ export default function IECPage({ config, setConfig }: IECPageProps) {
updateSetting(['iec', 'autoboot'], e.target.value)} + value={settings.autoboot || ''} + onChange={(e) => updateSetting(['settings', 'autoboot'], e.target.value)} className="flex-1 px-3 py-2 border border-neutral-300 rounded-lg" /> @@ -154,8 +154,8 @@ export default function IECPage({ config, setConfig }: IECPageProps) {
updateSetting(['iec', 'drive_rom', key], e.target.value)} + value={settings.drive_roms?.[key] || ''} + onChange={(e) => updateSetting(['settings', 'drive_roms', key], e.target.value)} className="flex-1 px-3 py-2 border border-neutral-300 rounded-lg" />
- {Object.entries(iec.directory || {}).map(([key, value]) => ( + {Object.entries(settings.directory || {}).map(([key, value]) => (
{value === 0 || value === 1 ? (