lepus

    A modern desktop application framework based on web technologies.

    webview
    desktop
    app
    framework
    Download zip
    Version
    0.2.3
    License
    Apache-2.0
    Last updated
    4 hours ago
    Downloads
    60

    #Lepus

    Lepus is a desktop application toolkit for MoonBit.

    It provides:

    • a native MoonBit application host
    • a WebView-based rendering runtime
    • a plugin system for exposing platform features to frontend code
    • a MoonBit CLI for scaffolding and local workflows

    #Status

    Lepus is still in an early stage.

    What is already in this repository:

    What is not finished yet:

    • many plugins are still example-backed or placeholders
    • dev and build are still being expanded
    • packaging and distribution are not production-ready yet

    #Quick Start

    #Requirements

    You need:

    • moon
    • a native toolchain that can build MoonBit native targets
    • a local environment where the bundled webview library can be loaded

    Check your environment:

    moon run --target native cli/lepus -- doctor

    #Run an Example

    Run the basic example:

    moon run --target native examples/hello

    Run a plugin example:

    moon run --target native examples/dialog

    #Create a New App

    moon run --target native cli/lepus -- init my-app

    #API

    The current top-level API is centered around:

    • WindowConfig::new(...)
    • App::new(main_window, plugins?)
    • App::with_windows(windows, plugins?)
    • App::add_window(config)

    Minimal app:

    ///|
    async fn main {
    let main_window = @lepus.WindowConfig::new(
    title="Hello Lepus",
    html=(
    #|<!doctype html>
    #|<html><body><h1>Hello Lepus</h1></body></html>,
    ),
    )
    @lepus.App(main_window).run()
    }

    Plugin-enabled app:

    ///|
    async fn main {
    let main_window = @lepus.WindowConfig::new(
    title="Dialog Example",
    html=@lepus_plugin_support.example_html("dialog", "Dialog Plugin Example"),
    )
    @lepus.App(main_window, plugins=[@lepus_plugin_dialog.plugin()]).run()
    }

    Window customization example:

    ///|
    async fn main {
    let window = @lepus.WindowConfig::new(
    title="Custom Window",
    frameless=true,
    transparent=true,
    title_bar_style=1, // hidden
    title_bar_overlay=true, // overlay controls
    html="<div class='lepus-titlebar'>Drag me</div>",
    )
    @lepus.App(window).run()
    }

    #Plugins

    Plugins live in plugins/.

    Each plugin package exports:

    • plugin()

    Each example installs plugins through:

    • plugins=[@lepus_plugin_x.plugin()]

    The current plugin status matrix is in PLUGINS.md.

    #Repository Layout

    . ├── cli/lepus/ # CLI implementation ├── examples/ # runnable native examples ├── plugins/ # official plugin packages ├── webview/ # WebView runtime and bridge ├── lepus.mbt # top-level Lepus API ├── moon.pkg # root package definition └── moon.mod

    #Development

    Useful commands:

    moon check moon test --target native -p lepus-apps/lepus/webview moon info && moon fmt

    CLI help:

    moon run --target native cli/lepus -- --help

    #License

    App

    pub struct App {
    windows : Array[WindowConfig]
    plugins : Array[
    Plugin
    ]
    }

    App::add_window

    fn App::add_window(self : App, config : WindowConfig) -> Unit

    App::new

    App::run

    async fn App::run(self : App, window_index? : Int) -> Unit

    App::run_single_process

    async fn App::run_single_process(self : App) -> Unit

    Runs all windows in a single process.

    Unlike App::run (which spawns one child process per window), this mode creates every window inside the current process. All windows share one NSApplication / Dock icon (macOS), one menu bar, and one event loop — matching the Tauri/Electron model. Cross-window events are delivered by direct eval (no IPC), so this is the recommended mode for most apps.

    The event loop runs once (a single webview_run services all windows); it returns when every window has closed.

    App::window_count

    fn App::window_count(self : App) -> Int

    App::with_windows

    fn App::with_windows(windows : Array[WindowConfig], plugins? : Array[
    Plugin
    ]) -> App

    EmitRequest

    pub struct EmitRequest {
    target : String
    event : String
    payload : Json
    } derive(ToJson,
    FromJson
    )

    EventBus

    pub struct EventBus {
    window_ids : Map[String, Int]
    }

    EventBus::new

    fn EventBus::new(window_ids : Map[String, Int]) -> EventBus

    EventBus::plugin

    Builds the events plugin. Its emit command resolves the target label into a window id and sends a directed IPC event carrying the JS snippet.

    wm must be the main-process WindowManager (the plugin is installed on the main-process router, whose handler performs the directed send).

    EventBus::window_id

    fn EventBus::window_id(self : EventBus, label : String) -> Int

    Returns the window id for a label, or -1 if unknown.

    FullscreenWindowRequest

    pub struct FullscreenWindowRequest {
    label : String
    fullscreen : Bool
    } derive(ToJson,
    FromJson
    )

    Toggle a target window's fullscreen state.

    FullscreenWindowRequest::to_json

    LabelRequest

    pub struct LabelRequest {
    label : String
    } derive(ToJson,
    FromJson
    )

    Target window label request.

    LabelRequest::to_json

    LocalEventBus

    pub struct LocalEventBus {
    webviews : Map[String,
    WebView
    [Unit]]
    }

    本地事件总线:label → WebView 句柄的直接映射。

    LocalEventBus::new

    LocalEventBus::plugin

    构建 events 插件。emit 直接对目标窗口的 WebView 执行 eval, 触发 CustomEvent("lepus_event")。同进程内即时送达,无 IPC 开销。

    LocalEventBus::register

    fn LocalEventBus::register(self : LocalEventBus, label : String, webview :
    WebView
    [Unit]) -> Unit

    MemoryFile

    pub struct MemoryFile {
    path : String
    content : Bytes
    } derive(Eq, Hash, ToJson,
    Debug
    ,
    FromJson
    )

    MemoryFile::equal

    fn MemoryFile::equal(MemoryFile, MemoryFile) -> Bool

    MemoryFile::from_bytes

    fn MemoryFile::from_bytes(path : String, content : Bytes) -> MemoryFile

    MemoryFile::from_string

    fn MemoryFile::from_string(path : String, content : String) -> MemoryFile

    MemoryFile::hash

    fn MemoryFile::hash(self : MemoryFile) -> Int

    MemoryFile::hash_combine

    fn MemoryFile::hash_combine(MemoryFile, Hasher) -> Unit

    MemoryFile::not_equal

    fn MemoryFile::not_equal(x : MemoryFile, y : MemoryFile) -> Bool

    MemoryFile::to_json

    fn MemoryFile::to_json(MemoryFile) -> Json

    MemorySource

    pub struct MemorySource {
    entry : String
    files : Array[MemoryFile]
    } derive(Eq, Hash, ToJson,
    Debug
    ,
    FromJson
    )

    MemorySource::MemorySource

    #alias(new, deprecated="Use `MemorySource()` instead")
    fn MemorySource::MemorySource(files : Array[MemoryFile], entry? : String) -> MemorySource

    MemorySource::equal

    MemorySource::hash

    fn MemorySource::hash(self : MemorySource) -> Int

    MemorySource::hash_combine

    fn MemorySource::hash_combine(MemorySource, Hasher) -> Unit

    MemorySource::not_equal

    fn MemorySource::not_equal(x : MemorySource, y : MemorySource) -> Bool

    MemorySource::to_json

    MoveWindowRequest

    pub struct MoveWindowRequest {
    label : String
    x : Int
    y : Int
    } derive(ToJson,
    FromJson
    )

    Move a target window to an absolute position.

    MoveWindowRequest::to_json

    ResizeWindowRequest

    pub struct ResizeWindowRequest {
    label : String
    width : Int
    height : Int
    } derive(ToJson,
    FromJson
    )

    Resize a target window.

    ResizeWindowRequest::to_json

    Source

    pub(all) enum Source {
    Url(String)
    Local(String)
    Memory(MemorySource)
    } derive(Eq, Hash, ToJson,
    Debug
    ,
    FromJson
    )

    Source::equal

    fn Source::equal(Source, Source) -> Bool

    Source::hash

    fn Source::hash(self : Source) -> Int

    Source::hash_combine

    fn Source::hash_combine(Source, Hasher) -> Unit

    Source::html

    fn Source::html(html : String) -> Source

    Source::not_equal

    fn Source::not_equal(x : Source, y : Source) -> Bool

    Source::to_json

    fn Source::to_json(Source) -> Json

    Source::to_repr

    Source::zip

    fn Source::zip(zip : Bytes) -> Source raise

    TitleBarStyle

    pub enum TitleBarStyle {
    Default
    Hidden
    } derive(Eq, Hash, ToJson,
    Debug
    ,
    FromJson
    )

    TitleBarStyle::equal

    TitleBarStyle::hash

    fn TitleBarStyle::hash(self : TitleBarStyle) -> Int

    TitleBarStyle::hash_combine

    fn TitleBarStyle::hash_combine(TitleBarStyle, Hasher) -> Unit

    TitleBarStyle::not_equal

    fn TitleBarStyle::not_equal(x : TitleBarStyle, y : TitleBarStyle) -> Bool

    TitleBarStyle::to_json

    Window

    pub struct Window {
    config : WindowConfig
    plugins : Array[
    Plugin
    ]
    }

    Window::Window

    #alias(new, deprecated="Use `Window()` instead")
    fn Window::Window(label? : String, title? : String, width? : Int, height? : Int, debug? : Int, devtools? : Bool, frameless? : Bool, resizable? : Bool, always_on_top? : Bool, transparent? : Bool, title_bar_style? : TitleBarStyle, title_bar_overlay? : Bool, traffic_light_position? : (Int, Int), position? : (Int, Int), hidden? : Bool, focused? : Bool, source? : Source, plugins? : Array[
    Plugin
    ]) -> Window

    Window::navigate

    fn Window::navigate(self : Window, url : String) -> Window

    Window::run

    async fn Window::run(self : Window) -> Unit

    Window::set_source

    fn Window::set_source(self : Window, source : Source) -> Window

    WindowConfig

    pub struct WindowConfig {
    label : String
    title : String
    width : Int
    height : Int
    debug : Int
    devtools : Bool
    frameless : Bool
    resizable : Bool
    always_on_top : Bool
    transparent : Bool
    title_bar_style : TitleBarStyle
    title_bar_overlay : Bool
    traffic_light_position : (Int, Int)
    position : (Int, Int)
    hidden : Bool
    focused : Bool
    source : Source?
    } derive(Eq, Hash, ToJson,
    Debug
    ,
    FromJson
    )

    WindowConfig::equal

    WindowConfig::hash

    fn WindowConfig::hash(self : WindowConfig) -> Int

    WindowConfig::hash_combine

    fn WindowConfig::hash_combine(WindowConfig, Hasher) -> Unit

    WindowConfig::label

    fn WindowConfig::label(self : WindowConfig) -> String

    Returns the window label used for stable cross-window addressing. Falls back to title when no explicit label was given.

    WindowConfig::navigate

    fn WindowConfig::navigate(self : WindowConfig, url : String) -> WindowConfig

    WindowConfig::new

    fn WindowConfig::new(label? : String, title? : String, width? : Int, height? : Int, debug? : Int, devtools? : Bool, frameless? : Bool, resizable? : Bool, always_on_top? : Bool, transparent? : Bool, title_bar_style? : TitleBarStyle, title_bar_overlay? : Bool, traffic_light_position? : (Int, Int), position? : (Int, Int), hidden? : Bool, focused? : Bool, source? : Source) -> WindowConfig

    WindowConfig::not_equal

    fn WindowConfig::not_equal(x : WindowConfig, y : WindowConfig) -> Bool

    WindowConfig::set_source

    fn WindowConfig::set_source(self : WindowConfig, source : Source) -> WindowConfig

    WindowConfig::to_json

    WindowInfo

    pub struct WindowInfo {
    label : String
    open : Bool
    } derive(ToJson,
    FromJson
    )

    Snapshot of one managed window returned by the windows plugin list API.

    WindowInfo::to_json

    fn WindowInfo::to_json(WindowInfo) -> Json

    WindowRegistry

    pub struct WindowRegistry {
    ids : Map[String, Int]
    pids : Map[String, Int]
    children : Array[Int]
    focused : Map[String, Bool]
    }

    Shared, mutable registry owned by the main process.

    ids maps label -> window-id (== spawn base); pids maps label -> child pid once spawned; children lists every live child so the serving loop can wait on them; focused mirrors each window's WindowConfig.focused flag so open knows whether a re-shown window should also grab focus.

    WindowRegistry::new

    Builds a window registry for the given configs. Window ids are assigned in declaration order, matching the base each child uses when creating its own window (see assign_window_ids in lepus.mbt).

    WindowRegistry::plugin

    Constructs the windows plugin. argv0 is the executable path used to spawn lazy child windows; wm must be the main-process WindowManager.

    Windows

    pub struct Windows {
    configs : Array[WindowConfig]
    plugins : Array[
    Plugin
    ]
    }

    A multi-window app with runtime window management (see Windows::run).

    Windows::new

    Builds a window-managed app.

    Windows::run

    async fn Windows::run(self : Windows) -> Unit

    Runs a window-managed app end-to-end.

    The main process spawns non-hidden windows up front, installs the label-addressed windows plugin plus the cross-window event bus, and serves commands from every child. Hidden windows are spawned lazily by open.