@@ -119,6 +120,11 @@
///
private string _previewPassword = string.Empty;
+ ///
+ /// The slider value (0-100) for non-linear password length selection.
+ ///
+ private double _sliderValue;
+
///
protected override async Task OnInitializedAsync()
{
@@ -133,6 +139,9 @@
UseNonAmbiguousChars = PasswordSettings.UseNonAmbiguousChars
};
+ // Initialize slider value from password length
+ _sliderValue = PasswordLengthSlider.LengthToSlider(_workingSettings.Length);
+
await RefreshPreview();
}
@@ -156,10 +165,10 @@
///
private async Task HandleLengthInput(ChangeEventArgs e)
{
- int newLength;
- if (int.TryParse(e.Value?.ToString(), out newLength))
+ if (double.TryParse(e.Value?.ToString(), out var sliderValue))
{
- _workingSettings.Length = newLength;
+ _sliderValue = sliderValue;
+ _workingSettings.Length = PasswordLengthSlider.SliderToLength(sliderValue);
await RefreshPreview();
}
}
diff --git a/apps/server/AliasVault.Client/Main/Utilities/PasswordLengthSlider.cs b/apps/server/AliasVault.Client/Main/Utilities/PasswordLengthSlider.cs
new file mode 100644
index 000000000..d2e963500
--- /dev/null
+++ b/apps/server/AliasVault.Client/Main/Utilities/PasswordLengthSlider.cs
@@ -0,0 +1,76 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) aliasvault. All rights reserved.
+// Licensed under the AGPLv3 license. See LICENSE.md file in the project root for full license information.
+//
+// -----------------------------------------------------------------------
+
+namespace AliasVault.Client.Main.Utilities;
+
+///
+/// Utility functions for password length slider with non-linear scaling.
+///
+/// The slider uses a power curve to provide fine-grained control at lower values
+/// (where most users operate, e.g., 12-32 chars) and coarser control at higher values
+/// (64-256 chars).
+///
+/// This makes it easy to select common password lengths while still allowing
+/// very long passwords when needed.
+///
+public static class PasswordLengthSlider
+{
+ ///
+ /// Minimum password length.
+ ///
+ public const int MinPasswordLength = 8;
+
+ ///
+ /// Maximum password length.
+ ///
+ public const int MaxPasswordLength = 256;
+
+ ///
+ /// Slider minimum value (internal representation).
+ ///
+ public const double SliderMin = 0;
+
+ ///
+ /// Slider maximum value (internal representation).
+ ///
+ public const double SliderMax = 100;
+
+ ///
+ /// Exponent for the power curve.
+ /// Higher values = more precision at lower lengths.
+ /// 2.0 gives a good balance where ~50% slider = ~70 chars.
+ ///
+ private const double Exponent = 2.0;
+
+ ///
+ /// Convert a slider position (0-100) to an actual password length (8-256).
+ /// Uses a power curve for non-linear scaling.
+ ///
+ ///
The slider position (0-100).
+ ///
The password length (8-256).
+ public static int SliderToLength(double sliderValue)
+ {
+ var normalized = Math.Max(0, Math.Min(1, sliderValue / SliderMax));
+ var curved = Math.Pow(normalized, Exponent);
+ var length = MinPasswordLength + (curved * (MaxPasswordLength - MinPasswordLength));
+ return (int)Math.Round(length);
+ }
+
+ ///
+ /// Convert a password length (8-256) to a slider position (0-100).
+ /// Inverse of SliderToLength.
+ ///
+ ///
The password length (8-256).
+ ///
The slider position (0-100).
+ public static double LengthToSlider(int length)
+ {
+ var clampedLength = Math.Max(MinPasswordLength, Math.Min(MaxPasswordLength, length));
+ var normalized = (double)(clampedLength - MinPasswordLength) / (MaxPasswordLength - MinPasswordLength);
+ var curved = Math.Pow(normalized, 1.0 / Exponent);
+ return curved * SliderMax;
+ }
+}
diff --git a/apps/server/AliasVault.Client/wwwroot/css/tailwind.css b/apps/server/AliasVault.Client/wwwroot/css/tailwind.css
index d396f891f..43e7e09bb 100644
--- a/apps/server/AliasVault.Client/wwwroot/css/tailwind.css
+++ b/apps/server/AliasVault.Client/wwwroot/css/tailwind.css
@@ -1237,6 +1237,11 @@ video {
min-width: 100%;
}
+.min-w-max {
+ min-width: -moz-max-content;
+ min-width: max-content;
+}
+
.max-w-2xl {
max-width: 42rem;
}