feat: convert apps/machine from Tauri to Electron (#18)
- Create electron/main.ts with BrowserWindow (1080x1920, kiosk mode) - Create electron/preload.ts with contextBridge API - Add electron/tsconfig.json for main process compilation - Update package.json: remove Tauri deps, add Electron + electron-builder - Update vite.config.ts: remove Tauri-specific build config - Update .gitignore: remove Tauri entries, add Electron build outputs - Delete src-tauri/ directory and root Cargo.toml - Update CLAUDE.md to reflect Electron architecture The Vue frontend is unchanged - works identically in Electron renderer. HAL drivers can now run directly in Electron main process (Node.js). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f7307c9159
commit
c26b6f6169
14 changed files with 2940 additions and 286 deletions
11
lamassu-next/.gitignore
vendored
11
lamassu-next/.gitignore
vendored
|
|
@ -48,14 +48,9 @@ coverage/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# Tauri
|
# Electron
|
||||||
apps/machine/src-tauri/target/
|
apps/machine/dist-electron/
|
||||||
apps/machine/src-tauri/WixTools/
|
apps/machine/release/
|
||||||
apps/machine/src-tauri/icons/
|
|
||||||
|
|
||||||
# Rust
|
|
||||||
Cargo.lock
|
|
||||||
**/*.rs.bk
|
|
||||||
|
|
||||||
# devenv
|
# devenv
|
||||||
.devenv/
|
.devenv/
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,11 @@ This file provides guidance to Claude Code when working with the Lamassu Next co
|
||||||
```
|
```
|
||||||
lamassu-next/
|
lamassu-next/
|
||||||
├── apps/
|
├── apps/
|
||||||
│ ├── machine/ # Tauri + Vue 3 ATM kiosk application (in progress)
|
│ ├── machine/ # Electron + Vue 3 ATM kiosk application ✅
|
||||||
│ ├── dashboard/ # Vue 3 operator dashboard (planned)
|
│ ├── dashboard/ # Vue 3 operator dashboard (planned)
|
||||||
│ └── relay/ # strfry relay configuration (planned)
|
│ └── relay/ # strfry relay configuration (planned)
|
||||||
├── packages/
|
├── packages/
|
||||||
│ ├── hal/ # Rust Hardware Abstraction Layer (planned)
|
│ ├── hal/ # TypeScript Hardware Abstraction Layer ✅
|
||||||
│ ├── nostr-client/ # Nostr client library ✅
|
│ ├── nostr-client/ # Nostr client library ✅
|
||||||
│ ├── clink/ # CLINK protocol implementation ✅
|
│ ├── clink/ # CLINK protocol implementation ✅
|
||||||
│ ├── state-machine/ # XState v5 ATM state machine ✅
|
│ ├── state-machine/ # XState v5 ATM state machine ✅
|
||||||
|
|
@ -40,6 +40,7 @@ lamassu-next/
|
||||||
| `@lamassu/clink` | CLINK protocol (kinds 21001-21003), noffer encoding | 7 |
|
| `@lamassu/clink` | CLINK protocol (kinds 21001-21003), noffer encoding | 7 |
|
||||||
| `@lamassu/lightning` | Lightning.Pub RPC client (kind 21000) | 10 |
|
| `@lamassu/lightning` | Lightning.Pub RPC client (kind 21000) | 10 |
|
||||||
| `@lamassu/state-machine` | XState v5 ATM state machine (idle, cashIn, cashOut) | 14 |
|
| `@lamassu/state-machine` | XState v5 ATM state machine (idle, cashIn, cashOut) | 14 |
|
||||||
|
| `@lamassu/hal` | Hardware drivers (ID003 validator, F56 dispenser) | - |
|
||||||
|
|
||||||
### Placeholder Packages
|
### Placeholder Packages
|
||||||
|
|
||||||
|
|
@ -48,11 +49,13 @@ lamassu-next/
|
||||||
| `@lamassu/cashu` | Cashu ecash for offline operation |
|
| `@lamassu/cashu` | Cashu ecash for offline operation |
|
||||||
| `@lamassu/ui-shared` | Shared Vue 3 components |
|
| `@lamassu/ui-shared` | Shared Vue 3 components |
|
||||||
|
|
||||||
|
### Completed Applications
|
||||||
|
|
||||||
|
- **apps/machine** - Electron ATM kiosk with Vue 3 UI (HAL integrated, ready for hardware testing)
|
||||||
|
|
||||||
### Planned Components
|
### Planned Components
|
||||||
|
|
||||||
- **apps/machine** - Tauri 2.x ATM kiosk with Vue 3 UI (UI shell implemented, HAL integration pending)
|
|
||||||
- **apps/dashboard** - Operator dashboard for fleet management
|
- **apps/dashboard** - Operator dashboard for fleet management
|
||||||
- **packages/hal** - Rust hardware drivers (bill validators, dispensers, printers)
|
|
||||||
|
|
||||||
### Critical Documentation
|
### Critical Documentation
|
||||||
|
|
||||||
|
|
@ -159,11 +162,11 @@ Use these to interact with infrastructure directly during development.
|
||||||
## Key Technologies
|
## Key Technologies
|
||||||
|
|
||||||
| Component | Technology | Notes |
|
| Component | Technology | Notes |
|
||||||
| ------------- | -------------- | ------------------------------------- |
|
| ------------- | -------------- | --------------------------------------- |
|
||||||
| Runtime | Node.js 22 LTS | Strict TypeScript, ESM |
|
| Runtime | Node.js 22 LTS | Strict TypeScript, ESM |
|
||||||
| ATM Shell | Tauri 2.x | Rust core, Vue 3 UI (planned) |
|
| ATM Shell | Electron | Node.js main process, Vue 3 renderer |
|
||||||
| State Machine | XState v5 | Actor model, service injection |
|
| State Machine | XState v5 | Actor model, service injection |
|
||||||
| Hardware | Rust + napi-rs | Ported from lamassu-machine (planned) |
|
| Hardware | TypeScript | ID003, F56 drivers from lamassu-machine |
|
||||||
| Messaging | Nostr | NIP-01, NIP-42, NIP-44 |
|
| Messaging | Nostr | NIP-01, NIP-42, NIP-44 |
|
||||||
| Payments | CLINK + RPC | Kind 21000 (RPC), 21001-21003 (CLINK) |
|
| Payments | CLINK + RPC | Kind 21000 (RPC), 21001-21003 (CLINK) |
|
||||||
| Backend | Lightning.Pub | Nostr-native account system |
|
| Backend | Lightning.Pub | Nostr-native account system |
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
[workspace]
|
|
||||||
resolver = "2"
|
|
||||||
members = ["apps/machine/src-tauri"]
|
|
||||||
78
lamassu-next/apps/machine/electron/main.ts
Normal file
78
lamassu-next/apps/machine/electron/main.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
* Electron Main Process
|
||||||
|
*
|
||||||
|
* Creates the ATM kiosk window and handles IPC with the renderer.
|
||||||
|
* In production, runs in kiosk mode (fullscreen, no decorations).
|
||||||
|
* HAL hardware drivers run here (Node.js environment).
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { app, BrowserWindow, ipcMain } from 'electron'
|
||||||
|
import path from 'node:path'
|
||||||
|
|
||||||
|
// Determine if we're in development
|
||||||
|
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
|
||||||
|
|
||||||
|
// Window dimensions (vertical ATM display)
|
||||||
|
const WINDOW_WIDTH = 1080
|
||||||
|
const WINDOW_HEIGHT = 1920
|
||||||
|
|
||||||
|
let mainWindow: BrowserWindow | null = null
|
||||||
|
|
||||||
|
function createWindow() {
|
||||||
|
mainWindow = new BrowserWindow({
|
||||||
|
width: WINDOW_WIDTH,
|
||||||
|
height: WINDOW_HEIGHT,
|
||||||
|
resizable: isDev, // Allow resize in dev for debugging
|
||||||
|
frame: isDev, // Show window frame in dev
|
||||||
|
kiosk: !isDev, // Kiosk mode in production
|
||||||
|
webPreferences: {
|
||||||
|
preload: path.join(__dirname, 'preload.js'),
|
||||||
|
contextIsolation: true,
|
||||||
|
nodeIntegration: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Load the app
|
||||||
|
if (isDev) {
|
||||||
|
// Development: load from Vite dev server
|
||||||
|
const devServerUrl = process.env.VITE_DEV_SERVER_URL || 'http://localhost:1420'
|
||||||
|
mainWindow.loadURL(devServerUrl)
|
||||||
|
mainWindow.webContents.openDevTools()
|
||||||
|
} else {
|
||||||
|
// Production: load from built files
|
||||||
|
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'))
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.on('closed', () => {
|
||||||
|
mainWindow = null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPC Handlers
|
||||||
|
ipcMain.handle('get-version', () => {
|
||||||
|
return app.getVersion()
|
||||||
|
})
|
||||||
|
|
||||||
|
// App lifecycle
|
||||||
|
app.whenReady().then(() => {
|
||||||
|
createWindow()
|
||||||
|
|
||||||
|
app.on('activate', () => {
|
||||||
|
// macOS: re-create window when dock icon clicked
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.on('window-all-closed', () => {
|
||||||
|
// Quit on all platforms (ATM doesn't need macOS dock behavior)
|
||||||
|
app.quit()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Security: prevent new window creation
|
||||||
|
app.on('web-contents-created', (_, contents) => {
|
||||||
|
contents.setWindowOpenHandler(() => {
|
||||||
|
return { action: 'deny' }
|
||||||
|
})
|
||||||
|
})
|
||||||
27
lamassu-next/apps/machine/electron/preload.ts
Normal file
27
lamassu-next/apps/machine/electron/preload.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Electron Preload Script
|
||||||
|
*
|
||||||
|
* Exposes a secure API to the renderer process via contextBridge.
|
||||||
|
* This is the only way for the Vue app to communicate with the main process.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { contextBridge, ipcRenderer } from 'electron'
|
||||||
|
|
||||||
|
// Expose protected methods to renderer
|
||||||
|
contextBridge.exposeInMainWorld('electronAPI', {
|
||||||
|
// Get app version
|
||||||
|
getVersion: (): Promise<string> => ipcRenderer.invoke('get-version'),
|
||||||
|
|
||||||
|
// Platform info
|
||||||
|
platform: process.platform,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Type declaration for the exposed API
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
electronAPI: {
|
||||||
|
getVersion: () => Promise<string>
|
||||||
|
platform: NodeJS.Platform
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
lamassu-next/apps/machine/electron/tsconfig.json
Normal file
13
lamassu-next/apps/machine/electron/tsconfig.json
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"outDir": "../dist-electron",
|
||||||
|
"rootDir": "."
|
||||||
|
},
|
||||||
|
"include": ["./**/*.ts"]
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
{
|
{
|
||||||
"name": "@lamassu/machine",
|
"name": "@lamassu/machine",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Tauri 2.x ATM kiosk application with Vue 3 UI",
|
"description": "Electron ATM kiosk application with Vue 3 UI",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "dist-electron/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "concurrently -n vite,electron \"vite\" \"pnpm run electron:dev\"",
|
||||||
"build": "vue-tsc --noEmit && vite build",
|
"dev:vite": "vite",
|
||||||
|
"electron:dev": "tsc -p electron/tsconfig.json && electron dist-electron/main.js",
|
||||||
|
"build": "vue-tsc --noEmit && vite build && tsc -p electron/tsconfig.json",
|
||||||
|
"build:electron": "pnpm build && electron-builder",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"tauri": "tauri",
|
|
||||||
"tauri:dev": "tauri dev",
|
|
||||||
"tauri:build": "tauri build",
|
|
||||||
"typecheck": "vue-tsc --noEmit",
|
"typecheck": "vue-tsc --noEmit",
|
||||||
"lint": "eslint src/"
|
"lint": "eslint src/"
|
||||||
},
|
},
|
||||||
|
|
@ -33,14 +34,33 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/vite": "^4.0.0",
|
"@tailwindcss/vite": "^4.0.0",
|
||||||
"@tauri-apps/api": "^2.1.0",
|
|
||||||
"@tauri-apps/cli": "^2.1.0",
|
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^22.0.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.0",
|
"@vitejs/plugin-vue": "^5.2.0",
|
||||||
|
"concurrently": "^9.0.0",
|
||||||
|
"electron": "^33.0.0",
|
||||||
|
"electron-builder": "^25.0.0",
|
||||||
"tailwindcss": "^4.0.0",
|
"tailwindcss": "^4.0.0",
|
||||||
"tw-animate-css": "^1.4.0",
|
"tw-animate-css": "^1.4.0",
|
||||||
"typescript": "^5.7.0",
|
"typescript": "^5.7.0",
|
||||||
"vite": "^6.0.0",
|
"vite": "^6.0.0",
|
||||||
"vue-tsc": "^2.1.0"
|
"vue-tsc": "^2.1.0"
|
||||||
|
},
|
||||||
|
"build": {
|
||||||
|
"appId": "dev.lamassu.atm",
|
||||||
|
"productName": "Lamassu ATM",
|
||||||
|
"directories": {
|
||||||
|
"output": "release"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/**/*",
|
||||||
|
"dist-electron/**/*"
|
||||||
|
],
|
||||||
|
"linux": {
|
||||||
|
"target": [
|
||||||
|
"AppImage",
|
||||||
|
"deb"
|
||||||
|
],
|
||||||
|
"category": "Finance"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "lamassu-machine"
|
|
||||||
version = "0.1.0"
|
|
||||||
description = "Lamassu ATM Kiosk Application"
|
|
||||||
authors = ["Lamassu Industries"]
|
|
||||||
edition = "2021"
|
|
||||||
rust-version = "1.70"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "lamassu_machine_lib"
|
|
||||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
tauri-build = { version = "2", features = [] }
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
tauri = { version = "2", features = [] }
|
|
||||||
tauri-plugin-shell = "2"
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
serde_json = "1"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
# This feature is used for production builds or when a dev server is not specified
|
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
tauri_build::build()
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
//! Lamassu ATM Tauri Application
|
|
||||||
//!
|
|
||||||
//! This is the Rust backend for the Lamassu ATM kiosk application.
|
|
||||||
//! Hardware drivers and native functionality will be implemented here.
|
|
||||||
|
|
||||||
use tauri::Manager;
|
|
||||||
|
|
||||||
/// Greet command for testing Tauri IPC
|
|
||||||
#[tauri::command]
|
|
||||||
fn greet(name: &str) -> String {
|
|
||||||
format!("Hello, {}! Welcome to Lamassu ATM.", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get application version
|
|
||||||
#[tauri::command]
|
|
||||||
fn get_version() -> String {
|
|
||||||
env!("CARGO_PKG_VERSION").to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
||||||
pub fn run() {
|
|
||||||
tauri::Builder::default()
|
|
||||||
.plugin(tauri_plugin_shell::init())
|
|
||||||
.setup(|app| {
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
{
|
|
||||||
let window = app.get_webview_window("main").unwrap();
|
|
||||||
window.open_devtools();
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.invoke_handler(tauri::generate_handler![greet, get_version])
|
|
||||||
.run(tauri::generate_context!())
|
|
||||||
.expect("error while running tauri application");
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
// Prevents additional console window on Windows in release
|
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
lamassu_machine_lib::run()
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
|
||||||
"productName": "Lamassu ATM",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"identifier": "io.lamassu.atm",
|
|
||||||
"build": {
|
|
||||||
"beforeDevCommand": "pnpm dev",
|
|
||||||
"devUrl": "http://localhost:1420",
|
|
||||||
"beforeBuildCommand": "pnpm build",
|
|
||||||
"frontendDist": "../dist"
|
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"withGlobalTauri": true,
|
|
||||||
"windows": [
|
|
||||||
{
|
|
||||||
"title": "Lamassu ATM",
|
|
||||||
"width": 1080,
|
|
||||||
"height": 1920,
|
|
||||||
"resizable": false,
|
|
||||||
"fullscreen": false,
|
|
||||||
"decorations": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"security": {
|
|
||||||
"csp": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bundle": {
|
|
||||||
"active": true,
|
|
||||||
"targets": "all",
|
|
||||||
"icon": [
|
|
||||||
"icons/32x32.png",
|
|
||||||
"icons/128x128.png",
|
|
||||||
"icons/128x128@2x.png",
|
|
||||||
"icons/icon.icns",
|
|
||||||
"icons/icon.ico"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -11,28 +11,23 @@ export default defineConfig({
|
||||||
'@': path.resolve(__dirname, './src'),
|
'@': path.resolve(__dirname, './src'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Tauri expects a fixed port during development
|
// Dev server configuration
|
||||||
server: {
|
server: {
|
||||||
port: 1420,
|
port: 1420,
|
||||||
strictPort: true,
|
strictPort: true,
|
||||||
watch: {
|
|
||||||
ignored: ['**/src-tauri/**'],
|
|
||||||
},
|
},
|
||||||
},
|
// Build configuration for Electron renderer
|
||||||
// Build configuration for Tauri
|
|
||||||
build: {
|
build: {
|
||||||
// Tauri uses Chromium on Windows and WebKit on macOS and Linux
|
// Target modern Chrome (Electron uses Chromium)
|
||||||
target: process.env.TAURI_ENV_PLATFORM === 'windows' ? 'chrome105' : 'safari14',
|
target: 'chrome120',
|
||||||
// Don't minify for debug builds
|
// Sourcemaps for debugging
|
||||||
minify: !process.env.TAURI_ENV_DEBUG ? 'esbuild' : false,
|
sourcemap: process.env.NODE_ENV === 'development',
|
||||||
// Produce sourcemaps for debug builds
|
|
||||||
sourcemap: !!process.env.TAURI_ENV_DEBUG,
|
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
// @lamassu/hal uses Node.js APIs (serialport, node:events) and will only
|
// @lamassu/hal uses Node.js APIs (serialport, node:events) and will only
|
||||||
// run in Electron's main process. Exclude from browser bundle.
|
// run in Electron's main process. Exclude from renderer bundle.
|
||||||
external: ['@lamassu/hal'],
|
external: ['@lamassu/hal'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Prevent Vite from clearing the screen
|
// Prevent Vite from clearing the screen (useful when running with Electron)
|
||||||
clearScreen: false,
|
clearScreen: false,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
2877
lamassu-next/pnpm-lock.yaml
generated
2877
lamassu-next/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue