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 Logo + 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 Logo +
+ +

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 @@ -
-
-
- -
- - - - - + {#if currentView === "config"} + + {/if} + {#if currentView === "security"} + + {/if} + {#if currentView === "logs"} + + {/if} + {#if currentView === "about"} + + {/if} +
- + + -
- - -
- -
-
- {#if currentView === "home"} -
-
-

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} -
- {/if} - - {#if currentView === "config"} -
-
-

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} -
- {/if} - - {#if currentView === "security"} -
-
-

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

-
- -
- - - -
- - -
-
- - - -
-
- {/if} - - {#if currentView === "logs"} -
-
-
-

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} -
-
-
- {/if} - - {#if currentView === "about"} -
-
-

About

-
- - - -
-
- PicoForge Logo -
- -

PicoForge

- v0.1.4-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 -
-
- -
- - -
-
-
-
-
- {/if} -
-
-
-
-
- - - - - {dialogTitle} - - {dialogMessage} - - - - (dialogOpen = false)}>Okay - - - - - - - - {isSettingPin ? "Set PIN" : "Change PIN"} - - {isSettingPin - ? "Set a PIN for your FIDO2 device. Minimum length is " + (device.fidoInfo?.minPinLength || 4) + " characters." - : "Enter your current PIN and choose a new one."} - - -
- {#if !isSettingPin} -
- - -
- {/if} -
- - -
-
- - -
- {#if pinError} -

{pinError}

- {/if} -
- - (pinDialogOpen = false)}>Cancel - Confirm - -
-
- - - - - Update Minimum PIN Length - - Set the minimum allowed PIN length (4-63 characters) and enter a new PIN that meets this requirement. - - -
-
- - -
-
- - -
-
- - -
-
- - -
- {#if minPinError} -

{minPinError}

- {/if} -
- - (minPinDialogOpen = false)}>Cancel - Update - -
-
- + + +