Skip to content

fix(browser): resolve GPUI migration bugs, add high-level drawing API…#28

Merged
niklabh merged 4 commits into
mainfrom
fix/gpui-migration-bugs-and-docs
Apr 11, 2026
Merged

fix(browser): resolve GPUI migration bugs, add high-level drawing API…#28
niklabh merged 4 commits into
mainfrom
fix/gpui-migration-bugs-and-docs

Conversation

@niklabh
Copy link
Copy Markdown
Owner

@niklabh niklabh commented Apr 11, 2026

…, and update docs

Three input-handling bugs introduced during the egui → GPUI migration are fixed, a new GPUI-inspired drawing module is added to the SDK, and all documentation is updated to reflect the current API surface.

Bug fixes (ui.rs)

  • Modifier keys never synced: modifiers_shift, modifiers_ctrl, and modifiers_alt in InputState were never populated from GPUI keystroke events. The SDK functions shift_held(), ctrl_held(), alt_held() always returned false. Fixed by reading event.keystroke.modifiers in both on_key_down and on_key_up handlers.

  • Canvas dimensions hardcoded to 800×600: CanvasState.width/height were set once in Default::default() and never updated from the actual GPUI canvas bounds. canvas_dimensions() always returned (800, 600). Fixed by writing bounds.size into CanvasState in the canvas prepare callback each frame.

  • Right/middle mouse clicks not tracked: Only the left mouse button was reflected in mouse_buttons_clicked. The on_mouse_up handlers for Right and Middle now set the corresponding clicked flag, and the duplicate left-click tracking in on_click is removed to avoid double-counting.

New: oxide-sdk/src/draw.rs

Added a high-level drawing module inspired by GPUI conventions:

  • Color — sRGB+alpha with rgb(), rgba(), hex(), named constants
  • Point2D — 2D canvas point
  • Rect — axis-aligned rectangle with contains() hit-testing
  • Canvas — zero-cost facade wrapping low-level canvas_* functions

Documentation

  • Rewrote DOCS.md with complete API reference covering all ~100 host functions: canvas (low-level + high-level), widgets, audio, video, media capture, timers, navigation, hyperlinks, crypto, and troubleshooting.
  • Updated oxide-sdk module docs with draw module, version references, and guest module contract.
  • Updated oxide-browser/src/lib.rs with GPUI-to-SDK mapping table showing how each SDK draw call translates to GPUI GPU primitives.
  • Updated oxide-docs/src/lib.rs with high-level drawing API, video, and media capture sections.

Made-with: Cursor

Summary by CodeRabbit

  • New Features

    • GPU-driven UI backend (GPUI) powering an accelerated desktop shell and rendering model
    • High-level drawing API (Canvas, Color, Point2D, Rect) for ergonomic immediate-mode drawing
    • Guest lifecycle: optional on_frame() and on_timer() hooks for interactive apps
  • API Changes

    • Low-level canvas text/line calls now accept an explicit alpha/opacity parameter
  • Documentation

    • Expanded guides, examples (static vs interactive), multimedia, input, and troubleshooting content

…, and update docs

Three input-handling bugs introduced during the egui → GPUI migration are
fixed, a new GPUI-inspired drawing module is added to the SDK, and all
documentation is updated to reflect the current API surface.

## Bug fixes (ui.rs)

- **Modifier keys never synced**: `modifiers_shift`, `modifiers_ctrl`, and
  `modifiers_alt` in `InputState` were never populated from GPUI keystroke
  events. The SDK functions `shift_held()`, `ctrl_held()`, `alt_held()`
  always returned false. Fixed by reading `event.keystroke.modifiers` in
  both `on_key_down` and `on_key_up` handlers.

- **Canvas dimensions hardcoded to 800×600**: `CanvasState.width/height`
  were set once in `Default::default()` and never updated from the actual
  GPUI canvas bounds. `canvas_dimensions()` always returned (800, 600).
  Fixed by writing `bounds.size` into `CanvasState` in the canvas prepare
  callback each frame.

- **Right/middle mouse clicks not tracked**: Only the left mouse button
  was reflected in `mouse_buttons_clicked`. The `on_mouse_up` handlers
  for Right and Middle now set the corresponding clicked flag, and the
  duplicate left-click tracking in `on_click` is removed to avoid
  double-counting.

## New: oxide-sdk/src/draw.rs

Added a high-level drawing module inspired by GPUI conventions:
- `Color` — sRGB+alpha with `rgb()`, `rgba()`, `hex()`, named constants
- `Point2D` — 2D canvas point
- `Rect` — axis-aligned rectangle with `contains()` hit-testing
- `Canvas` — zero-cost facade wrapping low-level `canvas_*` functions

## Documentation

- Rewrote DOCS.md with complete API reference covering all ~100 host
  functions: canvas (low-level + high-level), widgets, audio, video, media
  capture, timers, navigation, hyperlinks, crypto, and troubleshooting.
- Updated oxide-sdk module docs with draw module, version references, and
  guest module contract.
- Updated oxide-browser/src/lib.rs with GPUI-to-SDK mapping table showing
  how each SDK draw call translates to GPUI GPU primitives.
- Updated oxide-docs/src/lib.rs with high-level drawing API, video, and
  media capture sections.

Made-with: Cursor
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e2f06639-9d63-4087-a27c-378339137e5f

📥 Commits

Reviewing files that changed from the base of the PR and between d326a48 and 30ff2b0.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
✅ Files skipped from review due to trivial changes (2)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml

📝 Walkthrough

Walkthrough

Replaced egui/eframe with GPUI across docs and the desktop shell, added a high-level drawing API (oxide_sdk::draw), made low-level canvas text/line calls alpha-aware (RG B → RGBA), documented optional guest exports (on_frame, on_timer), and moved browser startup into oxide_browser::ui::run_browser. Changes span docs, SDK, host, and examples.

Changes

Cohort / File(s) Summary
Top-level docs & site
CONTRIBUTING.md, README.md, ROADMAP.md, oxide-docs/index.html, oxide-landing/index.html
Replaced egui/eframe references with GPUI; updated architecture, project-structure, and roadmap wording to reflect GPUI shell.
Comprehensive docs & API reference
DOCS.md, oxide-docs/src/lib.rs
Expanded guest module contract (required start_app, optional on_frame(dt_ms), optional on_timer(callback_id)); added high-level oxide_sdk::draw docs; expanded input, interactive widgets, multimedia, timers, storage, serving, and troubleshooting sections.
Crate deps & re-exports
oxide-browser/Cargo.toml, oxide-browser/src/lib.rs
Removed egui/eframe deps; added gpui and smallvec; added pub use gpui; and GPUI-focused crate documentation.
Host runtime & draw commands
oxide-browser/src/main.rs, oxide-browser/src/capabilities.rs
Delegated startup to ui::run_browser(...); DrawCommand::Text and DrawCommand::Line now include alpha a field; host FFI closures (api_canvas_text/api_canvas_line) updated; subtitle text alpha set to 255.
SDK low-level API changes
oxide-sdk/src/lib.rs
Updated public wrappers: canvas_text(..., r,g,b,a, text) and canvas_line(..., r,g,b,a, thickness); exported new pub mod draw;.
SDK high-level draw module
oxide-sdk/src/draw.rs
Added new module with Color, Point2D, Rect, and Canvas types and methods that forward to low-level canvas_* FFI.
Examples: canvas call updates
examples/*/src/lib.rs (e.g., audio-player, fullstack-notes/frontend, hello-oxide, index, media-capture, timer-demo, video-player, etc.)
Updated many canvas_text and several canvas_line call sites to include the new alpha argument (commonly 255) to match revised signatures.
Minor rustdoc/string tweaks
oxide-browser/src/audio_format.rs, oxide-browser/src/video_format.rs
Clarified docstrings to state the functions return AUDIO_FORMAT_*/VIDEO_FORMAT_* constants and replaced egui terminology with GPUI in host-layer docs.
CI workflows
.github/workflows/ci.yml, .github/workflows/release.yml
Added libxkbcommon-x11-dev to apt install lists in CI and release workflows.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Guest as Guest WASM
participant SDK as oxide_sdk (draw + canvas_*)
participant Host as Oxide Host
participant GPUI as GPUI Renderer

Guest->>SDK: call Canvas methods or low-level canvas_* (include RGBA)
SDK->>Host: emit host FFI canvas_* calls (with RGBA)
Host->>Host: enqueue DrawCommand(s) (Text/Line/Image with alpha)
Host->>GPUI: flush frame queue to GPUI (quads/paths/text shaping, textures)
GPUI-->>Host: frame rendered / textures updated
Host-->>Guest: optional on_frame/on_timer callbacks

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I swapped my brushes from egui to GPUI bright,

quads and alpha singing under GPU light.
A Canvas now hops with colors true,
text and lines with opacity too.
Hooray — the rabbit renders through the night!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically summarizes the main changes: fixing GPUI migration bugs and adding a high-level drawing API, which are the core contributions of this multi-file PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/gpui-migration-bugs-and-docs

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
DOCS.md (1)

55-55: Reword repeated sentence openings for readability.

Line 55 starts three consecutive sentences with “No …”; consider combining for smoother flow.

✍️ Suggested wording
-No layout engine. No style cascade. No DOM. The guest decides exactly where every pixel goes.
+There is no layout engine, style cascade, or DOM—the guest decides exactly where every pixel goes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DOCS.md` at line 55, The three consecutive sentences that each start with
"No" (the sentence beginning "No layout engine.", "No style cascade.", and "No
DOM.") should be rephrased/combined for better flow; edit the paragraph
containing "No layout engine. No style cascade. No DOM. The guest decides
exactly where every pixel goes." to either combine into a single sentence like
"There is no layout engine, style cascade, or DOM; the guest decides exactly
where every pixel goes." or vary the openings (e.g., "There is no layout engine.
There is no style cascade. There is no DOM; the guest...") so the repetition is
removed while preserving meaning. Ensure the revised sentence(s) keep the
original emphasis that the guest controls pixel placement.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@DOCS.md`:
- Around line 16-43: The fenced ASCII diagram block in the DOCS.md architecture
section is missing a language tag; update the opening fence for that diagram
(the triple backticks that currently start the ASCII art block) to include a
language like text (e.g., change ``` to ```text) so the markdown linter
recognizes the block as a code/fenced block with a language.

In `@oxide-browser/src/main.rs`:
- Line 10: The code currently panics on window creation inside
oxide_browser::ui::run_browser (the call that uses .expect("open window"));
remove the .expect and propagate the error instead so callers (like main's
run_browser call) can handle it. Replace the .expect("open window") on the
window creation Result with a fallible return (use the ? operator, or
map_err/with_context to convert to anyhow::Error and then ?), ensuring
run_browser keeps returning anyhow::Result<()> and that any context is added via
with_context or .context(...) before returning.

In `@oxide-sdk/src/draw.rs`:
- Around line 112-114: Rect::contains currently treats right and bottom edges as
inclusive which causes adjacent rectangles that touch to both report hits;
change the bounds to half-open by testing px >= self.x && px < self.x + self.w
and py >= self.y && py < self.y + self.h so the max edges are exclusive. Update
the contains method in the Rect implementation accordingly so touching edges are
only counted by one rect.

In `@oxide-sdk/src/lib.rs`:
- Around line 103-107: The docs currently suggest on_timer works independently;
update the comment block describing the guest contract to state that timer
callbacks (on_timer) are only delivered for modules that also export on_frame
(i.e., live/interactive modules), so guests exporting only on_timer will not
receive timer callbacks; reference the symbols start_app, on_frame, on_timer,
set_timeout, and set_interval and add a short note explaining this dependency in
the comment.

---

Nitpick comments:
In `@DOCS.md`:
- Line 55: The three consecutive sentences that each start with "No" (the
sentence beginning "No layout engine.", "No style cascade.", and "No DOM.")
should be rephrased/combined for better flow; edit the paragraph containing "No
layout engine. No style cascade. No DOM. The guest decides exactly where every
pixel goes." to either combine into a single sentence like "There is no layout
engine, style cascade, or DOM; the guest decides exactly where every pixel
goes." or vary the openings (e.g., "There is no layout engine. There is no style
cascade. There is no DOM; the guest...") so the repetition is removed while
preserving meaning. Ensure the revised sentence(s) keep the original emphasis
that the guest controls pixel placement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 005eabaf-5b3a-49cf-a249-4cb73da09ef9

📥 Commits

Reviewing files that changed from the base of the PR and between 6f0f42e and 0d90a4a.

📒 Files selected for processing (16)
  • CONTRIBUTING.md
  • DOCS.md
  • README.md
  • ROADMAP.md
  • oxide-browser/Cargo.toml
  • oxide-browser/src/audio_format.rs
  • oxide-browser/src/capabilities.rs
  • oxide-browser/src/lib.rs
  • oxide-browser/src/main.rs
  • oxide-browser/src/ui.rs
  • oxide-browser/src/video_format.rs
  • oxide-docs/index.html
  • oxide-docs/src/lib.rs
  • oxide-landing/index.html
  • oxide-sdk/src/draw.rs
  • oxide-sdk/src/lib.rs

Comment thread DOCS.md
Comment on lines +16 to +43
┌──────────────────────────────────────────────────────┐
│ Oxide Browser │
│ ┌──────────┐ ┌────────────┐ ┌──────────────┐ │
│ │ URL Bar │ │ Canvas │ │ Console │ │
│ └────┬─────┘ └──────┬─────┘ └──────┬───────┘ │
│ │ │ │ │
│ ┌────▼───────────────▼───────────────▼───────┐ │
│ │ Host Runtime │ │
│ │ wasmtime engine + sandbox policy │ │
│ │ fuel limit: 500M │ memory: 16MB max │ │
│ └────────────────────┬───────────────────────┘ │
│ │ │
│ ┌────────────────────▼───────────────────────┐ │
│ │ Capability Provider │ │
│ │ "oxide" import module │ │
│ │ canvas, console, storage, clipboard, │ │
│ │ fetch, audio, video, media capture, │ │
│ │ crypto, timers, navigation, widgets, │ │
│ │ input, hyperlinks, dynamic loading │ │
│ └────────────────────┬───────────────────────┘ │
│ │ │
│ ┌────────────────────▼───────────────────────┐ │
│ │ Guest .wasm Module │ │
│ │ exports: start_app(), on_frame(dt_ms) │ │
│ │ imports: oxide::* │ │
│ └────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a language tag to this fenced block.

The architecture block should declare a fence language (e.g., text) to satisfy markdown linting.

🧩 Suggested change
-```
+```text
 ┌──────────────────────────────────────────────────────┐
 ...
 └──────────────────────────────────────────────────────┘
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DOCS.md` around lines 16 - 43, The fenced ASCII diagram block in the DOCS.md
architecture section is missing a language tag; update the opening fence for
that diagram (the triple backticks that currently start the ASCII art block) to
include a language like text (e.g., change ``` to ```text) so the markdown
linter recognizes the block as a code/fenced block with a language.

Comment thread oxide-browser/src/main.rs
)
.map_err(|e| anyhow::anyhow!("eframe error: {e}"))?;

oxide_browser::ui::run_browser(host_state, status)?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether run_browser still panics on window-open failure.
rg -n 'expect\("open window"\)' oxide-browser/src/ui.rs -C2
ast-grep --pattern $'pub fn run_browser($_, $_) -> anyhow::Result<()> { $$$ expect($_) $$$ }' oxide-browser/src/ui.rs

Repository: niklabh/oxide

Length of output: 230


Remove expect("open window") to avoid panics in error path.

The run_browser function returns anyhow::Result<()>, but line 1671 in oxide-browser/src/ui.rs calls .expect("open window") which panics on window creation failure instead of propagating the error. This bypasses graceful error handling at the call site in main.rs:10. Replace the expect() call with proper error handling to return the error to the caller.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@oxide-browser/src/main.rs` at line 10, The code currently panics on window
creation inside oxide_browser::ui::run_browser (the call that uses .expect("open
window")); remove the .expect and propagate the error instead so callers (like
main's run_browser call) can handle it. Replace the .expect("open window") on
the window creation Result with a fallible return (use the ? operator, or
map_err/with_context to convert to anyhow::Error and then ?), ensuring
run_browser keeps returning anyhow::Result<()> and that any context is added via
with_context or .context(...) before returning.

Comment thread oxide-sdk/src/draw.rs
Comment thread oxide-sdk/src/lib.rs
Comment on lines +103 to +107
//! 1. **Export `start_app`** — `extern "C" fn()` entry point, called once on load.
//! 2. **Optionally export `on_frame`** — `extern "C" fn(dt_ms: u32)` for
//! interactive apps with a render loop (called every frame, fuel replenished).
//! 3. **Optionally export `on_timer`** — `extern "C" fn(callback_id: u32)`
//! to receive timer callbacks from [`set_timeout`] / [`set_interval`].
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Clarify on_timer dependency on on_frame in the guest contract.

As written, this implies on_timer works independently. In current runtime flow, timer callbacks are only driven for live modules that export on_frame, so on_timer-only guests won’t receive callbacks.

🛠️ Suggested doc correction
-//! 2. **Optionally export `on_frame`** — `extern "C" fn(dt_ms: u32)` for
-//!    interactive apps with a render loop (called every frame, fuel replenished).
-//! 3. **Optionally export `on_timer`** — `extern "C" fn(callback_id: u32)`
-//!    to receive timer callbacks from [`set_timeout`] / [`set_interval`].
+//! 2. **Optionally export `on_frame`** — `extern "C" fn(dt_ms: u32)` for
+//!    interactive apps with a render loop (called every frame, fuel replenished).
+//! 3. **Optionally export `on_timer`** — `extern "C" fn(callback_id: u32)`
+//!    to receive timer callbacks from [`set_timeout`] / [`set_interval`].
+//!    In the current host runtime, timer callbacks are dispatched during the live
+//!    frame loop, so exporting `on_frame` is also required.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//! 1. **Export `start_app`** — `extern "C" fn()` entry point, called once on load.
//! 2. **Optionally export `on_frame`** — `extern "C" fn(dt_ms: u32)` for
//! interactive apps with a render loop (called every frame, fuel replenished).
//! 3. **Optionally export `on_timer`** — `extern "C" fn(callback_id: u32)`
//! to receive timer callbacks from [`set_timeout`] / [`set_interval`].
//! 1. **Export `start_app`** — `extern "C" fn()` entry point, called once on load.
//! 2. **Optionally export `on_frame`** — `extern "C" fn(dt_ms: u32)` for
//! interactive apps with a render loop (called every frame, fuel replenished).
//! 3. **Optionally export `on_timer`** — `extern "C" fn(callback_id: u32)`
//! to receive timer callbacks from [`set_timeout`] / [`set_interval`].
//! In the current host runtime, timer callbacks are dispatched during the live
//! frame loop, so exporting `on_frame` is also required.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@oxide-sdk/src/lib.rs` around lines 103 - 107, The docs currently suggest
on_timer works independently; update the comment block describing the guest
contract to state that timer callbacks (on_timer) are only delivered for modules
that also export on_frame (i.e., live/interactive modules), so guests exporting
only on_timer will not receive timer callbacks; reference the symbols start_app,
on_frame, on_timer, set_timeout, and set_interval and add a short note
explaining this dependency in the comment.

niklabh and others added 2 commits April 11, 2026 20:43
- Add alpha channel support to canvas_text and canvas_line (FFI, SDK,
  host renderer, and all call sites) so the full Color API is honored
- Fix double-lock of status mutex in toolbar rendering (TOCTOU race)
- Add SAFETY comment to unsafe impl Send for RunResult
- Use can_back/can_fwd to visually disable navigation buttons
- Replace window.drop_image() with Arc drop (not a public GPUI API)
- Fix Rect::contains to use half-open intervals (standard convention)
- Add #[must_use] to Color::with_alpha
- Fix "builder-style" → "immediate-mode" in draw.rs docs
- Fix unused mut in draw.rs doc example
- Update README: "~50 host functions" → "~100", "Open File" → "Open"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@DOCS.md`:
- Around line 579-589: The docs state `on_frame` and `on_timer` are optional but
omit that timer callbacks are only dispatched during the frame loop; update the
Guest Module Contract section to explicitly note that `on_timer` callbacks are
only delivered during `LiveModule::advance()` (the frame loop) so a guest must
also export `on_frame` (i.e., participate in the frame loop) to receive timers;
mention the symbols `on_timer`, `on_frame`, and `LiveModule::advance` to make
locating the behavior clear.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 01a36304-2398-4077-a572-32217a881c20

📥 Commits

Reviewing files that changed from the base of the PR and between 0d90a4a and 0eda2b0.

📒 Files selected for processing (16)
  • CONTRIBUTING.md
  • DOCS.md
  • README.md
  • examples/audio-player/src/lib.rs
  • examples/fullstack-notes/frontend/src/lib.rs
  • examples/hello-oxide/src/lib.rs
  • examples/index/src/lib.rs
  • examples/media-capture/src/lib.rs
  • examples/timer-demo/src/lib.rs
  • examples/video-player/src/lib.rs
  • oxide-browser/src/capabilities.rs
  • oxide-browser/src/ui.rs
  • oxide-docs/src/lib.rs
  • oxide-sdk/README.md
  • oxide-sdk/src/draw.rs
  • oxide-sdk/src/lib.rs
✅ Files skipped from review due to trivial changes (1)
  • oxide-sdk/src/draw.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • CONTRIBUTING.md
  • README.md
  • oxide-docs/src/lib.rs

Comment thread DOCS.md
Comment on lines +579 to +589
## Guest Module Contract

Every `.wasm` module loaded by Oxide must:

1. **Export `start_app`** — `extern "C" fn()` entry point, called once on load.
2. **Optionally export `on_frame`** — `extern "C" fn(dt_ms: u32)` for interactive apps with a render loop.
3. **Optionally export `on_timer`** — `extern "C" fn(callback_id: u32)` to receive timer callbacks.
4. **Import from `"oxide"` module** — All host capabilities are under this namespace.
5. **Compile as `cdylib`** — `crate-type = ["cdylib"]` in `Cargo.toml`.
6. **Target `wasm32-unknown-unknown`** — no WASI, pure capability-based I/O.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify on_timer dependency on on_frame.

The Guest Module Contract states both on_frame and on_timer are optional, but timer callbacks are only dispatched during the frame loop (within LiveModule::advance()). A guest that exports on_timer without on_frame will not receive timer callbacks.

📝 Suggested clarification
 4. **Optionally export `on_frame`** — `extern "C" fn(dt_ms: u32)` for interactive apps with a render loop.
-5. **Optionally export `on_timer`** — `extern "C" fn(callback_id: u32)` to receive timer callbacks.
+5. **Optionally export `on_timer`** — `extern "C" fn(callback_id: u32)` to receive timer callbacks.
+   Timer callbacks are dispatched during the frame loop, so `on_frame` must also be exported.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@DOCS.md` around lines 579 - 589, The docs state `on_frame` and `on_timer` are
optional but omit that timer callbacks are only dispatched during the frame
loop; update the Guest Module Contract section to explicitly note that
`on_timer` callbacks are only delivered during `LiveModule::advance()` (the
frame loop) so a guest must also export `on_frame` (i.e., participate in the
frame loop) to receive timers; mention the symbols `on_timer`, `on_frame`, and
`LiveModule::advance` to make locating the behavior clear.

@niklabh niklabh merged commit 6e587bb into main Apr 11, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant