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"
|
mc:Ignorable="d"
|
||||||
Title="Webhook Server" Height="600" Width="1000"
|
Title="Webhook Server" Height="600" Width="1000"
|
||||||
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}">
|
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}">
|
||||||
|
<Window.InputBindings>
|
||||||
|
<KeyBinding Key="N" Modifiers="Control" Command="{Binding AddEndpointCommand}"/>
|
||||||
|
</Window.InputBindings>
|
||||||
<DockPanel LastChildFill="True">
|
<DockPanel LastChildFill="True">
|
||||||
<StatusBar DockPanel.Dock="Bottom">
|
<StatusBar DockPanel.Dock="Bottom">
|
||||||
<StatusBarItem>
|
<StatusBarItem>
|
||||||
@@ -19,21 +22,25 @@
|
|||||||
</StatusBarItem>
|
</StatusBarItem>
|
||||||
</StatusBar>
|
</StatusBar>
|
||||||
|
|
||||||
<ToolBar DockPanel.Dock="Top">
|
<Menu DockPanel.Dock="Top">
|
||||||
<Button Content="Refresh" Command="{Binding RefreshCommand}"/>
|
<MenuItem Header="_File">
|
||||||
<Separator/>
|
<MenuItem Header="_New endpoint…" Command="{Binding AddEndpointCommand}" InputGestureText="Ctrl+N"/>
|
||||||
<Button Content="Add" Command="{Binding AddEndpointCommand}"/>
|
<Separator/>
|
||||||
<Button Content="Edit" Command="{Binding EditEndpointCommand}"
|
<MenuItem Header="_Import config…" IsEnabled="False" ToolTip="Coming soon"/>
|
||||||
IsEnabled="{Binding SelectedEndpoint, Converter={StaticResource NotNull}}"/>
|
<MenuItem Header="_Export config…" IsEnabled="False" ToolTip="Coming soon"/>
|
||||||
<Button Content="Delete" Command="{Binding DeleteEndpointCommand}"
|
<MenuItem Header="_Backups" IsEnabled="False" ToolTip="Coming soon"/>
|
||||||
IsEnabled="{Binding SelectedEndpoint, Converter={StaticResource NotNull}}"/>
|
<Separator/>
|
||||||
<Separator/>
|
<MenuItem Header="E_xit" Command="{Binding ExitCommand}"/>
|
||||||
<Button Content="Copy URL" Command="{Binding CopyEndpointUrlCommand}"
|
</MenuItem>
|
||||||
IsEnabled="{Binding SelectedEndpoint, Converter={StaticResource NotNull}}"
|
<MenuItem Header="_Server">
|
||||||
ToolTip="Copy the full webhook URL for the selected endpoint to the clipboard"/>
|
<MenuItem Header="_Settings…" Command="{Binding EditServerSettingsCommand}"/>
|
||||||
<Separator/>
|
<Separator/>
|
||||||
<Button Content="Server Settings…" Command="{Binding EditServerSettingsCommand}"/>
|
<MenuItem Header="_Restart service" Command="{Binding RestartServiceCommand}"/>
|
||||||
</ToolBar>
|
</MenuItem>
|
||||||
|
<MenuItem Header="_Help">
|
||||||
|
<MenuItem Header="_About Webhook Server…" Command="{Binding ShowAboutCommand}"/>
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -53,6 +60,20 @@
|
|||||||
<DataGrid.RowStyle>
|
<DataGrid.RowStyle>
|
||||||
<Style TargetType="DataGridRow">
|
<Style TargetType="DataGridRow">
|
||||||
<EventSetter Event="MouseDoubleClick" Handler="OnRowDoubleClick"/>
|
<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>
|
</Style>
|
||||||
</DataGrid.RowStyle>
|
</DataGrid.RowStyle>
|
||||||
<DataGrid.Columns>
|
<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]
|
[RelayCommand]
|
||||||
private void CopyEndpointUrl()
|
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