Bridge Components - aguspe/turbo_desktop GitHub Wiki

Bridge Components

Bridge Components are the desktop equivalent of Strada — they let your web UI talk to native OS features through structured message passing.

How It Works

  1. Your JavaScript (or Stimulus controller) calls TurboDesktop.sendBridgeMessage()
  2. The message is sent to the Rust shell via Tauri's invoke system
  3. Rust processes the message and can respond back to JavaScript

Built-in Components

Component What It Does
notification Show native OS notifications (macOS, Windows, Linux)
menu-item Register custom items in the native menu bar
file-picker Open native file open/save dialogs
badge Set the dock/taskbar badge count
shortcut Register global keyboard shortcuts

JavaScript API

Send a Bridge Message

// TurboDesktop is injected by the desktop shell — only available in the native app
if (typeof TurboDesktop !== "undefined") {
  TurboDesktop.sendBridgeMessage("notification", "connect", {
    title: "Task Completed!",
    body: "Your task has been marked as done."
  })
}

Stimulus Bridge Mixin

For Stimulus controllers, use the stimulusBridge() helper:

import { Controller } from "@hotwired/stimulus"

export default class extends TurboDesktop.stimulusBridge(Controller, "notification") {
  connect() {
    super.connect()
    this.sendBridge("connect", { title: "My App" })
  }

  notify(event) {
    this.sendBridge("connect", {
      title: "New Message",
      body: event.target.dataset.body
    })
  }

  // Called when the native side responds
  receiveBridge(message) {
    console.log("Native says:", message)
  }
}

Rails View Helpers

The turbo_desktop-rails gem provides a turbo_desktop_bridge helper that outputs the right data attributes:

<%%= tag.button "Export PDF",
    **turbo_desktop_bridge("menu-item",
      title: "Export PDF",
      shortcut: "Cmd+E"
    ) %>

You can also use it inline with button_to:

<%%= button_to "Complete", task_path(task),
      method: :patch,
      data: {
        **turbo_desktop_bridge("notification",
          title: "Done!",
          body: task.title
        )
      } %>

Notification Example

The example app includes a notification bridge controller:

// app/javascript/controllers/notification_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static values = {
    title: { type: String, default: "Notification" },
    body:  { type: String, default: "" }
  }

  notify() {
    if (typeof TurboDesktop !== "undefined") {
      TurboDesktop.sendBridgeMessage("notification", "connect", {
        title: this.titleValue,
        body: this.bodyValue
      })
    }
  }
}

Custom Components

You can create your own bridge components. Any component name that isn't built-in gets forwarded as a custom event:

// Web side
TurboDesktop.sendBridgeMessage("my-custom-component", "some-event", {
  key: "value"
})

On the Rust side, you can listen for and handle these custom events.