Desktop Kit
Desktop application support with Tauri. It exposes a DesktopDriver that registers with the Core and acts as a bridge between your Iskra App’s logic and Tauri’s native APIs (windows, IPC, menus).
Quick Start
Section titled “Quick Start”import { App } from '@iskra-bun/core';import { DesktopDriver } from '@iskra-bun/desktop-kit';
const app = new App({ name: 'MiAppDeEscritorio' });
app.register(new DesktopDriver());
await app.start();// Al arrancar solo registra un warning: todavia no hay integracion con Tauri.The DesktopDriver implements the Core’s Driver interface, so it takes part in the standard lifecycle: it is initialized in app.start() and released in app.stop().
DesktopDriver
Section titled “DesktopDriver”The driver stores a reference to the App during init() and is then ready to relay messages between the domain and Tauri’s native layer.
import { DesktopDriver } from '@iskra-bun/desktop-kit';
const driver = new DesktopDriver();
// driver.name → 'DesktopDriver'// driver.init(app) → guarda la App y prepara el puente// driver.start() → activa el puente y loguea el arranque
app.register(driver);| Member | Type | Description |
|---|---|---|
name |
string |
Driver identifier ('DesktopDriver'). |
init(app) |
Promise<void> |
Receives the App and stores the internal reference. |
start() |
Promise<void> |
Activates the Tauri bridge and emits the startup log. |
Windows (Window)
Section titled “Windows (Window)”Window management is done with the Tauri API (@tauri-apps/api). The DesktopDriver gives you the hook point; from your handlers you can control the current window.
import { getCurrentWindow } from '@tauri-apps/api/window';
const appWindow = getCurrentWindow();
await appWindow.setTitle('Iskra Desktop');await appWindow.maximize();await appWindow.setResizable(false);React to window events from the App’s bus:
app.on('window:focus', async (ctx) => { ctx.logger.info('La ventana recupero el foco');});
const appWindow = getCurrentWindow();await appWindow.onFocusChanged(({ payload: focused }) => { app.emit('window:focus', { focused });});IPC (communication with the Rust backend)
Section titled “IPC (communication with the Rust backend)”Tauri exposes Rust commands to the frontend via invoke. Wrap those calls in your App’s services to keep the domain decoupled from Tauri.
import { invoke } from '@tauri-apps/api/core';
// Llamar a un comando Rust registrado en el backend de Tauriconst usuarios = await invoke<Usuario[]>('listar_usuarios', { activos: true });Register the service in the Core’s DI container to reuse it:
class DesktopBridge { // The Rust command asks where to save with the native dialog. async guardarArchivo(contenido: string) { return invoke('guardar_archivo', { contenido }); }}
app.context.set('desktopBridge', new DesktopBridge());
// En cualquier handlerconst bridge = app.context.get('desktopBridge');await bridge.guardarArchivo('Hola Iskra');Don’t give a command a path from the webview: a script that gets into the page (an injected string, a compromised dependency) could then read or write any file the user can, such as ~/.ssh/id_ed25519. Let the Rust command pick the file with the native dialog (tauri-plugin-dialog) and read or write it there, as the desktop-app template’s abrir_archivo does. Declare each command in build.rs (tauri_build::Attributes::new().app_manifest(AppManifest::new().commands(&[...]))) and grant it in a capability (allow-<command>): otherwise any webview can invoke it.
Listen to events emitted from Rust and relay them to the Iskra bus:
import { listen } from '@tauri-apps/api/event';
await listen<{ progreso: number }>('descarga:progreso', (evento) => { app.emit('descarga:progreso', evento.payload);});Native menus are built with Tauri’s menu API. Connect each item to an App event to process it with your handlers.
import { Menu, MenuItem } from '@tauri-apps/api/menu';
const itemAcercaDe = await MenuItem.new({ text: 'Acerca de', action: () => app.emit('menu:acerca-de', {}),});
const menu = await Menu.new({ items: [itemAcercaDe] });await menu.setAsAppMenu();
app.on('menu:acerca-de', async (ctx) => { ctx.logger.info('Abrir dialogo Acerca de');});Configuration
Section titled “Configuration”The DesktopDriver takes no options of its own: all native configuration (window size, permissions, icons) lives in tauri.conf.json, Tauri’s configuration file.
{ "productName": "Iskra Desktop", "app": { "windows": [ { "title": "Iskra Desktop", "width": 1024, "height": 768, "resizable": true } ] }}The logical name of your Iskra App is still managed from the Core:
const app = new App({ name: 'Iskra Desktop', logger: { level: 'info' } });Complete Example
Section titled “Complete Example”import { App } from '@iskra-bun/core';import { DesktopDriver } from '@iskra-bun/desktop-kit';import { getCurrentWindow } from '@tauri-apps/api/window';import { invoke } from '@tauri-apps/api/core';
const app = new App({ name: 'Iskra Desktop' });app.register(new DesktopDriver());
app.on('archivo:abrir', async (ctx) => { // abrir_archivo abre el dialogo nativo y lee en Rust el archivo elegido. const archivo = await invoke<{ ruta: string; contenido: string } | null>('abrir_archivo'); if (archivo) ctx.logger.info({ bytes: archivo.contenido.length }, 'Archivo cargado');});
async function main() { await app.start();
const ventana = getCurrentWindow(); await ventana.setTitle('Iskra Desktop');
app.emit('archivo:abrir', {});}
main().catch((err) => app.logger.error({ err }, 'Fallo al arrancar'));For a ready-to-use template, see templates/desktop-app. If your app also targets mobile, check out @iskra-bun/mobile-kit and the universal-app template.