Skip to main content

Overview

The jarvis-webview crate provides embedded web content for Jarvis panes. It wraps the wry crate (WebKit on macOS, WebView2 on Windows, WebKitGTK on Linux) and provides:
  • Managed WebView instances per pane
  • Bidirectional IPC (Rust ↔ JavaScript)
  • Custom jarvis:// protocol for serving bundled content
  • Navigation control and security
  • Event handling (page load, title change, etc.)

Public API

WebViewManager

Manages all WebView instances across tiling panes.
use jarvis_webview::WebViewManager;

let manager = WebViewManager::new();
new
fn() -> WebViewManager
Create a new WebView manager.
drain_events
fn(&self) -> Vec<WebViewEvent>
Drain all pending events from all WebViews.

WebViewConfig

Configuration for creating a WebView.
WebViewConfig
struct

WebViewHandle

Handle to a specific WebView instance.
use jarvis_webview::WebViewHandle;

// Navigate to a URL
handle.load_url("https://docs.rs");

// Load HTML
handle.load_html("<h1>Hello!</h1>");

// Go back/forward
handle.go_back();
handle.go_forward();

// Evaluate JavaScript
handle.eval("console.log('Hello from Rust!')");

WebViewRegistry

Global registry of all WebView instances.
use jarvis_webview::WebViewRegistry;

let registry = WebViewRegistry::new();
registry.register(pane_id, handle);

if let Some(handle) = registry.get(pane_id) {
    handle.load_url("https://example.com");
}

IPC System

IPC Messages (JS → Rust)

JavaScript sends messages to Rust via window.jarvis.ipc.postMessage().
IpcMessage
struct
IpcPayload
enum
  • Text(String) — Simple text payload
  • Json(serde_json::Value) — Structured JSON
  • None — No payload

JavaScript API

The IPC_INIT_SCRIPT injects this API into every WebView:
// Send a message to Rust
window.jarvis.ipc.send('command_name', { foo: 'bar' });

// Register a handler for messages from Rust
window.jarvis.ipc.on('rust_event', (payload) => {
    console.log('Received from Rust:', payload);
});

// Request-response pattern (async)
const result = await window.jarvis.ipc.request('get_data', {});
console.log(result);

Built-in IPC Message Types

These messages are automatically handled:
Built-in IPC Messages
object

IPC Events (Rust → JS)

Rust sends messages to JavaScript via js_dispatch_message().
use jarvis_webview::ipc::js_dispatch_message;
use serde_json::json;

let js = js_dispatch_message(
    "config_updated",
    &json!({ "theme": "tokyo-night" })
);

handle.eval(&js);
In JavaScript:
window.jarvis.ipc.on('config_updated', (payload) => {
    console.log('New theme:', payload.theme);
});

Built-in IPC Events

Built-in IPC Events
object

WebView Events

WebViewEvent
enum
Events emitted by WebView instances.
PageLoadState
enum
  • Started — Navigation has started
  • Finished — Page has fully loaded (DOMContentLoaded + resources)

Allowed Navigation Prefixes

use jarvis_webview::ALLOWED_NAV_PREFIXES;

for prefix in ALLOWED_NAV_PREFIXES {
    println!("Allowed: {}", prefix);
}
Allowed prefixes:
  • https://
  • http://localhost
  • http://127.0.0.1
  • jarvis://

is_navigation_allowed

use jarvis_webview::is_navigation_allowed;

assert!(is_navigation_allowed("https://docs.rs"));
assert!(is_navigation_allowed("http://localhost:3000"));
assert!(!is_navigation_allowed("file:///etc/passwd"));

Custom Protocol

The jarvis:// protocol serves bundled content.
// Load bundled HTML
window.location = 'jarvis://games/wordle.html';

// Load bundled image
const img = new Image();
img.src = 'jarvis://assets/logo.png';

ContentProvider

Provides content for the custom protocol.
use jarvis_webview::ContentProvider;

let provider = ContentProvider::new(assets_dir);

Command Palette Integration

The IPC system includes a full command palette implementation.

Show Palette from Rust

use jarvis_webview::ipc::js_dispatch_message;
use serde_json::json;

let js = js_dispatch_message("palette_show", &json!({
    "items": [
        { "label": "New Terminal", "keybind": "Cmd+T", "category": "Panels" },
        { "label": "Close Panel", "keybind": "Esc Esc", "category": "Panels" }
    ],
    "query": "",
    "selectedIndex": 0,
    "mode": "command",
    "placeholder": null
}));

handle.eval(&js);

Handle Palette Events

match event {
    WebViewEvent::IpcMessage { pane_id, body } => {
        if let Some(msg) = IpcMessage::from_json(&body) {
            match msg.kind.as_str() {
                "palette_click" => {
                    let index = msg.payload; // Extract index
                    // Execute command
                }
                "palette_dismiss" => {
                    // Hide palette
                }
                _ => {}
            }
        }
    }
    _ => {}
}

Usage Example

use jarvis_webview::{
    WebViewManager, WebViewConfig, WebViewEvent, IpcMessage
};

#[tokio::main]
async fn main() {
    let manager = WebViewManager::new();
    
    let config = WebViewConfig {
        pane_id: 1,
        initial_url: Some("https://docs.rs".to_string()),
        initial_html: None,
        transparent: false,
        devtools: true,
    };
    
    let handle = manager.create(config, &window).unwrap();
    
    loop {
        for event in manager.drain_events() {
            match event {
                WebViewEvent::PageLoad { pane_id, state, url } => {
                    println!("Pane {} loaded: {}", pane_id, url);
                }
                WebViewEvent::IpcMessage { pane_id, body } => {
                    if let Some(msg) = IpcMessage::from_json(&body) {
                        println!("IPC: {} - {:?}", msg.kind, msg.payload);
                    }
                }
                _ => {}
            }
        }
    }
}