Phase 1: versioning, menu bar, About dialog, right-click context menu
- Directory.Build.props sets Version=0.1.0 (semver pre-1.0 = beta) plus Authors / Product / RepositoryUrl, picked up by all three projects. - MainWindow gets a real menu bar (File / Server / Help) replacing the old toolbar. File: New endpoint / Import / Export / Backups (last three are stubs for the next phase) / Exit. Server: Settings / Restart service. Help: About. - Drop the Refresh button - the 3 s polling loop covers it. - DataGridRow gets a right-click context menu: Edit / Copy URL / toggle Enabled / Delete. - New About dialog reads AssemblyInformationalVersion at runtime and links jpaul.me + the GitHub repo via clickable hyperlinks. - Ctrl+N input binding for new-endpoint. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>0.1.0</Version>
|
||||
<Authors>Justin Paul</Authors>
|
||||
<Company>Justin Paul</Company>
|
||||
<Product>Webhook Server</Product>
|
||||
<Copyright>Copyright (c) Justin Paul</Copyright>
|
||||
<PackageProjectUrl>https://jpaul.me</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/recklessop/webhook-server</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -8,6 +8,9 @@
|
||||
mc:Ignorable="d"
|
||||
Title="Webhook Server" Height="600" Width="1000"
|
||||
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}">
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="N" Modifiers="Control" Command="{Binding AddEndpointCommand}"/>
|
||||
</Window.InputBindings>
|
||||
<DockPanel LastChildFill="True">
|
||||
<StatusBar DockPanel.Dock="Bottom">
|
||||
<StatusBarItem>
|
||||
@@ -19,21 +22,25 @@
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
|
||||
<ToolBar DockPanel.Dock="Top">
|
||||
<Button Content="Refresh" Command="{Binding RefreshCommand}"/>
|
||||
<Separator/>
|
||||
<Button Content="Add" Command="{Binding AddEndpointCommand}"/>
|
||||
<Button Content="Edit" Command="{Binding EditEndpointCommand}"
|
||||
IsEnabled="{Binding SelectedEndpoint, Converter={StaticResource NotNull}}"/>
|
||||
<Button Content="Delete" Command="{Binding DeleteEndpointCommand}"
|
||||
IsEnabled="{Binding SelectedEndpoint, Converter={StaticResource NotNull}}"/>
|
||||
<Separator/>
|
||||
<Button Content="Copy URL" Command="{Binding CopyEndpointUrlCommand}"
|
||||
IsEnabled="{Binding SelectedEndpoint, Converter={StaticResource NotNull}}"
|
||||
ToolTip="Copy the full webhook URL for the selected endpoint to the clipboard"/>
|
||||
<Separator/>
|
||||
<Button Content="Server Settings…" Command="{Binding EditServerSettingsCommand}"/>
|
||||
</ToolBar>
|
||||
<Menu DockPanel.Dock="Top">
|
||||
<MenuItem Header="_File">
|
||||
<MenuItem Header="_New endpoint…" Command="{Binding AddEndpointCommand}" InputGestureText="Ctrl+N"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="_Import config…" IsEnabled="False" ToolTip="Coming soon"/>
|
||||
<MenuItem Header="_Export config…" IsEnabled="False" ToolTip="Coming soon"/>
|
||||
<MenuItem Header="_Backups" IsEnabled="False" ToolTip="Coming soon"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="E_xit" Command="{Binding ExitCommand}"/>
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Server">
|
||||
<MenuItem Header="_Settings…" Command="{Binding EditServerSettingsCommand}"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="_Restart service" Command="{Binding RestartServiceCommand}"/>
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Help">
|
||||
<MenuItem Header="_About Webhook Server…" Command="{Binding ShowAboutCommand}"/>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@@ -53,6 +60,20 @@
|
||||
<DataGrid.RowStyle>
|
||||
<Style TargetType="DataGridRow">
|
||||
<EventSetter Event="MouseDoubleClick" Handler="OnRowDoubleClick"/>
|
||||
<Setter Property="ContextMenu">
|
||||
<Setter.Value>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="_Edit…" Command="{Binding DataContext.EditEndpointCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<MenuItem Header="_Copy URL" Command="{Binding DataContext.CopyEndpointUrlCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="Toggle _enabled"
|
||||
Command="{Binding DataContext.ToggleEnabledCommand, RelativeSource={RelativeSource AncestorType=Window}}"
|
||||
CommandParameter="{Binding}"/>
|
||||
<Separator/>
|
||||
<MenuItem Header="_Delete…" Command="{Binding DataContext.DeleteEndpointCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
|
||||
</ContextMenu>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</DataGrid.RowStyle>
|
||||
<DataGrid.Columns>
|
||||
|
||||
@@ -175,6 +175,41 @@ public sealed partial class MainViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RestartServiceAsync()
|
||||
{
|
||||
var ok = MessageBox.Show(
|
||||
"Restart the WebhookServer service? In-flight requests will be aborted.",
|
||||
"Restart service",
|
||||
MessageBoxButton.OKCancel,
|
||||
MessageBoxImage.Warning);
|
||||
if (ok != MessageBoxResult.OK) return;
|
||||
|
||||
try
|
||||
{
|
||||
await _client.RestartListenerAsync().ConfigureAwait(false);
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
await RefreshAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowError("Restart failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ShowAbout()
|
||||
{
|
||||
var dlg = new Views.AboutDialog { Owner = Application.Current.MainWindow };
|
||||
dlg.ShowDialog();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Exit()
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CopyEndpointUrl()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<Window x:Class="WebhookServer.Gui.Views.AboutDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="About Webhook Server"
|
||||
Height="320" Width="420"
|
||||
ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ShowInTaskbar="False">
|
||||
<Grid Margin="20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" Text="Webhook Server" FontSize="22" FontWeight="Bold"/>
|
||||
<TextBlock Grid.Row="1" x:Name="VersionText" Foreground="Gray" Margin="0,4,0,16"/>
|
||||
|
||||
<TextBlock Grid.Row="2" TextWrapping="Wrap">
|
||||
A Windows-native webhook server that runs PowerShell, cmd, or arbitrary
|
||||
executables in response to incoming HTTP requests.
|
||||
</TextBlock>
|
||||
|
||||
<StackPanel Grid.Row="3" Margin="0,16,0,0">
|
||||
<TextBlock>
|
||||
<Run Text="Created by"/>
|
||||
<Run Text="Justin Paul" FontWeight="SemiBold"/>
|
||||
</TextBlock>
|
||||
<TextBlock Margin="0,4,0,0">
|
||||
<Hyperlink NavigateUri="https://jpaul.me" RequestNavigate="OnHyperlink">https://jpaul.me</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock Margin="0,4,0,0">
|
||||
<Hyperlink NavigateUri="https://github.com/recklessop/webhook-server" RequestNavigate="OnHyperlink">github.com/recklessop/webhook-server</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
|
||||
<Button Grid.Row="5" Content="OK" Width="80" HorizontalAlignment="Right" IsDefault="True" IsCancel="True" Click="OnOk"/>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace WebhookServer.Gui.Views;
|
||||
|
||||
public partial class AboutDialog : Window
|
||||
{
|
||||
public AboutDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
var asm = Assembly.GetExecutingAssembly();
|
||||
var info = asm.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion
|
||||
?? asm.GetName().Version?.ToString()
|
||||
?? "0.0.0";
|
||||
VersionText.Text = $"Version {info}";
|
||||
}
|
||||
|
||||
private void OnHyperlink(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void OnOk(object sender, RoutedEventArgs e) => Close();
|
||||
}
|
||||
Reference in New Issue
Block a user