feat(DevicesPage): update device model options for clarity and add new models
This commit is contained in:
parent
f89e57a8be
commit
44736750b6
84
AGENTS.md
84
AGENTS.md
|
|
@ -10,6 +10,7 @@
|
||||||
- **Styling**: Tailwind CSS 4 (via `@tailwindcss/vite`)
|
- **Styling**: Tailwind CSS 4 (via `@tailwindcss/vite`)
|
||||||
- **UI components**: Radix UI primitives, Lucide React icons
|
- **UI components**: Radix UI primitives, Lucide React icons
|
||||||
- **3D / animation**: Three.js r0.160 (`WebGLRenderer`, `EffectComposer`, `UnrealBloomPass`, custom GLSL shaders)
|
- **3D / animation**: Three.js r0.160 (`WebGLRenderer`, `EffectComposer`, `UnrealBloomPass`, custom GLSL shaders)
|
||||||
|
- **Terminal**: xterm.js (`@xterm/xterm`, `@xterm/addon-fit`) — SerialConsolePage only
|
||||||
- **Routing**: React Router 7 (single-page, page state managed in `App.tsx`)
|
- **Routing**: React Router 7 (single-page, page state managed in `App.tsx`)
|
||||||
- **Build base path**: `/config/` (overridable via `BASE_PATH` env var)
|
- **Build base path**: `/config/` (overridable via `BASE_PATH` env var)
|
||||||
|
|
||||||
|
|
@ -18,12 +19,16 @@
|
||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
app/
|
app/
|
||||||
App.tsx # Root component: routing, nav, layout, SaveStatusBadge, WsProvider wrapper
|
App.tsx # Root component: routing, nav, layout, WsProvider wrapper
|
||||||
|
# fileManagerInitialPath / fileManagerReturnPage state for
|
||||||
|
# deep-linking into MediaManager from StatusPage
|
||||||
settings.ts # useSettings() hook — loads config.json + devices.json, saves split
|
settings.ts # useSettings() hook — loads config.json + devices.json, saves split
|
||||||
webdav.ts # WebDAV abstraction (listDirectory, stat, put/get, fileExists, etc.)
|
webdav.ts # WebDAV abstraction (listDirectory, stat, put/get, fileExists, etc.)
|
||||||
ws.tsx # WsProvider + useWs() — single shared WebSocket connection context
|
ws.tsx # WsProvider + useWs() — single shared WebSocket connection context
|
||||||
components/
|
components/
|
||||||
StatusPage.tsx # System status, activity log, reset (via WS), WS status indicator
|
StatusPage.tsx # System status, activity log, reset (via WS), WS status indicator
|
||||||
|
# Active Device panel: FolderOpen → MediaManager, DirectorySlideshow
|
||||||
|
# or per-entry cover image, MediaSet with named entries
|
||||||
DevicesPage.tsx # Device list, Rescan Bus (sends "iec scan" via WS)
|
DevicesPage.tsx # Device list, Rescan Bus (sends "iec scan" via WS)
|
||||||
DeviceDetailOverlay.tsx
|
DeviceDetailOverlay.tsx
|
||||||
GeneralPage.tsx # General/settings
|
GeneralPage.tsx # General/settings
|
||||||
|
|
@ -36,11 +41,31 @@ src/
|
||||||
MediaManager.tsx # WebDAV file browser: file icons by type, kebab actions,
|
MediaManager.tsx # WebDAV file browser: file icons by type, kebab actions,
|
||||||
# Configure Folder (.config editor), base_url mount logic,
|
# Configure Folder (.config editor), base_url mount logic,
|
||||||
# media set existence check, navigate into new folder
|
# media set existence check, navigate into new folder
|
||||||
|
# initialPath takes priority over localStorage when set
|
||||||
|
MediaBrowser.tsx # Lightweight file picker (used in device mount dialogs)
|
||||||
|
MediaEntry.tsx # Shared entry row: icon by extension, hover highlight (blue left border),
|
||||||
|
# leftSlot / nameSlot props; exports EntryIcon + extension sets
|
||||||
|
MediaViewerEditor.tsx # Viewer/editor shell: tiled icon background (z-index:-1), toolbar
|
||||||
|
MediaSet.tsx # Disk-swap button row; MediaSetEntry = string | {url, name?};
|
||||||
|
# exports mediaSetEntryUrl() helper
|
||||||
|
DirectorySlideshow.tsx # Auto-advancing image slideshow from a WebDAV directory;
|
||||||
|
# prev/next arrows, dot indicators, centered pause/play button;
|
||||||
|
# controls appear on hover or tap; paused + idx persisted to localStorage
|
||||||
|
HexEditor.tsx # Hex viewer: responsive 8/16 col via ResizeObserver; bright address col
|
||||||
|
ConfigEditor.tsx # YAML-style .config editor
|
||||||
|
CodeEditor.tsx # CodeMirror code editor (transparent background)
|
||||||
|
SerialConsolePage.tsx # xterm.js terminal over shared WS; line-buffered input with echo
|
||||||
|
# suppression; tiled background overlay
|
||||||
|
ProfilePage.tsx # Full-page profile menu (replaced dropdown); iOS-style grouped list
|
||||||
|
AboutMeatloafPage.tsx # Project info table, GPL3 license
|
||||||
RealityOverridePage.tsx # Full-screen WS command display; Three.js Digital Tokamak
|
RealityOverridePage.tsx # Full-screen WS command display; Three.js Digital Tokamak
|
||||||
# vortex + star field; double-tap to toggle background
|
# vortex + star field; double-tap to toggle background
|
||||||
RealityOverrideAdminPage.tsx # Command palette (Image/Audio/Video); freeform input; WS send
|
RealityOverrideAdminPage.tsx # Command palette (Image/Audio/Video); freeform input; WS send
|
||||||
figma/ # Figma-generated components
|
figma/ # Figma-generated components
|
||||||
ui/ # shadcn/Radix UI wrappers
|
ui/
|
||||||
|
lazy-loader.tsx # Animated progress bar for Suspense fallbacks (staged steps)
|
||||||
|
confirm-dialog.tsx
|
||||||
|
# … other shadcn/Radix UI wrappers
|
||||||
vendor/
|
vendor/
|
||||||
webdav-component/ # Vendored ESM WebDAV client (no external deps)
|
webdav-component/ # Vendored ESM WebDAV client (no external deps)
|
||||||
esm/index.js
|
esm/index.js
|
||||||
|
|
@ -67,24 +92,25 @@ vite.config.ts
|
||||||
|
|
||||||
Bottom tab bar + header icons:
|
Bottom tab bar + header icons:
|
||||||
|
|
||||||
| Nav item | Page key | Component |
|
| Nav item | Page key | Component |
|
||||||
|-------------|-------------|------------------|
|
|-------------|-------------------|------------------|
|
||||||
| Status | `status` | StatusPage |
|
| Status | `status` | StatusPage |
|
||||||
| Devices | `devices` | DevicesPage |
|
| Devices | `devices` | DevicesPage |
|
||||||
| IEC | `iec` | IECPage |
|
| IEC | `iec` | IECPage |
|
||||||
| Network | `network` | NetworkPage |
|
| Network | `network` | NetworkPage |
|
||||||
| More | `other` | OtherPage |
|
| System | `tools` | ToolsPage |
|
||||||
| (header) | `apps` | Apps grid |
|
| (header) | `apps` | Apps grid |
|
||||||
| (profile) | `general` | GeneralPage |
|
| (header) | `profile` | ProfilePage |
|
||||||
| (profile) | `tools` | ToolsPage |
|
| (profile) | `general` | GeneralPage |
|
||||||
|
| (profile) | `about-meatloaf` | AboutMeatloafPage |
|
||||||
|
|
||||||
Header also has: fullscreen toggle (Maximize2/Minimize2), search, apps grid, save-status badge, profile menu. Logo click → status page.
|
Header: fullscreen toggle, search, apps grid, profile button (→ ProfilePage). Logo click → status page. Toast messages appear `bottom-center` just above the navbar (`offset="calc(4rem + env(safe-area-inset-bottom))"`).
|
||||||
|
|
||||||
## Apps Page
|
## Apps Page
|
||||||
|
|
||||||
Grid of app cards grouped by category, each navigates to a stub `AppPage` unless implemented:
|
Grid of app cards grouped by category, each navigates to a stub `AppPage` unless implemented:
|
||||||
|
|
||||||
- **Management**: Media Manager, Print Manager, Serial Console, Short Codes
|
- **Management**: Media Manager, Print Manager, **Serial Console** (implemented), Short Codes
|
||||||
- **Disk**: RAM/ROM Explorer, BAM Editor, Directory Editor, Sector Editor, Disk Visualizer, Dump/Write Disk Image
|
- **Disk**: RAM/ROM Explorer, BAM Editor, Directory Editor, Sector Editor, Disk Visualizer, Dump/Write Disk Image
|
||||||
- **Cartridge**: PRG to CRT, Magic Desk Cart Builder, Easy Flash Cart Builder
|
- **Cartridge**: PRG to CRT, Magic Desk Cart Builder, Easy Flash Cart Builder
|
||||||
- **Development**: Basic Editor, Assembler, Sprite Editor, Character Set Editor, Petscii Editor
|
- **Development**: Basic Editor, Assembler, Sprite Editor, Character Set Editor, Petscii Editor
|
||||||
|
|
@ -130,13 +156,27 @@ Grid of app cards grouped by category, each navigates to a stub `AppPage` unless
|
||||||
36. **WiFi Scan via WebSocket** — NetworkPage "Scan" button sends `"scan"` via shared WS before opening overlay
|
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 <ssid>"` or `"connect <ssid> <password>"`; NetworkPage known-network click sends same command, marks network `enabled: 1`, moves it to top of list
|
37. **WiFi connect via WebSocket** — WiFiScanOverlay "Connect" sends `"connect <ssid>"` or `"connect <ssid> <password>"`; 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 `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
|
||||||
|
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
|
||||||
|
42. **HexEditor responsive columns** — `ResizeObserver` on scroll container switches between 8 columns (< 600px) and 16 columns (≥ 600px); address column brightened to `text-neutral-400`
|
||||||
|
43. **ProfilePage** — `ProfilePage.tsx` replaces the header dropdown; iOS-style grouped list with Preferences → GeneralPage, Notifications (stub), Documentation (stub), About Meatloaf → AboutMeatloafPage, Log Out; profile button in header navigates to `'profile'` page key
|
||||||
|
44. **AboutMeatloafPage** — `AboutMeatloafPage.tsx`: logo, project info table (Project, Platform, Website, GitHub), GPL3 license note
|
||||||
|
45. **MediaEntry hover highlight** — `border-l-2 border-l-transparent` always reserves space (no layout shift); `hover:bg-blue-50 hover:border-l-blue-400`; selected state mirrors hover; also applied to `..` (navigate-up) rows in MediaBrowser and MediaManager
|
||||||
|
46. **Toast position** — `<Toaster position="bottom-center" offset="calc(4rem + env(safe-area-inset-bottom))">` — toasts appear just above the navbar on all devices
|
||||||
|
47. **StatusPage: Browse active device** — `FolderOpen` button in Active Device panel header opens MediaManager at `base_url` (if set) or parent directory of `url`; uses `fileManagerInitialPath` / `fileManagerReturnPage` state in `App.tsx`; Apps page card clears `fileManagerInitialPath` so normal navigation restores last-visited path from localStorage
|
||||||
|
48. **MediaManager initialPath priority** — `initialPath` prop now takes priority over `localStorage` when explicitly set (uses `??` instead of `||`); default changed from `'/'` to `undefined`
|
||||||
|
49. **DirectorySlideshow component** — `DirectorySlideshow.tsx`: lists a WebDAV directory, filters for image files, auto-advances every 4 s; prev/next arrow buttons + dot indicators + centered pause/play button; all controls fade in on hover or tap (3 s auto-hide on touch); `paused` persisted to `localStorage` key `slideshow.paused`; `idx` persisted to `localStorage` key `slideshow.idx:<path>` (per directory, clamped to image count on restore)
|
||||||
|
50. **MediaSet named entries** — `MediaSetEntry = string | { url: string; name?: string }`; `MediaSet.tsx` exports the type and `mediaSetEntryUrl()` helper; `name` field displayed instead of filename when provided; `DeviceDetailOverlay` and `StatusPage` updated to use `MediaSetEntry[]`
|
||||||
|
51. **StatusPage: Active Device cover image** — when `activeDevice.url` has a matching image in the same directory, it is shown (fixed `h-48`) instead of the `DirectorySlideshow`; matching is case-insensitive with `_`/`-`/space normalization via a single `listDirectory` call; the media_set entry's `name` field is tried first, URL base name second
|
||||||
|
|
||||||
## Known Issues / Open Work
|
## Known Issues / Open Work
|
||||||
|
|
||||||
- **Service worker 404**: `https://meatloaf.cc/service-worker.js` returns 404. The SW is not being served at the correct path relative to the `/config/` base.
|
- **Service worker 404**: `https://meatloaf.cc/service-worker.js` returns 404. The SW is not being served at the correct path relative to the `/config/` base.
|
||||||
- **App pages**: Most individual app pages are stubs. Reality Override and Override Admin are implemented; all others show "coming soon".
|
- **App pages**: Most individual app pages are stubs. Reality Override, Override Admin, and Serial Console are implemented; all others show "coming soon".
|
||||||
- **WiFi scan results**: Currently mock data. Needs real scan results delivered over WebSocket from the device.
|
- **WiFi scan results**: Currently mock data. Needs real scan results delivered over WebSocket from the device.
|
||||||
- **Reset confirmation**: The "in-progress" and "done" states are simulated with `setTimeout`. Needs real completion signal from the device over WebSocket.
|
- **Reset confirmation**: The "in-progress" and "done" states are simulated with `setTimeout`. Needs real completion signal from the device over WebSocket.
|
||||||
|
- **StatusPage mock data**: Last File Access, Size, Transfer Speed, Uptime, IP/MAC, WiFi status, and memory stats are all hardcoded mock values.
|
||||||
|
|
||||||
## Configuration Shape
|
## Configuration Shape
|
||||||
|
|
||||||
|
|
@ -151,6 +191,20 @@ The `useSettings()` hook in `settings.ts` loads both files on mount, merges them
|
||||||
|
|
||||||
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 `iec.devices`) so the UI renders immediately without waiting.
|
||||||
|
|
||||||
|
### media_set format
|
||||||
|
|
||||||
|
`media_set` entries can be plain strings (URL) or named objects:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"media_set": [
|
||||||
|
{ "url": "/sd/game/1.d64", "name": "Boot Disk" },
|
||||||
|
{ "url": "/sd/game/2.d64", "name": "Disk 1 - Side A" },
|
||||||
|
"/sd/game/3.d64"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
When a `name` is present, it is shown in the MediaSet UI instead of the filename, and is used as the primary candidate when looking for a matching cover image.
|
||||||
|
|
||||||
## WebSocket Layer
|
## WebSocket Layer
|
||||||
|
|
||||||
A single `WebSocket` connection to `ws://<hostname>/ws` is managed by `WsProvider` in `ws.tsx` and shared app-wide via React Context.
|
A single `WebSocket` connection to `ws://<hostname>/ws` is managed by `WsProvider` in `ws.tsx` and shared app-wide via React Context.
|
||||||
|
|
|
||||||
|
|
@ -144,16 +144,17 @@ export default function DevicesPage({ config, setConfig, openDeviceId, onClearOp
|
||||||
onChange={(e) => updateSetting(['host', 'model'], e.target.value)}
|
onChange={(e) => updateSetting(['host', 'model'], e.target.value)}
|
||||||
className="w-full px-3 py-2 border border-neutral-300 rounded-lg"
|
className="w-full px-3 py-2 border border-neutral-300 rounded-lg"
|
||||||
>
|
>
|
||||||
<option value="c64">C64</option>
|
<option value="c64">C64/C64c/SX64</option>
|
||||||
<option value="c64c">C64C</option>
|
<option value="c64u">C64/C64c Ultimate / Ultimate 64</option>
|
||||||
<option value="c128">C128</option>
|
|
||||||
<option value="sx64">SX64</option>
|
|
||||||
<option value="plus4">Plus/4</option>
|
|
||||||
<option value="c16">C16</option>
|
|
||||||
<option value="cx16">CX16</option>
|
|
||||||
<option value="foenix">Foenix</option>
|
|
||||||
<option value="dtv">DTV</option>
|
<option value="dtv">DTV</option>
|
||||||
|
<option value="thec64">TheC64 Mini/Maxi</option>
|
||||||
|
<option value="c128">C128</option>
|
||||||
|
<option value="c16">C16</option>
|
||||||
|
<option value="plus4">C116/Plus/4</option>
|
||||||
<option value="pet">PET</option>
|
<option value="pet">PET</option>
|
||||||
|
<option value="cbm2">CBM2</option>
|
||||||
|
<option value="cx16">CX16 / OtterX</option>
|
||||||
|
<option value="foenix">Foenix</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
|
|
@ -195,7 +196,7 @@ export default function DevicesPage({ config, setConfig, openDeviceId, onClearOp
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<h2 className="text-sm text-neutral-500 flex items-center gap-2"><Cable className="w-4 h-4" /> IEC Devices</h2>
|
<h2 className="text-sm text-neutral-500 flex items-center gap-2"><Cable className="w-4 h-4" /> IEC Serial Devices</h2>
|
||||||
<button
|
<button
|
||||||
onClick={rescanBus}
|
onClick={rescanBus}
|
||||||
disabled={isScanning}
|
disabled={isScanning}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user