Skip to content

hyperpolymath/rescript-tauri

Repository files navigation

rescript-tauri

Overview

rescript-tauri provides comprehensive ReScript bindings for the Tauri 2.0 JavaScript API, enabling type-safe development of native desktop and mobile applications.

Key Features

  • Type-safe command bridge - Typed request/response pattern with error handling

  • Full event system - Listen, emit, and create typed event channels

  • Window management - Complete window API with properties and events

  • Plugin bindings - Filesystem, dialogs, and more

  • TEA integration - Examples using rescript-tea architecture

  • Zero npm option - Works with Deno imports

Installation

With npm/pnpm/yarn

npm install rescript-tauri @tauri-apps/api

With Deno

{
  "imports": {
    "rescript-tauri": "npm:rescript-tauri",
    "@tauri-apps/api": "npm:@tauri-apps/api@^2.0.0"
  }
}

rescript.json

{
  "bs-dependencies": ["rescript-tauri"]
}

Quick Start

Basic Invoke

open Tauri

// Call a Tauri command
let result = await invoke("greet", ~args={"name": "World"})
Console.log(result) // "Hello, World!"

Type-Safe Commands

The command bridge pattern provides compile-time type safety:

open Tauri

// Define a typed command
let greetCommand = Command.defineCommand(
  ~name="greet",
  ~encode=name => {"name": name},
  ~decode=json => {
    switch JSON.Decode.string(json) {
    | Some(s) => Ok(s)
    | None => Error("Expected string")
    }
  },
)

// Execute with type safety
switch await Command.execute(greetCommand, "World") {
| Ok(greeting) => Console.log(greeting)
| Error(err) => Console.error(Command.CommandError.toString(err))
}

With Retry and Timeout

// Execute with retry logic
let result = await Command.executeWithRetry(
  greetCommand,
  "World",
  ~maxRetries=3,
  ~delayMs=1000,
)

// Or use the fluent builder
let result = await Command.Builder.make(greetCommand)
  ->Command.Builder.withTimeout(5000)
  ->Command.Builder.withRetries(3)
  ->Command.Builder.run("World")

Events

open Tauri

// Listen for events
let unlisten = await Event.listen("my-event", event => {
  Console.log(`Received: ${event.payload}`)
})

// Emit events
await Event.emit("my-event", ~payload="Hello from ReScript!")

// Clean up
unlisten()

Window Management

open Tauri

// Get current window
let window = Window.getCurrentWindow()

// Window operations
await window->Window.setTitle("My App")
await window->Window.center()
await window->Window.maximize()

// Or use the convenience module
await Window.Current.setTitle("My App")
await Window.Current.center()

File Dialogs

open Tauri

// Pick a file
let path = await Dialog.openSingle(
  ~options={
    title: "Select a file",
    filters: [Dialog.Filters.json, Dialog.Filters.all],
  },
)

// Or use the fluent builder
let path = await Dialog.OpenDialog.make()
  ->Dialog.OpenDialog.title("Select a file")
  ->Dialog.OpenDialog.filter(Dialog.Filters.json)
  ->Dialog.OpenDialog.pickSingle()

Filesystem

open Tauri

// Read a file
let content = await Fs.readTextFile("config.json", ~options={baseDir: AppConfig})

// Write a file
await Fs.writeTextFile("output.txt", "Hello!", ~options={baseDir: AppData})

// JSON helpers
switch await Fs.readJsonFile("data.json") {
| Ok(json) => Console.log(json)
| Error(err) => Console.error(err)
}

Modules

Module Description

Tauri.Core

Core invoke API, channels, resources, app info

Tauri.Event

Event listeners, emitters, typed event channels

Tauri.Window

Window management, properties, events

Tauri.Command

Type-safe command bridge pattern

Tauri.Fs

Filesystem operations (requires @tauri-apps/plugin-fs)

Tauri.Dialog

Native file/message dialogs (requires @tauri-apps/plugin-dialog)

Examples

See the examples/ directory:

  • counter - TEA-based counter with Rust backend state

Tauri Plugins

Some modules require Tauri plugins to be installed:

# Filesystem
npm install @tauri-apps/plugin-fs

# Dialogs
npm install @tauri-apps/plugin-dialog

And add to tauri.conf.json:

{
  "plugins": {
    "fs": {
      "scope": ["$APPDATA/**", "$RESOURCE/**"]
    },
    "dialog": {}
  }
}

Integration with rescript-tea

rescript-tauri works seamlessly with rescript-tea:

type msg =
  | LoadData
  | DataLoaded(result<data, Tauri.Command.commandError>)

let update = (model, msg) => {
  switch msg {
  | LoadData => (
      {...model, loading: true},
      Cmd.call(dispatch => {
        let _ = Tauri.Command.execute(loadCommand, ())
          ->Promise.thenResolve(result => dispatch(DataLoaded(result)))
      }),
    )
  | DataLoaded(Ok(data)) => ({...model, data, loading: false}, Cmd.none)
  | DataLoaded(Error(err)) => ({...model, error: Some(err), loading: false}, Cmd.none)
  }
}

Part of rescript-full-stack

This library is part of the rescript-full-stack ecosystem, providing the P4 (Mobile & Desktop) layer.

License

AGPL-3.0-or-later

Contributing

See CONTRIBUTING.adoc for guidelines.

About

ReScript bindings for Tauri 2.0 - Build native desktop and mobile apps with type safety

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •