merge branch 'Design&Fix'

This commit is contained in:
2026-03-01 19:49:07 +03:00
6 changed files with 236 additions and 153 deletions

View File

@@ -0,0 +1,93 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="KeyKeeper.Views.CreateVaultFileWindow"
Title="Create New Vault"
Background="#fff"
Icon="/Assets/icon.ico"
Width="600" Height="450"
WindowStartupLocation="CenterOwner"
CanResize="False"
x:Name="ThisWindow">
<Grid ColumnDefinitions="1.5*, 2*">
<!-- Левая синяя панель -->
<Border Background="#2328C4" Grid.Column="0">
<StackPanel VerticalAlignment="Center" Margin="20">
<TextBlock Text="Choose where to save your password database and set a master password"
TextWrapping="Wrap"
Foreground="#E0E0FF"
FontSize="20"
TextAlignment="Center"/>
</StackPanel>
</Border>
<!-- Правая белая панель с формой -->
<ScrollViewer Grid.Column="1" VerticalScrollBarVisibility="Auto">
<StackPanel Margin="30" VerticalAlignment="Center" Spacing="20">
<TextBlock Text="Create new vault"
FontSize="22"
FontWeight="Bold"
Foreground="#2328C4"/>
<!-- Выбор файла -->
<StackPanel Spacing="10">
<TextBlock Text="File location" FontWeight="SemiBold" Foreground="Black" />
<Grid ColumnDefinitions="*, Auto" ColumnSpacing="10">
<TextBox x:Name="FilePathTextBox"
Grid.Column="0"
Watermark="Select file path..."
Padding="10,8"/>
<Button x:Name="BrowseButton"
Grid.Column="1"
Content="Browse..."
Classes="secondaryButton"
Padding="15,8"
Click="BrowseButton_Click"/>
</Grid>
<TextBlock x:Name="PathWarning"
FontSize="12"
Foreground="Orange"
Text=" "/>
</StackPanel>
<!-- Ввод мастер-пароля -->
<StackPanel Spacing="10">
<TextBlock Text="Master password" FontWeight="SemiBold" Foreground="Black" />
<TextBox x:Name="PasswordBox"
PasswordChar="*"
Watermark="Enter password"
Padding="10,8"/>
</StackPanel>
<StackPanel Spacing="10">
<TextBlock Text="Confirm password" FontWeight="SemiBold" Foreground="Black" />
<TextBox x:Name="ConfirmPasswordBox"
PasswordChar="*"
Watermark="Confirm password"
Padding="10,8"/>
</StackPanel>
<!-- Сообщение об ошибке пароля -->
<TextBlock x:Name="PasswordErrorText"
FontSize="12"
Foreground="Red"
Text=""
IsVisible="False"/>
<!-- Кнопки действий -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="10" Margin="0,20,0,0">
<Button Content="Cancel"
Classes="secondaryButton"
Width="80"
Click="CancelButton_Click"/>
<Button x:Name="CreateButton"
Content="Create"
Classes="accentButton"
Width="80"
IsEnabled="False"
Click="CreateButton_Click"/>
</StackPanel>
</StackPanel>
</ScrollViewer>
</Grid>
</Window>

View File

@@ -0,0 +1,124 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using System;
using System.Threading.Tasks;
namespace KeyKeeper.Views
{
public partial class CreateVaultFileWindow : Window
{
public string FilePath { get; private set; } = string.Empty;
public string Password { get; private set; } = string.Empty;
public bool Success { get; private set; }
public CreateVaultFileWindow()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
FilePathTextBox.TextChanged += OnTextChanged;
PasswordBox.TextChanged += OnPasswordTextChanged;
ConfirmPasswordBox.TextChanged += OnPasswordTextChanged;
}
private async void OnTextChanged(object? sender, TextChangedEventArgs e)
{
UpdateCreateButtonState();
PathWarning.Text = "";
string path = FilePathTextBox.Text ?? "";
if (string.IsNullOrWhiteSpace(path))
return;
try
{
var storageFile = await StorageProvider.TryGetFileFromPathAsync(path);
if (storageFile != null)
{
PathWarning.Text = "File already exists. It will be overwritten.";
}
}
catch
{
}
}
private void OnPasswordTextChanged(object? sender, TextChangedEventArgs e)
{
UpdateCreateButtonState();
PasswordErrorText.IsVisible = false;
}
private void UpdateCreateButtonState()
{
bool pathValid = !string.IsNullOrWhiteSpace(FilePathTextBox.Text);
bool passwordsEntered = !string.IsNullOrWhiteSpace(PasswordBox.Text) &&
!string.IsNullOrWhiteSpace(ConfirmPasswordBox.Text);
CreateButton.IsEnabled = pathValid && passwordsEntered;
}
private async void BrowseButton_Click(object? sender, RoutedEventArgs e)
{
var file = await StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
{
Title = "Create new password store",
SuggestedFileName = "passwords.kkp",
DefaultExtension = "kkp",
FileTypeChoices = new[]
{
new FilePickerFileType("KeyKeeper files")
{
Patterns = new[] { "*.kkp" }
}
}
});
if (file?.TryGetLocalPath() is string path)
{
FilePathTextBox.Text = path;
}
}
private void CreateButton_Click(object? sender, RoutedEventArgs e)
{
string path = FilePathTextBox.Text ?? "";
if (string.IsNullOrWhiteSpace(path))
return;
string password = PasswordBox.Text ?? "";
string confirm = ConfirmPasswordBox.Text ?? "";
if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(confirm))
{
ShowPasswordError("Password cannot be empty");
return;
}
if (password != confirm)
{
ShowPasswordError("Passwords don't match");
return;
}
FilePath = path;
Password = password;
Success = true;
Close();
}
private void ShowPasswordError(string message)
{
PasswordErrorText.Text = message;
PasswordErrorText.IsVisible = true;
}
private void CancelButton_Click(object? sender, RoutedEventArgs e)
{
Success = false;
Close();
}
}
}

View File

@@ -22,41 +22,26 @@ namespace KeyKeeper.Views
private async void CreateNewVault_Click(object sender, RoutedEventArgs e)
{
var file = await StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions
{
Title = "Create new password store",
SuggestedFileName = "passwords.kkp",
DefaultExtension = "kkp",
FileTypeChoices = new[]
{
new FilePickerFileType("KeyKeeper files")
{
Patterns = new[] { "*.kkp" }
}
}
});
var createVaultDialog = new CreateVaultFileWindow();
await createVaultDialog.ShowDialog(this);
if (file != null)
if (createVaultDialog.Success &&
!string.IsNullOrEmpty(createVaultDialog.FilePath) &&
!string.IsNullOrEmpty(createVaultDialog.Password))
{
if (file.TryGetLocalPath() is string path)
{
var passwordDialog = new PasswordDialog();
await passwordDialog.ShowDialog(this);
if (passwordDialog.Created && !string.IsNullOrEmpty(passwordDialog.Password))
var path = createVaultDialog.FilePath;
var password = createVaultDialog.Password;
var compositeKey = new CompositeKey(password, null);
var passStoreAccessor = new PassStoreFileAccessor(
filename: path,
create: true,
createOptions: new StoreCreationOptions()
{
var compositeKey = new CompositeKey(passwordDialog.Password, null);
var passStoreAccessor = new PassStoreFileAccessor(
filename: path,
create: true,
createOptions: new StoreCreationOptions()
{
Key = compositeKey,
LockTimeoutSeconds = 800
});
IPassStore passStore = passStoreAccessor;
OpenRepositoryWindow(passStore);
}
}
Key = compositeKey,
LockTimeoutSeconds = 800
});
IPassStore passStore = passStoreAccessor;
OpenRepositoryWindow(passStore);
}
}

View File

@@ -1,54 +0,0 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="KeyKeeper.Views.PasswordDialog"
Title="Создание хранилища"
Width="350"
Height="230">
<StackPanel Margin="20" VerticalAlignment="Center">
<TextBlock Text="Set Master Password"
FontSize="16"
FontWeight="Bold"
Margin="0,0,0,15"/>
<!-- Поле для пароля (звездочки) -->
<StackPanel Margin="0,0,0,10">
<TextBlock Text="Password:" Margin="0,0,0,5"/>
<TextBox x:Name="PasswordBox"
PasswordChar="*"
Width="250"/>
</StackPanel>
<!-- Поле для подтверждения (звездочки) -->
<StackPanel Margin="0,0,0,15">
<TextBlock Text="Confirm Password:" Margin="0,0,0,5"/>
<TextBox x:Name="ConfirmPasswordBox"
PasswordChar="*"
Width="250"/>
</StackPanel>
<!-- Сообщение об ошибке -->
<TextBlock x:Name="ErrorText"
Text="Passwords do not match"
Foreground="Red"
FontSize="12"
Margin="0,0,0,10"
IsVisible="False"/>
<!-- Кнопки -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="10">
<Button Content="Cancel"
Width="80"
Height="30"
Click="CancelButton_Click"/>
<Button Content="Create"
Width="80"
Height="30"
Click="CreateButton_Click"/>
</StackPanel>
</StackPanel>
</Window>

View File

@@ -1,65 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Interactivity;
namespace KeyKeeper.Views
{
public partial class PasswordDialog : Window
{
public string Password { get; private set; } = "";
public bool Created { get; private set; } = false;
public PasswordDialog()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void CreateButton_Click(object sender, RoutedEventArgs e)
{
var passwordBox = this.FindControl<TextBox>("PasswordBox");
var confirmBox = this.FindControl<TextBox>("ConfirmPasswordBox");
var errorText = this.FindControl<TextBlock>("ErrorText");
string password = passwordBox?.Text ?? "";
string confirmPassword = confirmBox?.Text ?? "";
if (string.IsNullOrEmpty(password) || string.IsNullOrEmpty(confirmPassword))
{
ShowError("Password cannot be empty");
return;
}
if (password != confirmPassword)
{
ShowError("Passwords don't match");
return;
}
Password = password;
Created = true;
Close();
}
private void ShowError(string message)
{
var errorText = this.FindControl<TextBlock>("ErrorText");
if (errorText != null)
{
errorText.Text = message;
errorText.IsVisible = true;
}
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
Created = false;
Close();
}
}
}

View File

@@ -28,7 +28,7 @@
Margin="0,0,0,20"/>
<!-- Рамочка -->
<Border BorderBrush="Gray"
<!-- <Border BorderBrush="Gray"
BorderThickness="1"
CornerRadius="5"
Padding="20"
@@ -42,7 +42,7 @@
HorizontalAlignment="Left"/>
</StackPanel>
</Border>
</Border> -->
<!-- Save Passwords -->
<Button Content="Save Passwords"
Classes="accentSidebarButton"