Build fast, lightweight desktop apps in TypeScript using native webview.
Designed to power
Pengu Loader.
Monorepo for the arctron runtime, native host, and example apps.
Arctron is a lightweight desktop app stack that lets you write app logic in TypeScript while delegating windowing, dialogs, filesystem, and shell integration to a native host.
At a high level:
- your app imports APIs from
arctron(runtime package), - the runtime forwards those calls through a native bridge object,
- the host executes them using platform-specific implementations,
- your renderer can stay as a normal web app (for example Vite).
This workspace contains three main parts:
packages/arctron— TypeScript runtime library and Vite plugin (arctron/vite)packages/arctron-host— .NET host process that embeds JS runtime + native platform integrationsexamples/vite-app— reference app demonstrating runtime + Vite development flow
- Node.js 20+
- pnpm@9
- .NET 9 SDK
pnpm buildpnpm devpnpm clean
Arctron is implemented as layered components:
- App code (TypeScript)
- Uses public APIs like
app,BrowserWindow,dialog,fs,path,shell,process,Tray.
- Uses public APIs like
- Runtime package (
packages/arctron)- Exposes typed APIs and maps calls to
globalThis.__arctronNative. - Handles in-runtime event routing (for example tray callbacks and window RPC dispatch).
- Exposes typed APIs and maps calls to
- Native bridge (
packages/arctron-host/Arctron.Host/NativeBridge.cs)- C# object injected into JS runtime as
__arctronNative. - Translates JS method calls to
IHostPlatformoperations.
- C# object injected into JS runtime as
- Host platform implementation (
IHostPlatform)- Executes OS-native behavior (windows, dialogs, shell, fs, process, etc.).
- Current production implementation is Windows-focused (
WindowsHostPlatform).
- Public exports are centralized in
src/index.tsand typed viasrc/api/*andsrc/types.ts. - Platform modules in
src/platform/*callgetNativeBridge()and forward operations. - Async APIs like
dialog.*,fs.*, andprocess.execcurrently wrap synchronous native bridge calls viaPromise.resolve(...). BrowserWindowsupports per-window RPC method registration and dispatch:- manifest is sent to native via
windowSetRpcManifest, - incoming invoke events are received through
__arctronOnWindowRpcInvoke, - handlers resolve/reject via
windowRpcResolve/windowRpcReject.
- manifest is sent to native via
- Tray events are bridged once globally and then routed to per-tray emitters.
- Entry (
Program.cs) resolves the main script path from:- first CLI argument, or
ARCTRON_MAINenv var.
AppHostwires together:- platform instance (
HostPlatformFactory.Create()), NativeBridge,- JS engine runtime (
JsRuntime), then executes the compiled main script.
- platform instance (
- JS runtime uses Jint and injects:
__arctronNative(native bridge),consolebridge,fetchbridge.
- Platform abstraction is defined by
IHostPlatform;WindowsHostPlatformcontains Win32/WebView2-backed behavior, whileDefaultHostPlatformprovides a reduced fallback implementation for non-Windows environments.
In dev mode, plugin behavior is deterministic and process-managed:
- Loads
arctron.config.tsfrom app root. - Starts app main watcher (
dev:mainby default). - Waits for
arctron.config.mainoutput file. - Starts host process (
pnpm --filter @arctron/host devby default). - Sets
ARCTRON_MAINto the resolved main output path. - Prefixes child logs as
[arctron-main]and[arctron-host]. - Stops watcher and host when Vite server exits.
Available plugin options include:
enabledhostFilterhostScriptworkspaceRootmainDevScriptmainReadyTimeoutMs
- Run
pnpm --filter arctron-vite-example dev. - Vite plugin starts main-process watcher (
dev:main). - Plugin waits for compiled main output (
dist/main-process.js). - Plugin launches host with
ARCTRON_MAINpointing to that output. - Host executes main-process code, which creates a
BrowserWindowand loads the Vite dev URL.
- App code calls runtime API (for example
dialog.openFile). - Runtime module forwards call to
__arctronNative. NativeBridgemaps call toIHostPlatform.- Platform executes native operation and returns result.
- Runtime exposes result back to app code (often as a Promise).
- Every Vite app should define
arctron.config.tswith a validmainoutput path. - The app should expose a long-running main build script (
dev:mainby default) that writes to that same output path. - If scripts/package names differ, configure plugin options (
mainDevScript,hostFilter,hostScript, etc.) instead of hardcoding defaults.
Run the Vite example end-to-end:
pnpm installpnpm --filter arctron-vite-example dev
When arctron/vite runs in dev mode, it automatically:
- loads
arctron.config.tsfrom the app root, - starts the app main-process watcher (
dev:mainby default), - waits for
arctron.config.mainoutput, - starts the host (
@arctron/hostdevscript by default), - sets
ARCTRON_MAINfor the host process, - prefixes logs as
[arctron-main]and[arctron-host], - stops child processes when the Vite server closes.
Use these targeted checks:
pnpm --filter arctron buildpnpm --filter arctron-vite-example build
The runtime exports:
appBrowserWindowTraydialogshellprocesspathfs
For full API signatures and examples, see docs/api.md.
- Windows has the most complete host implementation today (
WindowsHostPlatform). - A cross-platform default host exists (
DefaultHostPlatform) but is intentionally limited and does not provide full native parity. - Some host APIs are present but still partial in implementation (for example certain tray and RPC native completion paths on the host side).
For runtime/plugin changes:
pnpm --filter arctron buildpnpm --filter arctron-vite-example build
For dev orchestration changes, also smoke test:
pnpm --filter arctron-vite-example dev
import { app, BrowserWindow } from "arctron";
app.whenReady().then(() => {
const win = new BrowserWindow({
width: 1200,
height: 800,
title: "Arctron"
});
win.loadURL("http://localhost:5173");
});