chore(ui): refactor config view data/types

This commit is contained in:
Suyog Tandel
2026-01-31 00:50:56 +05:30
parent 8603dcc2d2
commit e2aadff1d8
3 changed files with 203 additions and 219 deletions
-43
View File
@@ -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"
]
}
}
+138 -109
View File
@@ -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,
]
}
}
+65 -67
View File
@@ -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<SelectState<Vec<VendorItem>>>,
vendor_select: Entity<SelectState<Vec<VendorSelectOption>>>,
vid_input: Entity<InputState>,
pid_input: Entity<InputState>,
product_name_input: Entity<InputState>,
led_gpio_input: Entity<InputState>,
led_driver_select: Entity<SelectState<Vec<DriverItem>>>,
led_driver_select: Entity<SelectState<Vec<DriverSelectOption>>>,
led_brightness_slider: Entity<SliderState>,
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<VendorItem> = VENDORS
// Prepare Vendor Options
let vendors: Vec<VendorSelectOption> = 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<DriverSelectOption> = 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(&current_vid, &current_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);
}
}
}