Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9,636 changes: 7,696 additions & 1,940 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ members = [
"tauri-runtime-verso-build",
# Examples
"examples/helloworld",
"examples/vendored",
"examples/api/src-tauri",
]

[workspace.dependencies]
verso = { git = "https://github.com/tauri-apps/verso", rev = "572f47ca7ead69ce3e8b0c433e5cff237f0514c9" }
versoview = { git = "https://github.com/tauri-apps/verso", rev = "572f47ca7ead69ce3e8b0c433e5cff237f0514c9" }
versoview_build = { git = "https://github.com/tauri-apps/verso", rev = "572f47ca7ead69ce3e8b0c433e5cff237f0514c9" }


[features]
# Required if you use tauri's macos-private-api feature
macos-private-api = ["tauri-runtime/macos-private-api"]
vendored = ["dep:winit", "dep:rustls", "dep:versoview"]

[dependencies]
verso = { workspace = true }
Expand All @@ -30,6 +34,10 @@ url = "2"
http = "1"
percent-encoding = "2"
log = "0.4"
# Dependencies used only for vendored feature
versoview = { workspace = true, optional = true}
winit = { version = "0.30", optional = true }
rustls = { version = "0.23", default-features = false, features = ["aws-lc-rs"], optional = true }

[target."cfg(windows)".dependencies]
windows = "0.61"
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,24 @@ tauri_runtime_verso::set_verso_devtools_port(1234);

Then go to `about:debugging` in Firefox and connect to `localhost:1234` there

### Embed verso webview directly into tauri App.

To avoid creating a verso binary next to your tauri app bin and distribute these 2 bin, you can embed directly verso into you tauri app with `vendored` feature

Please note you have to install all servo/verso dependencies because cargo will rebuild webview.

```rust
tauri_runtime_verso::builder()
.invoke_handler(tauri::generate_handler![greet])
.setup(|app| {
dbg!(app.get_webview_window("main").unwrap().inner_size()).unwrap();
// embed verso webview into app
tauri_runtime_verso::vendored::create_embedded_versoview().unwrap();

Ok(())
})
```

## Known limitations

### Security
Expand Down
2 changes: 2 additions & 0 deletions examples/vendored/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gen
versoview/
22 changes: 22 additions & 0 deletions examples/vendored/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "vendored"
version = "0.1.0"
edition = "2024"

[build-dependencies]
tauri-runtime-verso-build = { path = "../../tauri-runtime-verso-build" }
tauri-build = { version = "2", features = [] }

[dependencies]
tauri = { version = "2", default-features = false, features = [
# Default features
# "wry",
# "compression",
"common-controls-v6",
# "x11",
# "dynamic-acl",
# Additional features
# "tray-icon",
] }
tauri-runtime-verso = { path = "../../", features = ["vendored"] }
# serde = "1"
1 change: 1 addition & 0 deletions examples/vendored/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
To get started with this example, run `cargo run --package helloworld` (You'll need the [prerequisites](https://tauri.app/start/prerequisites/) of Tauri)
3 changes: 3 additions & 0 deletions examples/vendored/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
tauri_build::build();
}
10 changes: 10 additions & 0 deletions examples/vendored/capabilities/main.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main",
"windows": [
"main"
],
"permissions": [
"core:default"
]
}
111 changes: 111 additions & 0 deletions examples/vendored/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Welcome to Tauri!</title>
<style>
:root {
background-color: #f0f0f2;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;

--color-on-primary: white;
--color-primary: hsl(205, 85%, 40%);
--color-primary-hightlight: hsl(205, 85%, 35%);
}

button,
input,
textarea,
select {
font: inherit;
}

body {
display: flex;
flex-direction: column;
margin: 0;
height: 100vh;
}

.title {
margin-top: 3em;
margin-bottom: 2em;
text-align: center;
}

.form-and-message {
background-color: aliceblue;
padding: 1em;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1em;
}

.form-and-message > #form {
display: flex;
justify-content: center;
gap: 1em;
}

.form-and-message > #message {
display: flex;
justify-content: center;
}

.form-and-message > #form input {
padding: 0.5em 0.8em;
border: 1px solid hsl(0, 0%, 60%);
outline: none;
min-width: 0;
}
.form-and-message > #form input:focus {
border-color: var(--color-primary);
}

.form-and-message > #form button {
appearance: none;
border: none;
cursor: pointer;
outline: none;
padding: 0.4em 1em;
color: var(--color-on-primary);
background-color: var(--color-primary);
display: inline-flex;
align-items: center;
}
.form-and-message > #form button:hover,
.form-and-message > #form button:focus {
background-color: var(--color-primary-hightlight);
}
</style>
<script type="module">
const { invoke } = window.__TAURI__.core

const form = document.querySelector('#form')
const nameEl = document.querySelector('#name')
const messageEl = document.querySelector('#message')

form.addEventListener('submit', async (e) => {
e.preventDefault()

const name = nameEl.value
const newMessage = await invoke('greet', { name })
messageEl.textContent = newMessage
})
</script>
</head>
<body>
<h1 class="title">Welcome to Tauri!</h1>

<div class="form-and-message">
<form id="form">
<input id="name" placeholder="Enter a name..." />
<button>Greet</button>
</form>
<div id="message"></div>
</div>
</body>
</html>
29 changes: 29 additions & 0 deletions examples/vendored/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use tauri::Manager;

#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello {name}, You have been greeted from Rust!")
}

fn main() {
// You can also set the `versoview` executable path yourself
// tauri_runtime_verso::set_verso_path("../verso/target/debug/versoview");

// To use the devtools, set it like to let verso open a devtools server,
// and you can connect to it through Firefox's devtools in the `about:debugging` page
tauri_runtime_verso::set_verso_devtools_port(1234);

tauri_runtime_verso::builder()
.invoke_handler(tauri::generate_handler![greet])
.setup(|app| {
dbg!(app.get_webview_window("main").unwrap().inner_size()).unwrap();
// embed verso webview into app
tauri_runtime_verso::vendored::create_embedded_versoview().unwrap();

@Legend-Master Legend-Master Apr 29, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not sure I get this, isn't this just running the versoview? Don't we need to connect our app with it? And 2 different event loops can't work together, versoview will still need to be on another process for now


Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application")
}
28 changes: 28 additions & 0 deletions examples/vendored/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Hello World",
"version": "0.1.0",
"identifier": "com.tauri.dev",
"build": {
"frontendDist": ["index.html"]
},
"app": {
"withGlobalTauri": true,
"windows": [
{
"title": "Welcome to Tauri!",
"width": 700,
"height": 500
}
]
},
"bundle": {
"icon": [
"../.icons/icon.ico",
"../.icons/icon.png"
],
"externalBin": [
"versoview/versoview"
]
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ mod event_loop_ext;
mod monitor;
mod runtime;
mod utils;
#[cfg(feature = "vendored")]
mod vendored;
mod webview;
mod window;

Expand Down
62 changes: 62 additions & 0 deletions src/vendored.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use versoview::{Result, Verso, verso::EventLoopProxyMessage};
use winit::{
application::ApplicationHandler,
event_loop::{self, DeviceEvents, EventLoop, EventLoopProxy},
};

struct App {
verso: Option<Verso>,
proxy: EventLoopProxy<EventLoopProxyMessage>,
}

impl ApplicationHandler<EventLoopProxyMessage> for App {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
self.verso = Some(Verso::new(event_loop, self.proxy.clone()));
}

fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
window_id: winit::window::WindowId,
event: winit::event::WindowEvent,
) {
if let Some(v) = self.verso.as_mut() {
v.handle_window_event(event_loop, window_id, event);
}
}

fn user_event(
&mut self,
event_loop: &event_loop::ActiveEventLoop,
event: EventLoopProxyMessage,
) {
if let Some(v) = self.verso.as_mut() {
match event {
EventLoopProxyMessage::Wake => {
v.request_redraw(event_loop);
}
EventLoopProxyMessage::IpcMessage(message) => {
v.handle_incoming_webview_message(event_loop, *message);
}
}
}
}
}

pub fn create_embedded_versoview() -> Result<()> {
init_crypto();

let event_loop = EventLoop::<EventLoopProxyMessage>::with_user_event().build()?;
event_loop.listen_device_events(DeviceEvents::Never);
let proxy = event_loop.create_proxy();
let mut app = App { verso: None, proxy };
event_loop.run_app(&mut app)?;

Ok(())
}

fn init_crypto() {
rustls::crypto::aws_lc_rs::default_provider()
.install_default()
.expect("Error initializing crypto provider");
}
Loading