High-performance Node.js bindings for the saucer v8 webview runtime
Features • Installation • Quick Start • API Reference • CLI • Testing
saucer-nodejs is an ESM-first desktop app runtime for Node.js that uses the native system webview (WebKit/WebView2/WebKitGTK) with non-blocking integration into Node's event loop.
- Native webview windows with window controls, navigation, script injection, and custom URL schemes
- Node <-> webview bridge via
expose,onMessage, and typedSmartviewRPC - Thread-safe scheduling APIs:
post,dispatch,make,poolSubmit,poolEmplace - Desktop integrations: clipboard, notifications, system tray, file pickers, PDF export
- CLI for scaffolding, diagnostics, development mode, and SEA builds
- Comprehensive parity test suite in
examples/application-features.js
- Node.js
>= 20.0.0 - macOS 11+, Windows 10+, or Linux with GTK3/WebKitGTK
- CMake only needed when building native bindings from source
npm install saucer-nodejsVerify local environment:
npx saucer doctorimport { Application, Webview } from "saucer-nodejs";
const app = Application.init({ id: "com.example.myapp", threads: 2 });
const webview = new Webview(app, {
hardwareAcceleration: true,
preload: "window.APP_VERSION = '1.0.0';",
});
webview.title = "My App";
webview.size = { width: 1000, height: 700 };
webview.loadHtml(`
<!doctype html>
<html>
<body>
<h1>Hello from saucer-nodejs</h1>
<button onclick="window.saucer.internal.send_message('ping')">Send Message</button>
</body>
</html>
`);
webview.onMessage((msg) => {
console.log("message from webview:", msg);
return true;
});
webview.on("closed", () => app.quit());
webview.show();- Features
- Requirements
- Installation
- Quick Start
- API Reference
- CLI
- Debug Mode
- Platform Notes
- Testing and API Parity
- Examples
- Building from Source
- Contributing
- License
Top-level exports from saucer-nodejs:
- Classes:
Application,Webview,Stash,Icon,Desktop,PDF,SmartviewRPC,Notification,SystemTray - Functions/objects:
createRPC,Types,clipboard
Create and manage the application instance.
import { Application } from "saucer-nodejs";
const app = Application.init({ id: "com.example.myapp", threads: 4 });Static methods:
Application.init(options?)Application.active()
Instance methods:
isThreadSafe()quit()run()post(callback)dispatch(callback)make(factory)poolSubmit(callback)poolEmplace(callback)nativeHandle()
Accessors:
native(raw native handle)
Create a native webview window.
import { Webview } from "saucer-nodejs";
const webview = new Webview(app, {
persistentCookies: false,
hardwareAcceleration: true,
storagePath: "./.webview-data",
userAgent: "MyApp/1.0",
browserFlags: ["--disable-gpu"],
preload: "window.__PRELOADED = true;",
});Static methods:
Webview.registerScheme(name)
Properties:
- Window state:
focused(readonly),visible,minimized,maximized,resizable,decorations,alwaysOnTop,clickThrough - Window geometry:
title,size,position,maxSize,minSize,fullscreen,zoom - Webview/browser:
url,devTools,backgroundColor,forceDarkMode,contextMenu,favicon(readonly),pageTitle(readonly) - Handles:
parent(readonly),native(readonly)
Methods:
- Window control:
show(),hide(),close(),focus(),startDrag(),startResize(edge?),setIcon(pathOrBuffer) - Navigation/content:
navigate(url),setFile(path),loadHtml(html),reload(),back(),forward() - JavaScript bridge:
execute(code, ...args),evaluate(code, ...args),expose(name, handler, options?),clearExposed(name?),onMessage(callback) - Scripts/embedded content:
inject(script),clearScripts(),embed(files, policy?),serve(file),clearEmbedded(file?) - Custom schemes:
handleScheme(name, handler, policy?),removeScheme(name) - Events:
on(event, cb),once(event, cb),off(event, cb)
Window/Webview events:
- Window events:
resize,focus,close,closed,decorated,maximize,minimize - Webview events:
dom-ready,load,title,navigate,navigated,favicon
Notes:
closeandnavigatecallbacks can returntrue/falseto allow or deny the action.- Register schemes with
Webview.registerScheme(...)before creatingApplication/Webviewinstances.
Typed RPC helper with optional zero-copy binary transfer support.
import { Application, Webview, SmartviewRPC, createRPC, Types } from "saucer-nodejs";
SmartviewRPC.registerScheme(); // before Application/Webview creation
const app = Application.init();
const webview = new Webview(app);
const rpc = createRPC(webview);
rpc.define(
"sum",
{
params: [Types.param("a", Types.number), Types.param("b", Types.number)],
returns: "number",
},
(a, b) => a + b,
);SmartviewRPC static methods:
registerScheme()isSchemeRegistered()
SmartviewRPC instance methods:
define(name, schema, handler)undefine(name)getDefinedFunctions()generateTypes(options?)
SmartviewRPC accessors:
webview
Types helpers:
- Scalars:
Types.string,Types.number,Types.boolean,Types.buffer,Types.uint8,Types.any,Types.void - Builders:
Types.array(type),Types.object(shape),Types.optional(type),Types.param(name, type)
System dialogs and PDF export.
import { Desktop, PDF } from "saucer-nodejs";
const desktop = new Desktop(app);
const pdf = new PDF(webview);Desktop methods:
open(pathOrUrl)pickFile(options?)pickFolder(options?)pickFiles(options?)pickFolders(options?)
Desktop accessors:
native
PDF methods:
save(options?)
PDF accessors:
native
Binary and icon utility wrappers.
import { Stash, Icon } from "saucer-nodejs";
const stash = Stash.from(Buffer.from("hello"));
const icon = Icon.fromFile("./icon.png");Stash static methods:
Stash.from(buffer)Stash.view(buffer)
Stash methods/accessors:
getData()size(readonly)native(readonly)
Icon static methods:
Icon.fromFile(path)Icon.fromData(buffer)
Icon methods/accessors:
isEmpty()getData()save(path)native(readonly)
import { clipboard } from "saucer-nodejs";
clipboard.writeText("hello");
const text = clipboard.readText();
const hasText = clipboard.hasText();
const hasImage = clipboard.hasImage();
clipboard.clear();Methods:
readText()writeText(text)hasText()hasImage()clear()
import { Notification } from "saucer-nodejs";
if (Notification.isSupported()) {
const permission = await Notification.requestPermission();
if (permission === "granted") {
new Notification({ title: "Done", body: "Task complete" }).show();
}
}Static methods:
Notification.isSupported()Notification.requestPermission()
Instance methods:
show()
import { SystemTray } from "saucer-nodejs";
const tray = new SystemTray();
tray.setTooltip("My App");
tray.setMenu([
{ label: "Open" },
{ type: "separator" },
{ label: "Quit" },
]);
tray.onClick(() => console.log("tray clicked"));
tray.show();Methods:
setIcon(pathOrBuffer)setTooltip(text)setMenu(items)onClick(callback)show()hide()destroy()
The package installs a saucer CLI.
# Show help
npx saucer help
# Initialize a new app
npx saucer init my-app
# Run with watch mode
npx saucer dev index.js
# Build a standalone SEA binary
npx saucer build index.js my-app
# Run diagnostics
npx saucer doctor
# Show local package/system info
npx saucer infoUse saucer-nodejs/debug for category logging.
SAUCER_DEBUG=1 node app.js
SAUCER_DEBUG=rpc,window node app.jsimport { createDebugger, measure } from "saucer-nodejs/debug";
const debug = createDebugger("rpc");
debug("request", { id: 1 });
await measure("fetch-data", async () => {
// async work
});Common categories: rpc, window, webview, native, app, event, perf.
API availability is cross-platform, but some OS integrations are currently partial outside macOS.
- Core app/webview APIs work on macOS, Windows, and Linux
- Some extension APIs (for example tray/menu behavior and selected window extensions) can be partial/stub depending on platform/runtime
- Run
npx saucer infoandnpx saucer doctoron the target OS to validate behavior
examples/application-features.js is the source-of-truth parity suite for recent binding changes.
# Fully automated run (CI-safe)
node examples/application-features.js --mode=automated
# Interactive/manual run
node examples/application-features.js --mode=manualMode can also be provided via env var:
SAUCER_TEST_MODE=automated node examples/application-features.jsThe suite validates:
- Public API parity against the v8 manifest
- Event behavior (
on,once,off) - Messaging, RPC, and core desktop integrations
examples/basic.js- minimal startup exampleexamples/application-features.js- full feature and parity coverage
- Node.js >= 20
- CMake >= 3.15
- C++20 compatible compiler
- macOS: Xcode Command Line Tools
- Windows: Visual Studio 2019+
- Linux: GCC 10+ or Clang 12+
git clone https://github.com/MrLionware/saucer-nodejs.git
cd saucer-nodejs
npm install
npm run rebuildRun feature/parity suite:
node examples/application-features.js --mode=automatedContributions are welcome.
- Fork the repository
- Create a branch (
git checkout -b feature/my-change) - Commit changes
- Push branch
- Open a pull request
MIT - see LICENSE.