Enhance configuration and logging setup

- Added Microsoft.Extensions.Configuration.Json and Serilog.Settings.Configuration packages.
- Configured Serilog to read settings from appsettings.json.
- Updated .gitignore to exclude database and upload files.
- Refactored MainWindow to improve key handling and menu structure.
- Introduced appsettings.example.json for logging configuration.
This commit is contained in:
HueByte
2026-02-19 02:53:11 +01:00
parent fe3a120134
commit 1e6ecab583
5 changed files with 96 additions and 37 deletions
+3 -1
View File
@@ -426,4 +426,6 @@ appsettings.*.json
!appsettings.example.json !appsettings.example.json
CLAUDE.md CLAUDE.md
*.db *.db
*.db-* *.db-*
src/EchoHub.Server/uploads/*
.claude/*
+8
View File
@@ -6,11 +6,19 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="10.0.3" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="10.0.3" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.3" />
<PackageReference Include="Serilog" Version="4.3.1" /> <PackageReference Include="Serilog" Version="4.3.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="10.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="Terminal.Gui" Version="2.0.0-develop.5027" /> <PackageReference Include="Terminal.Gui" Version="2.0.0-develop.5027" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
+8 -9
View File
@@ -6,6 +6,7 @@ using EchoHub.Client.UI;
using EchoHub.Core.Constants; using EchoHub.Core.Constants;
using EchoHub.Core.DTOs; using EchoHub.Core.DTOs;
using EchoHub.Core.Models; using EchoHub.Core.Models;
using Microsoft.Extensions.Configuration;
using Serilog; using Serilog;
using Terminal.Gui.App; using Terminal.Gui.App;
using Terminal.Gui.Views; using Terminal.Gui.Views;
@@ -26,16 +27,14 @@ public static class Program
public static void Main() public static void Main()
{ {
// Configure Serilog file logger // Configure Serilog from appsettings.json
var logDir = Path.Combine( var configuration = new ConfigurationBuilder()
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".echohub", "logs"); .SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false)
.Build();
Log.Logger = new LoggerConfiguration() Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug() .ReadFrom.Configuration(configuration)
.WriteTo.File(
Path.Combine(logDir, "echohub-.log"),
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 7,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
.CreateLogger(); .CreateLogger();
Log.Information("EchoHub client starting"); Log.Information("EchoHub client starting");
+55 -27
View File
@@ -3,18 +3,17 @@ using EchoHub.Client.Themes;
using EchoHub.Core.DTOs; using EchoHub.Core.DTOs;
using EchoHub.Core.Models; using EchoHub.Core.Models;
using Terminal.Gui.App; using Terminal.Gui.App;
using Terminal.Gui.Views;
using Terminal.Gui.ViewBase;
using Terminal.Gui.Drawing;
using Terminal.Gui.Configuration; using Terminal.Gui.Configuration;
using Terminal.Gui.Input; using Terminal.Gui.Input;
using Terminal.Gui.ViewBase;
using Terminal.Gui.Views;
namespace EchoHub.Client.UI; namespace EchoHub.Client.UI;
/// <summary> /// <summary>
/// Main Terminal.Gui window for the EchoHub chat client. /// Main Terminal.Gui window for the EchoHub chat client.
/// </summary> /// </summary>
public sealed class MainWindow : Window public sealed class MainWindow : Runnable
{ {
private readonly IApplication _app; private readonly IApplication _app;
private readonly ListView _channelList; private readonly ListView _channelList;
@@ -24,6 +23,11 @@ public sealed class MainWindow : Window
private readonly Label _statusLabel; private readonly Label _statusLabel;
private MenuBar _menuBar; private MenuBar _menuBar;
// Cached Key constants — compare via .KeyCode to avoid Key.Equals (which also checks Handled)
private static readonly Key EnterKey = Key.Enter;
private static readonly Key AltEnterKey = Key.Enter.WithAlt;
private static readonly Key CtrlCKey = Key.C.WithCtrl;
private readonly List<string> _channelNames = []; private readonly List<string> _channelNames = [];
private readonly Dictionary<string, List<string>> _channelMessages = []; private readonly Dictionary<string, List<string>> _channelMessages = [];
private string _currentChannel = string.Empty; private string _currentChannel = string.Empty;
@@ -72,8 +76,7 @@ public sealed class MainWindow : Window
public MainWindow(IApplication app) public MainWindow(IApplication app)
{ {
_app = app; _app = app;
Title = "EchoHub"; Arrangement = ViewArrangement.Fixed;
BorderStyle = LineStyle.None;
// Menu bar at the top // Menu bar at the top
_menuBar = BuildMenuBar(); _menuBar = BuildMenuBar();
@@ -122,10 +125,10 @@ public sealed class MainWindow : Window
_chatFrame.Add(_messageList); _chatFrame.Add(_messageList);
Add(_chatFrame); Add(_chatFrame);
// Bottom input area (multiline) // Bottom input area
var inputFrame = new FrameView var inputFrame = new FrameView
{ {
Title = "Message (Enter=send, Shift+Enter=newline)", Title = "Message (Enter=send, Alt+Enter=newline)",
X = 25, X = 25,
Y = Pos.Bottom(_chatFrame), Y = Pos.Bottom(_chatFrame),
Width = Dim.Fill(), Width = Dim.Fill(),
@@ -138,7 +141,6 @@ public sealed class MainWindow : Window
Y = 0, Y = 0,
Width = Dim.Fill(), Width = Dim.Fill(),
Height = Dim.Fill(), Height = Dim.Fill(),
Text = "",
WordWrap = true WordWrap = true
}; };
_inputField.KeyDown += OnInputKeyDown; _inputField.KeyDown += OnInputKeyDown;
@@ -159,6 +161,9 @@ public sealed class MainWindow : Window
// Apply our custom color schemes to all views // Apply our custom color schemes to all views
ApplyColorSchemes(); ApplyColorSchemes();
// Window-level key handling for Ctrl+S (send) and Ctrl+C (quit)
KeyDown += OnWindowKeyDown;
} }
/// <summary> /// <summary>
@@ -203,34 +208,43 @@ public sealed class MainWindow : Window
} }
// Combine user items with theme items, separated by a line // Combine user items with theme items, separated by a line
var userMenuChildren = new MenuItem[] var allUserItems = new List<View>
{ {
new MenuItem("_My Profile", "Open your profile panel", () => OnProfileRequested?.Invoke(), Key.Empty), new MenuItem("_My Profile", "Open your profile panel", () => OnProfileRequested?.Invoke(), Key.Empty),
new MenuItem("Set _Status...", "Set your status", () => OnStatusRequested?.Invoke(), Key.Empty), new MenuItem("Set _Status...", "Set your status", () => OnStatusRequested?.Invoke(), Key.Empty),
new Line()
}; };
allUserItems.AddRange(themeItems);
// Merge: user items + separator + theme items var menuBar = new MenuBar(
var allUserItems = new MenuItem[userMenuChildren.Length + 1 + themeItems.Count];
userMenuChildren.CopyTo(allUserItems, 0);
allUserItems[userMenuChildren.Length] = null!; // null separator
for (int i = 0; i < themeItems.Count; i++)
allUserItems[userMenuChildren.Length + 1 + i] = themeItems[i];
return new MenuBar(
[ [
new MenuBarItem("_File", new MenuBarItem("_File",
[ [
new MenuItem("_Quit", "Quit EchoHub", () => _app.RequestStop(), Key.Empty) new MenuItem("_Quit", "Quit EchoHub", () => _app.RequestStop(), Key.Empty)
]), ]),
new MenuBarItem("_Server", new MenuBarItem("_Server", new View[]
[ {
new MenuItem("_Connect...", "Connect to a server", () => OnConnectRequested?.Invoke(), Key.Empty), new MenuItem("_Connect...", "Connect to a server", () => OnConnectRequested?.Invoke(), Key.Empty),
new MenuItem("_Disconnect", "Disconnect from server", () => OnDisconnectRequested?.Invoke(), Key.Empty), new MenuItem("_Disconnect", "Disconnect from server", () => OnDisconnectRequested?.Invoke(), Key.Empty),
null!, new Line(),
new MenuItem("_Saved Servers...", "View saved servers", () => OnSavedServersRequested?.Invoke(), Key.Empty) new MenuItem("_Saved Servers...", "View saved servers", () => OnSavedServersRequested?.Invoke(), Key.Empty)
]), }),
new MenuBarItem("_User", allUserItems) new MenuBarItem("_User", allUserItems)
]); ]);
menuBar.X = 0;
menuBar.Y = 0;
menuBar.Width = Dim.Fill();
// Workaround: Make CommandView (title text area) mouse-transparent on each MenuBarItem.
// Without this, clicks on the text hit the CommandView sub-view whose Source propagates
// as a plain View — MenuBar.OnAccepting checks "sourceView is MenuBarItem" which fails.
// Making CommandView transparent lets clicks pass through to the MenuBarItem itself.
foreach (var mbi in menuBar.SubViews.OfType<MenuBarItem>())
{
mbi.CommandView.ViewportSettings |= ViewportSettingsFlags.TransparentMouse;
}
return menuBar;
} }
/// <summary> /// <summary>
@@ -261,13 +275,12 @@ public sealed class MainWindow : Window
private void OnInputKeyDown(object? sender, Key e) private void OnInputKeyDown(object? sender, Key e)
{ {
if (e == Key.Enter.WithShift) if (e.KeyCode == AltEnterKey.KeyCode)
{ {
// Shift+Enter: let TextView handle it (inserts newline) _inputField.InsertText("\n");
return; e.Handled = true;
} }
else if (e.KeyCode == EnterKey.KeyCode)
if (e == Key.Enter)
{ {
var text = _inputField.Text?.Trim() ?? string.Empty; var text = _inputField.Text?.Trim() ?? string.Empty;
if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(_currentChannel)) if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(_currentChannel))
@@ -277,6 +290,21 @@ public sealed class MainWindow : Window
} }
e.Handled = true; e.Handled = true;
} }
else if (e.KeyCode == CtrlCKey.KeyCode)
{
_app.RequestStop();
e.Handled = true;
}
}
private void OnWindowKeyDown(object? sender, Key e)
{
// Ctrl+C quits from anywhere
if (e.KeyCode == CtrlCKey.KeyCode)
{
_app.RequestStop();
e.Handled = true;
}
} }
/// <summary> /// <summary>
@@ -0,0 +1,22 @@
{
"Serilog": {
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "logs/echohub-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 7,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
]
}
}