- Добавлены TuiResources (sealed record), Locale, LocalizationService - Все строки интерфейса вынесены из .razor-файлов в TuiResources - App.razor: клавиша L циклически переключает локаль, заголовок показывает [EN]/[RU] - Дочерние компоненты получают Loc как параметр (stateless) - Создан AGENT.tui.md с правилами работы с TUI для агентов - Обновлены AGENTS.md и CLAUDE.md со ссылками на AGENT.tui.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.3 KiB
4.3 KiB
AGENT.tui.md
Read this file whenever you touch anything in LazyBear.MCP/TUI/.
TUI Structure
- Entry point:
TUI/Components/App.razor— owns all keyboard input and tab routing. - Keyboard input:
TUI/GlobalKeyboardService.cs— single source; dedicated blocking thread onConsole.ReadKey(intercept: true). Do not add other console readers. - Localization:
TUI/Localization/—LocalizationServicesingleton +TuiResourcesrecord withEn/Rustatic instances. - Components:
OverviewTab,LogsTab,SettingsTab— pure display; receive state as parameters, fire no keyboard events. - Models:
TUI/Models/—OverviewRow,SettingsEntry. - Palette:
TUI/UiPalette.cs— all colors. Do not hardcodeSpectre.Console.Colorvalues in components.
Keyboard Rules
- All keys are handled in
App.razor.HandleKeyDown(). Do not attach@onkeydownto<Select>or any other RazorConsole component — framework intercepts Tab and arrows before they reach component-level callbacks. GlobalKeyboardServicefiresOnKeyPressed(ConsoleKeyInfo).App.razorconverts viaConvertKey()and callsHandleKeyDown()insideInvokeAsync.- Do not use
Console.KeyAvailablepolling — it acquires the console mutex on every call and causes rendering lag. Always use blockingConsole.ReadKeyin a dedicated thread. - Keyboard shortcuts:
Tab/Shift+Tab— switch tabs; Arrows — navigate list;Space— toggle;Enter— open/expand;L— cycle language.
RazorConsole Gotchas (0.5.0)
@onkeydownon<Select>is captured asAdditionalAttributesand not called for Tab/arrow keys.FocusManagerintercepts Tab globally — do not callFocusNextAsync()inOnAfterRenderAsyncunconditionally; it shifts focus on every re-render.- No public global key-intercept API exists in 0.5.0.
Select.Value= committed selection (updated on Enter).Select.FocusedValue= highlighted item during navigation. UseFocusedValueto reflect external state immediately.StateHasChanged()from a background thread must go throughInvokeAsync(() => { /* mutate state */ StateHasChanged(); }).- Every
StateHasChanged()triggers a full terminal redraw. Batch log-driven re-renders to avoid visible lag.
Localization Rules
- All UI strings live in
TUI/Localization/TuiResources.csonly. No hardcoded strings in.razorfiles or other.csfiles. - To add a string: add a property to
TuiResources, fill bothEnandRustatic instances. Build will fail if arequired initis missing — by design. - Log level names (
Info,Warn,Error) stay in English in all locales — they are technical identifiers, not UI labels. - Internal filter keys in
App.razor("All","Info","Warn","Error") are English regardless of locale;LogsTabmaps"All"→Loc.FilterAllfor display. - Language toggle:
Lkey cycles through locales.LocalizationService.SwitchNext()firesOnChanged;App.razorre-renders viaOnLocaleChanged(). - Locale indicator shown in panel title:
"LazyBear MCP [EN]"/"LazyBear MCP [RU]". - Do not inject
LocalizationServiceinto child tab components —App.razorpasses the currentTuiResourcesasLocparameter. Child components are stateless regarding locale.
Component Contract
- Child tab components (
OverviewTab,LogsTab,SettingsTab) accept[Parameter] TuiResources Loc— always pass it fromApp.razor. - Child components must not subscribe to events or inject services. Keep them as pure render components.
SelectedIndexChangedcallbacks exist for forward-compatibility; actual selection state is managed exclusively inApp.razor.
DI Registration Pattern
Services that must be both injectable and run as hosted services:
services.AddSingleton<MyService>();
services.AddHostedService(sp => sp.GetRequiredService<MyService>());
AddHostedService<T>() alone creates a transient instance — unusable as injectable singleton.
Working Rules
- Read
App.razorbefore touching any keyboard or tab logic. - Match the style of the file being edited.
- After changes, run
dotnet build. - Do not add new hardcoded strings to
.razorfiles — add toTuiResourcesinstead. - Do not add new
Console.KeyAvailableorConsole.ReadKeycalls outsideGlobalKeyboardService.