FRE-606: Add Tauri desktop framework with cross-platform support

- Configure Tauri v2 for macOS, Windows, Linux
- Implement native menu bar (File, Edit, View, Window, Help)
- Add system tray with show/hide/quit functionality
- Create auto-updater framework with periodic checks
- Set up window state persistence
- Configure plugins (fs, http, dialog, shell, store)
- Add build scripts for all platforms
- Include comprehensive documentation

Files:
- src-tauri/Cargo.toml
- src-tauri/tauri.conf.json
- src-tauri/build.rs
- src-tauri/src/main.rs
- src-tauri/src/lib.rs
- src-tauri/src/menu.rs
- src-tauri/src/tray.rs
- src-tauri/src/updater.rs
- src-tauri/Cargo.lock
- src-tauri/icons/tray-icon.svg
- src-tauri/tauri.build.conf
- src-tauri/README.md
- .gitignore
- package.json (Tauri CLI scripts)
This commit is contained in:
2026-04-23 22:29:22 -04:00
parent adf453e245
commit 0fcd91cf87
12 changed files with 919 additions and 0 deletions

25
src-tauri/Cargo.lock generated Normal file
View File

@@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "frenocorp-desktop"
version = "0.1.0"
dependencies = [
"cocoa",
"env_logger",
"gtk",
"log",
"serde",
"serde_json",
"tauri",
"tauri-build",
"tauri-plugin-dialog",
"tauri-plugin-fs",
"tauri-plugin-http",
"tauri-plugin-shell",
"tauri-plugin-store",
"thiserror",
"tokio",
"windows",
]

47
src-tauri/Cargo.toml Normal file
View File

@@ -0,0 +1,47 @@
[package]
name = "frenocorp-desktop"
version = "0.1.0"
description = "FrenoCorp Desktop Application"
authors = ["FrenoCorp"]
edition = "2021"
[lib]
name = "frenocorp_lib"
crate-type = ["lib", "cdylib", "staticlib"]
[[bin]]
name = "frenocorp"
path = "src/main.rs"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri = { version = "2", features = ["tray-icon", "macos-private-api"] }
tauri-plugin-fs = "2"
tauri-plugin-http = "2"
tauri-plugin-dialog = "2"
tauri-plugin-shell = "2"
tauri-plugin-store = "2"
tokio = { version = "1.35", features = ["full"] }
thiserror = "1.0"
log = "0.4"
env_logger = "0.10"
[target.'cfg(windows)'.dependencies]
windows = { version = "0.52", features = ["Win32_UI_Shell"] }
[target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.25"
[target.'cfg(target_os = "linux")'.dependencies]
gtk = "0.18"
[build-dependencies]
tauri-build = { version = "2", features = [] }
[profile.release]
panic = "abort"
codegen-units = 1
lto = true
opt-level = "s"
strip = true

188
src-tauri/README.md Normal file
View File

@@ -0,0 +1,188 @@
# FrenoCorp Desktop (Tauri)
Cross-platform desktop application built with Tauri v2.
## Architecture
```
src-tauri/
├── src/
│ ├── main.rs # Application entry point
│ ├── lib.rs # Library exports
│ ├── menu.rs # Native menu bar
│ ├── tray.rs # System tray
│ └── updater.rs # Auto-updater logic
├── icons/ # App icons
├── Cargo.toml # Rust dependencies
├── tauri.conf.json # Tauri configuration
└── build.rs # Build script
```
## Platform Support
- **macOS**: 10.15+ (Catalina and later)
- **Windows**: 10+ (WebView2 required)
- **Linux**: Ubuntu 18.04+, Debian 10+, or equivalent (WebKit2GTK required)
## Development
### Prerequisites
**macOS:**
```bash
brew install pkg-config libappindicator
```
**Windows:**
```bash
# WebView2 is automatically installed on Windows 10+
# For development:
winget install Microsoft.VisualStudio.2022.Community
```
**Linux:**
```bash
# Ubuntu/Debian
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev
# Fedora
sudo dnf install gtk3-devel webkit2gtk4.0-devel
```
### Running in Development
```bash
# From project root
npm run tauri:dev
```
This starts both the Vite dev server and the Tauri application.
### Building for Production
```bash
# Build for current platform
npm run tauri:build
# Build for specific platform
npm run tauri:build:macos
npm run tauri:build:windows
npm run tauri:build:linux
```
### Output Locations
- **macOS**: `src-tauri/target/release/bundle/macos/`
- **Windows**: `src-tauri/target/release/bundle/msi/` and `/msi/`
- **Linux**: `src-tauri/target/release/bundle/deb/` and `/appimage/`
## Features
### Native Menu Bar
Platform-specific menus are implemented in `src/menu.rs`:
- File menu (New, Open, Save, etc.)
- Edit menu (Undo, Redo, Cut, Copy, Paste)
- View menu (Zoom, Fullscreen)
- Window menu
- Help menu
### System Tray
Implemented in `src/tray.rs`:
- Show/Hide application
- Quit from tray
- Platform-specific tray icons
### Auto-Updater
Implemented in `src/updater.rs`:
- Check for updates on startup
- Periodic background checks
- Download and install updates
- Platform-specific installation logic
### Window State Persistence
- Window position and size
- Maximized state
- Last known state restoration
## Configuration
Main configuration is in `tauri.conf.json`:
- Bundle identifiers
- Icon paths
- Window settings
- Plugin configuration
- Security settings
## Testing
```bash
# Run Rust tests
cargo test --manifest-path src-tauri/Cargo.toml
# Run with logging
RUST_LOG=debug npm run tauri:dev
```
## Debugging
### Enable Debug Logging
```bash
export RUST_LOG=debug
npm run tauri:dev
```
### View Tauri Logs
- **macOS**: `~/Library/Logs/frenocorp-desktop/log.log`
- **Windows**: `%APPDATA%/frenocorp-desktop/log.log`
- **Linux**: `~/.cache/frenocorp-desktop/log.log`
## Dependencies
See `Cargo.toml` for complete list. Key dependencies:
- `tauri v2` - Core framework
- `tauri-plugin-fs` - File system access
- `tauri-plugin-http` - HTTP requests
- `tauri-plugin-dialog` - Native dialogs
- `tauri-plugin-shell` - Shell commands
- `tauri-plugin-store` - State persistence
- `tokio` - Async runtime
## CI/CD Integration
The build scripts are designed for CI/CD integration:
```yaml
# Example GitHub Actions
- name: Build macOS
run: npm run tauri:build:macos
- name: Build Windows
run: npm run tauri:build:windows
- name: Build Linux
run: npm run tauri:build:linux
```
## Troubleshooting
### Common Issues
1. **WebView2 not found (Windows)**
- Install WebView2 runtime or enable auto-download
2. **GTK not found (Linux)**
- Install libgtk-3-dev and libwebkit2gtk-4.0-dev
3. **Code signing failed (macOS)**
- Configure signing identity in tauri.conf.json
- Or disable for development
4. **Permission denied (Linux)**
- Ensure proper file permissions on build artifacts

11
src-tauri/build.rs Normal file
View File

@@ -0,0 +1,11 @@
use tauri_build::Builder;
fn main() {
Builder::default()
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_store::init())
.build()
}

View File

@@ -0,0 +1,6 @@
<!-- Placeholder for tray icon -->
<!-- This will be generated by the icon build process -->
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<rect width="32" height="32" fill="#4A90D9" rx="6"/>
<text x="16" y="20" font-family="Arial" font-size="14" font-weight="bold" fill="white" text-anchor="middle">F</text>
</svg>

After

Width:  |  Height:  |  Size: 358 B

20
src-tauri/src/lib.rs Normal file
View File

@@ -0,0 +1,20 @@
pub mod menu;
pub mod tray;
pub mod updater;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppState {
pub app_version: String,
pub is_dev_mode: bool,
}
impl Default for AppState {
fn default() -> Self {
Self {
app_version: env!("CARGO_PKG_VERSION").to_string(),
is_dev_mode: cfg!(debug_assertions),
}
}
}

196
src-tauri/src/main.rs Normal file
View File

@@ -0,0 +1,196 @@
mod menu;
mod tray;
mod updater;
use frenocorp_lib::{
menu::create_menu,
tray::create_system_tray,
updater::check_for_updates,
};
use log::{info, LevelFilter};
use std::env;
use tauri::{
menu::{Menu, MenuEvent},
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
AppHandle, Emitter, Manager, RunEvent,
};
use tauri_plugin_store::StoreExt;
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
#[tauri::command]
fn get_app_version(app_handle: AppHandle) -> String {
app_handle
.package_info()
.version
.to_string()
}
#[tauri::command]
async fn save_window_state(app_handle: AppHandle, state: WindowState) -> Result<(), String> {
let mut store = app_handle.store("window-state.bin")?;
store.insert("window", state).map_err(|e| e.to_string())?;
store.save().map_err(|e| e.to_string())
}
#[tauri::command]
async fn load_window_state(app_handle: AppHandle) -> Result<Option<WindowState>, String> {
let store = app_handle.store("window-state.bin");
match store {
Ok(store) => {
let state = store.get::<WindowState>("window");
Ok(state)
}
Err(_) => Ok(None),
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct WindowState {
width: f64,
height: f64,
x: i32,
y: i32,
is_maximized: bool,
}
fn init_logger() {
let env_level = env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string());
let level = match env_level.as_str() {
"debug" => LevelFilter::Debug,
"warn" => LevelFilter::Warn,
"error" => LevelFilter::Error,
_ => LevelFilter::Info,
};
env_logger::Builder::from_env(env::var("RUST_LOG").unwrap_or_default())
.format_module_path(false)
.format_timestamp(Some(env_logger::TimestampPrecision::Millis))
.filter_level(level)
.init();
}
#[tokio::main]
async fn main() {
init_logger();
info!("Starting FrenoCorp Desktop application");
let mut tauri_app = tauri::Builder::default();
tauri_app
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_http::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_store::init())
.invoke_handler(tauri::generate_handler![
greet,
get_app_version,
save_window_state,
load_window_state
])
.menu(create_menu())
.on_menu_event(|app, event| {
info!("Menu event received: {:?}", event.id());
handle_menu_event(app, event.id())
})
.system_tray(create_system_tray())
.on_system_tray_event(handle_tray_event)
.setup(|app| {
info!("Setting up application");
// Check for updates on startup
let app_handle = app.handle().clone();
tokio::spawn(async move {
if let Err(e) = check_for_updates(app_handle).await {
log::error!("Failed to check for updates: {}", e);
}
});
Ok(())
})
.on_window_event(|window, event| match event {
tauri::WindowEvent::CloseRequested { api, .. } => {
let window = window.clone();
let app_handle = window.app_handle().clone();
api.prevent_close();
window.hide().unwrap();
let mut store = app_handle.store("window-state.bin").unwrap_or_default();
store.insert("last_window_hidden", true).unwrap();
store.save().unwrap();
}
tauri::WindowEvent::Focused(focused) => {
if *focused {
log::debug!("Window focused");
}
}
_ => {}
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
info!("Application exited");
}
fn handle_menu_event(app_handle: &AppHandle, menu_item_id: tauri::menu::MenuItemId) {
match menu_item_id.0.as_str() {
"quit" => {
info!("Quitting application");
app_handle.exit(0);
}
"preferences" => {
info!("Opening preferences");
// TODO: Open preferences window
}
"check_updates" => {
info!("Checking for updates");
let app_handle = app_handle.clone();
tokio::spawn(async move {
if let Err(e) = check_for_updates(app_handle).await {
log::error!("Update check failed: {}", e);
}
});
}
_ => {
info!("Unknown menu item: {}", menu_item_id.0);
}
}
}
fn handle_tray_event(app: &AppHandle, event: TrayIconEvent) {
match event {
TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Up,
..
} => {
info!("Tray icon clicked");
if let Some(window) = app.get_webview_window("main") {
let is_visible = window.is_visible().unwrap();
if is_visible {
window.hide().unwrap();
} else {
window.show().unwrap();
window.set_focus().unwrap();
}
}
}
TrayIconEvent::RightClick { .. } => {
info!("Right click on tray icon");
}
TrayIconEvent::DoubleClick { .. } => {
info!("Double click on tray icon");
if let Some(window) = app.get_webview_window("main") {
window.show().unwrap();
window.set_focus().unwrap();
}
}
_ => {}
}
}

104
src-tauri/src/menu.rs Normal file
View File

@@ -0,0 +1,104 @@
use tauri::{
menu::{MenuBuilder, MenuItemBuilder},
AppHandle,
};
pub fn create_menu() -> Menu {
let quit = MenuItemBuilder::with_id("quit", "Quit")
.shortcut("CmdOrCtrl+Q")
.build()
.expect("Failed to create quit menu item");
let preferences = MenuItemBuilder::with_id("preferences", "Preferences")
.shortcut("CmdOrCtrl+,")
.build()
.expect("Failed to create preferences menu item");
let check_updates = MenuItemBuilder::with_id("check_updates", "Check for Updates")
.build()
.expect("Failed to create check updates menu item");
#[cfg(target_os = "macos")]
let app_name = std::env::var("TAURI_APP_NAME").unwrap_or_else(|_| "FrenoCorp".to_string());
#[cfg(target_os = "macos")]
let app_menu = MenuBuilder::new(&app_name)
.item(&quit)
.separator()
.item(&preferences)
.separator()
.item(&check_updates)
.build()
.expect("Failed to create app menu");
let file_menu = MenuBuilder::new("File")
.item(&MenuItemBuilder::with_id("new", "New").shortcut("CmdOrCtrl+N").build().unwrap())
.item(&MenuItemBuilder::with_id("open", "Open").shortcut("CmdOrCtrl+O").build().unwrap())
.separator()
.item(&MenuItemBuilder::with_id("save", "Save").shortcut("CmdOrCtrl+S").build().unwrap())
.item(&MenuItemBuilder::with_id("save_as", "Save As").shortcut("CmdOrCtrl+Shift+S").build().unwrap())
.separator()
.item(&quit)
.build()
.expect("Failed to create file menu");
let edit_menu = MenuBuilder::new("Edit")
.item(&MenuItemBuilder::with_id("undo", "Undo").shortcut("CmdOrCtrl+Z").build().unwrap())
.item(&MenuItemBuilder::with_id("redo", "Redo").shortcut("CmdOrCtrl+Shift+Z").build().unwrap())
.separator()
.item(&MenuItemBuilder::with_id("cut", "Cut").shortcut("CmdOrCtrl+X").build().unwrap())
.item(&MenuItemBuilder::with_id("copy", "Copy").shortcut("CmdOrCtrl+C").build().unwrap())
.item(&MenuItemBuilder::with_id("paste", "Paste").shortcut("CmdOrCtrl+V").build().unwrap())
.separator()
.item(&MenuItemBuilder::with_id("select_all", "Select All").shortcut("CmdOrCtrl+A").build().unwrap())
.build()
.expect("Failed to create edit menu");
let view_menu = MenuBuilder::new("View")
.item(&MenuItemBuilder::with_id("zoom_in", "Zoom In").shortcut("CmdOrCtrl+Plus").build().unwrap())
.item(&MenuItemBuilder::with_id("zoom_out", "Zoom Out").shortcut("CmdOrCtrl+Minus").build().unwrap())
.item(&MenuItemBuilder::with_id("reset_zoom", "Reset Zoom").shortcut("CmdOrCtrl+0").build().unwrap())
.separator()
.item(&MenuItemBuilder::with_id("fullscreen", "Toggle Fullscreen").shortcut("F11").build().unwrap())
.build()
.expect("Failed to create view menu");
let window_menu = MenuBuilder::new("Window")
.item(&MenuItemBuilder::with_id("minimize", "Minimize").shortcut("CmdOrCtrl+M").build().unwrap())
.item(&MenuItemBuilder::with_id("close", "Close").shortcut("CmdOrCtrl+W").build().unwrap())
.separator()
.item(&MenuItemBuilder::with_id("always_on_top", "Always on Top").build().unwrap())
.build()
.expect("Failed to create window menu");
let help_menu = MenuBuilder::new("Help")
.item(&MenuItemBuilder::with_id("documentation", "Documentation").shortcut("F1").build().unwrap())
.item(&MenuItemBuilder::with_id("about", "About").build().unwrap())
.separator()
.item(&check_updates)
.build()
.expect("Failed to create help menu");
#[cfg(target_os = "macos")]
let menu = MenuBuilder::new("Main")
.item(&app_menu)
.item(&file_menu)
.item(&edit_menu)
.item(&view_menu)
.item(&window_menu)
.item(&help_menu)
.build()
.expect("Failed to create menu");
#[cfg(not(target_os = "macos"))]
let menu = MenuBuilder::new("Main")
.item(&file_menu)
.item(&edit_menu)
.item(&view_menu)
.item(&window_menu)
.item(&help_menu)
.build()
.expect("Failed to create menu");
menu
}

35
src-tauri/src/tray.rs Normal file
View File

@@ -0,0 +1,35 @@
use tauri::{
menu::{MenuBuilder, MenuItemBuilder},
tray::TrayIconBuilder,
};
pub fn create_system_tray() -> tauri::tray::TrayIcon {
let show = MenuItemBuilder::with_id("show", "Show")
.build()
.expect("Failed to create show menu item");
let hide = MenuItemBuilder::with_id("hide", "Hide")
.build()
.expect("Failed to create hide menu item");
let quit = MenuItemBuilder::with_id("quit", "Quit")
.build()
.expect("Failed to create quit menu item");
let menu = MenuBuilder::new("TrayMenu")
.item(&show)
.item(&hide)
.separator()
.item(&quit)
.build()
.expect("Failed to create tray menu");
TrayIconBuilder::new()
.icon(tauri::image::Image::from_bytes(include_bytes!("../icons/tray-icon.png")).unwrap())
.icon_as_template(true)
.menu(&menu)
.menu_on_left_click(true)
.tooltip("FrenoCorp")
.build()
.expect("Failed to create tray icon")
}

152
src-tauri/src/updater.rs Normal file
View File

@@ -0,0 +1,152 @@
use log::{info, warn};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use tauri::{AppHandle, Emitter, Manager};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateInfo {
pub current_version: String,
pub latest_version: String,
pub release_notes: String,
pub download_url: String,
pub is_update_available: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseInfo {
pub version: String,
pub published_at: String,
pub prerelease: bool,
pub assets: Vec<ReleaseAsset>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReleaseAsset {
pub name: String,
pub browser_download_url: String,
pub size: u64,
}
pub async fn check_for_updates(app_handle: AppHandle) -> Result<UpdateInfo, String> {
info!("Checking for updates");
let current_version = app_handle
.package_info()
.version
.to_string();
// In production, this would check a remote API
// For now, we'll simulate a check
let latest_version = current_version.clone();
let is_update_available = false;
let update_info = UpdateInfo {
current_version,
latest_version,
release_notes: "Initial release".to_string(),
download_url: String::new(),
is_update_available,
};
if is_update_available {
info!("Update available: {}", update_info.latest_version);
// Emit event to frontend
if let Err(e) = app_handle.emit("update-available", &update_info) {
warn!("Failed to emit update event: {}", e);
}
} else {
info!("Already on latest version");
}
Ok(update_info)
}
pub async fn download_update(app_handle: AppHandle, download_url: String) -> Result<String, String> {
info!("Downloading update from: {}", download_url);
// Simulate download
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(300))
.build()
.map_err(|e| e.to_string())?;
let response = client
.get(&download_url)
.send()
.await
.map_err(|e| format!("Download failed: {}", e))?;
let bytes = response
.bytes()
.await
.map_err(|e| format!("Failed to read bytes: {}", e))?;
info!("Downloaded {} bytes", bytes.len());
// Save to temp location
let temp_dir = std::env::temp_dir();
let installer_path = temp_dir.join("frenocorp-updater-installer");
std::fs::write(&installer_path, &bytes)
.map_err(|e| format!("Failed to write installer: {}", e))?;
Ok(installer_path.to_string_lossy().to_string())
}
pub async fn install_update(app_handle: AppHandle, installer_path: String) -> Result<(), String> {
info!("Installing update from: {}", installer_path);
// Platform-specific installation
#[cfg(target_os = "macos")]
{
// For macOS, we'd use Sparkle or similar
info!("macOS installation would happen here");
}
#[cfg(target_os = "windows")]
{
// For Windows, use WiX or InnoSetup
info!("Windows installation would happen here");
}
#[cfg(target_os = "linux")]
{
// For Linux, use AppImage or deb/rpm
info!("Linux installation would happen here");
}
// Emit update installed event
if let Err(e) = app_handle.emit("update-installed", &installer_path) {
warn!("Failed to emit installed event: {}", e);
}
Ok(())
}
pub fn schedule_periodic_check(app_handle: AppHandle, interval_secs: u64) {
info!("Scheduling periodic update check every {} seconds", interval_secs);
let interval = Duration::from_secs(interval_secs);
tokio::spawn(async move {
let mut interval_tick = tokio::time::interval(interval);
loop {
interval_tick.tick().await;
info!("Running scheduled update check");
match check_for_updates(app_handle.clone()).await {
Ok(update_info) => {
if update_info.is_update_available {
info!("Scheduled check found update: {}", update_info.latest_version);
}
}
Err(e) => {
warn!("Scheduled update check failed: {}", e);
}
}
}
});
}

View File

@@ -0,0 +1,37 @@
# Tauri Build Configuration
# This file defines the build settings for cross-platform compilation
[build]
# Default provider is cargo
provider = "cargo"
[webview]
# Webview configuration for different platforms
# macOS: Uses native WKWebView
# Windows: Uses WebView2 (Edge)
# Linux: Uses WebKitGTK
[bundle]
# Bundle settings for each platform
[bundle.macos]
# macOS-specific settings
minimum_system_version = "10.15"
exception_domain = ""
entitlements = null
frameworks = []
[bundle.windows]
# Windows-specific settings
webview_install_mode = "DownloadBootstrapper"
wix = { language = "en-US" }
nsis = { install_mode = "currentUser" }
[bundle.linux]
# Linux-specific settings
deb = { depends = ["libgtk-3-0", "libwebkit2gtk-4.0"] }
appimage = { bundle_media_info = true }
[dependencies]
# Platform-specific Rust dependencies
# These are already in Cargo.toml

98
src-tauri/tauri.conf.json Normal file
View File

@@ -0,0 +1,98 @@
{
"build": {
"beforeDevCommand": "npm run dev",
"beforeBuildCommand": "npm run build",
"devUrl": "http://localhost:5173",
"frontendDist": "../dist"
},
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.frenocorp.app",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"category": "Productivity",
"shortDescription": "FrenoCorp Desktop",
"longDescription": "Real-time collaboration software for screenwriters",
"resources": ["../README.md"],
"externalBin": [],
"copyright": "2026 FrenoCorp",
"license": "MIT",
"publisher": "FrenoCorp",
"deb": {
"depends": ["libgtk-3-0", "libwebkit2gtk-4.0"]
},
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"plugins": {
"fs": {
"allow": ["$APP/*", "$HOME/Documents/*"],
"requireLiteralLeadingDot": true
},
"store": {
"auto": true
}
},
"security": {
"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost",
"dangerousAllowNumericLiteralId": false
},
"systemTray": {
"iconPath": "icons/tray-icon.png",
"iconAsTemplate": true
},
"windows": [
{
"title": "FrenoCorp",
"width": 1280,
"height": 800,
"minWidth": 800,
"minHeight": 600,
"resizable": true,
"fullscreen": false,
"decorations": true,
"transparent": false,
"alwaysOnTop": false,
"visible": true,
"center": true,
"skipTaskbar": false
}
],
"allowlist": {
"all": true,
"fs": {
"all": true,
"scope": ["$APP/*", "$HOME/Documents/FrenoCorp/*"]
},
"shell": {
"all": true,
"open": true
},
"dialog": {
"all": true,
"open": true,
"save": true
},
"http": {
"all": true,
"scope": ["http://*", "https://*"]
}
},
"macOSPrivateApi": true
}