[feat:] forgot to git-add the wiki source files... here they are

This commit is contained in:
2026-01-13 18:33:35 +01:00
parent aeb4092f28
commit 99b35c0aaf
7 changed files with 279 additions and 0 deletions

8
Wiki/EditorWindow.axaml Normal file
View File

@@ -0,0 +1,8 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Logof_Client.Wiki.EditorWindow"
Title="Wiki Editor (disabled)" Width="400" Height="150">
<Grid>
<TextBlock Margin="12" TextWrapping="Wrap">The editor feature has been disabled. The Edit button remains for UI compatibility.</TextBlock>
</Grid>
</Window>

View File

@@ -0,0 +1,17 @@
using System;
using Avalonia.Controls;
namespace Logof_Client.Wiki;
public partial class EditorWindow : Window
{
private void InitializeComponent()
{
Avalonia.Markup.Xaml.AvaloniaXamlLoader.Load(this);
}
public EditorWindow()
{
InitializeComponent();
}
}

126
Wiki/MarkdownRenderer.cs Normal file
View File

@@ -0,0 +1,126 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Media;
using Markdig;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using System.Text;
namespace Logof_Client.Wiki;
public static class MarkdownRenderer
{
public static Control Render(string markdown)
{
var panel = new StackPanel { Spacing = 6 };
if (string.IsNullOrWhiteSpace(markdown)) return panel;
var doc = Markdown.Parse(markdown);
foreach (var block in doc)
{
switch (block)
{
case HeadingBlock hb:
{
var text = GetInlineText(hb.Inline);
var tb = new TextBlock
{
Text = text,
FontWeight = FontWeight.Bold,
Margin = new Avalonia.Thickness(0, hb.Level == 1 ? 6 : 2, 0, 2)
};
tb.FontSize = hb.Level switch { 1 => 22, 2 => 18, 3 => 16, _ => 14 };
panel.Children.Add(tb);
break;
}
case ParagraphBlock pb:
{
var text = GetInlineText(pb.Inline);
var tb = new TextBlock { Text = text, TextWrapping = Avalonia.Media.TextWrapping.Wrap };
panel.Children.Add(tb);
break;
}
case FencedCodeBlock cb:
{
var sb = new StringBuilder();
foreach (var line in cb.Lines.Lines)
{
sb.Append(line.ToString());
}
var codeBox = new TextBox
{
Text = sb.ToString(),
FontFamily = "Consolas, monospace",
IsReadOnly = true,
AcceptsReturn = true
};
panel.Children.Add(codeBox);
break;
}
case ListBlock lb:
{
var sp = new StackPanel { Spacing = 2 };
var number = 1;
foreach (var item in lb)
{
if (item is ListItemBlock lib)
{
var itemText = new StringBuilder();
foreach (var sub in lib)
{
if (sub is ParagraphBlock pp)
itemText.Append(GetInlineText(pp.Inline));
}
var tb = new TextBlock { Text = (lb.IsOrdered ? (number++ + ". ") : "• ") + itemText.ToString() };
sp.Children.Add(tb);
}
}
panel.Children.Add(sp);
break;
}
default:
{
// fallback: raw text
panel.Children.Add(new TextBlock { Text = block.ToString() });
break;
}
}
}
return panel;
}
private static string GetInlineText(ContainerInline? container)
{
if (container == null) return string.Empty;
var sb = new StringBuilder();
foreach (var inline in container)
{
switch (inline)
{
case LiteralInline li:
sb.Append(li.Content.ToString());
break;
case EmphasisInline ei:
sb.Append(GetInlineText(ei));
break;
case CodeInline ci:
sb.Append(ci.Content);
break;
case LinkInline li:
sb.Append(GetInlineText(li));
break;
default:
sb.Append(inline.ToString());
break;
}
}
return sb.ToString();
}
}

View File

@@ -0,0 +1,13 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Logof_Client.Wiki.NamePromptWindow"
Width="400" Height="120" Title="Input">
<StackPanel Margin="8">
<TextBlock Name="LblPrompt" />
<TextBox Name="TbInput" Margin="0,6,0,6" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="8">
<Button Name="BtnOk" Content="OK" />
<Button Name="BtnCancel" Content="Cancel" />
</StackPanel>
</StackPanel>
</Window>

View File

@@ -0,0 +1,26 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace Logof_Client.Wiki;
public partial class NamePromptWindow : Window
{
private void InitializeComponent()
{
Avalonia.Markup.Xaml.AvaloniaXamlLoader.Load(this);
}
public NamePromptWindow(string prompt)
{
InitializeComponent();
LblPrompt.Text = prompt;
BtnOk.Click += BtnOk_Click;
BtnCancel.Click += (_, __) => Close(null);
}
private void BtnOk_Click(object? sender, RoutedEventArgs e)
{
Close(TbInput.Text);
}
}

13
Wiki/WikiItem.cs Normal file
View File

@@ -0,0 +1,13 @@
using System.Collections.ObjectModel;
namespace Logof_Client.Wiki;
public class WikiItem
{
public string Name { get; set; }
public string Path { get; set; }
public bool IsFolder { get; set; }
public ObservableCollection<WikiItem> Children { get; } = new();
public override string ToString() => Name;
}

76
Wiki/WikiService.cs Normal file
View File

@@ -0,0 +1,76 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace Logof_Client.Wiki;
public class WikiService
{
public string WikiRoot { get; }
public string WikiRootFullPath { get; }
public WikiService(string wikiRoot = "wiki")
{
// prefer global wiki storage path if configured
if (Global._instance != null && !string.IsNullOrWhiteSpace(Global._instance.wiki_storage_path))
{
var cfg = Global._instance.wiki_storage_path;
WikiRootFullPath = Path.IsPathRooted(cfg)
? cfg
: Path.Combine(Directory.GetCurrentDirectory(), cfg);
WikiRoot = WikiRootFullPath;
}
else
{
WikiRoot = wikiRoot;
WikiRootFullPath = Path.Combine(Directory.GetCurrentDirectory(), wikiRoot);
}
}
public List<WikiItem> GetRootItems()
{
var list = new List<WikiItem>();
if (!Directory.Exists(WikiRootFullPath)) return list;
var dirInfo = new DirectoryInfo(WikiRootFullPath);
// Add folders
foreach (var dir in dirInfo.GetDirectories())
{
list.Add(BuildFolderItem(dir));
}
// Add files in root
foreach (var file in dirInfo.GetFiles("*.md"))
{
list.Add(new WikiItem { Name = file.Name, Path = file.FullName, IsFolder = false });
}
return list.OrderBy(i => i.IsFolder ? 0 : 1).ToList();
}
private WikiItem BuildFolderItem(DirectoryInfo dir)
{
var node = new WikiItem { Name = dir.Name, Path = dir.FullName, IsFolder = true };
foreach (var subdir in dir.GetDirectories())
{
node.Children.Add(BuildFolderItem(subdir));
}
foreach (var file in dir.GetFiles("*.md"))
{
node.Children.Add(new WikiItem { Name = file.Name, Path = file.FullName, IsFolder = false });
}
return node;
}
public Task<string?> LoadFileContentAsync(string path)
{
if (!File.Exists(path)) return Task.FromResult<string?>(null);
return File.ReadAllTextAsync(path);
}
}