Skip to content

Misc Changes#50

Merged
niklabh merged 7 commits into
mainfrom
misc2
Jun 10, 2026
Merged

Misc Changes#50
niklabh merged 7 commits into
mainfrom
misc2

Conversation

@niklabh

@niklabh niklabh commented Jun 10, 2026

Copy link
Copy Markdown
Owner
  • fix(storage): scope KV and session storage to a stable app origin
  • feat(manifest): optional TOML app manifests with capability declarations
  • feat(permissions): per-origin permission model with in-browser prompt
  • fix(hello-oxide): migrate ui_button calls to callback style

Summary by CodeRabbit

  • New Features

    • App manifests: Optional TOML configuration files for declaring app metadata and required permissions
    • In-browser permission system for sensitive APIs (camera, microphone, geolocation, screen capture)
    • Permission prompt UI overlay in browser shell
  • Documentation

    • Updated button interaction patterns to use callback-based API
    • Added permission declaration and app manifest guidance
  • Improvements

    • Refactored button event handling from polling-based returns to callback-driven execution across all examples

niklabh and others added 5 commits June 10, 2026 23:34
Commit 0569a00 changed the SDK ui_button signature from returning a bool
to taking an on_click closure, but hello-oxide still used the old API,
breaking `cargo clippy --workspace --all-targets`.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sensitive APIs (camera, microphone, geolocation, screen capture) are no
longer granted automatically. The first request per origin queues a
Chrome-style prompt rendered by the GPUI shell at the top-left under the
toolbar, with Allow/Block buttons; decisions are remembered per
(origin, kind) for the lifetime of the tab.

Because guest frame callbacks run on the UI thread, a blocking modal
inside a host function would deadlock the renderer. The flow is instead
non-blocking: gated APIs return PERMISSION_PENDING (-5) while the prompt
is up and the guest retries on a later frame, mirroring the async nature
of the web's getUserMedia/geolocation APIs.

- new oxide-browser/src/permissions.rs: PermissionKind/Status, per-origin
  decision store, single pending prompt slot, unit tests
- OxideUrl::app_origin / app_origin_of: stable app identity (scheme+host+
  port for http(s); containing directory for file:// so local apps don't
  share grants)
- media_capture: replace blocking rfd dialogs with the permission gate
- api_get_location: gated; returns 0 bytes while pending or denied
- ui: render the pending prompt with Allow/Block wired to resolve_pending
- sdk: PERMISSION_PENDING const + doc updates for the gated wrappers
- media-capture example: "wanted" flags retry opens each frame while the
  prompt is pending, so capture starts right after the user clicks Allow

Co-authored-by: Cursor <cursoragent@cursor.com>
Apps can ship metadata next to their .wasm module: the browser resolves
the manifest at the same URL with .wasm replaced by .toml
(https://host/app.wasm -> https://host/app.toml), for HTTP(S), file://
loads, and the toolbar Open picker.

Format:

  name = "Media Capture"            # required, shown as the tab title
  description = "..."               # optional
  version = "0.1.0"                 # optional
  permissions = ["camera", "microphone", "geolocation", "screen-capture"]

A present manifest acts as a capability declaration that integrates with
the permission model: sensitive APIs not listed in `permissions` are
denied without prompting, so users are only ever asked for capabilities
an app declares up front. Apps without a manifest keep the legacy
prompt-on-first-use behavior. Malformed manifests log a console warning
and the app loads without one.

- new oxide-browser/src/manifest.rs: AppManifest parse/fetch (64 KiB cap,
  10s timeout, 404 treated as absent), sibling-path lookup for local
  files, manifest_allows() gate, unit tests
- HostState.manifest set on navigation (fetch_and_run), file picker
  (poll_file_pick reads the sibling .toml), and LoadLocal requests
- permission gates (camera/mic/screen/geolocation) consult the manifest
  before the per-origin prompt flow
- tab title prefers the manifest name over the URL-derived title
- ship a manifest with every example app, named after its build artifact
  (e.g. examples/media-capture/media_capture.toml); media-capture
  declares camera/microphone/screen-capture
- document the format and permission integration in DOCS.md

Co-authored-by: Cursor <cursoragent@cursor.com>
Persistent KV keys were prefixed with the full current_url string, which
broke storage in two ways:

- current_url mutates when the guest calls push_state/replace_state, so
  an app that pushed a route lost access to KV data it wrote earlier
- every path got its own store (https://host/a.wasm vs https://host/b.wasm),
  while scheme variants of the same host were also treated as unrelated

Introduce HostState::module_origin, captured once per module load (in
run_module, before start_app) via set_module_origin() and immune to
guest history calls. Origins use OxideUrl::app_origin: scheme+host+port
for http(s), the containing directory for file:// so separate local
apps don't share state. KV storage and the permission gates from the
permission model now key off this stable origin.

Session storage (api_storage_*) previously survived navigation across
apps in the same tab, leaking one app's data to the next. It is now
cleared whenever a load crosses origins, while still surviving
same-origin reloads (sessionStorage-like semantics).

Existing KV data stored under old full-URL prefixes is not migrated.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@niklabh, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 15 minutes and 6 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 06a5a767-1733-4d8c-ba40-a25c646e73eb

📥 Commits

Reviewing files that changed from the base of the PR and between 5c01498 and dba34a4.

📒 Files selected for processing (14)
  • DOCS.md
  • README.md
  • ROADMAP.md
  • SECURITY.md
  • examples/worker-demo/src/lib.rs
  • forge/skills/oxide-wasm-app/references/CAPABILITIES.md
  • oxide-browser/src/capabilities.rs
  • oxide-browser/src/manifest.rs
  • oxide-browser/src/media_capture.rs
  • oxide-browser/src/runtime.rs
  • oxide-browser/src/ui.rs
  • oxide-docs/src/lib.rs
  • oxide-landing/index.html
  • oxide-sdk/src/lib.rs
📝 Walkthrough

Walkthrough

The PR migrates the Oxide UI button API from boolean-return polling to callback closures, and introduces a complete permission gating system with optional app manifests. The host implements per-origin permission tracking, manifest loading, and UI overlays; the SDK changes the button signature; and all examples and documentation are updated to use the new patterns.

Changes

UI Button API Migration and Permission System Implementation

Layer / File(s) Summary
Permission and Manifest Data Models
oxide-browser/src/permissions.rs, oxide-browser/src/manifest.rs
Introduces PermissionKind, PermissionStatus, and PermissionsState for tracking per-origin permission decisions; defines AppManifest with TOML parsing and allowance checking; includes unit tests for prompt queuing and permission scoping.
App Origin and Storage Scoping
oxide-browser/src/url.rs, oxide-browser/src/capabilities.rs
Implements app_origin() to compute stable per-module identities; adds set_module_origin() and HostState fields for permissions, manifest, and scoped KV storage; session storage persists across same-origin reloads but clears on cross-origin navigation.
Capability and Media Permission Gating
oxide-browser/src/media_capture.rs, oxide-browser/src/capabilities.rs
Replaces synchronous prompts with per-origin permission state; adds permission_gate() helper and updates camera_open, microphone_open, screen_capture, and geolocation to check manifest declarations and return PERMISSION_PENDING while a prompt is awaiting user input.
Runtime Manifest Loading and Module Origin Setup
oxide-browser/src/runtime.rs, oxide-browser/src/lib.rs
Fetches optional companion manifests during app load and stores them in HostState; sets up module origin for stable storage/permission scoping before guest instantiation.
Permission Prompt UI Overlay
oxide-browser/src/ui.rs
Renders a top-left overlay when a pending permission request exists, showing the origin and capability description with Allow/Block buttons to resolve the prompt.
SDK Button API Callback Migration
oxide-sdk/src/lib.rs, oxide-docs/src/lib.rs
Changes ui_button and ui_button_variant signatures to accept on_click: impl FnOnce() closures instead of returning booleans; adds PERMISSION_PENDING constant and documents permission-gated media APIs.
Audio Player Example Button Refactoring
examples/audio-player/audio_player.toml, examples/audio-player/src/lib.rs
Converts tone pad buttons, custom playback, URL fetch, and SFX controls to callback style; adds manifest declaring empty permissions.
Media Capture Example Permission Flow
examples/media-capture/media_capture.toml, examples/media-capture/src/lib.rs
Introduces wanted-flag state (CAM_WANTED, MIC_WANTED, SCR_WANTED) to manage permission-pending retries; buttons set flags, and on_frame retries API calls until permission is granted or denied; manifest declares required camera, microphone, and screen-capture permissions.
Remaining Example Button Refactoring
examples/*/src/lib.rs, examples/*/*.toml
Converts all event, file-picker, hello, MIDI, RAF, RTC, shadcn, stream-fetch, timer, video, worker, and WebSocket chat examples to callback-based buttons; adds manifest files to each declaring name, description, version, and permissions.
Forge and SDK Documentation Updates
forge/skills/oxide-wasm-app/SKILL.md, forge/skills/oxide-wasm-app/references/CAPABILITIES.md, .../PATTERNS.md, .../RECIPES.md
Updates preferred button patterns, capability references, and recipes to show callback-driven buttons and permission-pending retry semantics.
User Documentation and Example Manifests
DOCS.md, README.md
Documents permission-gated sensitive APIs, app manifest format, and manifest lifecycle; updates code examples to show callback-based ui_button usage.
Cargo Dependency
oxide-browser/Cargo.toml
Adds toml = "1.1.2" dependency for manifest TOML parsing.

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • niklabh/oxide#37: Updates examples/events-demo/src/lib.rs to refactor "Emit ping" and "Reset count" buttons to the new callback form, directly tied to the events-demo example added in the retrieved PR.
  • niklabh/oxide#47: Concurrent changes to the ui_button_variant widget interface for shadcn components, closely coupled with this PR's SDK-level button API callback migration.
  • niklabh/oxide#48: Both PRs update examples/worker-demo/src/lib.rs UI wiring; the retrieved PR introduces the example while this PR refactors its button interactions to callback style.

Poem

🐰 Hop along, dear developers, to buttons that call—
No polling for clicks, just callbacks for all!
Permissions now prompt in the browser's own way,
With manifests guiding what apps can display.
From Oxide's warren, a fresh UI day! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Misc Changes' is vague and generic, using a non-descriptive term that fails to convey meaningful information about the substantial changeset covering storage scoping, manifest support, permission model implementation, and API refactoring. Consider a more specific title that captures the main themes, such as 'feat: implement permission system, app manifests, and storage origin scoping' or 'refactor: migrate to callback-based ui_button and add manifest/permission support'.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 misc2

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.

Bring the documentation and the landing page in line with the recently
shipped permission model, manifest system, and origin model fix:

- README: permission prompts + manifests in the intro and security model,
  "Optional app manifest" section with the TOML format, manifest step in
  the loading pipeline, origin-scoped storage in the capability table
- DOCS.md: storage section explains origin scoping and sessionStorage-like
  clearing; media capture section documents the non-blocking
  PERMISSION_PENDING retry flow with an updated example; get_location
  notes permission gating
- SECURITY.md: permission bypasses and cross-origin storage leaks added
  to scope; security design lists prompts, manifest declarations, and
  origin-scoped storage as layers
- ROADMAP.md: mark permission prompts, app manifests, and origin-scoped
  storage as shipped in Phase 0
- oxide-docs: storage and media-capture overviews updated to match
- oxide-landing: hero stat 150+ -> 200+ host APIs (203 registered),
  Sandboxed card mentions permission prompts and manifests, permissions
  capability tag in the architecture diagram, Phase 0 roadmap card lists
  the new security features

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
DOCS.md (1)

331-348: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the Interactive Widgets section to the callback API.

Line 335 and Line 341 still show the old ui_button(...)->bool usage, which no longer matches the current SDK and will fail when copied.

Suggested doc fix
-Widgets are **immediate-mode**: call them every frame from `on_frame()`. They return the current value. The host renders them as native GPUI elements overlaid on the canvas.
+Widgets are **immediate-mode**: call them every frame from `on_frame()`. Most return current value; buttons run a callback on click. The host renders them as native GPUI elements overlaid on the canvas.

-| `ui_button` | `fn(id, x, y, w, h: f32, label: &str) -> bool` | Button; returns `true` when clicked |
+| `ui_button` | `fn(id, x, y, w, h: f32, label: &str, on_click: impl FnOnce())` | Button; runs callback when clicked |

-if ui_button(1, 20.0, 100.0, 120.0, 28.0, "Click Me!") {
-    log("Button was clicked!");
-}
+ui_button(1, 20.0, 100.0, 120.0, 28.0, "Click Me!", || {
+    log("Button was clicked!");
+});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DOCS.md` around lines 331 - 348, The docs still show the old immediate-mode
signatures for ui_button, ui_checkbox, ui_slider, and ui_text_input that return
values; update the "Interactive Widgets" section to the new callback-style API
by replacing those signatures and examples with the callback-based usage
(ui_button, ui_checkbox, ui_slider, ui_text_input) where you pass a
closure/handler invoked with events or the current value instead of expecting a
return value—ensure the table entries and the example block reflect the new
signatures and demonstrate handling the click/event or receiving the current
state via the provided callback.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/worker-demo/src/lib.rs`:
- Around line 90-118: The click handler currently sets s.phase = 1 and
overwrites s.handle by calling spawn_worker(...) unconditionally, which can
orphan a running worker if the button is clicked again; update the closure to
first check s.handle (the current worker handle) and either ignore the click
while a worker is active, or call worker_terminate(s.handle) and wait/confirm
termination before spawning a new worker, then only assign s.handle = handle
when spawn_worker returns a positive value and call
worker_post_message(s.handle, ...); also ensure s.handle is reset to 0 if
termination fails or after successful termination to avoid accidental reuse.

In `@oxide-browser/src/capabilities.rs`:
- Around line 750-756: When the module origin changes in set_module_origin, also
clear the live media-capture state so a new origin cannot reuse prior
MediaCaptureState; locate set_module_origin and, alongside the existing
state.storage.lock().unwrap().clear(), acquire and reset/clear the media capture
structure (e.g. state.media_capture.lock().unwrap().clear() or otherwise reset
the MediaCaptureState held on HostState) so any existing camera/microphone
handles cannot be used by the new origin (affecting api_camera_capture_frame and
api_microphone_read_samples).
- Around line 1365-1382: The current geolocation host-call closure collapses
manifest-denied, user-denied, and prompt-pending into the same 0 return; update
the closure so each PermissionStatus is mapped to a distinct u32 so the guest
can distinguish states: use crate::manifest::manifest_allows to return a
specific "manifest not declared" code, handle
crate::permissions::check_or_request and map PermissionStatus::Granted to the
existing success path, PermissionStatus::Denied to a distinct "denied" code, and
the prompt/pending variant to a distinct "pending" code; ensure callers of this
closure (ABI consumers) are updated to expect these distinct codes if needed.

In `@oxide-browser/src/manifest.rs`:
- Around line 142-151: The code currently calls response.bytes().await then
checks bytes.len(), which buffers the entire body; instead, first check
response.content_length() (or the Content-Length header) and return Err if it
exceeds MAX_MANIFEST_SIZE, and if unknown stream the body with
response.bytes_stream() or response.chunk() inside the function that currently
calls response.bytes().await: iterate chunks, accumulate into a Vec<u8> (or a
BytesMut) while tracking total_read and immediately return an Err when
total_read > MAX_MANIFEST_SIZE; replace the direct use of response.bytes().await
and the bytes variable with this streaming read and the same error messages,
referencing response, MAX_MANIFEST_SIZE, and the current bytes handling logic.
- Around line 76-82: manifest_url_for currently strips query and fragment by
using without_query, which loses signed/versioned query params; instead, keep
the original suffix (query/fragment) when converting ".wasm" to ".toml". In
manifest_url_for, compute the split index of the first '?' or '#' (if any), let
base = &url[..idx] and suffix = &url[idx..] (or suffix = "" if none); check
base.strip_suffix(".wasm") and, if present, return
format!("{base_without_wasm}.toml{suffix}"). Update references in the function
(wasm_url.as_str(), without_query) accordingly to preserve query/fragment in the
returned manifest URL.

In `@oxide-browser/src/runtime.rs`:
- Around line 254-267: run_bytes() is inheriting the previous BrowserHost
origin/manifest (host_state.current_url and host_state.manifest) and must not
reuse them; either extend run_bytes() to accept an explicit source URL/origin
and optional Manifest (mirroring fetch_and_run()) and set those into
self.host_state before invoking run_module(), or explicitly reset
self.host_state.current_url and self.host_state.manifest to a safe local
default/None under the same lock() guard before calling run_module(); update any
callers accordingly so in-memory/local modules never reuse the previous app's
origin/manifest.

In `@oxide-browser/src/ui.rs`:
- Around line 282-290: The tab title logic in the PageStatus::Running arm
currently always prefers host_state.manifest.name, causing internal pages to
show an app's manifest title; change the branch to first detect internal pages
(e.g., URL scheme starts_with "oxide://" or the specific paths "oxide://home",
"oxide://history", "oxide://about", "oxide://forge") and return
url_to_title(url) for those; only fall back to using
self.host_state.manifest.lock().unwrap().as_ref().name when the URL is not an
internal oxide:// page and the manifest name is non-empty. Ensure you update the
PageStatus::Running match arm in ui.rs accordingly.

---

Outside diff comments:
In `@DOCS.md`:
- Around line 331-348: The docs still show the old immediate-mode signatures for
ui_button, ui_checkbox, ui_slider, and ui_text_input that return values; update
the "Interactive Widgets" section to the new callback-style API by replacing
those signatures and examples with the callback-based usage (ui_button,
ui_checkbox, ui_slider, ui_text_input) where you pass a closure/handler invoked
with events or the current value instead of expecting a return value—ensure the
table entries and the example block reflect the new signatures and demonstrate
handling the click/event or receiving the current state via the provided
callback.
🪄 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: 375001cf-bdb5-4db8-aadf-85733774b1bb

📥 Commits

Reviewing files that changed from the base of the PR and between ccea897 and 5c01498.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (50)
  • DOCS.md
  • README.md
  • examples/audio-player/audio_player.toml
  • examples/audio-player/src/lib.rs
  • examples/events-demo/events_demo.toml
  • examples/events-demo/src/lib.rs
  • examples/file-picker-demo/file_picker_demo.toml
  • examples/file-picker-demo/src/lib.rs
  • examples/fullstack-notes/frontend/fullstack_notes_frontend.toml
  • examples/gpu-graphics-demo/gpu_graphics_demo.toml
  • examples/gradient-demo/gradient_demo.toml
  • examples/hello-oxide/hello_oxide.toml
  • examples/hello-oxide/src/lib.rs
  • examples/index/index.toml
  • examples/media-capture/media_capture.toml
  • examples/media-capture/src/lib.rs
  • examples/midi-demo/midi_demo.toml
  • examples/midi-demo/src/lib.rs
  • examples/raf-demo/raf_demo.toml
  • examples/raf-demo/src/lib.rs
  • examples/rtc-chat/rtc_chat.toml
  • examples/rtc-chat/src/lib.rs
  • examples/shadcn-demo/shadcn_demo.toml
  • examples/shadcn-demo/src/lib.rs
  • examples/stream-fetch-demo/src/lib.rs
  • examples/stream-fetch-demo/stream_fetch_demo.toml
  • examples/timer-demo/src/lib.rs
  • examples/timer-demo/timer_demo.toml
  • examples/typography-demo/typography_demo.toml
  • examples/video-player/src/lib.rs
  • examples/video-player/video_player.toml
  • examples/worker-demo/src/lib.rs
  • examples/worker-demo/worker_demo.toml
  • examples/ws-chat/src/lib.rs
  • examples/ws-chat/ws_chat.toml
  • forge/skills/oxide-wasm-app/SKILL.md
  • forge/skills/oxide-wasm-app/references/CAPABILITIES.md
  • forge/skills/oxide-wasm-app/references/PATTERNS.md
  • forge/skills/oxide-wasm-app/references/RECIPES.md
  • oxide-browser/Cargo.toml
  • oxide-browser/src/capabilities.rs
  • oxide-browser/src/lib.rs
  • oxide-browser/src/manifest.rs
  • oxide-browser/src/media_capture.rs
  • oxide-browser/src/permissions.rs
  • oxide-browser/src/runtime.rs
  • oxide-browser/src/ui.rs
  • oxide-browser/src/url.rs
  • oxide-docs/src/lib.rs
  • oxide-sdk/src/lib.rs

Comment thread examples/worker-demo/src/lib.rs
Comment thread oxide-browser/src/capabilities.rs
Comment thread oxide-browser/src/capabilities.rs Outdated
Comment thread oxide-browser/src/manifest.rs
Comment thread oxide-browser/src/manifest.rs Outdated
Comment thread oxide-browser/src/runtime.rs
Comment thread oxide-browser/src/ui.rs
Comment thread oxide-browser/src/ui.rs
Resolve all 8 actionable comments plus the outside-diff docs issue from
the CodeRabbit review of #50:

- capabilities: set_module_origin also resets MediaCaptureState on an
  origin change, so a new origin can't read camera frames or mic samples
  from devices the previous origin opened (critical)
- capabilities/sdk: api_get_location returns i32 — bytes written,
  PERMISSION_PENDING (-5) while the prompt shows, -1 once blocked (by
  the user or an undeclared manifest permission); the SDK wrapper is now
  get_location() -> Result<String, i32> so guests can retry only while
  pending (same wire type, no ABI break)
- manifest: manifest_url_for preserves the query string so signed or
  versioned module URLs (app.wasm?token=…) resolve their sibling .toml;
  fragment is still dropped
- manifest: the 64 KiB cap is enforced while downloading — Content-Length
  is rejected up front and the body is streamed with a running byte
  budget instead of buffering before the check
- runtime/ui: run_bytes() takes an explicit source URL + manifest and
  sets both on HostState before run_module, so a reused BrowserHost
  never scopes storage/permissions to the previous app; RunRequest::
  LoadLocal threads the URL from the file picker and forge runs
- ui: tab titles only use the manifest name when no internal page is
  shown, so oxide:// pages stop displaying a stale app name
- ui: keyboard path for the permission prompt — Enter allows, Escape
  blocks (unmodified keys, swallowed before the guest sees them), with
  a hint line in the prompt
- worker-demo: ignore clicks while a worker is computing so a running
  worker is never orphaned by overwriting its handle
- DOCS/forge skill: Interactive Widgets section updated to the callback
  ui_button API; get_location docs reflect the Result return

Co-authored-by: Cursor <cursoragent@cursor.com>
@niklabh niklabh merged commit 93336c3 into main Jun 10, 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