diff --git a/deno.lock b/deno.lock
index 0138200..d4489ae 100644
--- a/deno.lock
+++ b/deno.lock
@@ -3,7 +3,7 @@
"specifiers": {
"npm:@biomejs/biome@^2.3.11": "2.3.11",
"npm:@internationalized/date@^3.10.1": "3.10.1",
- "npm:@lucide/svelte@0.562": "0.562.0_svelte@5.46.1__acorn@8.15.0",
+ "npm:@lucide/svelte@0.561": "0.561.0_svelte@5.46.1__acorn@8.15.0",
"npm:@sveltejs/adapter-static@^3.0.6": "3.0.10_@sveltejs+kit@2.49.3__@sveltejs+vite-plugin-svelte@5.1.1___svelte@5.46.1____acorn@8.15.0___vite@6.4.1____picomatch@4.0.3__svelte@5.46.1___acorn@8.15.0__typescript@5.9.3__vite@6.4.1___picomatch@4.0.3__acorn@8.15.0_@sveltejs+vite-plugin-svelte@5.1.1__svelte@5.46.1___acorn@8.15.0__vite@6.4.1___picomatch@4.0.3_svelte@5.46.1__acorn@8.15.0_typescript@5.9.3_vite@6.4.1__picomatch@4.0.3",
"npm:@sveltejs/kit@^2.9.0": "2.49.3_@sveltejs+vite-plugin-svelte@5.1.1__svelte@5.46.1___acorn@8.15.0__vite@6.4.1___picomatch@4.0.3_svelte@5.46.1__acorn@8.15.0_typescript@5.9.3_vite@6.4.1__picomatch@4.0.3_acorn@8.15.0",
"npm:@sveltejs/vite-plugin-svelte@5": "5.1.1_svelte@5.46.1__acorn@8.15.0_vite@6.4.1__picomatch@4.0.3",
@@ -257,8 +257,8 @@
"@jridgewell/sourcemap-codec"
]
},
- "@lucide/svelte@0.562.0_svelte@5.46.1__acorn@8.15.0": {
- "integrity": "sha512-wDMULwtTFN2Sc/TFBm6gfuVCNb4Y5P9LDrwxNnUbV52+IEU7NXZmvxwXoz+vrrpad6Xupq+Hw5eUlqIHEGhouw==",
+ "@lucide/svelte@0.561.0_svelte@5.46.1__acorn@8.15.0": {
+ "integrity": "sha512-vofKV2UFVrKE6I4ewKJ3dfCXSV6iP6nWVmiM83MLjsU91EeJcEg7LoWUABLp/aOTxj1HQNbJD1f3g3L0JQgH9A==",
"dependencies": [
"svelte"
]
@@ -1093,7 +1093,7 @@
"dependencies": [
"npm:@biomejs/biome@^2.3.11",
"npm:@internationalized/date@^3.10.1",
- "npm:@lucide/svelte@0.562",
+ "npm:@lucide/svelte@0.561",
"npm:@sveltejs/adapter-static@^3.0.6",
"npm:@sveltejs/kit@^2.9.0",
"npm:@sveltejs/vite-plugin-svelte@5",
diff --git a/package.json b/package.json
index f920e70..786aa27 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "picoforge",
- "version": "0.1.4",
+ "version": "0.1.5",
"description": "",
"type": "module",
"scripts": {
@@ -17,7 +17,6 @@
},
"license": "MIT",
"dependencies": {
- "@lucide/svelte": "^0.562.0",
"@tailwindcss/vite": "^4.1.18",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-opener": "^2",
@@ -27,6 +26,7 @@
"devDependencies": {
"@biomejs/biome": "^2.3.11",
"@internationalized/date": "^3.10.1",
+ "@lucide/svelte": "^0.561.0",
"@sveltejs/adapter-static": "^3.0.6",
"@sveltejs/kit": "^2.9.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index ba8414b..cd97a6c 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -2949,7 +2949,7 @@ dependencies = [
[[package]]
name = "picoforge"
-version = "0.1.3"
+version = "0.1.4"
dependencies = [
"anyhow",
"bitflags 2.10.0",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index d5a78e0..e371d40 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "picoforge"
-version = "0.1.4"
+version = "0.1.5"
description = "An open source commissioning tool for Pico FIDO security keys. Developed with Rust, Tauri, and Svelte."
authors = ["Suyog Tandel"]
license = "AGPL-3.0"
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 29a8097..ed9a28b 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "picoforge",
- "version": "0.1.4",
+ "version": "0.1.5",
"identifier": "in.suyogtandel.picoforge",
"build": {
"beforeDevCommand": "deno task dev",
@@ -19,7 +19,7 @@
"minHeight": 250,
"resizable": true,
"decorations": false,
- "transparent": true
+ "transparent": false
}
],
"security": {
diff --git a/src/lib/components/dialogs/messageDialog.svelte b/src/lib/components/dialogs/messageDialog.svelte
new file mode 100644
index 0000000..1dd29d5
--- /dev/null
+++ b/src/lib/components/dialogs/messageDialog.svelte
@@ -0,0 +1,22 @@
+
+
+
+
+
+ {state.dialogTitle}
+
+ {state.dialogMessage}
+
+
+
+ (state.dialogOpen = false)}>Okay
+
+
+
diff --git a/src/lib/components/dialogs/minPinDialog.svelte b/src/lib/components/dialogs/minPinDialog.svelte
new file mode 100644
index 0000000..c50b8ca
--- /dev/null
+++ b/src/lib/components/dialogs/minPinDialog.svelte
@@ -0,0 +1,45 @@
+
+
+
+
+
+ Update Minimum PIN Length
+
+ Set the minimum allowed PIN length (4-63 characters) and enter a new PIN that meets this requirement.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {#if state.minPinError}
+
{state.minPinError}
+ {/if}
+
+
+ (state.minPinDialogOpen = false)}>Cancel
+ Update
+
+
+
diff --git a/src/lib/components/dialogs/setPinDialog.svelte b/src/lib/components/dialogs/setPinDialog.svelte
new file mode 100644
index 0000000..71f4d09
--- /dev/null
+++ b/src/lib/components/dialogs/setPinDialog.svelte
@@ -0,0 +1,44 @@
+
+
+
+
+
+ {state.isSettingPin ? "Set PIN" : "Change PIN"}
+
+ {state.isSettingPin
+ ? "Set a PIN for your FIDO2 device. Minimum length is " + (state.minPinLength || 4) + " characters."
+ : "Enter your current PIN and choose a new one."}
+
+
+
+ {#if !state.isSettingPin}
+
+
+
+
+ {/if}
+
+
+
+
+
+
+
+
+ {#if state.pinError}
+
{state.pinError}
+ {/if}
+
+
+ (state.setPinDialogOpen = false)}>Cancel
+ state.handlePinChange()}>Confirm
+
+
+
diff --git a/src/lib/device/manager.svelte.ts b/src/lib/device/manager.svelte.ts
index 21a86dd..73574ce 100644
--- a/src/lib/device/manager.svelte.ts
+++ b/src/lib/device/manager.svelte.ts
@@ -1,5 +1,5 @@
import { invoke } from "@tauri-apps/api/core";
-import { logger } from "$lib/utils/log.svelte";
+import { logger } from "$lib/services/log.svelte";
import { DEFAULT_CONFIG, DEFAULT_DEVICE_INFO, VENDORS } from "$lib/device/constants.svelte";
import type {
DeviceConfig,
diff --git a/src/lib/layout/sidebar.svelte b/src/lib/layout/sidebar.svelte
new file mode 100644
index 0000000..8ca4fc6
--- /dev/null
+++ b/src/lib/layout/sidebar.svelte
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+

+
PicoForge
+
+
+
+
+
+ Menu
+
+
+ {#each menuItems as item}
+
+ onViewChange(item.view)}>
+
+ {item.label}
+
+
+ {/each}
+
+
+
+
+
+
+
+
+ Device Status
+ {#if device.connected}
+ Online
+ {:else}
+ Offline
+ {/if}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {@render children()}
+
+
+
diff --git a/src/lib/utils/log.svelte.ts b/src/lib/services/log.svelte.ts
similarity index 100%
rename from src/lib/utils/log.svelte.ts
rename to src/lib/services/log.svelte.ts
diff --git a/src/lib/state/configState.svelte.ts b/src/lib/state/configState.svelte.ts
new file mode 100644
index 0000000..b4a3c18
--- /dev/null
+++ b/src/lib/state/configState.svelte.ts
@@ -0,0 +1,108 @@
+import { device } from "$lib/device/manager.svelte";
+
+class configState {
+ setPinDialogOpen = $state(false);
+ currentPin = $state("");
+ newPin = $state("");
+ confirmPin = $state("");
+ pinError = $state("");
+ isSettingPin = $state(false);
+
+ minPinDialogOpen = $state(false);
+ minPinCurrentPin = $state("");
+ minPinNewPin = $state("");
+ minPinConfirmPin = $state("");
+ minPinLength = $state(4);
+ minPinError = $state("");
+
+ dialogOpen = $state(false);
+ dialogTitle = $state("");
+ dialogMessage = $state("");
+
+ async handleSave() {
+ const result = await device.save();
+ if (result) {
+ this.showStatusDialog(result.success ? "Success" : "Write Failed", result.msg);
+ }
+ }
+
+ showStatusDialog(title: string, message: string) {
+ this.dialogTitle = title;
+ this.dialogMessage = message;
+ this.dialogOpen = true;
+ }
+
+ openPinDialog() {
+ this.pinError = "";
+ this.currentPin = "";
+ this.newPin = "";
+ this.confirmPin = "";
+ if (device.fidoInfo?.options?.clientPin) {
+ this.isSettingPin = false;
+ } else {
+ this.isSettingPin = true;
+ }
+ this.setPinDialogOpen = true;
+ }
+
+ async openMinPinDialog() {
+ this.minPinError = "";
+ this.minPinCurrentPin = "";
+ this.minPinNewPin = "";
+ this.minPinConfirmPin = "";
+ this.minPinLength = device.fidoInfo?.minPinLength || 4;
+ this.minPinDialogOpen = true;
+ }
+
+ async handlePinChange() {
+ this.pinError = "";
+ if (this.newPin.length < (device.fidoInfo?.minPinLength || 4)) {
+ this.pinError = `PIN must be at least ${device.fidoInfo?.minPinLength || 4} characters`;
+ return;
+ }
+ if (this.newPin !== this.confirmPin) {
+ this.pinError = "PINs do not match";
+ return;
+ }
+ const result = await device.changePin(this.isSettingPin ? null : this.currentPin, this.newPin);
+ if (result.success) {
+ this.setPinDialogOpen = false;
+ this.showStatusDialog("Success", this.isSettingPin ? "PIN set successfully" : "PIN changed successfully");
+ } else {
+ this.pinError = result.msg as string;
+ }
+ }
+
+ async handleMinPinChange() {
+ this.minPinError = "";
+ if (this.minPinLength < 4 || this.minPinLength > 63) {
+ this.minPinError = "Minimum PIN length must be between 4 and 63";
+ return;
+ }
+ if (this.minPinNewPin.length < this.minPinLength) {
+ this.minPinError = `New PIN must be at least ${this.minPinLength} characters`;
+ return;
+ }
+ if (this.minPinNewPin !== this.minPinConfirmPin) {
+ this.minPinError = "New PINs do not match";
+ return;
+ }
+ const minLengthResult = await device.updateMinPinLength(this.minPinCurrentPin, this.minPinLength);
+ if (!minLengthResult.success) {
+ this.minPinError = minLengthResult.msg as string;
+ return;
+ }
+ const pinResult = await device.changePin(this.minPinCurrentPin, this.minPinNewPin);
+ if (pinResult.success) {
+ this.minPinDialogOpen = false;
+ this.showStatusDialog(
+ "Success",
+ `Minimum PIN length updated to ${this.minPinLength} and PIN changed successfully`,
+ );
+ } else {
+ this.minPinError = pinResult.msg as string;
+ }
+ }
+}
+
+export const configViewState = new configState();
diff --git a/src/lib/views/aboutView.svelte b/src/lib/views/aboutView.svelte
new file mode 100644
index 0000000..d09b669
--- /dev/null
+++ b/src/lib/views/aboutView.svelte
@@ -0,0 +1,52 @@
+
+
+
+
+
About
+
+
+
+
+
+
+

+
+
+
PicoForge
+
v0.1.5-Alpha
+
+ An open source commissioning tool for Pico FIDO security keys. Developed with Rust, Tauri, and Svelte.
+
+
+
+
+ Code By: Suyog Tandel
+
+
+ Copyright:
+ © 2026 Suyog Tandel
+
+
+
+
+
+
+
+
+
+
diff --git a/src/lib/views/configView.svelte b/src/lib/views/configView.svelte
new file mode 100644
index 0000000..e1d6ef9
--- /dev/null
+++ b/src/lib/views/configView.svelte
@@ -0,0 +1,251 @@
+
+
+
+
+
Configuration
+
Customize device settings and behavior.
+
+
+ {#if !device.connected}
+
+
+ No Device Connected
+ Connect your device to access configuration options.
+
+ {:else}
+
+
+
+
+
+ PIN Management
+
+ Configure FIDO2 PIN security
+
+
+
+
+
Current PIN Status
+
+ {device.fidoInfo?.options?.clientPin ? "PIN is set" : "No PIN configured"}
+
+
+
+
+
+
+
+
Minimum PIN Length
+
+ Current: {device.fidoInfo?.minPinLength || 4} characters
+
+
+
+
+
+
+
+
+
+
+
+ Identity
+
+ USB Identification settings
+
+
+
+
+ device.setVendor(v)}
+ disabled={!device.connected}
+ >
+
+ {VENDORS.find((v) => v.value === device.selectedVendor)?.label ?? "Select a vendor"}
+
+
+ {#each VENDORS as vendor}
+
+ {vendor.label}
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ LED Settings
+
+ Adjust visual feedback behavior
+
+
+
+
+
+
+
+
+
+
+
+ {LED_DRIVERS.find((d) => d.value === device.config.ledDriver)?.label ?? "Select driver"}
+
+
+ {#each LED_DRIVERS as driver}
+
+ {driver.label}
+
+ {/each}
+
+
+
+
+
+
+
+
+
+
+ Level {device.config.ledBrightness}
+
+
+
+
+
+
+
Allow brightness adjustment
+
+
+
+
+
+
+
+
Keep LED on constantly
+
+
+
+
+
+
+
+
+
+
+ Touch & Timing
+
+ Configure interaction timeouts
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Device Options
+
+ Toggle advanced features
+
+
+
+
+
+
Restart device on reset
+
+
+
+
+
+
+
+
Does not work on Android!
+
+
+
+
+
+
+
+
+
+
+ {/if}
+
diff --git a/src/lib/views/homeView.svelte b/src/lib/views/homeView.svelte
new file mode 100644
index 0000000..5a15d07
--- /dev/null
+++ b/src/lib/views/homeView.svelte
@@ -0,0 +1,184 @@
+
+
+
+
+
Device Overview
+
Quick view of your device status and specifications.
+
+
+ {#if !device.connected}
+
+
+ No Device Connected
+ Please connect your device and click Refresh to begin.
+
+ {:else}
+
+
+
+
+
+ Device Information
+
+
+
+
+
+
Serial Number
+
{device.info.serial}
+
+
+
Firmware Version
+
v{device.info.firmwareVersion}
+
+
+
VID:PID
+
{device.config.vid}:{device.config.pid}
+
+
+
Product Name
+
{device.config.productName}
+
+
+
+
+
+
+
+ Flash Memory
+
+ {device.info.flashUsed} / {device.info.flashTotal} KB
+
+
+
+
+
+
+
+
+
+
+
+ FIDO2 Information
+
+
+
+ {#if device.fidoInfo}
+
+
+
FIDO Version
+
{device.fidoInfo.versions[0] || "N/A"}
+
+
+
PIN Set
+
+ {device.fidoInfo.options?.clientPin ? "Yes" : "No"}
+
+
+
+
Min PIN Length
+
{device.fidoInfo.minPinLength}
+
+
+
Resident Keys
+
+ {device.fidoInfo.options?.rk ? "Supported" : "Not Supported"}
+
+
+
+
+
+
+
+
AAGUID
+
{device.fidoInfo.aaguid}
+
+ {:else}
+ FIDO information not available
+ {/if}
+
+
+
+
+
+
+
+ LED Configuration
+
+
+
+
+ LED GPIO Pin
+ GPIO {device.config.ledGpio}
+
+
+ LED Brightness
+ {device.config.ledBrightness}
+
+
+ Presence Touch Timeout
+ {device.config.touchTimeout}s
+
+
+ LED Dimmable
+
+ {device.config.ledDimmable ? "Yes" : "No"}
+
+
+
+ LED Steady Mode
+
+ {device.config.ledSteady ? "On" : "Off"}
+
+
+
+
+
+
+
+
+
+ Security Status
+
+
+
+
+
Boot Mode
+
+ {#if device.security.secureBoot}
+
+ {:else}
+
+ {/if}
+
+ {device.security.secureBoot ? "Secure Boot" : "Development"}
+
+
+
+
+ Debug Interface
+
+ {device.security.secureLock ? "Read-out Locked" : "Debug Enabled"}
+
+
+
+ Secure Lock
+
+ {device.security.confirmed ? "Acknowledged" : "Pending"}
+
+
+
+
+
+ {/if}
+
diff --git a/src/lib/views/logsView.svelte b/src/lib/views/logsView.svelte
new file mode 100644
index 0000000..7535500
--- /dev/null
+++ b/src/lib/views/logsView.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+
+
System Logs
+
Real-time device communication and application events.
+
+
+
+
+
+
+ {#if logger.logs.length === 0}
+
+
+
No events recorded yet.
+
+ {:else}
+
+
+ {#each logger.logs as log}
+
+ [{log.timestamp}]
+
+ {#if log.type === "success"}➜{/if}
+ {#if log.type === "error"}✖{/if}
+ {log.message}
+
+
+ {/each}
+
+
+ {/if}
+
+
+
diff --git a/src/lib/views/securityView.svelte b/src/lib/views/securityView.svelte
new file mode 100644
index 0000000..29964b7
--- /dev/null
+++ b/src/lib/views/securityView.svelte
@@ -0,0 +1,72 @@
+
+
+
+
+
Secure Boot
+
Permanently lock this device to the current firmware vendor.
+
+
+
+
+ Feature Unstable
+ This feature is currently under work and disabled for safety.
+
+
+
+
+ Lock Settings
+
+
+
+
+
+
Verifies firmware signature on startup
+
+
+
+
+
+
+
+
Prevents reading key material via debug ports
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 1860cf5..7f1a6ac 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -1,84 +1,27 @@
-
-
-
-
-
-
-
-
+
+
+