Inline local functions; simplify URL handling; localize OutputPathTooltip

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-24 17:13:16 +00:00
parent 2b20bf97e3
commit 2efddb3b2c
9 changed files with 146 additions and 103 deletions

View File

@@ -114,55 +114,52 @@ public class MarkdownToInlinesConverter : IValueConverter
var isFirst = true;
void RenderParagraph(ParagraphBlock paragraph)
{
if (!isFirst)
{
// Insert a blank line between paragraphs
inlines.Add(new LineBreak());
inlines.Add(new LineBreak());
}
isFirst = false;
foreach (var markdownInline in paragraph.Inline!)
ProcessInline(inlines, markdownInline);
}
void RenderListBlock(ListBlock list)
{
var itemOrder = 1;
if (list.IsOrdered && int.TryParse(list.OrderedStart, out var startNum))
itemOrder = startNum;
foreach (var listItem in list.OfType<ListItemBlock>())
{
if (!isFirst)
inlines.Add(new LineBreak());
isFirst = false;
var prefix = list.IsOrdered ? $"{itemOrder++}. " : $"{list.BulletType} ";
inlines.Add(new Run(prefix));
foreach (var subBlock in listItem.OfType<ParagraphBlock>())
{
if (subBlock is { Inline: not null } p)
foreach (var markdownInline in p.Inline)
ProcessInline(inlines, markdownInline);
}
}
}
foreach (var block in Markdown.Parse(text, MarkdownPipeline))
{
switch (block)
{
case ParagraphBlock { Inline: not null } paragraph:
RenderParagraph(paragraph);
{
if (!isFirst)
{
// Insert a blank line between paragraphs
inlines.Add(new LineBreak());
inlines.Add(new LineBreak());
}
isFirst = false;
foreach (var markdownInline in paragraph.Inline!)
ProcessInline(inlines, markdownInline);
break;
}
case ListBlock list:
RenderListBlock(list);
{
var itemOrder = 1;
if (list.IsOrdered && int.TryParse(list.OrderedStart, out var startNum))
itemOrder = startNum;
foreach (var listItem in list.OfType<ListItemBlock>())
{
if (!isFirst)
inlines.Add(new LineBreak());
isFirst = false;
var prefix = list.IsOrdered ? $"{itemOrder++}. " : $"{list.BulletType} ";
inlines.Add(new Run(prefix));
foreach (var subBlock in listItem.OfType<ParagraphBlock>())
{
if (subBlock is { Inline: not null } p)
foreach (var markdownInline in p.Inline)
ProcessInline(inlines, markdownInline);
}
}
break;
}
}
}

View File

@@ -68,6 +68,26 @@ public partial class LocalizationManager
// Export Setup
[nameof(ChannelsSelectedText)] = "channels selected",
[nameof(OutputPathLabel)] = "Output path",
[nameof(OutputPathTooltip)] = """
Output file or directory path.
If a directory is specified, file names will be generated automatically based on the channel names and export parameters.
Directory paths must end with a slash to avoid ambiguity.
Available template tokens:
* **%g** server ID
* **%G** server name
* **%t** category ID
* **%T** category name
* **%c** channel ID
* **%C** channel name
* **%p** channel position
* **%P** category position
* **%a** after date
* **%b** before date
* **%d** current date
""",
[nameof(FormatLabel)] = "Format",
[nameof(FormatTooltip)] = "Export format",
[nameof(AfterDateLabel)] = "After (date)",

View File

@@ -70,6 +70,26 @@ public partial class LocalizationManager
// Export Setup
[nameof(ChannelsSelectedText)] = "canaux sélectionnés",
[nameof(OutputPathLabel)] = "Chemin de sortie",
[nameof(OutputPathTooltip)] = """
Chemin du fichier ou répertoire de sortie.
Si un répertoire est spécifié, les noms de fichiers seront générés automatiquement en fonction des noms de canaux et des paramètres d'exportation.
Les chemins de répertoire doivent se terminer par un slash pour éviter toute ambiguïté.
Jetons de modèle disponibles :
* **%g** ID du serveur
* **%G** nom du serveur
* **%t** ID de la catégorie
* **%T** nom de la catégorie
* **%c** ID du canal
* **%C** nom du canal
* **%p** position du canal
* **%P** position de la catégorie
* **%a** date après
* **%b** date avant
* **%d** date actuelle
""",
[nameof(FormatLabel)] = "Format",
[nameof(FormatTooltip)] = "Format d'exportation",
[nameof(AfterDateLabel)] = "Après (date)",

View File

@@ -70,6 +70,26 @@ public partial class LocalizationManager
// Export Setup
[nameof(ChannelsSelectedText)] = "Kanäle ausgewählt",
[nameof(OutputPathLabel)] = "Ausgabepfad",
[nameof(OutputPathTooltip)] = """
Ausgabedatei- oder Verzeichnispfad.
Wenn ein Verzeichnis angegeben wird, werden Dateinamen automatisch basierend auf den Kanalnamen und Exportparametern generiert.
Verzeichnispfade müssen mit einem Schrägstrich enden, um Mehrdeutigkeiten zu vermeiden.
Verfügbare Vorlagen-Token:
* **%g** Server-ID
* **%G** Servername
* **%t** Kategorie-ID
* **%T** Kategoriename
* **%c** Kanal-ID
* **%C** Kanalname
* **%p** Kanalposition
* **%P** Kategorieposition
* **%a** Datum ab
* **%b** Datum bis
* **%d** aktuelles Datum
""",
[nameof(FormatLabel)] = "Format",
[nameof(FormatTooltip)] = "Exportformat",
[nameof(AfterDateLabel)] = "Nach (Datum)",

View File

@@ -68,6 +68,26 @@ public partial class LocalizationManager
// Export Setup
[nameof(ChannelsSelectedText)] = "canales seleccionados",
[nameof(OutputPathLabel)] = "Ruta de salida",
[nameof(OutputPathTooltip)] = """
Ruta del archivo o directorio de salida.
Si se especifica un directorio, los nombres de archivo se generarán automáticamente según los nombres de los canales y los parámetros de exportación.
Las rutas de directorio deben terminar con una barra diagonal para evitar ambigüedades.
Tokens de plantilla disponibles:
* **%g** ID del servidor
* **%G** nombre del servidor
* **%t** ID de categoría
* **%T** nombre de categoría
* **%c** ID del canal
* **%C** nombre del canal
* **%p** posición del canal
* **%P** posición de la categoría
* **%a** fecha desde
* **%b** fecha hasta
* **%d** fecha actual
""",
[nameof(FormatLabel)] = "Formato",
[nameof(FormatTooltip)] = "Formato de exportación",
[nameof(AfterDateLabel)] = "Después (fecha)",

View File

@@ -68,6 +68,26 @@ public partial class LocalizationManager
// Export Setup
[nameof(ChannelsSelectedText)] = "каналів вибрано",
[nameof(OutputPathLabel)] = "Шлях збереження",
[nameof(OutputPathTooltip)] = """
Шлях до файлу або директорії виводу.
Якщо вказано директорію, імена файлів генеруватимуться автоматично на основі назв каналів та параметрів експорту.
Шляхи до директорій повинні закінчуватись слешем для уникнення неоднозначності.
Доступні шаблонні токени:
* **%g** ID сервера
* **%G** назва сервера
* **%t** ID категорії
* **%T** назва категорії
* **%c** ID каналу
* **%C** назва каналу
* **%p** позиція каналу
* **%P** позиція категорії
* **%a** дата після
* **%b** дата до
* **%d** поточна дата
""",
[nameof(FormatLabel)] = "Формат",
[nameof(FormatTooltip)] = "Формат експорту",
[nameof(AfterDateLabel)] = "Після (дата)",

View File

@@ -120,6 +120,7 @@ public partial class LocalizationManager
public string ChannelsSelectedText => Get();
public string OutputPathLabel => Get();
public string OutputPathTooltip => Get();
public string FormatLabel => Get();
public string FormatTooltip => Get();
public string AfterDateLabel => Get();

View File

@@ -1,5 +1,4 @@
using System;
using System.Diagnostics;
using System.Diagnostics;
using System.Windows.Input;
using Avalonia;
using Avalonia.Controls;
@@ -58,11 +57,7 @@ public partial class HyperLink : UserControl
if (Command.CanExecute(CommandParameter))
Command.Execute(CommandParameter);
}
else if (
!string.IsNullOrWhiteSpace(Url) && Uri.TryCreate(Url, UriKind.Absolute, out var uri)
)
{
Process.StartShellExecute(uri.AbsoluteUri);
}
else if (!string.IsNullOrWhiteSpace(Url))
Process.StartShellExecute(Url);
}
}

View File

@@ -73,60 +73,10 @@
Text="{Binding OutputPath}"
Theme="{DynamicResource FilledTextBox}">
<ToolTip.Tip>
<TextBlock>
<Run Text="Output file or directory path." />
<LineBreak />
<Run Text="If a directory is specified, file names will be generated automatically based on the channel names and export parameters." />
<LineBreak />
<Run Text="Directory paths must end with a slash to avoid ambiguity." />
<LineBreak />
<LineBreak />
<Run Text="Available template tokens:" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%g" />
<Run Text="— server ID" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%G" />
<Run Text="— server name" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%t" />
<Run Text="— category ID" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%T" />
<Run Text="— category name" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%c" />
<Run Text="— channel ID" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%C" />
<Run Text="— channel name" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%p" />
<Run Text="— channel position" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%P" />
<Run Text="— category position" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%a" />
<Run Text="— after date" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%b" />
<Run Text="— before date" />
<LineBreak />
<Run Text=" " />
<Run FontWeight="SemiBold" Text="%d" />
<Run Text="— current date" />
</TextBlock>
<TextBlock
MaxWidth="400"
TextWrapping="Wrap"
Inlines="{Binding LocalizationManager.OutputPathTooltip, Converter={x:Static converters:MarkdownToInlinesConverter.Instance}}" />
</ToolTip.Tip>
<TextBox.InnerRightContent>
<Button