mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 08:36:11 +02:00
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:
+3
-1
@@ -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/*
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user