feat: native file and folder picker API#38
Conversation
Implements the four Phase 4 System APIs from ROADMAP.md: file_pick,
folder_pick, file_read / file_read_range, and file_metadata. Guests
receive opaque u32 handles — host paths never cross the sandbox.
Host (oxide-browser)
- New module `file_picker.rs` with `FilePickerState` (HashMap<u32,
PathBuf>) and six host functions:
api_file_pick(title, filters, multiple, out) -> i32
api_folder_pick(title) -> u32
api_folder_entries(handle, out) -> i32 (JSON)
api_file_read(handle, out) -> i64
api_file_read_range(handle, off, len, out) -> i64
api_file_metadata(handle, out) -> i32 (JSON)
- Negative returns communicate required buffer size so guests can
resize and retry once (avoids fixed 1 MiB caps like api_upload_file).
- Filter strings are comma-separated extensions ("png,jpg,gif"); empty
allows all files.
- folder_entries pre-allocates sub-handles for each child so guests can
recurse or read files without ever seeing the underlying path.
- file_metadata emits JSON with name, size, mime (extension-inferred),
modified_ms, and is_dir.
- HostState gains `file_picker: Arc<Mutex<FilePickerState>>`; module
registered at the tail of register_host_functions.
SDK (oxide-sdk)
- FFI imports plus safe wrappers: `file_pick`, `folder_pick`,
`folder_entries` (-> Vec<FolderEntry>), `file_read`, `file_read_range`,
`file_metadata` (-> FileMetadata).
- Hand-rolled JSON reader for the strict flat objects the host emits —
avoids pulling serde into guest builds.
- file_read retries once with an exact-sized buffer when the host
reports -(required_size), capped at 64 MiB.
Example (examples/file-picker-demo)
- Split-panel app exercising every new API:
* "Pick Image…" — filtered single select
* "Pick Files…" — unfiltered multi select
* "Pick Folder…" — folder browser with drill-in
- Left panel lists picked files or folder_entries children; clicking a
folder drills in, clicking a file selects for preview.
- Right panel shows metadata and either a canvas_image preview (via
file_read) for image/* or a 2 KiB text snippet (via file_read_range)
for text-ish MIME types.
- Registered in workspace Cargo.toml.
Security
- No new linker capabilities beyond the six registered imports.
- No WASI, no paths exposed to the guest — handles only.
- Access is read-only and scoped to files the user explicitly picked
via the OS dialog.
Verification
- cargo build -p oxide-browser ok
- cargo build -p oxide-sdk --target wasm32-unknown-unknown ok
- cargo build --target wasm32-unknown-unknown --release
-p file-picker-demo ok (118 KB wasm)
- cargo fmt --all && cargo clippy --workspace --all-targets
-- -D warnings ok
- cargo test --workspace ok
ROADMAP.md: the four Phase 4 file API bullets are now checked off.
Made-with: Cursor
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughIntroduces a complete file/folder picker API system spanning host (oxide-browser) and guest (oxide-sdk) layers. Implements native OS dialogs, handle-based file access, metadata queries, and ranged file reads. Adds a new example crate demonstrating the picker UI with image and text preview functionality. Changes
Sequence Diagram(s)sequenceDiagram
participant Guest as Guest App
participant Host as Host Runtime
participant Dialog as OS File Dialog
participant FS as Filesystem
Note over Guest,FS: File Picking & Reading Workflow
Guest->>Host: file_pick(title, filters, multiple)
Host->>Dialog: Open native file dialog
Dialog-->>Host: User selects files
Host->>Host: Allocate handles, store paths
Host-->>Guest: Return handles [u32]
Guest->>Host: file_metadata(handle)
Host->>FS: Stat file (size, mtime, extension)
Host->>Host: Derive MIME from extension
Host-->>Guest: JSON metadata
Guest->>Host: file_read(handle)
Host->>FS: Read entire file
Host-->>Guest: Raw bytes
Guest->>Host: file_read_range(handle, offset, len)
Host->>FS: Read [offset..offset+len)
Host-->>Guest: Bytes (up to len)
sequenceDiagram
participant Guest as Guest App
participant Host as Host Runtime
participant Dialog as OS Folder Dialog
participant FS as Filesystem
Note over Guest,FS: Folder Browsing Workflow
Guest->>Host: folder_pick(title)
Host->>Dialog: Open native folder dialog
Dialog-->>Host: User selects folder
Host->>Host: Allocate directory handle
Host-->>Guest: Return handle (u32)
Guest->>Host: folder_entries(handle)
Host->>FS: Enumerate directory contents
Host->>Host: Allocate handles for each entry
Host-->>Guest: JSON array [{name,size,is_dir,handle}]
Guest->>Host: file_metadata(sub_handle)
Host->>FS: Stat entry
Host-->>Guest: Metadata (reuse same API)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Implements the four Phase 4 System APIs from ROADMAP.md: file_pick, folder_pick, file_read / file_read_range, and file_metadata. Guests receive opaque u32 handles — host paths never cross the sandbox.
Host (oxide-browser)
file_picker.rswithFilePickerState(HashMap<u32, PathBuf>) and six host functions:api_file_pick(title, filters, multiple, out) -> i32
api_folder_pick(title) -> u32
api_folder_entries(handle, out) -> i32 (JSON)
api_file_read(handle, out) -> i64
api_file_read_range(handle, off, len, out) -> i64
api_file_metadata(handle, out) -> i32 (JSON)
resize and retry once (avoids fixed 1 MiB caps like api_upload_file).
allows all files.
recurse or read files without ever seeing the underlying path.
modified_ms, and is_dir.
file_picker: Arc<Mutex<FilePickerState>>; moduleregistered at the tail of register_host_functions.
SDK (oxide-sdk)
file_pick,folder_pick,folder_entries(-> Vec),file_read,file_read_range,file_metadata(-> FileMetadata).Example (examples/file-picker-demo)
Security
Verification
-p file-picker-demo ok (118 KB wasm)
-- -D warnings ok
ROADMAP.md: the four Phase 4 file API bullets are now checked off.
Made-with: Cursor
Summary by CodeRabbit
Release Notes
New Features
Documentation