Rename application to Orbit and simplify library status and undo

This commit is contained in:
Dario P. committed 2026-10-16 03:34:47 +02:00
1 parent 8be2445753
commit 02b273ecd5
19 files changed
+63 -41

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
<Application x:Class="ShortcutsPanel.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application x:Class="Orbit.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
+1 -1
View File
@@ -1,5 +1,5 @@
using System.Windows;
namespace ShortcutsPanel;
namespace Orbit;
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
+1 -1
View File
@@ -1,7 +1,7 @@
using System.IO;
using System.Windows.Media.Imaging;
namespace ShortcutsPanel;
namespace Orbit;
public static class CustomIcons
{
+1 -1
View File
@@ -2,7 +2,7 @@ using System.IO;
using System.Text.Json;
using System.Text.RegularExpressions;
namespace ShortcutsPanel;
namespace Orbit;
public sealed record LibraryExport(int Version, List<Shortcut> Links, List<string> Collections);
+11 -6
View File
@@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text.Json;
@@ -7,7 +7,7 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace ShortcutsPanel;
namespace Orbit;
public partial class MainWindow
{
@@ -72,7 +72,7 @@ public partial class MainWindow
if (choice == MessageBoxResult.Cancel) return;
var next = choice == MessageBoxResult.Yes ? LibraryOperations.Merge(entries, incoming.Links) : incoming.Links;
var order = choice == MessageBoxResult.Yes ? collectionOrder.Concat(incoming.Collections).Distinct().ToList() : incoming.Collections;
if (Save(next, order)) { deleted.Clear(); if (undoButton != null) undoButton.IsEnabled = false; dialog.Close(); }
if (Save(next, order)) { deleted.Clear(); UpdateUndoNotice(); dialog.Close(); }
}
catch (Exception ex) { MessageBox.Show(dialog, ex.Message, "Import failed — library unchanged"); }
}); import.Margin = new Thickness(0,12,0,18); panel.Children.Add(import);
@@ -83,14 +83,19 @@ public partial class MainWindow
var index = entries.FindIndex(x => x.Id == item.Id); if (index < 0) return false;
var next = entries.ToList(); next.RemoveAt(index);
if (!Save(next)) return false;
deleted.Push((item, index)); if (undoButton != null) undoButton.IsEnabled = true;
status.Text = $"Deleted {item.Name} · Undo is available"; return true;
deleted.Push((item, index)); UpdateUndoNotice(); return true;
}
void UpdateUndoNotice()
{
undoNotice.Visibility = deleted.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
status.Text = deleted.TryPeek(out var latest) ? $"Removed {latest.Item.Name}" : "";
if (undoButton != null) undoButton.IsEnabled = deleted.Count > 0;
}
void UndoDelete()
{
if (!deleted.TryPeek(out var restore)) return;
var next = entries.ToList(); next.Insert(Math.Min(restore.Index, next.Count), restore.Item);
if (Save(next)) { deleted.Pop(); if (undoButton != null) undoButton.IsEnabled = deleted.Count > 0; }
if (Save(next)) { deleted.Pop(); UpdateUndoNotice(); }
}
void ManageCollections()
{
+22 -14
View File
@@ -1,4 +1,4 @@
using System.Diagnostics;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Windows;
@@ -7,7 +7,7 @@ using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Net.Http;
namespace ShortcutsPanel;
namespace Orbit;
public record Shortcut(string Name, string Url, string Group, string Color, string? Icon = null, string Id = "");
public partial class MainWindow : Window
{
@@ -17,7 +17,9 @@ public partial class MainWindow : Window
readonly TextBlock heading = new() { FontSize = 30, FontWeight = FontWeights.SemiBold };
static readonly Dictionary<string, Task<BitmapSource?>> logos = new();
readonly TextBox search = new() { Width = 280, ToolTip = "Search by name, website, or collection" };
readonly TextBlock status = new() { Foreground = Brush("#939DB8"), FontSize = 12 };
readonly TextBlock websiteCount = new() { FontSize = 12, Foreground = Brush("#7B8377"), VerticalAlignment = VerticalAlignment.Center };
readonly TextBlock status = new() { FontSize = 12, VerticalAlignment = VerticalAlignment.Center, TextTrimming = TextTrimming.CharacterEllipsis };
Border undoNotice = null!;
List<Shortcut> entries = [];
string selectedGroup = "All";
List<string> collectionOrder = [];
@@ -31,7 +33,7 @@ public partial class MainWindow : Window
try { Theme.Apply(settings.LoadDarkTheme()); }
catch { Theme.Apply(false); }
Background = Brush("#FAF9F6"); Foreground = Brush("#252924"); FontFamily = new FontFamily("Segoe UI");
Title = "Orbit • Shortcuts Panel"; Width = 1100; Height = 720; MinWidth = 800; MinHeight = 520;
Title = "Orbit"; Width = 1100; Height = 720; MinWidth = 800; MinHeight = 520;
WindowStartupLocation = WindowStartupLocation.CenterScreen; WindowState = WindowState.Maximized;
var root = new Grid(); Content = root;
root.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(210) });
@@ -39,7 +41,7 @@ public partial class MainWindow : Window
var sidebar = new DockPanel { Margin = new Thickness(20, 32, 20, 24) };
var sideSurface = new Border { Background = Brush("#F1F2ED"), BorderBrush = Brush("#E2E5DD"), BorderThickness = new Thickness(0,0,1,0), Child = sidebar }; root.Children.Add(sideSurface);
var brand = new StackPanel { Margin = new Thickness(12, 0, 0, 36) };
brand.Children.Add(new TextBlock { Text = "◉ orbit", FontSize = 27, FontWeight = FontWeights.SemiBold, Foreground = Brush("#294B3C") });
brand.Children.Add(new TextBlock { Text = "◉ Orbit", FontSize = 27, FontWeight = FontWeights.SemiBold, Foreground = Brush("#294B3C") });
brand.Children.Add(new TextBlock { Text = "A little more organized.", FontSize = 11, Foreground = Brush("#80877D"), Margin = new Thickness(1, 6, 0, 0) });
DockPanel.SetDock(brand, Dock.Top); sidebar.Children.Add(brand);
var bottom = new StackPanel();
@@ -68,17 +70,23 @@ public partial class MainWindow : Window
var top = new StackPanel(); DockPanel.SetDock(top, Dock.Top); main.Children.Add(top);
var header = new DockPanel(); top.Children.Add(header);
var add = Button("+ Add website", () => Edit(null), "#294B3D"); add.Foreground = Brushes.White; add.VerticalAlignment = VerticalAlignment.Center; DockPanel.SetDock(add, Dock.Right); header.Children.Add(add);
header.Children.Add(heading);
var titleRow = new DockPanel();
var countBadge = new Border { Background = Brush("#DFE6DA"), CornerRadius = new CornerRadius(10), Padding = new Thickness(10,5,10,5), Margin = new Thickness(14,0,18,0), VerticalAlignment = VerticalAlignment.Center, Child = websiteCount };
DockPanel.SetDock(heading, Dock.Left); titleRow.Children.Add(heading); titleRow.Children.Add(countBadge);
countBadge.HorizontalAlignment = HorizontalAlignment.Left;
header.Children.Add(titleRow);
top.Children.Add(new TextBlock { Text = "Your favorite places, thoughtfully collected.", FontSize = 13, Foreground = Brush("#7B8377"), Margin = new Thickness(0, 9, 0, 26) });
search.Width = double.NaN; search.HorizontalAlignment = HorizontalAlignment.Stretch;
search.ToolTip = "Search websites and collections";
System.Windows.Automation.AutomationProperties.SetName(search, "Search websites");
var searchRow = new DockPanel { Margin = new Thickness(0, 0, 8, 28) };
var searchLabel = new TextBlock { Text = "Search", VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0,0,12,0), Foreground = Brush("#7B8377"), FontSize = 12 }; searchRow.Children.Add(searchLabel); searchRow.Children.Add(search); top.Children.Add(searchRow);
var footer = new DockPanel { Margin = new Thickness(0, 16, 8, 0) }; DockPanel.SetDock(footer, Dock.Bottom); main.Children.Add(footer);
undoButton = Button("Undo deletion", UndoDelete); undoButton.IsEnabled = false;
DockPanel.SetDock(undoButton, Dock.Right); footer.Children.Add(undoButton);
status.Foreground = Brush("#868D80"); footer.Children.Add(status);
var noticeRow = new DockPanel();
undoButton = Button("Undo", UndoDelete); undoButton.Margin = new Thickness(16,0,0,0);
DockPanel.SetDock(undoButton, Dock.Right); noticeRow.Children.Add(undoButton);
status.Foreground = Brush("#374632"); noticeRow.Children.Add(status);
undoNotice = new Border { Background = Brush("#DFE6DA"), BorderBrush = Brush("#E2E5DD"), BorderThickness = new Thickness(1), CornerRadius = new CornerRadius(10), Padding = new Thickness(14,8,8,8), Margin = new Thickness(0,12,8,0), MaxWidth = 460, HorizontalAlignment = HorizontalAlignment.Left, Visibility = Visibility.Collapsed, Child = noticeRow };
DockPanel.SetDock(undoNotice, Dock.Bottom); main.Children.Add(undoNotice);
main.Children.Add(new ScrollViewer { Content = cards, VerticalScrollBarVisibility = ScrollBarVisibility.Auto, HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled });
search.TextChanged += (_, _) => RenderCards();
Load(); Render();
@@ -126,9 +134,9 @@ public partial class MainWindow : Window
b.HorizontalContentAlignment = HorizontalAlignment.Stretch;
var row = new DockPanel { Width = 138 };
var number = new TextBlock { Text = count.ToString(), Foreground = Brush("#7B8377"), FontSize = 11, VerticalAlignment = VerticalAlignment.Center }; DockPanel.SetDock(number, Dock.Right); row.Children.Add(number);
row.Children.Add(new TextBlock { Text = name == "All" ? "▦ All websites" : "• " + name, FontSize = 12, TextTrimming = TextTrimming.CharacterEllipsis, Foreground = Brush("#374632") });
row.Children.Add(new TextBlock { Text = name == "All" ? "▦ All Websites" : "• " + name, FontSize = 12, TextTrimming = TextTrimming.CharacterEllipsis, Foreground = Brush("#374632") });
b.Content = row; b.Margin = new Thickness(0,0,0,6); groups.Children.Add(b);
System.Windows.Automation.AutomationProperties.SetName(b, name == "All" ? "All websites" : name);
System.Windows.Automation.AutomationProperties.SetName(b, name == "All" ? "All Websites" : name);
if (name != "All") ConfigureCollection(b, name);
}
RenderCards();
@@ -160,8 +168,8 @@ public partial class MainWindow : Window
cards.Children.Add(card); ConfigureCard(card, item);
}
if (filtered.Count == 0) cards.Children.Add(new TextBlock { Text = entries.Count == 0 ? "A fresh start. Add your first website above." : "No websites match. Try another search or collection.", FontSize = 18, Margin = new Thickness(0, 35, 0, 35) });
heading.Text = selectedGroup == "All" ? "All websites" : selectedGroup;
status.Text = $"{filtered.Count} websites · Opens in your default browser";
heading.Text = selectedGroup == "All" ? "All Websites" : selectedGroup;
websiteCount.Text = $"{filtered.Count} {(filtered.Count == 1 ? "Website" : "Websites")}";
}
static async Task ApplyLogo(Border badge, string url)
{
+5 -2
View File
@@ -6,8 +6,11 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<ApplicationIcon>app.ico</ApplicationIcon>
<AssemblyName>ShortcutsPanel</AssemblyName>
<Version>1.1.2</Version>
<AssemblyName>Orbit</AssemblyName>
<Version>1.1.3</Version>
<Product>Orbit</Product>
<Title>Orbit</Title>
<Description>Orbit website launcher</Description>
</PropertyGroup>
<ItemGroup><Compile Remove="tests/**/*.cs" /></ItemGroup>
<ItemGroup><EmbeddedResource Include="app.ico" LogicalName="OrbitIcon" /></ItemGroup>
+2 -2
View File
@@ -26,7 +26,7 @@ A portable C# / WPF website launcher for Windows.
## Run
Download the portable Windows x64 package from this repository's Releases page,
extract it, and run `ShortcutsPanel.exe`. No installer or separate .NET runtime
extract it, and run `Orbit.exe`. No installer or separate .NET runtime
is required. Windows 10 or Windows 11, 64-bit.
## Settings
@@ -44,7 +44,7 @@ the theme remains local. Undo history lasts for the current session.
Requires Windows and the .NET 8 SDK.
```powershell
dotnet publish ShortcutsPanel.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:DebugSymbols=false -o release
dotnet publish Orbit.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:DebugSymbols=false -o release
```
## Checks
+6 -4
View File
@@ -1,7 +1,7 @@
ORBIT 1.1.2 - WEBSITE SHORTCUTS
ORBIT 1.1.3 - WEBSITE SHORTCUTS
Created by Dario P.
Run ShortcutsPanel.exe on 64-bit Windows 10 or Windows 11.
Run Orbit.exe on 64-bit Windows 10 or Windows 11.
No installer or separate .NET installation is required.
NEW IN 1.1
@@ -20,7 +20,7 @@ NEW IN 1.1
Importing a library clears undo history. Undo history is not saved on exit.
- Open collection is available in Manage collections and the right-click menu.
Opening multiple sites asks for confirmation and opens each distinct URL once.
- About Orbit displays version 1.1.2 and author Dario P.
- About Orbit displays version 1.1.3 and author Dario P.
Add, edit, delete, search, and group website cards. Dark mode is optional.
Website icons are discovered from page metadata, then /favicon.ico.
@@ -44,7 +44,7 @@ The bundled .NET runtime extracts supporting files to temporary storage.
This executable is not digitally signed.
Build:
dotnet publish ShortcutsPanel.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:DebugSymbols=false -o release
dotnet publish Orbit.csproj -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=None -p:DebugSymbols=false -o release
Checks:
dotnet run --project tests/StorageChecks.csproj
@@ -54,3 +54,5 @@ dotnet run --project tests/ThemeChecks.csproj
1.1.1: aligned sidebar rows, whole-card dragging, and the Orbit icon in About.
1.1.2: starts maximized; green outer borders for drag/focus; Dark mode is at the sidebar bottom; About credits Dario P. and 2026.
1.1.3: Orbit naming throughout; website count beside the heading; contextual Undo notice; cleaner library layout.
+1 -1
View File
@@ -2,7 +2,7 @@ using System.IO;
using System.Text.Json;
using Microsoft.Win32;
namespace ShortcutsPanel;
namespace Orbit;
public sealed class SettingsStore
{
+2 -2
View File
@@ -1,4 +1,4 @@
using System.Windows;
using System.Windows;
using System.Windows.Media;
@@ -8,7 +8,7 @@ using System.ComponentModel;
namespace ShortcutsPanel;
namespace Orbit;
+1 -1
View File
@@ -5,7 +5,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Media.Imaging;
namespace ShortcutsPanel;
namespace Orbit;
public static class WebsiteIcons
{
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 46 KiB

+5 -1
View File
@@ -3,7 +3,7 @@ using System.Reflection;
using System.Text.Json;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ShortcutsPanel;
using Orbit;
internal static class FeatureChecks
{
@@ -17,6 +17,8 @@ internal static class FeatureChecks
var app = new App(); app.InitializeComponent();
Check(MainWindow.LoadAppIcon().PixelWidth > 0, "About icon loads from embedded resource");
var window = new MainWindow(store);
var notice = (System.Windows.Controls.Border)typeof(MainWindow).GetField("undoNotice", BindingFlags.NonPublic | BindingFlags.Instance)!.GetValue(window)!;
Check(window.Title == "Orbit" && notice.Visibility == System.Windows.Visibility.Collapsed, "Orbit title and hidden initial Undo notice");
object? Call(string method, params object?[] args) => typeof(MainWindow).GetMethod(method, BindingFlags.NonPublic | BindingFlags.Instance)!.Invoke(window, args);
try
{
@@ -31,7 +33,9 @@ internal static class FeatureChecks
Check(JsonSerializer.Deserialize<List<Shortcut>>(store.Load()!)![0].Name == "C", "Card order persists");
Check((bool)Call("DeleteShortcut", original[0])!, "Delete shortcut");
Check(JsonSerializer.Deserialize<List<Shortcut>>(store.Load()!)!.Count == 2, "Deletion saved");
Check(notice.Visibility == System.Windows.Visibility.Visible, "Undo notice appears after deletion");
Call("UndoDelete");
Check(notice.Visibility == System.Windows.Visibility.Collapsed, "Undo notice hides after restoring last deletion");
Check(JsonSerializer.Deserialize<List<Shortcut>>(store.Load()!)!.Select(x => x.Name).SequenceEqual(new[] { "C", "A", "B" }), "Undo restores original position");
var pngPath = Path.Combine(directory, "icon.png");
var bitmap = BitmapSource.Create(2, 2, 96, 96, PixelFormats.Bgra32, null, new byte[] { 0,0,255,255, 0,255,0,255, 255,0,0,255, 255,255,255,255 }, 8);
+1 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net8.0-windows</TargetFramework><UseWPF>true</UseWPF><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><EnableDefaultCompileItems>false</EnableDefaultCompileItems></PropertyGroup>
<ItemGroup><Compile Include="FeatureChecks.cs"/><ProjectReference Include="../ShortcutsPanel.csproj"/></ItemGroup>
<ItemGroup><Compile Include="FeatureChecks.cs"/><ProjectReference Include="../Orbit.csproj"/></ItemGroup>
</Project>
+1 -1
View File
@@ -1,5 +1,5 @@
using Microsoft.Win32;
using ShortcutsPanel;
using Orbit;
var key = @"Software\OrbitStorageChecks\" + Guid.NewGuid().ToString("N");
var folder = Path.Combine(Path.GetTempPath(), "OrbitStorageChecks-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(folder);
+1 -1
View File
@@ -3,7 +3,7 @@ using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.IO;
using ShortcutsPanel;
using Orbit;
internal static class ThemeChecks
{
+1 -1
View File
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net8.0-windows</TargetFramework><UseWPF>true</UseWPF><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable><EnableDefaultCompileItems>false</EnableDefaultCompileItems></PropertyGroup>
<ItemGroup><Compile Include="ThemeChecks.cs"/><ProjectReference Include="../ShortcutsPanel.csproj"/></ItemGroup>
<ItemGroup><Compile Include="ThemeChecks.cs"/><ProjectReference Include="../Orbit.csproj"/></ItemGroup>
</Project>