diff --git a/README.md b/README.md index 143067e..2dca25a 100755 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@

deskify

- A blazing fast, lightweight Nativefier alternative written in Rust & Tauri.
- Instantly turn any website or web app into a native, standalone Linux desktop application. + Turn websites into first-class Linux desktop applications with Rust, Tauri, and the system WebView.
+ Build native-feeling, standalone web app wrappers with CLI-first Linux integration.

@@ -35,7 +35,7 @@ The comparison below is intentionally rough and practical (not benchmark marketi | Electron / Nativefier | Bundled Chromium per app | Good | Low to medium | Per-app process, app-bundled runtime | Easy to medium | Depends on the site | | PWA | Browser runtime | Limited / browser-dependent | Low | Shared browser context | Very easy | Depends on browser + site | | Flatpak web app models | Sandbox-oriented runtime model | Good | Limited | Strong sandbox model | Medium | Depends on runtime + site | -| **Deskify** | System WebView | Native (`.desktop`, icons, WMClass) | **High (CLI-first)** | Medium (system webview model) | Medium (Tauri prerequisites) | Depends on the site | +| **Deskify** | System WebView | Native (`.desktop`, icons, WMClass) | **High (CLI-first)** | Medium (shared system WebView security model) | Medium (Tauri prerequisites) | Depends on the site | ### Deskify Focus (Today) @@ -119,7 +119,7 @@ The table above covers the technical tradeoffs in more detail. In practice, `des - a system-WebView-based runtime model instead of shipping a bundled browser per app ### Key Features -- **Extremely Lightweight:** Generated binaries are tiny (~4-6 MB) and RAM consumption is minimal. +- **System-WebView Runtime Model:** Generated wrappers stay small because Deskify relies on the system WebView instead of bundling a browser runtime per app. - **Automatic Icon Fetching:** Scrapes high-quality 128x128 favicons automatically using the Google Favicon API. - **XDG-Compliant System Integration:** Safely creates `.desktop` entries in `~/.local/share/applications` and manages application icons in `~/.local/share/icons/hicolor/`. - **Wayland/X11 Ready:** Perfectly binds `StartupWMClass` to ensure your DE groups the app exactly to its custom icon (no generic gear icons in GNOME/KDE taskbars). diff --git a/src/main.rs b/src/main.rs index a6e95a1..5c93d61 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use anyhow::{Context, Result, anyhow}; use clap::{Parser, Subcommand}; use directories::BaseDirs; +use image::{DynamicImage, ImageFormat}; use regex::Regex; use serde_json::{Map, Value, json}; use std::fs; @@ -92,6 +93,16 @@ fn is_deskify_desktop_entry(content: &str) -> bool { && (content.contains(".local/bin/") || content.contains("deskify"))) } +fn write_rgba_png(img: DynamicImage, output_path: &Path) -> Result<()> { + // Tauri expects PNG icons in RGBA format; many favicons are palette/LA/ICO etc. + let rgba = img.to_rgba8(); + let rgba_img = DynamicImage::ImageRgba8(rgba); + rgba_img + .save_with_format(output_path, ImageFormat::Png) + .with_context(|| format!("Failed to write RGBA PNG icon to {}", output_path.display()))?; + Ok(()) +} + fn fetch_or_create_icon( website_url: &str, custom_icon: Option<&String>, @@ -99,8 +110,9 @@ fn fetch_or_create_icon( ) -> Result<()> { if let Some(icon_path) = custom_icon { if Path::new(icon_path).exists() { - fs::copy(icon_path, output_path) - .with_context(|| format!("Failed to copy custom icon from {}", icon_path))?; + let img = image::open(icon_path) + .with_context(|| format!("Failed to read custom icon image from {}", icon_path))?; + write_rgba_png(img, output_path)?; return Ok(()); } else { eprintln!( @@ -118,8 +130,12 @@ fn fetch_or_create_icon( if let Ok(response) = ureq::get(&api_url).call() { let mut bytes = Vec::new(); if response.into_reader().read_to_end(&mut bytes).is_ok() && !bytes.is_empty() { - fs::write(output_path, bytes).context("Failed to write downloaded icon to disk")?; - return Ok(()); + if let Ok(img) = image::load_from_memory(&bytes) { + write_rgba_png(img, output_path)?; + return Ok(()); + } else { + eprintln!("Warning: Downloaded favicon could not be decoded as an image, falling back to dummy icon."); + } } } }