From e2aadff1d81964f3cbb66da3602e215677808994 Mon Sep 17 00:00:00 2001 From: Suyog Tandel Date: Sat, 31 Jan 2026 00:50:56 +0530 Subject: [PATCH] chore(ui): refactor config view data/types --- src/tauri.conf.json | 43 ------- src/ui/ui_types.rs | 247 +++++++++++++++++++++++------------------ src/ui/views/config.rs | 132 +++++++++++----------- 3 files changed, 203 insertions(+), 219 deletions(-) delete mode 100644 src/tauri.conf.json diff --git a/src/tauri.conf.json b/src/tauri.conf.json deleted file mode 100644 index 4c84f4a..0000000 --- a/src/tauri.conf.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "https://schema.tauri.app/config/2", - "productName": "picoforge", - "version": "0.3.0", - "identifier": "in.suyogtandel.picoforge", - "build": { - "beforeDevCommand": "deno task dev", - "devUrl": "http://localhost:1420", - "beforeBuildCommand": "deno task build", - "frontendDist": "../build" - }, - "app": { - "macOSPrivateApi": true, - "windows": [ - { - "title": "picoforge", - "width": 1280, - "height": 720, - "minWidth": 500, - "minHeight": 250, - "resizable": true, - "decorations": false, - "transparent": false - } - ], - "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", - "icons/icon.png", - "icons/in.suyogtandel.picoforge.svg" - ] - } -} diff --git a/src/ui/ui_types.rs b/src/ui/ui_types.rs index d3f5c76..5155e2b 100644 --- a/src/ui/ui_types.rs +++ b/src/ui/ui_types.rs @@ -1,3 +1,5 @@ +use gpui::SharedString; + use crate::device::types::{FidoDeviceInfo, FullDeviceStatus}; #[derive(Clone, Copy, PartialEq, Debug)] @@ -27,114 +29,141 @@ impl GlobalDeviceState { } } -pub struct VendorData { - pub value: &'static str, - pub label: &'static str, - pub vid: &'static str, - pub pid: &'static str, +// config view: + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum UsbIdentityPreset { + Custom, + Generic, + PicoHsm, + PicoFido, + PicoOpenPgp, + Pico, + SoloKeys, + NitroHsm, + NitroFido2, + NitroStart, + NitroPro, + NitroKey3, + YubiKey5, + YubiKeyNeo, + YubiHsm2, + Gnuk, + GnuPg, } -pub const VENDORS: &[VendorData] = &[ - VendorData { - value: "custom", - label: "Custom (Manual Entry)", - vid: "", - pid: "", - }, - VendorData { - value: "generic", - label: "Generic (FEFF:FCFD)", - vid: "FEFF", - pid: "FCFD", - }, - VendorData { - value: "pico-hsm", - label: "Pico Keys HSM (2E8A:10FD)", - vid: "2E8A", - pid: "10FD", - }, - VendorData { - value: "pico-fido", - label: "Pico Keys Fido (2E8A:10FE)", - vid: "2E8A", - pid: "10FE", - }, - VendorData { - value: "pico-openpgp", - label: "Pico Keys OpenPGP (2E8A:10FF)", - vid: "2E8A", - pid: "10FF", - }, - VendorData { - value: "pico", - label: "Pico (2E8A:0003)", - vid: "2E8A", - pid: "0003", - }, - VendorData { - value: "solokeys", - label: "SoloKeys (0483:A2CA)", - vid: "0483", - pid: "A2CA", - }, - VendorData { - value: "nitrohsm", - label: "NitroHSM (20A0:4230)", - vid: "20A0", - pid: "4230", - }, - VendorData { - value: "nitrofido2", - label: "NitroFIDO2 (20A0:42D4)", - vid: "20A0", - pid: "42D4", - }, - VendorData { - value: "nitrostart", - label: "NitroStart (20A0:4211)", - vid: "20A0", - pid: "4211", - }, - VendorData { - value: "nitropro", - label: "NitroPro (20A0:4108)", - vid: "20A0", - pid: "4108", - }, - VendorData { - value: "nitro3", - label: "Nitrokey 3 (20A0:42B2)", - vid: "20A0", - pid: "42B2", - }, - VendorData { - value: "yubikey5", - label: "YubiKey 5 (1050:0407)", - vid: "1050", - pid: "0407", - }, - VendorData { - value: "yubikeyneo", - label: "YubiKey Neo (1050:0116)", - vid: "1050", - pid: "0116", - }, - VendorData { - value: "yubihsm", - label: "YubiHSM 2 (1050:0030)", - vid: "1050", - pid: "0030", - }, - VendorData { - value: "gnuk", - label: "Gnuk Token (234B:0000)", - vid: "234B", - pid: "0000", - }, - VendorData { - value: "gnupg", - label: "GnuPG (234B:0000)", - vid: "234B", - pid: "0000", - }, -]; +impl UsbIdentityPreset { + pub fn details(&self) -> (SharedString, Option<&'static str>, Option<&'static str>) { + match self { + Self::Custom => ("Custom (Manual Entry)".into(), None, None), + Self::Generic => ("Generic (FEFF:FCFD)".into(), Some("FEFF"), Some("FCFD")), + Self::PicoHsm => ( + "Pico Keys HSM (2E8A:10FD)".into(), + Some("2E8A"), + Some("10FD"), + ), + Self::PicoFido => ( + "Pico Keys Fido (2E8A:10FE)".into(), + Some("2E8A"), + Some("10FE"), + ), + Self::PicoOpenPgp => ( + "Pico Keys OpenPGP (2E8A:10FF)".into(), + Some("2E8A"), + Some("10FF"), + ), + Self::Pico => ("Pico (2E8A:0003)".into(), Some("2E8A"), Some("0003")), + Self::SoloKeys => ("SoloKeys (0483:A2CA)".into(), Some("0483"), Some("A2CA")), + Self::NitroHsm => ("NitroHSM (20A0:4230)".into(), Some("20A0"), Some("4230")), + Self::NitroFido2 => ("NitroFIDO2 (20A0:42D4)".into(), Some("20A0"), Some("42D4")), + Self::NitroStart => ("NitroStart (20A0:4211)".into(), Some("20A0"), Some("4211")), + Self::NitroPro => ("NitroPro (20A0:4108)".into(), Some("20A0"), Some("4108")), + Self::NitroKey3 => ("Nitrokey 3 (20A0:42B2)".into(), Some("20A0"), Some("42B2")), + Self::YubiKey5 => ("YubiKey 5 (1050:0407)".into(), Some("1050"), Some("0407")), + Self::YubiKeyNeo => ("YubiKey Neo (1050:0116)".into(), Some("1050"), Some("0116")), + Self::YubiHsm2 => ("YubiHSM 2 (1050:0030)".into(), Some("1050"), Some("0030")), + Self::Gnuk => ("Gnuk Token (234B:0000)".into(), Some("234B"), Some("0000")), + Self::GnuPg => ("GnuPG (234B:0000)".into(), Some("234B"), Some("0000")), + } + } + + /// Helper to find a preset by VID/PID string matching + pub fn from_vid_pid(vid: &str, pid: &str) -> Self { + let vid = vid.to_uppercase(); + let pid = pid.to_uppercase(); + + match (vid.as_str(), pid.as_str()) { + ("FEFF", "FCFD") => Self::Generic, + ("2E8A", "10FD") => Self::PicoHsm, + ("2E8A", "10FE") => Self::PicoFido, + ("2E8A", "10FF") => Self::PicoOpenPgp, + ("2E8A", "0003") => Self::Pico, + ("0483", "A2CA") => Self::SoloKeys, + ("20A0", "4230") => Self::NitroHsm, + ("20A0", "42D4") => Self::NitroFido2, + ("20A0", "4211") => Self::NitroStart, + ("20A0", "4108") => Self::NitroPro, + ("20A0", "42B2") => Self::NitroKey3, + ("1050", "0407") => Self::YubiKey5, + ("1050", "0116") => Self::YubiKeyNeo, + ("1050", "0030") => Self::YubiHsm2, + ("234B", "0000") => Self::Gnuk, + _ => Self::Custom, + } + } + + pub fn all() -> &'static [Self] { + &[ + Self::Custom, + Self::Generic, + Self::PicoHsm, + Self::PicoFido, + Self::PicoOpenPgp, + Self::Pico, + Self::SoloKeys, + Self::NitroHsm, + Self::NitroFido2, + Self::NitroStart, + Self::NitroPro, + Self::NitroKey3, + Self::YubiKey5, + Self::YubiKeyNeo, + Self::YubiHsm2, + Self::Gnuk, + Self::GnuPg, + ] + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum LedDriverType { + PicoGpio = 1, + PimoroniRgb = 2, + Ws2812Neopixel = 3, + Esp32Neopixel = 5, +} + +impl LedDriverType { + pub fn label(&self) -> SharedString { + match self { + Self::PicoGpio => "Pico (Standard GPIO)".into(), + Self::PimoroniRgb => "Pimoroni (RGB)".into(), + Self::Ws2812Neopixel => "WS2812 (Neopixel)".into(), + Self::Esp32Neopixel => "ESP32 Neopixel".into(), + } + } + + /// Returns the u8 value expected by the firmware configuration + pub fn value(&self) -> u8 { + *self as u8 + } + + pub fn all() -> &'static [Self] { + &[ + Self::PicoGpio, + Self::PimoroniRgb, + Self::Ws2812Neopixel, + Self::Esp32Neopixel, + ] + } +} diff --git a/src/ui/views/config.rs b/src/ui/views/config.rs index 4b1b573..8f33c96 100644 --- a/src/ui/views/config.rs +++ b/src/ui/views/config.rs @@ -1,7 +1,7 @@ use crate::device::io; use crate::device::types::{AppConfigInput, FullDeviceStatus}; use crate::ui::components::{card::Card, page_view::PageView}; -use crate::ui::ui_types::VENDORS; +use crate::ui::ui_types::{LedDriverType, UsbIdentityPreset}; use gpui::*; use gpui_component::{ ActiveTheme, Disableable, Icon, Theme, @@ -14,48 +14,48 @@ use gpui_component::{ }; #[derive(Clone, PartialEq)] -struct VendorItem { - value: SharedString, +struct VendorSelectOption { + preset: UsbIdentityPreset, label: SharedString, } -impl SelectItem for VendorItem { - type Value = SharedString; +impl SelectItem for VendorSelectOption { + type Value = UsbIdentityPreset; fn title(&self) -> SharedString { self.label.clone() } fn value(&self) -> &Self::Value { - &self.value + &self.preset } } #[derive(Clone, PartialEq)] -struct DriverItem { - value: u8, +struct DriverSelectOption { + driver_type: LedDriverType, label: SharedString, } -impl SelectItem for DriverItem { - type Value = u8; +impl SelectItem for DriverSelectOption { + type Value = LedDriverType; fn title(&self) -> SharedString { self.label.clone() } fn value(&self) -> &Self::Value { - &self.value + &self.driver_type } } pub struct ConfigView { - vendor_select: Entity>>, + vendor_select: Entity>>, vid_input: Entity, pid_input: Entity, product_name_input: Entity, led_gpio_input: Entity, - led_driver_select: Entity>>, + led_driver_select: Entity>>, led_brightness_slider: Entity, led_dimmable: bool, led_steady: bool, @@ -75,34 +75,28 @@ impl ConfigView { ) -> Self { let config = device_status.as_ref().map(|s| &s.config); - let vendors: Vec = VENDORS + // Prepare Vendor Options + let vendors: Vec = UsbIdentityPreset::all() .iter() - .map(|v| VendorItem { - value: v.value.into(), - label: v.label.into(), + .map(|preset| { + let (label, _, _) = preset.details(); + VendorSelectOption { + preset: *preset, + label, + } }) .collect(); - let drivers = vec![ - DriverItem { - value: 1, - label: "Pico (Standard GPIO)".into(), - }, - DriverItem { - value: 2, - label: "Pimoroni (RGB)".into(), - }, - DriverItem { - value: 3, - label: "WS2812 (Neopixel)".into(), - }, - DriverItem { - value: 5, - label: "ESP32 Neopixel".into(), - }, - ]; + // Prepare Driver Options + let drivers: Vec = LedDriverType::all() + .iter() + .map(|driver| DriverSelectOption { + driver_type: *driver, + label: driver.label(), + }) + .collect(); - // Determine initial vendor selection + // Determine Initial State let current_vid: SharedString = config .map(|c| c.vid.clone().into()) .unwrap_or_else(|| "CAFE".into()); @@ -120,22 +114,15 @@ impl ConfigView { .unwrap_or_else(|| "10".into()); let current_brightness = config.map(|c| c.led_brightness as f32).unwrap_or(8.0); - let mut initial_vendor_idx = 0; // Default to first item (Custom) - let mut is_custom_vendor = true; + // Identify Preset + let initial_preset = UsbIdentityPreset::from_vid_pid(¤t_vid, ¤t_pid); + let is_custom_vendor = initial_preset == UsbIdentityPreset::Custom; - for (i, vendor) in VENDORS.iter().enumerate() { - // Check matching VID/PID, but skip the generic "custom" entries if they don't have specific values - if vendor.value == "custom" && vendor.vid.is_empty() { - continue; - } - if vendor.vid.eq_ignore_ascii_case(current_vid.as_ref()) - && vendor.pid.eq_ignore_ascii_case(current_pid.as_ref()) - { - initial_vendor_idx = i; - is_custom_vendor = false; - break; - } - } + // Find index for the SelectState + let initial_vendor_idx = UsbIdentityPreset::all() + .iter() + .position(|p| *p == initial_preset) + .unwrap_or(0); let vendor_select = cx.new(|cx| { SelectState::new( @@ -154,11 +141,17 @@ impl ConfigView { let led_gpio_input = cx.new(|cx| InputState::new(window, cx).default_value(current_led_gpio.clone())); - let _initial_driver_idx = config.and_then(|c| c.led_driver).unwrap_or(0) as usize; + // Identify Driver + let current_driver_val = config.and_then(|c| c.led_driver).unwrap_or(0); + let initial_driver_idx = LedDriverType::all() + .iter() + .position(|d| d.value() == current_driver_val) + .unwrap_or(0); + let led_driver_select = cx.new(|cx| { SelectState::new( drivers, - Some(gpui_component::IndexPath::default()), + Some(gpui_component::IndexPath::default().row(initial_driver_idx)), window, cx, ) @@ -168,18 +161,20 @@ impl ConfigView { &vendor_select, window, |this: &mut Self, _, event, window, cx| { - if let gpui_component::select::SelectEvent::Confirm(Some(value)) = event { - if let Some(vendor) = VENDORS.iter().find(|v| value == v.value) { - this.is_custom_vendor = vendor.value == "custom"; + if let gpui_component::select::SelectEvent::Confirm(Some(preset)) = event { + let (_, vid_opt, pid_opt) = preset.details(); - if !this.is_custom_vendor { - this.vid_input - .update(cx, |input, cx| input.set_value(vendor.vid, window, cx)); - this.pid_input - .update(cx, |input, cx| input.set_value(vendor.pid, window, cx)); - } - cx.notify(); + if let (Some(vid), Some(pid)) = (vid_opt, pid_opt) { + this.is_custom_vendor = false; + this.vid_input + .update(cx, |input, cx| input.set_value(vid, window, cx)); + this.pid_input + .update(cx, |input, cx| input.set_value(pid, window, cx)); + } else { + // Custom selected + this.is_custom_vendor = true; } + cx.notify(); } }, ) @@ -259,12 +254,15 @@ impl ConfigView { } } + // Get Driver Value let driver_idx = self.led_driver_select.read(cx).selected_index(cx); if let Some(idx) = driver_idx { - // Assuming values are 0, 1, 2 matches index - let val = idx.row as u8; - if Some(val) != current_config.led_driver { - changes.led_driver = Some(val); + // Map index back to enum value safely + if let Some(driver) = LedDriverType::all().get(idx.row) { + let val = driver.value(); + if Some(val) != current_config.led_driver { + changes.led_driver = Some(val); + } } }