feat(SearchPane, SearchLocal, webdav): enhance search functionality with new tabs, inline progress, and directory management

This commit is contained in:
Jaime Idolpx 2026-06-18 04:07:17 -04:00
parent bbd38746df
commit c3a324976a

View File

@ -26,7 +26,10 @@ src/
# version-based migration: migrateConfig() renames old keys and
# restructures; deepMergeDefaults() fills missing keys from bundled
# defaults; auto-saves migrated config immediately on load
webdav.ts # WebDAV abstraction (listDirectory, stat, put/get, fileExists, etc.)
webdav.ts # WebDAV abstraction (listDirectory, stat, put/get, fileExists, etc.);
# FetchProgress type; clearDirectory(path) deletes all files in a dir;
# streamFetch(res, onProgress) streams Response → ArrayBuffer with progress;
# listDirectory accepts optional 4th signal?: AbortSignal param
ws.tsx # WsProvider + useWs() — single shared WebSocket connection context
components/
StatusPage.tsx # System status, activity log, reset (via WS), WS status indicator
@ -39,14 +42,22 @@ src/
NetworkPage.tsx # Network settings, WiFi scan/connect (via WS)
OtherPage.tsx # Misc settings
ToolsPage.tsx # Tools
SearchPane.tsx # Swipeable search shell: spring slide-up, tab bar (Local | Assembly64),
# X close button, horizontal snap-x scroll container; initialTab prop
# (0=Local, 1=Assembly64); onScroll updates active tab indicator
SearchPane.tsx # Swipeable search shell: spring slide-up, 4 tabs (Local | Assembly64 |
# CommoServe | CSDb-ng), swipe dot indicators (pill = active), horizontal
# snap-x scroll container; initialTab prop (0-3); onScroll updates active
# tab; load bar removed (progress now inline in SearchLocal)
SearchLocal.tsx # Local file search panel (panel inside SearchPane, no fixed positioning);
# TOSEC/No-Intro tag parsing (PAL/NTSC, system, ISO 639-1 language);
# wildcard search (* → %, ? → _); MediaEntry rows with badges + path;
# faceted filter chips; module-level _store persists state across unmounts;
# actions dialog: "Mount on virtual drive" + "Open containing folder"
# actions dialog: "Mount on virtual drive" + "Open containing folder";
# SD card check on mount: stat('/sd') → if missing show red error, skip
# engine load; fileExists('/sd/.locate') → if missing highlight scan button;
# inline load status: hides search input while loading engine/DB, shows
# progress bar with bytes in its place; scan controls: Stop button aborts
# via AbortController (also auto-aborts on unmount); scan shows dir/file
# counts + elapsed time (Xh Xm Xs) + current path (rtl direction);
# "Search your device" empty state hidden during scan
SearchAssembly64.tsx # Assembly64 Leet API browser panel (panel inside SearchPane);
# fetches /search/categories + /search/aql/presets on mount;
# AQL search with pagination; category filter chips; ContentItem result rows;
@ -55,6 +66,15 @@ src/
# Client-Id: Ultimate + User-Agent: Assembly Query headers (identifies
# the 1541 Ultimate cartridge; server returns 464 for unknown Client-Id
# and 463 for missing/foreign User-Agent); module-level _store
SearchCommoServe.tsx # CommoServe Leet API browser (panel inside SearchPane);
# AQL search + preset filter chips + paginated ContentItem rows;
# Assembly64-parity mount: tapping a file entry clears /sd/downloads/commoserve/,
# streams main file via leetFetch+streamFetch, saves to device, downloads
# cover image (matching image entry), then mounts by local dest path;
# isMounting overlay in device picker shows fetching progress bar / saving
# spinner / image spinner; isMounted checks local DOWNLOAD_DIR path;
# LEET_BASE='https://commoserve.files.commodore.net/leet', Client-Id:'Commodore',
# DOWNLOAD_DIR='/sd/downloads/commoserve'; module-level _store
WiFiScanOverlay.tsx # WiFi scan results; Connect sends "connect <ssid> [<pass>]" via WS
MediaManager.tsx # WebDAV file browser: file icons by type, kebab actions,
# Configure Folder (.config editor), base_url mount logic,
@ -211,6 +231,12 @@ Header search icon opens SearchPane at tab 0 (Local). "Assembly64" AppCard opens
59. **`.vms` Virtual Media Stack format** — `.vms` files are like `.lst` but each line is `path,name` (comma-separated); name after comma becomes the `MediaSetEntry.name` display label; `PLAYLIST_EXTS = new Set(['lst', 'vms'])` exported from `MediaEntry.tsx` with `Layers` icon (indigo); `MediaManager` and `DeviceDetailOverlay` both parse `.vms` into `MediaSetEntry[]`; `mediaSetEntryUrl()` used for `fileExists` checks and `dev.url`
60. **MarqueeText component**`ui/marquee-text.tsx`: extracted from inline MediaManager code; module-level `_seq` counter generates unique animation IDs; per-instance `@keyframes` rule injected into `<head>` and cleaned up on unmount; `ResizeObserver` re-measures on resize; ping-pong (alternate) scroll with `ease-in-out` and 0.8 s delay; used by MediaManager, SearchLocal, SearchAssembly64
61. **SearchLocal + SearchAssembly64 + SearchPane**`SearchOverlay.tsx` split into three components: `SearchPane.tsx` (swipeable shell: `fixed inset-0 bg-white/80 backdrop-blur-md`, spring slide-up via `motion/react`, tab bar with underline indicator + X button, horizontal `snap-x snap-mandatory` scroll container, `onScroll` updates active tab); `SearchLocal.tsx` (local file search panel: TOSEC/No-Intro tag parsing for system/video/language facet chips, wildcard search via `toSqlLike()` in `locate-db.ts` converting `*`→`%` and `?`→`_`, `MediaEntry` rows with badges + path, module-level `_store` persists results/scroll/filters across unmounts, actions dialog with MarqueeText + "Mount on virtual drive" + "Open containing folder", mounted-path highlight via `base_url + url` resolution); `SearchAssembly64.tsx` (Assembly64 Leet API: AQL search via `GET /search/aql/{offset}/{limit}`, categories + presets on mount, category filter chips, paginated ContentItem results, item tap fetches file entries, Download saves to `/sd/downloads/` via `putFileContents`, Mount opens device picker dialog, `Client-Id: Ultimate` + `User-Agent: Assembly Query` headers (identifies the 1541 Ultimate cartridge; server returns 464 for wrong Client-Id and 463 for missing/foreign User-Agent), module-level `_store`); header search icon → tab 0, Apps "Assembly64" card → tab 1
62. **SearchPane expanded to 4 tabs**`SearchCommoServe.tsx` and `SearchCSDbNG.tsx` added as tabs 2 and 3; `initialTab` now accepts `0|1|2|3`; tab indicator changed from underline bar to pill-shaped swipe dots (active dot is wider, blue); load-progress bar removed from SearchPane and relocated inline in SearchLocal
63. **SearchCommoServe: Assembly64-parity mount** — entry rows are now a single tappable `<button>` (no separate Download/Mount split); tapping opens the device picker; on confirm: `clearDirectory(DOWNLOAD_DIR)` wipes `/sd/downloads/commoserve/`, main file streamed via `leetFetch`+`streamFetch` with progress bar, saved via `putFileContents`, cover image (same item's image entry) downloaded and saved; device config set to the local dest path; `isMounted` resolved against local path not remote URL; `isMounting` overlay in device picker shows phase-specific UI (progress bar → saving spinner → image spinner); `LEET_BASE = 'https://commoserve.files.commodore.net/leet'`, `Client-Id: 'Commodore'`, `DOWNLOAD_DIR = '/sd/downloads/commoserve'`
64. **webdav.ts: clearDirectory + streamFetch**`clearDirectory(path)` lists files in a directory and deletes them all (ignores errors, handles missing dir); `streamFetch(res, onProgress)` drains a fetch `Response` body chunk-by-chunk, reporting `{received, total}` progress, returns `ArrayBuffer`; `FetchProgress = { received: number; total: number }` exported type; `listDirectory` accepts new 4th `signal?: AbortSignal` parameter passed to `fetch()`
65. **locate-db.ts: new schema + BFS walk + gzip + stop** — Schema changed to: `dirs (id, path, scanned)`, `files (id, dir_id, name, size, mtime, is_dir, UNIQUE(dir_id,name))`, `files_fts (FTS5 contentless, path, content='')`, `status (id, total_dirs, total_files, last_scan, duration, last_folder)`; BFS walk replaces Depth:infinity — one `listDirectory(dir, false, ...)` per directory from a queue; directories with a `.config` whose `base_url` has a URL scheme are skipped entirely; FTS rowid alignment via `sqlite3_last_insert_rowid` after each file insert; `buildLocateDb` accepts `signal?: AbortSignal` for cancellation, `onProgress` now receives `(phase, value, path?, counts?)``ScanCounts = { dirs, files }` exported; `setTimeout(0)` yield every `SCAN_PROGRESS_INTERVAL` (= 50) entries to unblock browser paint; after saving `/sd/.locate`, gzip at level 9 via `fflate` and save to `/sd/.locate.gz`; `searchLocate` JOINs dirs+files reconstructing full path as `d.path || '/' || f.name`; `dirPath()` helper extracts parent directory of a path
66. **SearchLocal: SD card checks + inline load status + scan UX** — on mount: `stat('/sd')` → if missing show red `AlertCircle` error and skip engine load; `fileExists('/sd/.locate')` → if missing highlight scan button (blue ring); search input slot is a 4-branch conditional: SD missing → error, checking → spinner, loading engine/DB → progress bar with bytes, else → search input + controls; scan Stop button aborts via `scanAbortRef` (`AbortController`); `useEffect` cleanup auto-aborts on unmount so navigating away stops a scan; scan body shows phase label, `N folders · N files · Xh Xm Xs` elapsed time, current path (rtl text direction for long paths), Stop button; `formatDuration(seconds)` helper formats as `Xh Xm Xs` / `Xm Xs` / `Xs`; "Search your device" empty state suppressed while scanning; `subscribeLoadProgress` drives the inline load bar
67. **DeviceDetailOverlay: close button moved left** — X button is now the first element in the header row so it is never obscured by a phone camera cutout; device counter moved to the right edge as a standalone flex item
## Known Issues / Open Work
@ -297,6 +323,9 @@ All WebDAV I/O goes through `src/app/webdav.ts`:
| `movePath(from, to)` | MOVE |
| `copyPath(from, to)` | COPY |
| `fileExists(path)` | HEAD → `boolean` |
| `clearDirectory(path)` | Deletes all files inside a directory (non-fatal if dir missing) |
| `streamFetch(res, onProgress)` | Drains a Response body with progress, returns `ArrayBuffer` |
| `FetchProgress` | `{ received: number; total: number }` |
| `humanFileSize(bytes)` | Formatting helper |
| `normalizePath` / `splitPath` / `joinPath` / `basename` | Path utilities |