Files
av-parser/README.md
T
Leonid PershinandClaude Opus 5 aeafe0af36 Add build and run scripts; drop the broken Avalonia.Diagnostics reference
A wrapper around `dotnet build` earns nothing, so build.ps1/build.sh are the
full local gate instead — tool restore, package restore, format check, build,
test — in the order that fails cheapest first, with a non-zero exit on failure.
run.ps1/run.sh default to Debug and pass extra arguments through to the app.
Both flavours ship because the Desktop head targets Windows, Linux and macOS.

Writing them immediately paid for itself: the very first run failed restore on
Avalonia.Diagnostics 12.1.1, which does not exist — the package stops at 11.3.x
because Avalonia 12 moved the inspector into a separate tool with its own
installation. The reference had survived because it sat behind
Condition="'$(Configuration)' == 'Debug'", and `dotnet restore` evaluates with
the default configuration while every build so far had passed -c Release. So
`dotnet build -c Release` worked and a bare `dotnet restore` did not.

Reference removed rather than replaced: AvaloniaUI.DiagnosticsSupport pulls in a
separately installed tool, which is not a dependency to add to a skeleton without
asking. README and CLAUDE.md no longer promise F12, and both traps are written
down where the next person will hit them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 16:32:16 +03:00

7.3 KiB
Raw Blame History

AvParser

Каркас desktop-приложения на Avalonia 12 с ReactiveUI-MVVM, адаптивным layout поверх Semi.Avalonia, единым DI-контейнером и тремя уровнями тестов.

Доменная часть пока намеренно абстрактная: ядро — это pluggable-контракт IParser<TInput, TOutput> и два демо-парсера, чтобы каркас был запускаемым и проверяемым end-to-end до появления настоящей логики.


Быстрый старт

Нужен .NET SDK 10.0.100 (закреплён в global.json).

Полный локальный гейт — восстановление, проверка форматирования, сборка, тесты:

./build.ps1

Запуск приложения (по умолчанию Debug):

./run.ps1

На Linux и macOS — ./build.sh и ./run.sh, аргументы те же.

Полезные флаги:

./build.ps1 -Fix -Configuration Debug

-Fix переформатирует код вместо того, чтобы падать на непрошедшей проверке; -SkipTests собирает без прогона тестов. То же в bash: --fix, -c Debug, --skip-tests.

Если нужны отдельные шаги, скрипты ничего не прячут:

dotnet build AvParser.slnx -c Release
dotnet test AvParser.slnx -c Release
dotnet csharpier check .

Структура

src/
  AvParser.Core            домен: IParser, IParserCatalog, модели, демо-парсеры
                           ноль зависимостей кроме DI.Abstractions — ни Avalonia, ни IO
  AvParser.Infrastructure  AppPaths, JSON-настройки с debounce, Serilog
  AvParser.UI              Avalonia class library: App-независимые View, ViewModel,
                           ResponsiveLayout, дизайн-токены, навигация
  AvParser.Desktop         WinExe-хост: Program.cs, App.axaml, composition root
tests/
  AvParser.Core.Tests      парсеры, реестр, отмена, прогресс
  AvParser.UI.Tests        ViewModel'и без Avalonia
  AvParser.UI.HeadlessTests    реальное дерево контролов через [AvaloniaFact]

Ссылки идут строго в одну сторону: Core ← Infrastructure ← UI ← Desktop. UI — библиотека, а не exe, именно чтобы headless-тесты собирали настоящие View, не подтягивая Program.cs, Serilog и контейнер.


Адаптивный layout

В Avalonia нет AdaptiveTrigger, VisualStateManager и media-queries. Есть три примитива: наблюдаемый Visual.Bounds, псевдоклассы и SplitView. ResponsiveLayout связывает первое со вторым — получается CSS-подобная реакция на ширину.

Брейкпоинт Ширина окна Навигация
Compact < 720 px выезжающий drawer поверх контента
Medium 720 1100 px рельс из одних иконок (56 px)
Expanded ≥ 1100 px полный сайдбар с подписями (248 px)

Переключение с гистерезисом в 24 px: без неё перетаскивание края окна заставляет layout мигать между двумя состояниями на каждом пикселе дрожания.

Разделение обязанностей, которое важно не сломать:

  • SplitView.DisplayMode и IsPaneOpen биндятся во ViewModel. Style-сеттер навсегда проигрывает локальному значению, поэтому первый же клик по гамбургеру заморозил бы любой стиль, который тоже пишет в эти свойства.
  • Всё чисто визуальное — ширины панели, видимость подписей, паддинги — живёт в Styles/Shell.axaml.

Селекторы там написаны как :is(UserControl).shell, а не UserControl.shell: селектор типа в Avalonia матчит точный тип, а ShellView наследуется от ReactiveUserControl<T> — обычная форма молча не сматчилась бы ни с чем. На это есть тест (ShellViewTests.The_shell_stylesheet_is_actually_applied).


Дизайн-токены

Все цвета, отступы, радиусы и типографика — в Styles/Tokens.axaml, с отдельными словарями для Light и Dark. В остальном XAML нет ни одного литерального цвета и ни одного «магического» отступа, так что перекрасить тему или уплотнить интерфейс — это правка одного файла.

Semi.Avalonia даёт темы контролов; токены — это семантический слой приложения поверх них. Кнопки .primary / .destructive описаны своими стилями, а не классами Semi, чтобы акцентный цвет не разъезжался между двумя палитрами.


Стек

Пакет Версия Заметка
Avalonia 12.1.1 compiled bindings по умолчанию → x:DataType обязателен
ReactiveUI.Avalonia 12.1.1 Avalonia.ReactiveUI — deprecated, это его преемник
ReactiveUI 24.1.0 дистрибутив Primitives: RxVoid вместо Unit, ISequencer вместо IScheduler
Semi.Avalonia 12.1.0.1 темы контролов
xUnit v3 (3.2.2) Avalonia.Headless.XUnit 12.x требует именно v3

Что проверить руками

  1. Потянуть окно по ширине — сайдбар проходит путь полный → только иконки → выезжающий drawer, без мигания на границах.
  2. Переключить тему кнопкой в заголовке и в Settings; перезапустить — выбор сохранился.
  3. На странице Parse нажать 50k rows, затем Parse — виден прогресс; Cancel останавливает на середине и пишет, сколько успело разобраться. В Avalonia 12 инспектора «из коробки» больше нет: Avalonia.Diagnostics остановился на 11.3.x, а DevTools вынесли в отдельный инструмент со своей установкой (AvaloniaUI.DiagnosticsSupport + .WithDeveloperTools()). Поэтому F12 здесь ничего не открывает — зависимость намеренно не добавлена.

Настройки и логи лежат в %APPDATA%/AvParser (Windows) или ~/.config/AvParser (Linux/macOS).