Enhance the landing page and include the recent projects for faster opening
Some checks failed
Build / build (push) Has been cancelled
Some checks failed
Build / build (push) Has been cancelled
This commit is contained in:
12
Examples/Nodify.Calculator/Models/RecentProjectEntry.cs
Normal file
12
Examples/Nodify.Calculator/Models/RecentProjectEntry.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Nodify.Calculator.Models
|
||||||
|
{
|
||||||
|
public class RecentProjectEntry
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string ProjectName { get; set; } = string.Empty;
|
||||||
|
public string ProjectPath { get; set; } = string.Empty;
|
||||||
|
public DateTime LastOpened { get; set; } = DateTime.Now;
|
||||||
|
}
|
||||||
|
}
|
||||||
74
Examples/Nodify.Calculator/RecentProjectsStore.cs
Normal file
74
Examples/Nodify.Calculator/RecentProjectsStore.cs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using LiteDB;
|
||||||
|
using Nodify.Calculator.Models;
|
||||||
|
|
||||||
|
namespace Nodify.Calculator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Manages a per-user database of recently opened projects.
|
||||||
|
/// The DB lives in %APPDATA%/APIVisualExecutor/recentprojects.db
|
||||||
|
/// </summary>
|
||||||
|
public static class RecentProjectsStore
|
||||||
|
{
|
||||||
|
private static string DbPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||||
|
var dir = Path.Combine(appData, "APIVisualExecutor");
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
return Path.Combine(dir, "recentprojects.db");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all recent projects ordered by last-opened descending.
|
||||||
|
/// </summary>
|
||||||
|
public static List<RecentProjectEntry> GetAll()
|
||||||
|
{
|
||||||
|
using var db = new LiteDatabase(DbPath);
|
||||||
|
var col = db.GetCollection<RecentProjectEntry>("projects");
|
||||||
|
return col.FindAll().OrderByDescending(p => p.LastOpened).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or updates a project entry (matched by path).
|
||||||
|
/// </summary>
|
||||||
|
public static void Upsert(string projectName, string projectFilePath)
|
||||||
|
{
|
||||||
|
using var db = new LiteDatabase(DbPath);
|
||||||
|
var col = db.GetCollection<RecentProjectEntry>("projects");
|
||||||
|
var normalized = Path.GetFullPath(projectFilePath);
|
||||||
|
var existing = col.FindOne(p => p.ProjectPath == normalized);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
existing.ProjectName = projectName;
|
||||||
|
existing.LastOpened = DateTime.Now;
|
||||||
|
col.Update(existing);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
col.Insert(new RecentProjectEntry
|
||||||
|
{
|
||||||
|
ProjectName = projectName,
|
||||||
|
ProjectPath = normalized,
|
||||||
|
LastOpened = DateTime.Now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a project entry by path.
|
||||||
|
/// </summary>
|
||||||
|
public static void Remove(string projectFilePath)
|
||||||
|
{
|
||||||
|
using var db = new LiteDatabase(DbPath);
|
||||||
|
var col = db.GetCollection<RecentProjectEntry>("projects");
|
||||||
|
var normalized = Path.GetFullPath(projectFilePath);
|
||||||
|
col.DeleteMany(p => p.ProjectPath == normalized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,38 +2,195 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
Title="API Visual Executor"
|
Title="API Visual Executor"
|
||||||
Width="500"
|
Width="720"
|
||||||
Height="380"
|
Height="520"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
ResizeMode="NoResize"
|
ResizeMode="CanResizeWithGrip"
|
||||||
Background="#1E1E1E"
|
Background="#1E1E1E"
|
||||||
Foreground="White">
|
Foreground="White">
|
||||||
<Grid>
|
<Grid Margin="32,24">
|
||||||
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Width="360">
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<StackPanel Grid.Row="0" HorizontalAlignment="Center" Margin="0,0,0,6">
|
||||||
<TextBlock Text="API Visual Executor"
|
<TextBlock Text="API Visual Executor"
|
||||||
FontSize="26" FontWeight="Bold"
|
FontSize="28" FontWeight="Bold"
|
||||||
Foreground="#6366f1"
|
Foreground="#6366f1"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center" />
|
||||||
Margin="0 0 0 8" />
|
|
||||||
<TextBlock Text="Create or open a project to get started"
|
<TextBlock Text="Create or open a project to get started"
|
||||||
FontSize="13" Foreground="Gray"
|
FontSize="13" Foreground="Gray"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
Margin="0 0 0 30" />
|
Margin="0,4,0,0" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Action buttons -->
|
||||||
|
<StackPanel Grid.Row="1" Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center" Margin="0,14,0,14">
|
||||||
<Button Content="➕ New Project"
|
<Button Content="➕ New Project"
|
||||||
Height="48" FontSize="16"
|
Width="200" Height="42" FontSize="15"
|
||||||
Margin="0 0 0 14"
|
Margin="0,0,12,0"
|
||||||
Background="#6366f1" Foreground="White"
|
Background="#6366f1" Foreground="White"
|
||||||
BorderThickness="0" Cursor="Hand"
|
BorderThickness="0" Cursor="Hand"
|
||||||
Click="NewProject_Click" />
|
Click="NewProject_Click" />
|
||||||
|
|
||||||
<Button Content="📂 Open Project"
|
<Button Content="📂 Open Project"
|
||||||
Height="48" FontSize="16"
|
Width="200" Height="42" FontSize="15"
|
||||||
Margin="0 0 0 14"
|
|
||||||
Background="#3E3E42" Foreground="White"
|
Background="#3E3E42" Foreground="White"
|
||||||
BorderThickness="1" BorderBrush="#555"
|
BorderThickness="1" BorderBrush="#555"
|
||||||
Cursor="Hand"
|
Cursor="Hand"
|
||||||
Click="OpenProject_Click" />
|
Click="OpenProject_Click" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Recent projects grid -->
|
||||||
|
<Border Grid.Row="2" Background="#252526" CornerRadius="6"
|
||||||
|
BorderBrush="#3E3E42" BorderThickness="1"
|
||||||
|
Padding="0">
|
||||||
|
<Grid>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Text="Recent Projects"
|
||||||
|
FontSize="14" FontWeight="SemiBold"
|
||||||
|
Foreground="#DCDCDC" Margin="14,10,14,6" />
|
||||||
|
|
||||||
|
<!-- Empty state -->
|
||||||
|
<TextBlock Grid.Row="1" x:Name="EmptyLabel"
|
||||||
|
Text="No recent projects yet. Create or open a project to see it here."
|
||||||
|
Foreground="#777" FontSize="12"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||||
|
Visibility="Collapsed" />
|
||||||
|
|
||||||
|
<DataGrid Grid.Row="1" x:Name="RecentProjectsGrid"
|
||||||
|
AutoGenerateColumns="False"
|
||||||
|
IsReadOnly="True"
|
||||||
|
CanUserAddRows="False"
|
||||||
|
CanUserDeleteRows="False"
|
||||||
|
CanUserReorderColumns="False"
|
||||||
|
SelectionMode="Single"
|
||||||
|
HeadersVisibility="Column"
|
||||||
|
GridLinesVisibility="Horizontal"
|
||||||
|
HorizontalGridLinesBrush="#2D2D30"
|
||||||
|
Background="Transparent"
|
||||||
|
RowBackground="#252526"
|
||||||
|
AlternatingRowBackground="#2A2A2E"
|
||||||
|
BorderThickness="0"
|
||||||
|
Foreground="#DCDCDC"
|
||||||
|
FontSize="13"
|
||||||
|
Margin="6,0,6,6"
|
||||||
|
MouseDoubleClick="RecentProjectsGrid_MouseDoubleClick">
|
||||||
|
<DataGrid.ColumnHeaderStyle>
|
||||||
|
<Style TargetType="DataGridColumnHeader">
|
||||||
|
<Setter Property="Background" Value="#2D2D30" />
|
||||||
|
<Setter Property="Foreground" Value="#AAAAAA" />
|
||||||
|
<Setter Property="FontSize" Value="12" />
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
|
<Setter Property="Padding" Value="10,6" />
|
||||||
|
<Setter Property="BorderBrush" Value="#3E3E42" />
|
||||||
|
<Setter Property="BorderThickness" Value="0,0,0,1" />
|
||||||
|
</Style>
|
||||||
|
</DataGrid.ColumnHeaderStyle>
|
||||||
|
<DataGrid.RowStyle>
|
||||||
|
<Style TargetType="DataGridRow">
|
||||||
|
<Setter Property="Cursor" Value="Hand" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter Property="Background" Value="#333337" />
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsSelected" Value="True">
|
||||||
|
<Setter Property="Background" Value="#3E3E42" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.RowStyle>
|
||||||
|
<DataGrid.CellStyle>
|
||||||
|
<Style TargetType="DataGridCell">
|
||||||
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
|
<Setter Property="Padding" Value="10,6" />
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="DataGridCell">
|
||||||
|
<Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
|
||||||
|
<ContentPresenter VerticalAlignment="Center" />
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsSelected" Value="True">
|
||||||
|
<Setter Property="Background" Value="Transparent" />
|
||||||
|
<Setter Property="Foreground" Value="White" />
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.CellStyle>
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="Project Name" Binding="{Binding ProjectName}" Width="*">
|
||||||
|
<DataGridTextColumn.ElementStyle>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#E0E0E0" />
|
||||||
|
<Setter Property="FontWeight" Value="Medium" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
</Style>
|
||||||
|
</DataGridTextColumn.ElementStyle>
|
||||||
|
</DataGridTextColumn>
|
||||||
|
<DataGridTextColumn Header="Location" Binding="{Binding ProjectPath}" Width="2*">
|
||||||
|
<DataGridTextColumn.ElementStyle>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#999999" />
|
||||||
|
<Setter Property="TextTrimming" Value="CharacterEllipsis" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
</Style>
|
||||||
|
</DataGridTextColumn.ElementStyle>
|
||||||
|
</DataGridTextColumn>
|
||||||
|
<DataGridTextColumn Header="Last Opened" Binding="{Binding LastOpened, StringFormat='{}{0:MMM dd, yyyy hh:mm tt}'}" Width="160">
|
||||||
|
<DataGridTextColumn.ElementStyle>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#888888" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
</Style>
|
||||||
|
</DataGridTextColumn.ElementStyle>
|
||||||
|
</DataGridTextColumn>
|
||||||
|
<DataGridTemplateColumn Header="" Width="70">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Button Content="Open"
|
||||||
|
Click="OpenRecentProject_Click"
|
||||||
|
Background="#6366f1" Foreground="White"
|
||||||
|
BorderThickness="0" Cursor="Hand"
|
||||||
|
Padding="10,4" FontSize="12"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
<DataGridTemplateColumn Header="" Width="36">
|
||||||
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Button Content="✕"
|
||||||
|
Click="RemoveRecentProject_Click"
|
||||||
|
Background="Transparent" Foreground="#888"
|
||||||
|
BorderThickness="0" Cursor="Hand"
|
||||||
|
Padding="4,2" FontSize="12"
|
||||||
|
ToolTip="Remove from list"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center" />
|
||||||
|
</DataTemplate>
|
||||||
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
|
</DataGridTemplateColumn>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Footer hint -->
|
||||||
|
<TextBlock Grid.Row="3" Text="Double-click a project to open it quickly"
|
||||||
|
FontSize="11" Foreground="#555"
|
||||||
|
HorizontalAlignment="Center" Margin="0,8,0,0" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
|||||||
@@ -1,14 +1,32 @@
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
using Nodify.Calculator.Models;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace Nodify.Calculator
|
namespace Nodify.Calculator
|
||||||
{
|
{
|
||||||
public partial class StartupWindow : Window
|
public partial class StartupWindow : Window
|
||||||
{
|
{
|
||||||
|
public ObservableCollection<RecentProjectEntry> RecentProjects { get; } = new();
|
||||||
|
|
||||||
public StartupWindow()
|
public StartupWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
LoadRecentProjects();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadRecentProjects()
|
||||||
|
{
|
||||||
|
RecentProjects.Clear();
|
||||||
|
foreach (var entry in RecentProjectsStore.GetAll())
|
||||||
|
RecentProjects.Add(entry);
|
||||||
|
|
||||||
|
RecentProjectsGrid.ItemsSource = RecentProjects;
|
||||||
|
|
||||||
|
EmptyLabel.Visibility = RecentProjects.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NewProject_Click(object sender, RoutedEventArgs e)
|
private void NewProject_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -19,7 +37,7 @@ namespace Nodify.Calculator
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ProjectManager.CreateProject(dialog.ProjectName, dialog.ProjectDirectory);
|
ProjectManager.CreateProject(dialog.ProjectName, dialog.ProjectDirectory);
|
||||||
OpenMainWindow();
|
TrackAndOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenProject_Click(object sender, RoutedEventArgs e)
|
private void OpenProject_Click(object sender, RoutedEventArgs e)
|
||||||
@@ -35,6 +53,55 @@ namespace Nodify.Calculator
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ProjectManager.OpenProject(ofd.FileName);
|
ProjectManager.OpenProject(ofd.FileName);
|
||||||
|
TrackAndOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenRecentProject_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Button btn && btn.DataContext is RecentProjectEntry entry)
|
||||||
|
OpenRecentEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveRecentProject_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is Button btn && btn.DataContext is RecentProjectEntry entry)
|
||||||
|
{
|
||||||
|
RecentProjectsStore.Remove(entry.ProjectPath);
|
||||||
|
LoadRecentProjects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecentProjectsGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (RecentProjectsGrid.SelectedItem is RecentProjectEntry entry)
|
||||||
|
OpenRecentEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenRecentEntry(RecentProjectEntry entry)
|
||||||
|
{
|
||||||
|
if (!File.Exists(entry.ProjectPath))
|
||||||
|
{
|
||||||
|
var result = MessageBox.Show(
|
||||||
|
$"Project file not found:\n{entry.ProjectPath}\n\nRemove it from the list?",
|
||||||
|
"Project Not Found",
|
||||||
|
MessageBoxButton.YesNo,
|
||||||
|
MessageBoxImage.Warning);
|
||||||
|
|
||||||
|
if (result == MessageBoxResult.Yes)
|
||||||
|
{
|
||||||
|
RecentProjectsStore.Remove(entry.ProjectPath);
|
||||||
|
LoadRecentProjects();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProjectManager.OpenProject(entry.ProjectPath);
|
||||||
|
TrackAndOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TrackAndOpen()
|
||||||
|
{
|
||||||
|
RecentProjectsStore.Upsert(ProjectManager.ProjectName, ProjectManager.ProjectFilePath);
|
||||||
OpenMainWindow();
|
OpenMainWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user