Polish sample apps for SDL3: Photo Viewer, Music Player, Notepad - #148
Conversation
…t handoff Ships real sample files in the binary (ambient_dawn.mp3, oasis_sample.png, welcome.txt, notes.txt) so every fresh VFS has working content without fetch-samples.sh. Adds an extension → app map and a new AppAction::LaunchAppWithFile so Confirm on a typed file in File Manager hands off to Photo Viewer / Music Player / Text Editor with the file pre-loaded. Music Player now drives real audio via a new media_controller using the same VFS-IPC pattern as the radio. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…pad GUI - Photo Viewer: decode PNG/JPEG via png + jpeg-decoder and blit the real image centered in the window with aspect-fit + zoom; footer shows filename + dimensions. Handles RGB/RGBA/grayscale PNGs and RGB24/L8 JPEGs, downscales images above 1024px so phone photos don't blow the pixel budget. - Music Player: replaces the ID3-dump text with a PSP-style GUI — album-art tile on the left, title + filename + metadata on the right, transport row with prev/rewind/play/ff/next, progress bar above it. Extracts a proper track title from ID3v2 TIT2 and estimates duration from MP3 frame bitrate. - Text Editor: Windows-Notepad-style chrome — title bar, File/Edit/ View/Help menu bar, white text area with selection-highlighted current line + insert-mode caret, status bar with Ln/Col and mode. Plain text (welcome.txt etc.) now renders with proper editor UI instead of the generic terminal-style listing. - Drop oasis_chime.wav — ambient_dawn.mp3 already plays when clicked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous commit only overrode `draw_windowed` — but the classic (PSP-style) skin launches apps fullscreen and renders through `update_sdi`, which was still calling `render_content_sdi` and producing the old `> 1 | text` listing with `-- NORMAL -- Ln 1, Col 1` in a trailing line. That's what surfaced as "still showing CLI". Add `render_notepad_sdi` that builds the full editor chrome (menu bar, white text area with a current-line highlight, caret, status bar with Ln/Col) as named SDI objects, and wire it into `update_sdi` instead of the generic content renderer. Matching `hide_notepad_sdi` tears those objects down when the editor closes so chrome doesn't leak onto the next app. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…on close Two related defects surfaced after the last Notepad-GUI fix: 1. The Notepad chrome could look absent because a previously-opened app's generic content objects (`app_line_*`, `app_sel_*`, `app_scroll`, `app_divider`) stayed visible and — depending on sort order — bled through the white text area. `render_notepad_sdi` now hides those explicitly before drawing its own chrome. 2. The reverse leak: when the Text Editor closed and another app opened, its `np_*` chrome kept rendering on top because `AppRunner::hide_sdi` only knew about `app_*` names. Teach it to hide the Notepad's `np_*` family as well. Also wire a Music-Player shutdown into every window-close path. The app's own Cancel handler emits a `stop` VFS IPC, but the window-manager close button drops the runner before the controller tick can process it — so the track kept playing after the window went away. New `stop_music_if_music_runner` helper mirrors the existing `stop_radio_if_radio_runner` and is called from all three close sites (desktop WM close, desktop Exit, fullscreen Exit). Text Editor, Calculator, Clock, Paint, and Games are now also seeded as `/apps/*` directories so they show up on the Dashboard — previously the registry knew about them but the demo VFS didn't. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Four apps on the dashboard were CLI-style placeholders that either duplicated a GUI sibling or were nothing but a static text dump: - Notepad (plugin) duplicated Text Editor — both are note takers. The `note write / read / list` terminal command is still registered, so the CLI workflow survives; only the dashboard tile is gone. A regression test prevents accidental re-adding. - Network, Package Manager, System Monitor were SimpleApp instances that rendered a handful of hard-coded lines. Their factories stay in `APP_REGISTRY` so scripts and the terminal can still spawn them by name, but they no longer get seeded into `/apps/*` so they don't appear on the dashboard. Bonus: the `populate_creates_all_app_dirs` test now asserts both sides — GUI apps must be present, CLI placeholders must be absent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Only Button::Confirm (Enter) was typing into the buffer because TextEditorApp never overrode the `handle_text_input` / `handle_backspace` defaults on the App trait — the input dispatcher was already forwarding TextInput / Backspace events to the active window's runner, they just hit a no-op. - Implement handle_text_input: filters control chars (Enter/Tab already come through as ButtonPress), auto-enters Insert mode when the user starts typing from Normal mode, and routes chars through the existing undo-tracked `insert_char` path. - Implement handle_backspace: deletes in Insert (and auto-drops into Insert from Normal so backspace after clicking in also works). - Implement handle_click: translates (lx, ly) into a buffer position and switches to Insert mode so the next keystroke types there. Clicks on File/Edit/View/Help post a status message so the user sees the click registered (drop-down menus are future work). - Bump the Insert-mode caret to a deeper blue so the position marker is clearly visible against the white text area. Five new tests cover text input, control-char filtering, backspace, click-to-position, and menu-click feedback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New reusable widget in oasis-ui: `oasis_ui::menu_bar::MenuBar`. Each top-level menu holds a list of `MenuEntry`s (Action or Separator); an Action carries a `label`, `id` string the host recognises, optional `shortcut` hint, and enabled flag. The widget owns open/close state and hover tracking; hit_test returns a `MenuHit` (Label / Item / NoOp / Outside) the host acts on. Two draw methods so the drop-down can float above the window content: `draw_bar` for the strip itself and `draw_dropdown` for the active menu. Visuals match Windows 95/98/2000 — chrome-grey bar, blue-highlighted open label, raised-bezel drop-down, blue hover with white text, indented separators, right-aligned shortcut hints. Eight unit tests cover label hits, item dispatch, disabled items, hover tracking, close/reset, and the entry builder. Wire into Text Editor: - Replace the cosmetic menu labels with a real `MenuBar` field. Defaults to File / Edit / View / Help with ids like `file.save`, `edit.undo`, `help.about`. - `handle_click` routes through the widget: label toggles the drop-down; item dispatches through a new `run_menu_action`; clicks outside close the menu without also moving the caret. - Any button press or text input while a drop-down is open closes it (Win95 modal behaviour) rather than typing behind the menu. - Both `draw_notepad` (windowed) and `render_notepad_sdi` (fullscreen classic skin) render the bar + active drop-down. SDI path uses a fixed pool of 16 item slots so the registry doesn't churn across frames. - `AppRunner::hide_sdi` and `hide_notepad_sdi` now also hide every drop-down SDI object so nothing leaks when the editor closes. Five new text-editor tests: label toggle, File→Save IPC emission, File→Exit returning AppAction::Exit, key-closes-open-dropdown. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The time is already on the status bar and in the taskbar, and the Clock app itself didn't do anything useful — remove it from the dashboard, workspace, and registry rather than leave a redundant tile taking up a slot. - Drop `Clock` entry from the app registry and the VFS seed list (plus the expected-apps test). - Remove `oasis-app-clock` from `oasis-core`'s Cargo.toml deps, the workspace members, and the workspace path map. - Delete the `crates/oasis-app-clock/` crate. - Update CLAUDE.md and docs/design.md to drop the crate from the module tree + extracted-apps list (11 → 10). No runtime callers left outside the crate; clippy / format / 7175 tests all pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claude AI Architecture & Quality ReviewIssues (if any)
Previous Issues (if any)(none — first review of this branch) Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Openrouter AI General ReviewIssues (if any)
Previous Issues (for incremental reviews)(none) Suggestions (if any)
Notes
Generated by Openrouter AI (qwen/qwen3.6-plus). Supplementary to human reviews. |
Claude AI Incremental Security & Correctness ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Automated fix by Claude in response to AI review feedback. Iteration: 1/5 Co-Authored-By: AI Review Agent <noreply@anthropic.com>
Review Response Agent (Iteration 1)Status: Changes committed, pushing... Commit: Fixed Issues
Ignored Issues
Deferred to Human
Notes
Automated summary of agent fixes. |
Fixes the advisory cargo-deny was flagging on every pre-push. The affected code is a reachable panic in CRL parsing inside `BorrowedCertRevocationList::from_der` / `OwnedCertRevocationList::from_der` triggered by a syntactically valid empty BIT STRING in an `onlySomeReasons` element. We don't currently parse CRLs, so this wasn't a live exploit for OASIS, but the advisory was blocking clean pre-push runs. The patch lives entirely in Cargo.lock — semver-compatible bump, no API changes. `cargo deny check` now reports advisories / bans / licenses / sources all OK; 7175 workspace tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Fixed the cargo-deny pre-push advisory in 8d1abdf: bumped We don't currently parse CRLs, so it wasn't a live issue for OASIS, but the advisory was failing every pre-push and Verified:
|
Claude AI Incremental Architecture & Quality ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Claude AI Incremental Security & Correctness ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)(none — no new issues found in TIER 1 files) Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Openrouter AI Incremental General ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Openrouter AI (qwen/qwen3.6-plus). Supplementary to human reviews. |
Automated fix by Claude in response to AI review feedback. Iteration: 2/5 Co-Authored-By: AI Review Agent <noreply@anthropic.com>
Review Response Agent (Iteration 2)Status: Changes committed, pushing... Commit: Fixed Issues
Ignored Issues
Deferred to Human
Notes
Automated summary of agent fixes. |
Claude AI Incremental Security & Correctness ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Claude AI Incremental Architecture & Quality ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)(none — no new actionable issues found in TIER 1 files) Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Openrouter AI Incremental General ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Openrouter AI (qwen/qwen3.6-plus). Supplementary to human reviews. |
Automated fix by Claude in response to AI review feedback. Iteration: 3/5 Co-Authored-By: AI Review Agent <noreply@anthropic.com>
Review Response Agent (Iteration 3)Status: Changes committed, pushing... Commit: Fixed Issues
Ignored Issues
Deferred to Human
Notes
Automated summary of agent fixes. |
Openrouter AI Incremental General ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Openrouter AI (qwen/qwen3.6-plus). Supplementary to human reviews. |
Claude AI Incremental Architecture & Quality ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Claude AI Incremental Security & Correctness ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Automated fix by Claude in response to AI review feedback. Iteration: 4/5 Co-Authored-By: AI Review Agent <noreply@anthropic.com>
Review Response Agent (Iteration 4)Status: Changes committed, pushing... Commit: Fixed Issues
Ignored Issues
Deferred to Human
Notes
Automated summary of agent fixes. |
Claude AI Incremental Architecture & Quality ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)(none — no new actionable issues found in the TIER 1 file) Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Claude AI Incremental Security & Correctness ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Openrouter AI Incremental General ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Openrouter AI (qwen/qwen3.6-plus). Supplementary to human reviews. |
Automated fix by Claude in response to AI review feedback. Iteration: 5/5 Co-Authored-By: AI Review Agent <noreply@anthropic.com>
Review Response Agent (Iteration 5)Status: Changes committed, pushing... Commit: Fixed Issues
Ignored Issues
Deferred to Human
Notes
Automated summary of agent fixes. |
Claude AI Incremental Architecture & Quality ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Claude AI Incremental Security & Correctness ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)(none — no new issues found in the TIER 1 file) Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Claude AI (sonnet). Supplementary to human reviews. |
Openrouter AI Incremental General ReviewThis is an incremental review focusing on changes since the last review. Issues (if any)
Previous Issues (for incremental reviews)
Suggestions (if any)
Notes
Generated by Openrouter AI (qwen/qwen3.6-plus). Supplementary to human reviews. |
Incremental review flagged five items; this commit addresses all of them. New issues: - fit_within: u32 arithmetic in `y * h` / `src_y * w` could wrap for >~4Mpx inputs. Cast to u64 for intermediate products — the MAX_DIMENSION cap already makes this rare, but the fix is trivial and correct regardless of input. - media_controller dropped queued requests: the tick consumed the whole IPC buffer after parsing only the first line, silently losing a second `play_file` queued in the same frame. Now splits on newlines, drains every recognised command in order, and rewrites the file with only the unhandled lines so other controllers sharing the path keep their payloads. - id3v2_title ignored v2.4 syncsafe frame sizes: the function read every frame size as plain big-endian, which is v2.3 but not v2.4. Modern MP3s fell through to the filename fallback. Now detects the major version byte (`data[3]`) and uses syncsafe decoding for v2.4 frames. Rejects v2.2 (different 3-byte frame layout) rather than misparsing. Previously-unresolved items: - Photo Viewer texture leak on runner replacement: when File Manager reused a Photo Viewer window for a different image, `launch_app_window_for_file` dropped the old `AppRunner` without destroying its cached GPU texture. Widened `BrowsingApp::stale_photo_texture` from a single `Cell<Option<...>>` to a `RefCell<Vec<...>>` queue and added `inherit_textures_from` which moves both the cached and pending-stale textures from the outgoing app into the incoming app's queue; the next render frame in `photo_ui::draw` drains and destroys them. Wired into both SDL3 (`launch.rs`) and WASM (`backend-wasm/lib.rs`) replacement paths. - MenuBar `update_hover` had no call site: renamed to `pointer_move` and documented that it's a host-side hook for dispatchers that expose mouse-move events (the current desktop dispatcher only forwards click + text + wheel, so `hovered_item` stays `None` and the drop-down simply renders without a live hover row — correct). Keeps the widget ready for when a pointer-move event variant is added to `InputEvent`. Six new tests: v2.3/v2.4 ID3 parsing, v2.2 rejection, texture transfer, overflow-safe downscaling still produces correct geometry, and the widget rename (existing hover test migrated). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>





Summary
Ships real sample media, wires File Manager -> app dispatch, gives Photo Viewer / Music Player / Text Editor proper GUIs, and adds a reusable Win95-style drop-down menu widget. Retires the Clock app.
ambient_dawn.mp3,oasis_sample.png,welcome.txt,notes.txtunder/home/user/{music,photos,documents}/) so every fresh VFS has working content withoutfetch-samples.sh.AppAction::LaunchAppWithFile+ extension map — Confirm on a.pngopens Photo Viewer,.mp3opens Music Player with real audio,.txtopens Text Editor with the content pre-loaded.png+jpeg-decoder+ auto-downscaling, blitted viaload_texture/blitwith aspect-fit + zoom and a filename/dimensions footer.media_controllerdrives theAudioBackendvia VFS IPC. Stops on window close (bug fix — it kept playing before).draw_windowedfor WM skins,render_notepad_sdifor classic/fullscreen). Title bar, File/Edit/View/Help menu bar, white text area with current-line highlight + blue caret, status bar withLn N, Col M. Keyboard typing + backspace + click-to-position all wired through theApptrait'shandle_text_input/handle_backspace/handle_clickhooks.oasis_ui::menu_barwidget — Windows 95 bezel, hover highlight, right-aligned shortcut hints. Text Editor uses it; any other app can drop in its own menu withMenuBar::new(vec![...]).Test plan
cargo test --workspace— 7175 tests, 0 failurescargo clippy --workspace -- -D warnings— cleancargo fmt --all --check— cleancargo build --release -p oasis-app— boots, 12 dashboard apps, audio subsystem up, disk samples loaded on top of bundled.png/.mp3/.txtdispatches to the right app (unit + integration tests)Generated with Claude Code