diff --git a/catalog/Frameworks/SharpConsoleUI/manifest.json b/catalog/Frameworks/SharpConsoleUI/manifest.json new file mode 100644 index 0000000..5a5272c --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "SharpConsoleUI", + "title": "SharpConsoleUI", + "description": "Terminal application framework for .NET with overlapping windows, a real compositor, a DOM layout engine, and 40+ reactive controls.", + "links": { + "repository": "https://github.com/nickprotop/ConsoleEx", + "docs": "https://nickprotop.github.io/ConsoleEx/", + "nuget": "https://www.nuget.org/packages/SharpConsoleUI/" + } +} diff --git a/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/SKILL.md b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/SKILL.md new file mode 100644 index 0000000..2b3f7d1 --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/SKILL.md @@ -0,0 +1,143 @@ +--- +name: sharpconsoleui +description: "Use SharpConsoleUI to build full terminal (TUI) applications in .NET — equally suited to full-screen single-window apps and multi-window desktops with overlapping draggable windows — using a compositor, a DOM layout engine, and 40+ reactive controls (data tables, tree views, forms, an embedded PTY terminal, markdown, charts, video). USE FOR: interactive console apps, full-screen TUIs, multi-window terminal desktops, dashboards, wizards, and admin/monitoring UIs that need focus, mouse, and flicker-free rendering over local terminals or SSH; NativeAOT console tools. DO NOT USE FOR: simple line-based CLI output or argument parsing; non-interactive scripts; GUI/desktop (WPF/WinForms) or web UIs. INVOKES: inspect the project, add the SharpConsoleUI package, scaffold a window/control tree, and build/run to verify the app renders." +compatibility: "Requires a .NET 8.0+ console project (net8.0/net9.0/net10.0) that can reference the `SharpConsoleUI` package and run against a modern terminal. Uses a retained-mode UI on a single cooperative UI thread, not immediate-mode Console.Write." +--- + +# SharpConsoleUI terminal application framework + +## Trigger On + +- building an interactive terminal UI: dashboards, wizards, settings screens, admin/monitoring consoles +- building a full-screen single-window TUI: a `.Frameless()` (no title bar, no title buttons) + `.Maximized()` app that owns the whole terminal +- building a multi-window terminal desktop: overlapping windows with drag/resize/minimize/maximize, z-order, focus routing, modal windows, and mouse support (full-screen and multi-window are equally first-class here) +- wanting flicker-free rendering that stays clean over SSH (diff-based cell buffer, not full repaints) +- needing rich terminal controls: data tables, tree/list views, forms, an embedded PTY terminal, markdown, charts, or video +- shipping a NativeAOT-ready console application with a real UI + +Do not trigger for plain line-based CLI tools, argument parsing, or non-interactive scripts. + +## Install + +- NuGet: + - `dotnet add package SharpConsoleUI` + - `dotnet add package SharpConsoleUI --version ` +- XML package reference: + - `` +- Targets `net8.0`, `net9.0`, `net10.0`. +- Sources: + - [NuGet: SharpConsoleUI](https://www.nuget.org/packages/SharpConsoleUI/) + - [GitHub: nickprotop/ConsoleEx](https://github.com/nickprotop/ConsoleEx) + - [Docs site](https://nickprotop.github.io/ConsoleEx/) + +## Workflow + +```mermaid +flowchart LR + A["NetConsoleDriver (RenderMode.Buffer)"] --> B["ConsoleWindowSystem"] + B --> C["WindowBuilder -> Window"] + C --> D["window.AddControl(Controls.*)"] + D --> E["DOM layout: Measure -> Arrange -> Paint"] + E --> F["windowSystem.AddWindow(window)"] + F --> G["windowSystem.Run() (blocks until Shutdown)"] + G --> H["compositor merges per-window buffers -> terminal"] +``` + +1. Create a `NetConsoleDriver` and a `ConsoleWindowSystem` that owns all windows. +2. Build one or more windows with `WindowBuilder` (title, size, position, borders, padding). +3. Add controls to each window with `window.AddControl(...)`, usually via the `Controls` static factory. +4. Wire interactivity through control events (e.g. `Button.OnClick`); call `windowSystem.Shutdown()` to exit. +5. `windowSystem.AddWindow(window)` then `windowSystem.Run()` starts the render/input loop (blocks until shutdown). +6. For layout, dialogs, portals/overlays, forms, and the full control set, load the reference files below. + +### Minimal app (read + show + interact) + +```csharp +using SharpConsoleUI; +using SharpConsoleUI.Builders; +using SharpConsoleUI.Controls; +using SharpConsoleUI.Drivers; + +var driver = new NetConsoleDriver(RenderMode.Buffer); +var windowSystem = new ConsoleWindowSystem(driver); + +var window = new WindowBuilder(windowSystem) + .WithTitle("Hello World") + .WithSize(50, 12) + .Centered() + .Build(); + +window.AddControl(Controls.Markup() + .AddLine("[bold cyan]Hello, SharpConsoleUI![/]") + .Build()); + +window.AddControl(Controls.Button("Quit") + .OnClick((sender, e, win) => windowSystem.Shutdown()) + .Build()); + +windowSystem.AddWindow(window); +windowSystem.Run(); +``` + +### Layout + data example + +Use a `GridControl` when you need columns/rows with fixed, size-to-content, or +proportional (`Star`) tracks, and put content controls (tables, lists, markdown) +into the cells: + +```csharp +var grid = Controls.Grid() + .Columns(GridLength.Cells(20), GridLength.Star(1)) // fixed sidebar + fill + .Rows(GridLength.Auto(), GridLength.Star(1)) // toolbar + body + .RowGap(1) + .Place(Controls.Markup("[bold]Dashboard[/]").Build(), 0, 0, colSpan: 2) + .Place(sidebarList, 1, 0) + .Place(dataTable, 1, 1) + .Build(); + +window.AddControl(grid); +``` + +See `references/recipes.md` for full grid/table/form/dialog examples and +`references/controls.md` for the control chosen per region. + +## Best practices + +- Describe the UI declaratively (retained mode). Let the framework own redraws, focus, and input — do not mix raw `Console.Write` into a running app. +- The app runs on one cooperative UI thread. Never block it: don't call `.Result` / `.Wait()` on async work inside a handler (it deadlocks the loop). Push blocking/CPU work off-thread and marshal UI mutations back with `EnqueueOnUIThread` / `InvokeAsync`. See `references/architecture.md`. +- Use portals for dropdowns, overlays, and toasts, and the built-in `Dialogs` for confirm/prompt/progress, instead of hand-positioning windows. See `references/recipes.md`. +- Apply semantic control `Role`s (Primary, Success, Danger, …) so colors come from the active theme instead of being hand-set. +- Style all text with `[tag]text[/]` markup — it works everywhere text renders (labels, titles, status bars, table cells, tree nodes), including `[markdown]`, `[gradient=…]`, and inline `[spinner]`. Escape untrusted text with `MarkupParser.Escape(...)`. See `references/markup.md`. +- Prefer `Controls.*` factory + fluent builders over manual control construction for consistent wiring. +- For a full app (not a single screen), follow the production patterns in `references/app-patterns.md`: one maximized window with content-swap navigation, an app-owned header/hint bar, custom awaitable modals closed via `CloseModalWindow`, dialogs clamped to `DesktopDimensions`, a semantic color/theme layer (no hardcoded hex), and a wrapper so no async action can crash the loop. + +## Limitations to check before production + +- It is a full retained-mode application framework, not a drop-in for one-off `Console.WriteLine` output. Small non-interactive tools do not need it. +- Cooperative single-threaded UI model: unsynchronized cross-thread control mutation, or blocking the UI thread on async work, is a bug — not a shortcut. +- `TerminalControl` (the embedded PTY emulator) runs on Linux and Windows 10 1809+ (openpty on Linux, ConPTY on Windows); it throws `PlatformNotSupportedException` on other OSes. On Linux it requires `PtyShim.RunIfShim(args)` as the very first line of `Main` (a safe no-op on Windows) — see `references/controls.md`. +- Advanced media (video, Kitty-graphics image rendering) auto-detects terminal capability and degrades to half-block/ASCII/braille fallbacks; verify on the target terminal. +- NativeAOT is supported (the library is `IsAotCompatible` and CI-verified), but confirm your own reflection/serialization code is trim/AOT-safe; `HtmlControl` is the one documented AOT caveat. See `references/architecture.md`. + +## Deliver + +- an install + first-run guide ready to paste into a .NET console project +- a minimal window app and a layout/data example that actually render +- clear notes on the threading model, portals/dialogs, and terminal-capability tradeoffs + +## Validate + +- `dotnet add package SharpConsoleUI` restores and the project compiles. +- The minimal app above builds and runs, showing a centered window with a working Quit button (Tab to focus, Enter/click to quit). +- One layout/control sample from `references/recipes.md` renders correctly. +- Any control used is confirmed against `references/controls.md` (correct factory/API). + +## Load References + +- `references/controls.md` — the 40+ control set grouped by purpose, with the factory/API entry point and a "use when" for each group. +- `references/markup.md` — the `[tag]text[/]` markup language (colors, decorations, links, `[spinner]`, `[markdown]`, `[gradient]`, `MarkupParser` API). Markup works everywhere text renders — load this for any styled-text work. +- `references/architecture.md` — compositor, DOM layout pipeline, cooperative UI-thread model, marshalling, and NativeAOT notes. +- `references/recipes.md` — full-screen and multi-window apps, window builders, grids, dialogs, portals/toasts, and forms, with verified code. +- `references/features.md` — gradients, transparency/alpha blending, compositor effects, animations, desktop background, and image/video/syntax-highlighting rendering. +- `references/system.md` — constructor overloads, state services, panels, configuration, registry, MVVM data binding, flows, plugins, clipboard, shell-pipeline/schost distribution, and the "when to choose this framework" comparison + pattern catalog. +- `references/app-patterns.md` — how to structure and polish a real production app: single-window shell + content-swap navigation, own-your-header, custom awaitable modals, live process output, the semantic color/theme layer, escaping untrusted text, threading discipline, and a never-crash wrapper. Load this when building a full app, not just one screen. diff --git a/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/manifest.json b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/manifest.json new file mode 100644 index 0000000..3535b19 --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/manifest.json @@ -0,0 +1,7 @@ +{ + "version": "1.0.0", + "category": "Terminal UI", + "packages": [ + "SharpConsoleUI" + ] +} diff --git a/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/app-patterns.md b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/app-patterns.md new file mode 100644 index 0000000..7078fa1 --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/app-patterns.md @@ -0,0 +1,131 @@ +# SharpConsoleUI app architecture and UX patterns + +How to structure and polish a real production SharpConsoleUI app, distilled from +shipping apps (the cx-/lazy- series and third-party consumers). These are +app-level patterns built on the framework APIs named below — adopt them when +building anything beyond a single-screen demo. + +## Single-window shell + content-swap navigation + +Most polished apps are **one maximized window**, not many. "Screens" are +content-swap units inside that one window, driven by a small navigator — this +gives full control of every row and a consistent frame. + +- Bootstrap once: `new ConsoleWindowSystem(new NetConsoleDriver(RenderMode.Buffer), theme, options)`, one `WindowBuilder(...).Maximized().Movable(false).Resizable(false).Closable(false).HideTitle().Build()`, `AddWindow`, `Run()`. Disable the built-in panels (`ShowTopPanel: false, ShowBottomPanel: false`) so the app owns every row. +- Navigator holds a `Stack` with `Root/Push/Replace/Pop/PopToRoot`. Each transition calls `window.ClearControls()` then the top screen's `Build(...)`, which re-adds its controls. A screen abstraction is just an interface you define: a breadcrumb label, a `Build` method, and an optional `HandleKey(ConsoleKeyInfo) -> bool`. +- Guard content swaps against async races: bump a generation counter on every navigation, capture it before a screen's `Build` awaits, and discard the stale continuation's mutations if navigation advanced meanwhile. Prevents a slow screen painting over one the user already left. + +```csharp +void Show(IScreen screen) +{ + window.ClearControls(); + window.AddControl(BuildHeader()); // your own header (see below) + screen.Build(this); // screen appends its controls + window.FocusControl(screen.FirstFocusable); +} +``` + +## Own your header / status / hints + +With the built-in panels off, render your own 3-line header on every navigation: +a live status line, a breadcrumb + right-aligned global hints, and a full-width +rule (`─`). Every screen also appends a bottom **hint bar** listing exactly the +keys its `HandleKey` implements, so hints never drift from behavior. Build all of +it as markup in a `MarkupControl`. To right-align two segments despite markup +tags, measure visible width by stripping tags (see "escaping" below). + +## Keyboard-first interaction + +- Global keys via `window.PreviewKeyPressed` (fires before controls): offer the key to the current screen's `HandleKey` first, then handle `Esc` = back/pop, `?` = help, etc. App-wide hotkeys via `system.RegisterGlobalShortcut(ConsoleModifiers.Control, ConsoleKey.Q, () => system.RequestExit(0))`. +- Conventions to keep consistent across screens: `Esc` cancels/goes back; `Enter` activates/confirms; single-letter mnemonics for actions; arrow Up/Down for history recall. Resolve list selection on `ListControl.ItemActivated` (Enter/double-click), not `SelectedIndexChanged` (mere highlight). + +## Custom modal dialog with an awaitable result + +Beyond the built-in `Dialogs.*` helpers, the standard pattern for custom dialogs +is a modal window that a caller can `await`: + +```csharp +var tcs = new TaskCompletionSource(); +var (w, h) = ClampSize(system, desiredW, desiredH); // clamp to desktop, see below +var dialog = new WindowBuilder(system) + .WithTitle(" Pick ").WithSize(w, h).Centered().AsModal().Build(); +list.ItemActivated += (s, e) => { chosen = list.SelectedIndex; system.CloseModalWindow(dialog); }; +dialog.PreviewKeyPressed += (s, e) => { if (e.KeyInfo.Key == ConsoleKey.Escape) system.CloseModalWindow(dialog); }; +dialog.OnClosed += (_, _) => tcs.TrySetResult(chosen); +dialog.AddControl(list); +system.AddWindow(dialog); +dialog.FocusControl(list); +return await tcs.Task; +``` + +- Close a **modal** with `system.CloseModalWindow(window)` (not `CloseWindow`) so the modal stack unwinds. +- Clamp every dialog to the terminal so content can't overflow it: + +```csharp +(int W, int H) ClampSize(ConsoleWindowSystem s, int dw, int dh) => ( + Math.Clamp(dw, 40, Math.Max(40, s.DesktopDimensions.Width - 4)), + Math.Clamp(dh, 10, Math.Max(10, s.DesktopDimensions.Height - 4))); +``` + +- Use a **toast** (`system.ToastService.Show(msg, NotificationSeverity.Success)`) for don't-interrupt feedback; a **modal** for must-read/must-acknowledge. Disable Enter/Esc-close while a modal is running a long task. + +## Live external-process output + +Run a cancellable process and stream its output into a modal without leaving the +TUI: a `ScrollablePanel` + `MarkupControl`, lines guarded by a `lock` and +republished as an immutable snapshot via `system.EnqueueOnUIThread(() => +markup.SetContent(snapshot))`. Tick the window title with elapsed seconds from a +`Timer`, also through `EnqueueOnUIThread`. Escape untrusted output before it +enters markup (below). This is the canonical pattern for any long/cancellable +external work. + +## Semantic color layer (no hardcoded color in screens) + +Route every color through one place, never hardcode hex in screen code: + +1. A `Palette` of hex constants. +2. A theme derived once: `Theme.From(new ModernGrayTheme()).WithName("app").With(t => { /* set ~30 ITheme slots from Palette */ }).Build()`, passed to the `ConsoleWindowSystem(driver, theme, options)` constructor. (`Theme.FromPalette(...)` is the shortcut when you don't need slot-level control.) +3. A `Styles` helper module of semantic markup wrappers (`Accent`, `Title`, `Muted`, `Danger`, `Key`, `Hints`, `Section`, `Rule`) that every screen uses instead of raw `[color]` tags. Wrap primary content explicitly — unstyled markup falls back to control defaults. + +Apply control-level theming with a semantic `Role` (`ColorRole`: Primary, +Success, Danger, …) so a control's colors come from the theme. + +## Escape untrusted text, always + +Any string the app didn't author — process stdout, model names, user input — +must be escaped before entering a markup control, or stray `[...]` is parsed as +tags and can throw at render time: + +```csharp +static string EscapeMarkup(string t) => t.Replace("[", "[[").Replace("]", "]]"); +// or: MarkupParser.Escape(t) +static int VisibleWidth(string markup) => /* strip [tags] */ ...; // for alignment math +``` + +Add self-authored markup verbatim; escape everything else. + +## Threading discipline (never block the UI thread) + +`system.Run()` is a single cooperative UI loop. Never call `.Result` / `.Wait()` +/ `Thread.Sleep` / sync I/O in a handler — it freezes rendering and input and +trips the watchdog. `await` real async work; marshal any resulting UI mutation +with `system.EnqueueOnUIThread(...)` (fire-and-forget) or `system.InvokeAsync(...)` +(marshal + await). Turn on `InstallSynchronizationContext: true` so `await` +continuations in handlers resume on the UI thread automatically. See +`references/architecture.md`. + +## Never crash the TUI + +Wrap every fire-and-forget action (navigation, hotkey handler, menu pick) so a +thrown exception can't tear down the app: treat `OperationCanceledException` as a +quiet cancel; for anything else, log it, show a toast +(`NotificationSeverity.Danger`), and return to a safe screen (`PopToRoot()`). + +## Checklist for a polished app + +- One maximized window; screens swap content; you own the header/breadcrumb/hints. +- Every screen: a hint bar matching its keys, an actionable empty state, `Esc` goes back. +- All color via a `Styles`/`Palette`/theme layer — no hex in screens. +- Every dialog clamped to `DesktopDimensions`; modals closed with `CloseModalWindow`. +- Untrusted text escaped; UI mutations from background work marshalled. +- Every async action wrapped so it can never crash the loop. diff --git a/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/architecture.md b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/architecture.md new file mode 100644 index 0000000..c970615 --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/architecture.md @@ -0,0 +1,119 @@ +# SharpConsoleUI architecture + +How the framework renders, lays out, threads, and stays AOT-safe. This governs +the correctness rules an agent must respect when writing SharpConsoleUI code. + +## Rendering pipeline + +`ConsoleWindowSystem.Run()` drives a tight main loop (frame-limited, ~60 FPS by +default via `FrameDelayMilliseconds`). Each frame `ProcessOnce()` handles input, +drains queued events, and conditionally renders. Rendering is a multi-stage, +double-buffered pipeline with dirty tracking at three levels (a per-window +`PendingWork` intent accumulator of `Repaint`/`Relayout`, plus per-cell and +per-line dirty tracking) and occlusion culling. + +```mermaid +flowchart TD + A["Application: window creation, control invalidation"] --> B["ConsoleWindowSystem: event loop, frame limiting, UpdateDisplay()"] + B --> C["Renderer: build render list (Z-order), occlusion cull, 3 passes (Normal -> Active -> AlwaysOnTop)"] + C --> D["Window content (DOM): Measure -> Arrange -> Paint into per-window CharacterBuffer; Pre/PostBufferPaint compositor hooks"] + D --> E["ConsoleBuffer (screen double buffer): direct cell copy, diff-based (Cell/Line/Smart)"] + E --> F["Physical console: raw libc write (Unix) / Console.Write (Windows); ANSI generated once"] +``` + +Key components: `ConsoleWindowSystem` (loop + coordinator), `Renderer` +(multi-pass, occlusion), `VisibleRegions` (rectangle subtraction / overlap), +`Window` (content + DOM rebuild), `CharacterBuffer` (per-window cell buffer, +DOM paint target), `ConsoleBuffer` (screen-level double buffer, diff render), +`NetConsoleDriver` (console abstraction, I/O). + +Because output is a diff-based cell copy (only changed cells emit ANSI, once at +the end) rather than direct string writes to stdout, redraws stay flicker-free +and cheap — including over SSH, where latency tracks what actually changed, not a +full-screen repaint. + +## Compositor + +Each window owns its own `CharacterBuffer`. The compositor merges per-window +buffers with occlusion culling and per-cell Porter-Duff **alpha blending**, so +overlapping windows, animated desktop backgrounds, and tween/easing animations +composite cleanly. Gradient backgrounds propagate through transparent controls +automatically. `PreBufferPaint` / `PostBufferPaint` hooks let effects (blur, +fade, custom) run at the window-buffer stage. + +## DOM layout system + +Layout is a WPF-style two-pass model over a `LayoutNode` tree that mirrors the +control hierarchy: + +1. **Build tree** — `LayoutNode` tree mirrors controls. +2. **Measure (bottom-up)** — "how much space do you need?" Controls implement + `MeasureDOM(LayoutConstraints)` returning desired `LayoutSize`. +3. **Arrange (top-down)** — "here is your final rect." +4. **Paint** — `PaintDOM(buffer, bounds, clipRect, defaultFg, defaultBg)` renders + to the character buffer at computed positions. + +Track sizing (used by `GridControl` / `HorizontalGridControl`) is `GridLength`: +`Cells(n)` fixed, `Auto()` size-to-content, `Star(weight)` proportional share of +leftover space. Star tracks collapse to 0 when measured unbounded (WinUI +contract); set `ContentSizedStars = true` to self-size at measure and fill at +arrange. Cells support `rowSpan`/`colSpan`, `RowGap`/`ColumnGap`, and per-cell +alignment/margin; splitters make columns resizable. + +## Threading and async model + +The framework is **cooperative single-threaded**: one UI thread (the caller of +`Run()`) owns all rendering and event dispatch. + +| Runs ON the UI thread | Runs OFF it | +|---|---| +| Input dispatch (`ProcessKey` / `ProcessMouseEvent`) | `Window.WindowThreadDelegateAsync` (background `Task`) | +| `Button.Click` and other control handlers | `Task.Run(...)` — anything you push off-thread | +| Paint hooks (`PreBufferPaint` / `PostBufferPaint`) | Timers, file watchers, socket receive loops | +| Window lifecycle (`Activated`, `OnResize`) | | +| Actions marshalled via `EnqueueOnUIThread` / `InvokeAsync` | | + +Golden rules: + +- **Never block the UI thread.** Do not call `.Result` / `.Wait()` / + `.GetAwaiter().GetResult()` inside a handler. With a UI `SynchronizationContext` + installed, an awaited continuation is posted back to the UI thread; if that + thread is blocked waiting on the same task, the continuation never runs and the + loop deadlocks. A watchdog detects the stall. +- **Marshal UI mutations from background threads** with `EnqueueOnUIThread` / + `InvokeAsync`. By default (`InstallSynchronizationContext = false`) awaited + continuations resume on the thread pool, so you marshal back yourself. +- Opt-in `ConsoleWindowSystemOptions.InstallSynchronizationContext = true` makes + `async` handlers resume on the UI thread automatically (WinForms/WPF model). + It is off by default so upgrading apps that block on async freeze-then-recover + instead of deadlocking; it is slated to become the default in a future major + version, so writing `await`-based handlers now is correct under both. +- Per-window opt-in `WithWindowThreadOnUI(...)` runs a window's async delegate on + the UI thread (no marshalling needed) but must never block it; use + `WithAsyncWindowThread` for CPU-bound/blocking work and marshal mutations. A + window may have only one window thread. +- Most notification events have an async twin `Async` (delegate + `AsyncEventHandler`); subscribe to `Click` or `ClickAsync` (or both). + Prefer async handlers when you need to await I/O: `await task`, not `.Result`. +- Thread checks work regardless of the option: `IsOnUIThread` is the + `InvokeRequired` analogue (`!IsOnUIThread` ≡ WinForms `InvokeRequired`), + `VerifyAccess` asserts UI-thread affinity, and `SynchronizationContextInstalled` + reports the *resolved* async mode after `Run()` starts. The WinForms + check-then-marshal pattern translates directly: + `if (!system.IsOnUIThread) system.EnqueueOnUIThread(() => UpdateUI()); else UpdateUI();` + +## NativeAOT + +The library is marked `true` (full trim/AOT +analyzer suite on) and a Release build produces zero `IL` warnings, with no +`[RequiresUnreferencedCode]` / `[RequiresDynamicCode]` in the public surface. CI +publishes a native `AotSmoke` binary (`PublishAot=true`, +`IlcTreatWarningsAsErrors=true`) on every push and runs it headlessly, exercising +the broad control set plus the real AOT-risk paths (`[markdown]`/Markdig, syntax +highlighters, `SpectreRenderableControl`, `ImageControl`/ImageSharp, the +data-binding engine, `[gradient=…]`). Under NativeAOT, expression-tree binding +falls back to the LINQ interpreter rather than `Reflection.Emit`. + +Caveats: this guarantees the *library* won't break your AOT publish — your own +reflection/dynamic-code/serialization is still your responsibility. +`HtmlControl` is the one documented AOT exception. diff --git a/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/controls.md b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/controls.md new file mode 100644 index 0000000..67ed736 --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/controls.md @@ -0,0 +1,89 @@ +# SharpConsoleUI control reference + +SharpConsoleUI ships 40+ built-in controls. All implement `IWindowControl` and +are added to a window (or container) with `AddControl(...)`. Interactive controls +add `IInteractiveControl` (keyboard), `IFocusableControl` (focus), and/or +`IMouseAwareControl` (mouse). Most controls implement `IRoleableControl`, so a +semantic `Role` (a `ColorRole` value: Primary, Success, Danger, …) pulls colors +from the active theme instead of hand-set colors. Prefer the `Controls` static factory + fluent +builders (`Controls.Button("Quit").OnClick(...).Build()`). + +Common properties on every control: `Name` (for `FindControl(name)`), +`Visible`, `Container`, `Tag`, and layout properties (`Width`, `Margin`, +`Alignment`, `StickyPosition`). To move focus programmatically, the simplest call +is `window.FocusControl(control)` (a convenience over `window.FocusManager.SetFocus(...)`). + +## Text and markup + +Use when: displaying styled text, editing text, or rendering markdown/HTML. + +- `MarkupControl` — `Controls.Markup()` / `Controls.Markup("[bold]hi[/]")`. Rich `[tag]text[/]` styling, clickable/keyboard links (`[link=url]`), inline `[markdown]`, `[spinner]`, `[gradient=…]`; optional border/header via `.WithBorder()`. The default label control. Shorthand for a one-line static label: `Controls.Label("[bold]text[/]")` returns a pre-wrapped `MarkupControl`. +- `MultilineEditControl` — multi-line editor with syntax highlighting (13 languages via `SyntaxHighlighters.For(...)`), gutter, find/replace, undo/redo, word wrap. +- `PromptControl` — single-line text input; Enter events, validation, max length. +- `HtmlControl` — parse & render HTML (images, links, tables). One documented NativeAOT caveat. +- `FigleControl` — large ASCII-art (Figlet) text in multiple fonts. + +## Input + +Use when: capturing user actions or values. + +- `ButtonControl` — `Controls.Button("Text").OnClick((sender, e, win) => …)`. Focusable; Enter/click fires `Click`. The `win` arg reaches sibling controls via `FindControl()`. +- `CheckboxControl` — toggle with label; checked/unchecked change events. +- `RadioControl` — single-select group via typed `RadioGroup`; `Required`/`AllowDeselect`, cross-layout grouping. +- `DropdownControl` — click-to-expand selection list; keyboard nav; renders via portal. +- `SliderControl` / `RangeSliderControl` — value slider / dual-thumb range slider; step/large-step, keyboard + mouse drag; `RangeSlider` enforces `MinRange`. +- `DatePickerControl` / `TimePickerControl` — locale-aware pickers; segmented editing, calendar popup, 12h/24h, min/max. +- `FormControl` — labeled two-column form; typed field overloads (text/multiline/checkbox/dropdown/radio/slider), sections, validation, `GetValues`/`Submit`/`Submitted`. Also loadable from declarative XML (see `references/recipes.md`). + +## Data and collections + +Use when: showing rows, lists, or hierarchies. + +- `TableControl` — interactive data grid: virtual data, sorting, filtering (AND/OR), inline editing, multi-select, cell navigation, scrollbars. +- `ListControl` — scrollable single-select list with item activation and keyboard nav. +- `TreeControl` — hierarchical tree; expand/collapse, selection, keyboard nav. +- `GridControl` — WinUI-``-style 2D layout: Fixed/Auto/Star tracks, row/col spans, gaps, any control per cell. `Controls.Grid()`; see the grid recipe in `references/recipes.md`. +- `HorizontalGridControl` — single-row multi-column layout with variable-width columns and splitters. + +## Navigation and layout + +Use when: organizing screens, panes, and chrome. + +- `MenuControl` — menu bar with dropdowns/submenus, separators, keyboard shortcuts. +- `NavigationView` — WinUI-inspired sidebar nav + content area; responsive Expanded/Compact/Minimal modes; content factories. +- `TabControl` — multi-page tabs; keyboard/mouse switching; per-tab state preserved. +- `WizardControl` / `FlowControl` — run a multi-step wizard / a composed flow inline in a pane (vs. modal); `wizard.Run(Flow.Wizard()…)`. +- `CollapsiblePanel` — click-to-expand container; can also serve as a plain panel via `.NonCollapsible()` / `.HideHeader()`. +- `ScrollablePanelControl` — vertical scrolling content area hosting multiple controls (compose with `GridControl` for the ``-in-`` pattern). +- `PanelControl` — bordered container hosting child controls. +- `ToolbarControl` — horizontal button toolbar; auto-height, wrapping, Tab nav. +- `StatusBarControl` — three-zone (left/center/right) status bar; clickable items, shortcut hints. +- `RuleControl` / `SeparatorControl` — horizontal rule/separator, optional title. Factory `Controls.Rule()` / `Controls.RuleBuilder()` (a heavily used divider in real apps). + +## Charts and status + +Use when: showing metrics, progress, or logs. + +- `LineGraphControl` — multi-series line graph; braille/ASCII modes, gradients, Y-axis labels, live updates. +- `BarGraphControl` — horizontal bar graph with gradient thresholds and value labels. +- `SparklineControl` — compact time-series sparkline (block/braille/bidirectional). +- `SpinnerControl` — animated indeterminate-progress spinner; presets or custom frames; also the inline `[spinner]` markup tag. +- `ProgressBarControl` — determinate progress bar; `Controls.ProgressBar()`, `Value` 0.0–1.0 (single solid fill; use `CanvasControl` for a gradient bar). +- `LogViewerControl` — log viewer with auto-scroll, filtering, severity colors. + +## Drawing and media + +Use when: custom graphics, images, or video. + +- `CanvasControl` — free-form drawing surface; 30+ primitives, retained & immediate modes, thread-safe async painting. +- `ImageControl` — image display via Kitty/WezTerm/Ghostty full-resolution with half-block fallback; PNG/JPEG/BMP/GIF/WebP/TIFF; async load. (Not `IRoleableControl`.) +- `VideoControl` — terminal video player; Kitty graphics + half-block/ASCII/braille fallbacks (auto-detected); FFmpeg decode; overlay bar; looping. +- `SpectreRenderableControl` — wrap and display any Spectre.Console renderable (Table, Tree, Panel, Chart, …). +- `ChatTranscriptControl` — agent/chat transcript: role-tagged messages, token-by-token streaming, collapsible tool messages, thinking indicator. + +## Terminal and portal (special) + +- `TerminalControl` — PTY-backed terminal emulator; full xterm-256color, keyboard/mouse passthrough, auto-resize. Two build paths: `Controls.Terminal("bash"|"pwsh"|…).Open(ws)` opens it in its own window; or `Controls.Terminal().WithWorkingDirectory(dir).WithExe(...).WithArgs(...).Build()` returns a `TerminalControl` you place like any control (e.g. `tabControl.AddTab("Shell", term)`). Default exe: `bash` on Linux, `cmd.exe` on Windows. Runs on **Linux and Windows 10 1809+** (openpty on Linux, ConPTY on Windows); throws `PlatformNotSupportedException` elsewhere. **On Linux you must call `PtyShim.RunIfShim(args)` as the very first line of `Main`** (before any UI init) or the child process never starts — it is a safe no-op on Windows, so keep it in cross-platform code. +- `PortalContentContainer` — host child controls in [portal overlays](https://nickprotop.github.io/ConsoleEx/) (dropdowns, menus, tooltips) with mouse/keyboard routing and focus tracking. See the portal recipe in `references/recipes.md`. + +For wiring these into windows and layouts, see `references/recipes.md`. diff --git a/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/features.md b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/features.md new file mode 100644 index 0000000..2c744dd --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/features.md @@ -0,0 +1,117 @@ +# SharpConsoleUI visual features + +Gradients, transparency/compositing, animations, desktop background, and +image/video/syntax rendering. Exact API names are as they appear in the docs. + +## Gradients + +Built on `ColorGradient`. Works in text (markup) and window/desktop backgrounds. + +- Markup tag: `[gradient=name]`, `[gradient=c1->c2]`, `[gradient=c1->c2->c3]` (separator `->` or `→`; nested `[bold]`/`[underline]` preserved). +- `ColorGradient.Predefined["spectrum"|"warm"|"cool"]`; `ColorGradient.FromColors(Color, Color, …)`; `ColorGradient.Parse("red->blue" | "#FF0000->#0000FF" | "spectrum")`; `.Interpolate(t)` for a single sampled color. +- `GradientDirection`: `Horizontal`, `Vertical`, `DiagonalDown`, `DiagonalUp`. +- Window backgrounds: `WindowBuilder.WithBackgroundGradient(gradient, direction)` or `window.BackgroundGradient = new GradientBackground(gradient, direction)` (set `null` to remove; painted via `PreBufferPaint`, controls paint on top). +- Direct buffer fill: `buffer.FillGradient(LayoutRect, gradient, direction)`. +- Gradients can fade to transparent — `ColorGradient.BlendColors` interpolates all 4 RGBA channels: `ColorGradient.FromColors(Color.Cyan, Color.FromArgb(0,0,255,255))`. + +```csharp +new WindowBuilder(ws) + .WithBackgroundGradient(ColorGradient.FromColors(Color.DarkBlue, Color.Black), GradientDirection.Vertical) + .Build(); +``` + +## Alpha blending and transparency + +Per-cell Porter-Duff "over" compositing; the result is always opaque (A=255). + +- Colors: `new Color(r,g,b,a)`, `Color.Transparent` (A=0), `Color.Red.WithAlpha(128)`; `Color.Blend(src, dst)` (src.A==255→src, ==0→dst, else RGB blend). Foreground blends against the *resolved* background. +- Transparent window: `WindowBuilder.WithBackgroundColor(new Color(r,g,b,a))` with a<255. Default (no brush) = true transparency: empty cells bubble up characters from below with a parabolic foreground fade. +- `TransparencyBrush` overrides the compositing style (not the color/alpha): `Acrylic()` (Gaussian fg+bg blend, `Acrylic(fadeExponent:…)`/`Acrylic(textCoverage:…)`), `Mica()` (color blend, no character bubble-up), `Tinted()` (flat bg overlay), `WithCustom((topCell, cellBelow, alpha) => new Cell(...))`. Apply via `WindowBuilder.WithTransparencyBrush(...)`. +- Control-level alpha via markup: `[#RRGGBBAA]text[/]` (fg), `[on #RRGGBBAA]text[/]` (bg). +- Terminal-native transparency: set `windowSystem.DesktopBackgroundColor = Color.Transparent` and/or window `.WithBackgroundColor(Color.Transparent)` — emits ANSI `\x1b[49m` (terminal default bg) when a cell's alpha is 0. Needs emulator support (Kitty `background_opacity`, Alacritty `opacity`, WezTerm `window_background_opacity`). `ConsoleWindowSystemOptions.TerminalTransparencyMode`: `PreserveWindowColor` (default) vs `PreserveTerminalTransparency`. +- Perf: opaque windows take a zero-overhead fast path; transparent windows pay a per-cell `ResolveCellBelow` cost that scales with overlap. Keep alpha ~160–220 for a usable effect. + +```csharp +new WindowBuilder(ws) + .WithBackgroundColor(new Color(0, 20, 60, 180)) + .WithTransparencyBrush(TransparencyBrush.Mica()) + .BuildAndShow(); +``` + +## Compositor effects + +Hook the window's `CharacterBuffer` before/after control painting for blur, fade, +glow, overlays, or screenshots. + +- Order per frame: rebuild DOM → layout → `Buffer.Clear()` → **`PreBufferPaint`** → `PaintDOM()` → **`PostBufferPaint`** → convert to lines. +- `Window.Renderer` (`WindowRenderer?`) exposes `Buffer` and the two events. `PreBufferPaint` is for backgrounds (fires after clear); `PostBufferPaint` is for post-processing (fires after controls, within the render lock — defer heavy work via `Task.Run`). Delegate signature: `(CharacterBuffer buffer, LayoutRect dirtyRegion, LayoutRect clipRect)`. +- `CharacterBuffer`: `GetCell/SetCell(x,y,…)`, `WriteString(x,y,text,fg,bg)`, `CopyFrom(other, rect)`, `CreateSnapshot()` → deep-copied `BufferSnapshot` (safe for screenshots/recording). `Cell` has `Rune Character`, `Foreground`, `Background`, `Decorations`, `Dirty`, `IsWideContinuation`, `Combiners` — always skip/preserve `IsWideContinuation` cells so you don't split wide (CJK/emoji) pairs. +- Best practice: process only `dirtyRegion`; use a temp buffer + `CopyFrom` to avoid feedback loops. `ColorBlendHelper.ApplyColorOverlay(buffer, color, intensity, …)` for tints. `CanvasControl` is the per-control alternative (local coords, `BeginPaint()/EndPaint()`, 30+ drawing primitives, thread-safe). + +## Animations + +Time-based tweens integrated into the render loop via `ConsoleWindowSystem.Animations` (`AnimationManager`). + +- `ws.Animations.Animate(from, to, duration, easing?, onUpdate, onComplete?)` — overloads for `float`/`int`/`byte`/`Color` or a custom `IInterpolator`. Also `HasActiveAnimations`, `ActiveCount`, `Cancel(anim)`, `CancelAll()`, `Add(IAnimation)`. +- `EasingFunctions`: `Linear`, `EaseIn`, `EaseOut`, `EaseInOut`, `Bounce`, `Elastic`, `SinePulse` (flash/pulse). Any `t => …` delegate works as custom easing. +- `WindowAnimations` transitions (use `PostBufferPaint` internally): `FadeIn(window, …)`, `FadeOut(window, …)`, `SlideIn(window, SlideDirection, …)`, `SlideOut(window, SlideDirection, …)`. `SlideDirection`: `Left/Right/Top/Bottom`. +- Delta capped at `MaxFrameDeltaMs` (33ms) so animations don't jump-complete after idle. Disable globally with `ConsoleWindowSystemOptions.EnableAnimations`. + +```csharp +WindowAnimations.FadeOut(window, onComplete: () => ws.CloseWindow(window)); +WindowAnimations.SlideIn(window, SlideDirection.Left, easing: EasingFunctions.Bounce); +``` + +## Desktop background + +The area behind all windows, managed by `DesktopBackgroundService` (cached buffer, +re-rendered only on change). Layers: base fill → gradient overlay → pattern overlay. + +- Startup: `ConsoleWindowSystemOptions(DesktopBackground: DesktopBackgroundConfig.FromGradient(gradient, direction))`. Runtime: `windowSystem.DesktopBackground = DesktopBackgroundConfig.FromGradient(...)` (set `null` to revert). +- Patterns: `DesktopBackgroundConfig.FromPattern(DesktopPattern)`. Built-ins in `DesktopPatterns`: `Checkerboard`, `Dots`, `HatchDown`/`HatchUp`, `Crosshatch`, `LightShade`/`MediumShade`/`DenseShade`, `HorizontalLines`, `VerticalLines`, `Grid`. Custom: `new DesktopPattern(char[,])` with optional per-cell color grids. +- Animated: set `PaintCallback` on `DesktopBackgroundConfig` (runs on the render thread every `AnimationIntervalMs`, default 100ms — keep it fast). Built-in effects: `DesktopEffects.ColorCycling(...)`, `DesktopEffects.Pulse(...)`, `DesktopEffects.DriftingGradient(...)`. + +```csharp +windowSystem.DesktopBackground = DesktopEffects.ColorCycling(cycleDurationSeconds: 12.0, direction: GradientDirection.Vertical); +``` + +## Syntax highlighting + +Lexical (token-based) highlighting; namespace `SharpConsoleUI.Highlighting`. 13 +built-ins (`csharp`/`cs`, `bash`/`sh`/`shell`/`zsh`, `json`, `javascript`/`js`, +`css`, `html`, `xml`, `yaml`/`yml`, `razor`/`cshtml`, `dockerfile`/`docker`, `sln`, +`diff`/`patch`, `markdown`/`md`), all `ISyntaxHighlighter`. + +- Registry: `SyntaxHighlighters.For(lang)` → `ISyntaxHighlighter?`, `Register(lang, highlighter)` (additive), `Has(lang)`. +- Consumers: Markdown fenced code blocks (auto), and `MultilineEditControl` via `.WithSyntaxHighlighter(SyntaxHighlighters.For("csharp"))` or the `SyntaxHighlighter` property. +- Custom: implement `ISyntaxHighlighter.Tokenize(line, lineIndex, startState)` → `(tokens, endState)`; register to reach both consumers. + +## Image rendering + +`ImageControl` over a `PixelBuffer`, auto-selecting Kitty graphics (full-res) or +Unicode half-block fallback. + +- Load: `PixelBuffer.FromFile(path)` / `FromStream(stream)` / `FromImageSharp(...)` / `FromArgbArray(...)` (ImageSharp: PNG/JPEG/BMP/GIF/TIFF/TGA/PBM/WebP). `Controls.Image(pixelBuffer)`. +- `ImageControl.Source` (`PixelBuffer?`), `ScaleMode` (`ImageScaleMode`: `Fit` default, `Fill`, `Stretch`, `None`); `InvalidateImageCache()` to swap at runtime. +- Kitty protocol (Kitty/WezTerm/Ghostty) is detected automatically; half-block (`▀`) fallback gives 2× vertical resolution elsewhere. Cap height explicitly (e.g. `.Height = 12`) so an image doesn't crowd out other controls. + +```csharp +window.AddControl(Controls.Image(PixelBuffer.FromFile("photo.png"))); +``` + +## Video playback + +`VideoControl` plays files/streams via an FFmpeg subprocess (FFmpeg must be on +PATH; otherwise the control shows an in-place install hint). + +- `Controls.Video()` / `Controls.Video("path")`; builder `.WithSource(...)`/`.WithFile(...)`, `.WithRenderMode(mode)`, `.WithTargetFps(n)`, `.WithLooping()`, `.WithOverlay()`, `.Fill()`, `.OnPlaybackStateChanged(...)`, `.OnPlaybackEnded(...)`. +- `VideoRenderMode`: `Auto` (default — Kitty where capable, else HalfBlock), `Kitty`, `HalfBlock`, `Ascii`, `Braille`. Requesting Kitty on a non-Kitty terminal silently falls back to HalfBlock. +- Methods: `Play()`, `Pause()`, `TogglePlayPause()`, `Stop()`, `CycleRenderMode()`, `PlayFile(path)`, `Stream(url)` (HTTP/HLS/RTSP/RTMP; live streams have no seek, `DurationSeconds`=0). Keys when focused: Space play/pause, M cycle mode, L loop, R refresh, Esc stop. +- Threading: playback runs on a background thread and calls `Container?.Invalidate(Invalidation.Relayout)`; marshal state changes to the UI thread with `EnqueueOnUIThread`. Dispose on window close. + +```csharp +var video = Controls.Video("video.mp4").Fill().Build(); +window.AddControl(video); +video.Play(); +window.OnClosed += (_, _) => { video.Stop(); video.Dispose(); }; +``` diff --git a/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/markup.md b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/markup.md new file mode 100644 index 0000000..31680b0 --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/markup.md @@ -0,0 +1,130 @@ +# SharpConsoleUI markup language + +Markup is a first-class, cross-cutting feature: a native Spectre-compatible +`[tag]text[/]` parser that produces typed `Cell` structs directly (no ANSI +intermediate, no external dependency). **It works everywhere text is rendered** — +`MarkupControl` labels, window titles, status bars, table cells, tree nodes, +list items, tooltips, and any control that renders markup — and the same string +can mix colors, decorations, links, spinners, gradients, and Markdown. + +Prefer markup for all styled text instead of manual color handling. + +## Basic syntax + +``` +[style]text[/] +``` + +- `[style]` = any space-separated combination of color names, hex/RGB colors, and decorations. +- `[/]` closes the most recent opening tag; tags nest and `[/]` pops the innermost. +- Escape literal brackets by doubling: `[[` → `[`, `]]` → `]` (e.g. `items[[0]]` shows `items[0]`). + +```csharp +"[bold red]Error:[/] Something went wrong" +"[italic underline blue]Fancy blue text[/]" +"[bold]Bold [red]bold+red[/] just bold again[/]" // nesting +``` + +## Colors + +- **Named:** basic 16 (`red`, `lime`, `cyan`/`aqua`, `grey`/`gray`, …) plus aliases (`darkred`→maroon, etc.) and a large extended set (`orange1`, `deepskyblue1`, `hotpink`, `gold1`, …). +- **Grey scale:** `grey0` (black) … `grey100` (white). +- **Hex:** `[#RRGGBB]`, `[#RGB]`, `[#RRGGBBAA]` (8-digit sets foreground alpha, Porter-Duff "over" the cell background; `AA=00` invisible, `AA=FF` opaque). +- **RGB:** `[rgb(r,g,b)]`. +- **Background:** `on` — `[white on red]`, `[on green] OK [/]`. + +```csharp +"[#FF8000]Orange[/]" +"[#00DCDC80]Semi-transparent cyan[/]" // 50% opacity, composites onto background +"[white on red]Error banner[/]" +``` + +### Fill background to end of line + +`[fillwidth]` is a **self-closing** marker (no `[/]`, emits nothing) placed at +the end of a line; it extends that line's trailing background to the full width +(turns a per-line tint into a solid block). Only affects row-painting hosts like +`MarkupControl`; a no-op in plain parsing. Escape as `[[fillwidth]]`. + +```csharp +"[on grey19] Build succeeded [/][fillwidth]" // full-width shaded banner +``` + +## Text decorations + +`bold`, `dim`, `italic`, `underline`, `strikethrough` (`strike`), `invert` +(`reverse`), `blink` (`slowblink`/`rapidblink`). Combine freely: +`[bold yellow on darkblue]Warning[/]`. + +## Inline spinner + +`[spinner]` embeds an animated spinner glyph inline in any markup text — it +animates wherever markup renders (labels, status bars, titles, table cells) with +no separate control. Styles: `[spinner]`/`braille` (default), `circle`, `dots`, +`line`, `arc`, `bounce`, `star`, `growvertical`, `growhorizontal`, `toggle`, +`arrow`, `bouncingbar`, `aestheticbar`, `brailledots`, `dotsbounce`. Override +speed with a trailing ms value: `[spinner dots 250]`. The glyph inherits the +surrounding color scope and reserves a fixed width (no reflow). Requires a +running `ConsoleWindowSystem` with animations enabled; renders a static glyph +otherwise. Escape as `[[spinner]]`. For a placeable control, use `SpinnerControl`. + +```csharp +"[yellow]Saving [spinner][/]" +``` + +## Inline Markdown + +`[markdown]…[/]` parses its inner content as Markdown (via Markdig) and renders +it as native markup — so it works anywhere markup is accepted and mixes with +ordinary markup. Supports headings (H1–H6), emphasis, inline code, links (text +shown, URL dropped), bullet/numbered/nested lists, blockquotes, horizontal rules, +fenced/indented code blocks, and GitHub-style pipe tables. + +- **Non-nesting:** a region ends at the *first* `[/]`. Unclosed → renders to end of string. Escape as `[[markdown]]`. +- **Code-block highlighting:** a fenced block with a language hint is auto syntax-highlighted. Built-ins include `csharp`/`cs`, `bash`/`sh`, `json`, `javascript`/`js`, `css`, `html`, `xml`, `yaml`/`yml`, `razor`, `dockerfile`, `sln`, `diff`, `markdown`/`md`. No/unknown hint → flat shaded block. Register more globally with `SyntaxHighlighters.Register("toml", …)` or per-style via `MarkdownStyle.CodeHighlighters`. +- **Styling** via the `SharpConsoleUI.Configuration.MarkdownStyle` record (theme-agnostic — the parser is static): `CodeForeground`/`CodeBackground`, `QuoteColor`, `LinkColor`, `BorderColor`, `TableRowSeparators` (false=header rule only, true=full grid), `BulletGlyph`, `ListIndent`, `QuoteGlyph`, `H1Color`…`H6Color`. Set `MarkdownStyle.Default` globally or `.WithMarkdownStyle(s => s with { … })` per build. + +```csharp +var c = Controls.Markdown("# Report\n\n**Status:** OK\n\n- one\n- two") + .WithMarkdownStyle(s => s with { LinkColor = Color.Cyan1, TableRowSeparators = true }) + .Build(); +window.AddControl(c); +``` + +Fluent helpers: `Controls.Markdown(text)`, `.AddMarkdown(text)` / `.WithMarkdown(text)`, +`MarkupControl.SetMarkdown(text)`, `MarkupControl.MarkdownStyle`. + +## Updating MarkupControl content + +`MarkupControl` updates live with a `StringBuilder`/`Console`-style API: + +- `Append(text)` — inline append onto the current last line (new line only at embedded `\n`). +- `AppendLine(text)` — append as its own new line. +- `AppendLines(lines)` — each item as its own line. +- `SetContent(lines)` — replace all content. + +`Append`/`AppendLine` are the recommended pair (`AppendText`/`AppendInline` remain +as aliases). The builder mirrors this: `Controls.Markup().Append(...).AddLine(...)`. + +```csharp +var c = new MarkupControl(new List()); +c.Append("[green]●[/] "); +c.Append("all healthy"); // -> "● all healthy" (same line) +c.AppendLine("[grey]done[/]"); // -> next line +``` + +## MarkupParser API (SharpConsoleUI.Parsing) + +For programmatic parsing/measuring outside a control: + +- `MarkupParser.Parse(text, defaultFg, defaultBg)` → `List` (char + fg + bg + decoration). +- `MarkupParser.ParseLines(text, width, defaultFg, defaultBg)` → `List>` with word-wrap; style stack carries across line breaks. +- `MarkupParser.StripLength(text)` → visible length (tags stripped; max line length for multi-line). +- `MarkupParser.Truncate(text, maxVisible)` → truncates to visible length, preserving/closing tags. +- `MarkupParser.Escape(text)` → doubles brackets so plain text isn't interpreted (`array[0]` → `array[[0]]`). +- `MarkupParser.Remove(text)` → strips all tags to plain text (escaped brackets become single). + +Always run untrusted/dynamic text through `MarkupParser.Escape(...)` before +embedding it in markup, or stray brackets will be parsed as tags. + +For `[gradient=…]` see `references/features.md`. diff --git a/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/recipes.md b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/recipes.md new file mode 100644 index 0000000..d5fd8f5 --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/recipes.md @@ -0,0 +1,291 @@ +# SharpConsoleUI recipes + +Verified patterns for wiring windows, layout, dialogs, portals/toasts, and forms. +All code uses APIs from the SharpConsoleUI docs; do not invent method names. +Assumes `windowSystem` is a `ConsoleWindowSystem` and `using` directives for +`SharpConsoleUI`, `SharpConsoleUI.Builders`, `SharpConsoleUI.Controls`, +`SharpConsoleUI.Drivers`. + +Two idioms real apps use that keep code shorter: + +- **`.Build()`** — control builders have an implicit conversion to their control, + so `.Build()` is technically optional in an `AddControl(...)` argument. Real apps + still call it explicitly for clarity and because you usually keep the control in a + variable (to look it up, focus, or bind it later); prefer showing `.Build()`. +- **Run the loop off the entry thread:** `await Task.Run(() => windowSystem.Run())` + is the common way to start it so `Main` can stay `async`. + +If you use `TerminalControl` anywhere, make the **first line of `Main`** +`if (SharpConsoleUI.PtyShim.RunIfShim(args)) return 127;` — on Linux the host +re-launches itself as the PTY child, and this short-circuits that re-entry (a safe +no-op returning `false` on Windows). See `references/controls.md`. + +## Window builder configuration + +`WindowBuilder` is a fluent API; `.Build()` returns the window (add it with +`windowSystem.AddWindow(window)`), and `.BuildAndShow()` builds and shows in one +call. + +```csharp +var window = new WindowBuilder(windowSystem) + .WithTitle("My Window") + .WithSize(80, 25) + .AtPosition(4, 2) // or .Centered() + .WithMinimumSize(40, 10) + .WithColors(Color.White, Color.Grey11) + .WithPadding(1) // inner space; also (h, v) or new Padding(l,t,r,b) + .Movable(true) + .Resizable(true) + .Build(); +``` + +Border/chrome options: `.Borderless()` keeps the invisible 1-cell frame (still +interactive); `.Frameless()` reclaims the frame entirely (chrome-less, +non-interactive frame — content fills the whole rect). + +## Full-screen / single-window app + +SharpConsoleUI is well suited to full-screen TUIs, not just floating windows. +For a full-screen app the main window should have **no title bar and no title +buttons** — use `.Frameless()`, which reclaims the border/title frame entirely: +no title bar, no drag handle, no resize grip, and no title buttons, with content +filling the whole rect. Combine it with `.Maximized()` (size to the whole +desktop): + +```csharp +new WindowBuilder(windowSystem) + .Frameless() // no title bar, no title buttons — content owns the whole rect + .Maximized() // fills the terminal + .WithPadding(1) // optional breathing room + .AddControl(rootLayout) // e.g. a GridControl that owns the whole screen + .BuildAndShow(); +``` + +Do not use `.Borderless()` for this: it blanks the border but keeps the invisible +1-cell interactive frame. `.Frameless()` is the chrome-less, title-less choice for +a full-screen main window. + +Real single-window apps often keep a bordered window but lock it down instead of +going frameless — the same visual result with a title kept for the theme: + +```csharp +var system = new ConsoleWindowSystem( + new NetConsoleDriver(RenderMode.Buffer), + myTheme, // ITheme overload (see references/system.md) + new ConsoleWindowSystemOptions(InstallSynchronizationContext: true, + ShowTopPanel: false, ShowBottomPanel: false)); + +var window = new WindowBuilder(system) + .WithTitle("app") + .Maximized() + .Movable(false).Resizable(false).Closable(false).HideTitle() + .WithPadding(1, 0) + .Build(); +system.AddWindow(window); + +system.RegisterGlobalShortcut(ConsoleModifiers.Control, ConsoleKey.Q, () => system.RequestExit(0)); +var exitCode = system.Run(); // blocks; returns the code passed to RequestExit/Shutdown +``` + +`RegisterGlobalShortcut(modifiers, key, Action | Func)` registers an +app-wide hotkey; `system.RequestExit(code)` (or `system.Shutdown(code)`) ends the +loop and makes `Run()` return that code. `.HideTitle()` hides the title text while +keeping the bordered frame; `.Closable(false)` removes the close button. + +## Multi-window desktop + +Full-screen and multi-window are equally first-class. A `ConsoleWindowSystem` +owns any number of bordered windows that stack in z-order and can be +moved/resized/minimized/maximized. Add several windows; each is a `WindowBuilder` +that `.BuildAndShow()`s onto the desktop: + +```csharp +var explorer = new WindowBuilder(windowSystem) + .WithTitle("Explorer") + .WithBounds(2, 2, 40, 20) // x, y, width, height in desktop coords + .WithBorderStyle(BorderStyle.Rounded) + .AddControl(tree) + .BuildAndShow(); + +var editor = new WindowBuilder(windowSystem) + .WithTitle("Editor") + .WithBounds(44, 2, 60, 24) + .Movable(true) + .Resizable(true) + .AddControl(multilineEdit) + .BuildAndShow(); +``` + +Windows draw a border and title bar and are interactive by default (drag the +title to move, edges to resize). Border styles: `DoubleLine` (default), `Single`, +`Rounded`, `None` (invisible frame, still interactive), `Frameless` (no frame). +For a window the user must dismiss before returning to the rest, make it modal +with `.AsModal()` (or `.WithModal(true)`). Runtime state control: +`window.Maximize()`, `window.Minimize()`, `window.Restore()`; positions are in +desktop coordinates (excluding any status bars). + +Window state also has `.Minimized()` and `.WithState(WindowState.Normal | +Maximized | Minimized)`; at runtime `window.Maximize()`, `window.Minimize()`, +`window.Restore()`. Move/resize still work in code on a frameless window +(`SetPosition` / `SetSize`) — only the mouse grab surface is gone. + +## Grid layout with a data control + +`GridControl` (`Controls.Grid()`) is the 2D layout primitive. Tracks are +`GridLength.Cells(n)` (fixed), `.Auto()` (size-to-content), `.Star(weight)` +(proportional). Place controls explicitly with `Place(control, row, col, +rowSpan, colSpan)` or append in row-major order with `.Add(control)`. + +```csharp +var table = Controls.Table() + // ... configure columns/rows per TableControl ... + .Build(); + +var grid = Controls.Grid() + .Columns(GridLength.Cells(20), GridLength.Star(1), GridLength.Star(2)) + .Rows(GridLength.Auto(), GridLength.Star(1, min: 5)) + .RowGap(1) + .ColumnGap(2) + .Place(Controls.Markup("[bold]Report[/]").Build(), 0, 0, colSpan: 3) // header row + .Place(sidebar, 1, 0) + .Place(mainPanel, 1, 1) + .Place(table, 1, 2) + .Build(); + +window.AddControl(grid); +``` + +If a Star grid is placed in a content-sizing parent and renders as nothing, set +`grid.ContentSizedStars = true` (self-size at measure, fill at arrange). The grid +never scrolls; wrap it in a `ScrollablePanelControl` for the +``-in-`` pattern. + +## Built-in dialogs (confirm / prompt / progress) + +`Dialogs` (`SharpConsoleUI.Dialogs`) provides typed, themed modal dialogs usable +from any async handler — no flow setup required. + +```csharp +using SharpConsoleUI.Dialogs; + +button.ClickAsync += async (s, e) => +{ + bool ok = await Dialogs.ConfirmAsync(windowSystem, "Save changes", "Save before closing?"); + if (!ok) return; + + string? name = await Dialogs.PromptAsync(windowSystem, "Your name", + "What should we call you?", initial: "World"); + + string? result = await Dialogs.RunWithProgressAsync(windowSystem, + "Syncing", "Connecting…", + async (ct, progress) => + { + progress.Report("Downloading…"); + await Task.Delay(1000, ct); + return "done"; + }); +}; +``` + +`ConfirmAsync` returns `bool`, `PromptAsync` returns `string?` (null on cancel), +`RunWithProgressAsync` returns the worker's result. All take an optional +`NotificationSeverityEnum severity` and `Window? parent`. + +## Custom modal dialog with a result + +Once a dialog needs custom content or buttons beyond confirm/prompt/progress, the +common real-world pattern is a modal window plus a `TaskCompletionSource` that +`Run()`-style callers can `await`. Build the window `.AsModal()`, close it with +`window.Close()`, and complete the source with the result: + +```csharp +async Task AskNameAsync(ConsoleWindowSystem ws) +{ + var tcs = new TaskCompletionSource(); + + var input = Controls.Prompt("Name").WithName("name").Build(); + var dialog = new WindowBuilder(ws) + .WithTitle("Rename") + .WithSize(40, 8) + .Centered() + .AsModal() + .Build(); + + dialog.AddControl(input); + dialog.AddControl(Controls.Button("OK") + .OnClick((s, e, win) => { tcs.TrySetResult(input.Input); ws.CloseModalWindow(dialog); }) + .Build()); + dialog.AddControl(Controls.Button("Cancel") + .OnClick((s, e, win) => { tcs.TrySetResult(null); ws.CloseModalWindow(dialog); }) + .Build()); + + ws.AddWindow(dialog); + dialog.FocusControl(input); + return await tcs.Task; +} +``` + +Close a **modal** window with `ws.CloseModalWindow(window)` (not `CloseWindow`) so +the modal stack unwinds correctly; use `ws.CloseWindow(window)` for non-modal +windows. Resolve the result in `OnClosed` or in the button handler via a +`TaskCompletionSource` so the `await`ing caller gets it. Clamp a dialog's size to +the terminal with `ws.DesktopDimensions` before building if content can overflow. + +Reach for this only when the built-in `Dialogs` helpers above don't fit; they +cover the common confirm/prompt/progress cases with less code. For file/folder +selection use the built-in `FileDialogs` (namespace `SharpConsoleUI.Dialogs`): +`FileDialogs.ShowFilePickerAsync(ws, …)`, `FileDialogs.ShowFolderPickerAsync(ws, …)`, +`FileDialogs.ShowSaveFileAsync(ws, …)` — each returns `Task` (null on cancel). + +## Toasts and notifications + +Two systems, both on the `ConsoleWindowSystem` instance: + +```csharp +// ToastService — non-blocking, auto-dismissing corner overlays for transient status. +windowSystem.ToastService.Show("Saved successfully", NotificationSeverity.Success); +string id = windowSystem.ToastService.Show("Sync started", NotificationSeverity.Info); +// keep `id` to dismiss it later + +// NotificationStateService — title+message, optionally modal (blockUi: true) for +// messages the user must acknowledge. +windowSystem.NotificationStateService.ShowNotification("Error", "Disk full", NotificationSeverity.Error, blockUi: true); +``` + +Use `ToastService` for "Saved" / "Sync started"; use `NotificationStateService` +for errors/confirmations that must be read. + +## Portals (overlays) + +Portals render content above the normal layout, unclipped by parent containers — +the mechanism behind dropdowns, context menus, and tooltips. Most controls that +need overlays (e.g. `DropdownControl`, `MenuControl`) manage their own portals. +To host custom overlay content, use `PortalContentContainer`, whose children get +mouse/keyboard routing and focus tracking; portal nodes attach to the root +`LayoutNode` (`AddPortalChild`) and paint last, on top. Reach for a portal when +content must escape its container bounds; reach for `Dialogs` / notifications for +standard modal/transient messages. + +## Forms + +Build a labeled form imperatively with `FormControl`, or declaratively from XML +with `FormXml` (a thin, NativeAOT-safe call-through to the same runtime). + +```csharp +using SharpConsoleUI.Controls.Forms; + +var form = FormXml.FromXml(@" +
+ + + +"); + +window.AddControl(form); +``` + +The XML root must be `
`; fields (``, ``, ``, +``, ``, ``) map to the typed `FormControl` field +overloads, with structure elements `
`, ``, ``. Read values +via the `FormControl` API (`GetValues` / `Submit` / `Submitted`). Use +`FormXml.FromXmlFile(path)` to load from a file. diff --git a/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/system.md b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/system.md new file mode 100644 index 0000000..3bdc86c --- /dev/null +++ b/catalog/Frameworks/SharpConsoleUI/skills/sharpconsoleui/references/system.md @@ -0,0 +1,184 @@ +# SharpConsoleUI system, services, and patterns + +Application-level services, configuration, data binding, flows, distribution, and +when to choose this framework. Exact API names are as they appear in the docs. + +## State services (on `ConsoleWindowSystem`) + +Reached as properties on the `ConsoleWindowSystem` instance (except `FocusManager`, +which is per-`Window`): + +- `PanelStateService` — `TopPanel`/`BottomPanel`, `ShowTopPanel`/`ShowBottomPanel`, `TopStatus`/`BottomStatus` (set-only; updates first `StatusTextElement`), `MarkDirty()`. Shorthand: `windowSystem.BottomPanel`. +- `WindowStateService` — `Windows`, `ActiveWindow`, `IsDragging`/`IsResizing`; `RegisterWindow`, `BringToFront`, `GetWindowsInZOrder()`, `StartDrag/EndDrag`, … +- `window.FocusManager` (per-window; replaced the old system-wide focus service) — `FocusedControl`, `FocusPath`, `SetFocus(control, FocusReason)`, `MoveFocus(bool backward)`, `HandleClick(hit)`, `FocusChanged` event. +- `ModalStateService` — `HasModals`, `TopModal`, `ModalCount`; `PushModal`/`RemoveModal`, `IsModal`, `IsBlockedByModal`. `WindowBuilder.AsModal()` calls `PushModal` internally. +- `NotificationStateService` — `ShowNotification(title, message, severity, blockUi = false, timeout = 5000)`. Separate corner-toast system is `windowSystem.ToastService`. +- `ThemeStateService` — `CurrentTheme`, `SetTheme(ITheme)`, `SwitchTheme(string name)`, `ThemeChanged` event. +- `CursorStateService`, `InputStateService` — cursor visibility/position; key/mouse-button state queries. +- `PluginStateService`, `RegistryStateService` — see below (registry is null unless configured). + +```csharp +windowSystem.PanelStateService.TopStatus = "[bold cyan]Connected[/]"; +windowSystem.NotificationStateService.ShowNotification("Saved", "Doc saved", NotificationSeverity.Success); +``` + +## Panels (top/bottom bars) + +Composable screen-level bars built from **elements** via `PanelBuilder`, configured +through `ConsoleWindowSystemOptions.TopPanelConfig`/`BottomPanelConfig` +(`Func`). Each panel has `Left`/`Center`/`Right` zones. + +- `SharpConsoleUI.Panel.Elements` factories: `StatusText(text)`, `Separator()`, `TaskBar()` (Alt+1–9 window switch), `Clock()`, `Performance()`, `StartMenu()`, `Custom(name)`. Each has fluent config (`.WithColor`, `.WithFormat`, etc.). +- `StartMenuElement.RegisterAction(name, callback, category?, order?)`; a loaded plugin contributes actions via `IPlugin.GetActionProviders()` → `IPluginActionProvider` (`GetAvailableActions()` returning `ActionDescriptor` records, `ExecuteAction(name, context?)`). +- Runtime: `windowSystem.BottomPanel!.FindElement("startmenu")`, `panel.AddLeft/AddCenter/AddRight(...)`, `ClearLeft()`, `MarkDirty()`. +- When no config is supplied, default panels are created with a `StatusTextElement` so `TopStatus`/`BottomStatus` work out of the box. (Replaces the old `StatusBarOptions` model.) + +```csharp +var options = new ConsoleWindowSystemOptions( + TopPanelConfig: p => p.Left(Elements.StatusText("[bold cyan]My App[/]")).Right(Elements.Performance()), + BottomPanelConfig: p => p.Left(Elements.StartMenu()).Center(Elements.TaskBar()).Right(Elements.Clock())); +var windowSystem = new ConsoleWindowSystem(new NetConsoleDriver(RenderMode.Buffer), options: options); +``` + +## Constructing the window system + +`ConsoleWindowSystem` has four constructor overloads — pass the theme and/or a +custom driver at construction: + +- `new ConsoleWindowSystem(RenderMode, options?, registryConfiguration?)` +- `new ConsoleWindowSystem(IConsoleDriver, options?, registryConfiguration?)` +- `new ConsoleWindowSystem(IConsoleDriver, string themeName, options?, registryConfiguration?)` +- `new ConsoleWindowSystem(IConsoleDriver, ITheme, options?, registryConfiguration?)` + +Lifecycle: `system.Run()` blocks the calling thread and returns the exit code. +`system.RequestExit(code)` / `system.Shutdown(code)` end the loop. +`system.RegisterGlobalShortcut(ConsoleModifiers, ConsoleKey, Action | Func)` +registers app-wide hotkeys (e.g. Ctrl+Q to quit). `system.DesktopDimensions` +gives the usable desktop `Size` (for clamping window sizes). + +## Configuration + +`ConsoleWindowSystemOptions` (passed as `options:`): `EnablePerformanceMetrics`, +`EnableFrameRateLimiting`, `TargetFPS` (default 60; 0 = unlimited), +`ShowTopPanel`/`ShowBottomPanel`, `TopPanelConfig`/`BottomPanelConfig`, +`DesktopBackground`, `EnableAnimations`, `InstallSynchronizationContext`, +`TerminalTransparencyMode`. Factories: `.Default`, `.Create(...)`, `.WithMetrics`, +`.WithoutFrameRateLimiting`, `.WithTargetFPS(n)`. Env vars: +`SHARPCONSOLEUI_DEBUG_LOG=`, `SHARPCONSOLEUI_DEBUG_LEVEL=...`, +`SHARPCONSOLEUI_PERF_METRICS=true`. Guidance: 30 FPS for dashboards, 60 general, +unlimited for games/animation. + +## Registry (persistent settings) + +JSON-backed hierarchical key-value store; survives restarts. Enable by passing +`registryConfiguration:` to the `ConsoleWindowSystem` ctor; access via +`windowSystem.RegistryStateService` (null if not configured). + +- `RegistryConfiguration(FilePath = "registry.json", EagerFlush = false, FlushInterval = null, Storage = null)`; `.Default` (per-process platform path) / `.ForFile(path)`. +- `RegistryStateService`: `OpenSection(path)` → `RegistrySection`, `Save()`, `Load()` (auto-load on init, auto-save on dispose). +- `RegistrySection`: typed `GetString/SetString`, `GetInt/SetInt`, `GetBool/SetBool`, `GetDouble/SetDouble`, `GetDateTime/SetDateTime`; AOT-safe `Get/Set(key, …, JsonTypeInfo)`; `HasKey`, `GetKeys()`, `DeleteKey`, `DeleteSection`. `Get*` never throw (return default). Nested paths via `OpenSection("app/ui")`. +- Thread safety: the registry root is thread-safe, but a `RegistrySection` instance is NOT — open a fresh section per thread. `MemoryStorage` for tests. + +```csharp +var registry = windowSystem.RegistryStateService!; +var ui = registry.OpenSection("app/ui"); +string theme = ui.GetString("theme", "ModernGray"); +ui.SetString("theme", "Solarized"); +``` + +## Data binding (MVVM) + +`.Bind()` / `.BindTwoWay()` wire an `INotifyPropertyChanged` view model to control +properties (on controls, builders, and `MenuItem`). + +- One-way: `control.Bind(vm, v => v.Prop, c => c.TargetProp)` (applies initial value immediately); with converter `(…, Func)`. +- Two-way: `control.BindTwoWay(vm, v => v.Prop, c => c.TargetProp)` (re-entrancy guarded); with `toTarget:`/`toSource:` converters. +- Two-way-capable properties include `CheckboxControl.Checked`, `SliderControl.Value`, `DropdownControl.SelectedIndex`, `ListControl.SelectedIndex`, `TabControl.ActiveTabIndex`, `PromptControl.Input` (note: `Input`, not `Text`), `MultilineEditControl.Content`, `TableControl.SelectedRowIndex`, `CollapsiblePanel.IsExpanded`, `TreeNode.IsExpanded/Text`. Display-only one-way targets: `MarkupControl.Text`, `ProgressBarControl.Value`, `BarGraphControl.Value`. +- Every control derives from `BaseControl` (already `INotifyPropertyChanged`) — only your view model must implement it. Bindings are `IDisposable`, stored in the control's `Bindings`, disposed with the control (no manual unsubscribe). AOT-safe (LINQ interpreter fallback). + +```csharp +bar.Bind(vm, v => v.Cpu, c => c.Value); // one-way +prompt.BindTwoWay(vm, v => v.Name, c => c.Input); // two-way +``` + +## Flows (multi-step workflows) + +`SharpConsoleUI.Flows` removes Back-stack / cancellation / navigation boilerplate. + +- `Flow.Run(ws, parent, async ctx => …)` → `FlowResult` (`Completed`, `Value`, `Cancelled`, `Faulted`, `Error`). `FlowContext`: `Token`, `Show(content, title, buttons)`, `Confirm(...)`, `Prompt(...)`, `RunWithProgress(...)`, `Commit()` (Back barrier). Throw `OperationCanceledException` to cancel. +- `Flow.Wizard()` (`TState : new()`) → `FlowWizardBuilder`: `.Seed(state)`, `.WithStepIndicator()`, `.WithTitle(...)`, `.WithSeamlessHost()` (one shared window), `.Step(...)` (code step or content step with `.CanGoNext(...)`/`.OnNext/.OnBack/.OnCancel(...)`), `.Run(ws, parent)`. `FlowVerdict`: `Next`, `Finish`, `Back`, `Cancel`, `Stay`. +- Default host is a modal window per step (`ModalWindowHost`); `SwapContentHost` reuses one window. For **inline** (non-modal) flows, embed `FlowControl` / `WizardControl` in a window region instead. + +```csharp +var result = await Flow.Run(ws, myWindow, async ctx => +{ + if (!await ctx.Confirm("Deploy", "Deploy to staging?", ok: "Deploy")) throw new OperationCanceledException(); + return await ctx.RunWithProgress("Deploying", "Connecting…", + async (ct, progress) => { progress.Report("Uploading…"); await DeployAsync(ct); return "OK"; }); +}); +``` + +## Plugins + +Extend with themes/controls/windows/services without touching core. Implement +`IPlugin` / `PluginBase` (`Info`, `Initialize(ws)`, `GetThemes()`, `GetControls()`, +`GetWindows()`, `GetServicePlugins()`, `Dispose()`). Register via +`windowSystem.PluginStateService.LoadPlugin()` / `LoadPlugin(dllPath)` / +`LoadPluginsFromDirectory(...)`; create with `CreateControl(name)` / +`CreateWindow(name)`; call agnostic services with +`GetService(name).Execute(op, parameters)` (reflection-free). Auto-load via +`PluginConfiguration(AutoLoad, PluginsDirectory)` on the ctor. + +## Clipboard + +`SharpConsoleUI.Helpers.ClipboardHelper.SetText(text)` / `GetText()`. Copy works +transparently over SSH via OSC 52 (falls back to `clip.exe`/`pbcopy`/`wl-copy`/ +`xclip`/`xsel`). Knobs: `ClipboardHelper.Osc52Mode` (`Auto`/`Enabled`/`Disabled`), +`MaxOsc52Bytes` (default ~74000). Bracketed paste is enabled at startup; focused +controls implementing `IPasteTarget` (`MultilineEditControl`, `PromptControl`, +`TableControl`) receive `Ctrl+V` and pasted blocks. + +## Shell scripting and distribution + +- **Pipelines:** a SharpConsoleUI app can be an interactive picker/wizard inside a shell pipeline (like `fzf`/`gum`). On Unix the driver opens `/dev/tty` when stdin/stdout are redirected, keeping the pipe free for data. Read `windowSystem.PipedInput` / `PipedLines` **before** `Run()`, write results **after** it returns. Exit codes follow the `fzf`/`gum` convention (0 selected, 1 cancelled, 2 invalid). .NET 10 file-based apps (`#:package SharpConsoleUI@…` + `dotnet run script.cs`) suit one-off scripts; templates live under the docs `scripting/templates/`. +- **schost:** a separate CLI tool (`SharpConsoleUI.Host` NuGet, `dotnet tool install -g SharpConsoleUI.Host`) that launches an app inside a configured terminal window and packages it for desktop distribution. Commands: `schost init`, `schost run [--inline]`, `schost pack [--installer]`, `schost install`. Config in `schost.json`. `dotnet new schost-app` scaffolds a full-screen NavigationView starter. schost is a launcher/packager, not a renderer — the app still uses the real `NetConsoleDriver`. + +## When to choose SharpConsoleUI (vs other .NET TUI libs) + +The docs position it as "a desktop" — overlapping windows + a real compositor — +distinct from Spectre.Console ("a printer" for rich static output), Terminal.Gui +("a dialog box", single-screen forms with the widest mature control library), and +XenoAtom.Terminal.UI ("a WPF for the terminal", source-generated reactive +bindings, .NET 10 only). They are complementary — SharpConsoleUI can host +Spectre renderables via `SpectreRenderableControl`. + +Prefer SharpConsoleUI for: multi-window desktop-style apps; full-screen apps +(`.Frameless().Maximized()`); visual effects/compositing/transparency; dashboards +and monitoring (independent async window threads); IDE-like tools; an embedded +terminal (`TerminalControl`) alongside UI; terminal video; markup/Markdown +everywhere; MVVM data binding; plugin architectures. + +Prefer something else when: you only need pretty CLI output (Spectre.Console); +you want the widest mature single-screen control set (Terminal.Gui); you need +source-generated reactive bindings (XenoAtom); you target .NET 6 or older +(SharpConsoleUI requires .NET 8+); or you need maximum community/ecosystem size. +Documented gaps: proportional sizing only via Grid `Star` tracks (no flex +allocator), vertical-only dock, and no built-in ColorPicker/HexView. + +## Pattern catalog + +The docs' patterns cookbook (from real apps) covers, among others: app bootstrap +(`NetConsoleDriver(RenderMode.Buffer)` → `ConsoleWindowSystem` → maximized +`WindowBuilder` + `WithAsyncWindowThread` + `OnKeyPressed` → `Run()`); split layout +with a resizable splitter (`HorizontalGrid` + `.WithSplitterAfter`); async data +updates (continuous `WithAsyncWindowThread` loop; fire-and-forget with a +`CancellationTokenSource`; `EnqueueOnUIThread` from external threads — only +`Container?.Invalidate(...)` is safe to call directly off-thread); modal dialog +with result (`ModalBase` + `TaskCompletionSource`); global keyboard +shortcuts (`OnKeyPressed` / `PreviewKeyPressed` before controls consume keys); +debounced search; width-based responsive relayout; control discovery by name +(`.WithName(...)` + `window.FindControl("name")`); rolling log viewer +(`MarkupControl.SetContent` + `MarkupParser.Escape`); and explicit handler cleanup +in an `OnCleanup()` override. Key gotcha for `WithAsyncWindowThread`: its lambda +is built during `Build()`, so every captured control must be declared *before* the +`WindowBuilder` call, while `AddControl` wiring happens *after* `Build()`. diff --git a/catalog/Testing/Official-DotNet-Test/agents/code-testing-builder/AGENT.md b/catalog/Testing/Official-DotNet-Test/agents/code-testing-builder/AGENT.md index c333c1a..3155edf 100644 --- a/catalog/Testing/Official-DotNet-Test/agents/code-testing-builder/AGENT.md +++ b/catalog/Testing/Official-DotNet-Test/agents/code-testing-builder/AGENT.md @@ -6,7 +6,7 @@ description: >- errors, verifying project builds successfully. name: code-testing-builder user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/catalog/Testing/Official-DotNet-Test/agents/code-testing-fixer/AGENT.md b/catalog/Testing/Official-DotNet-Test/agents/code-testing-fixer/AGENT.md index 9787511..2745add 100644 --- a/catalog/Testing/Official-DotNet-Test/agents/code-testing-fixer/AGENT.md +++ b/catalog/Testing/Official-DotNet-Test/agents/code-testing-fixer/AGENT.md @@ -6,7 +6,7 @@ description: >- imports, correcting type mismatches, fixing compilation failures. name: code-testing-fixer user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/catalog/Testing/Official-DotNet-Test/agents/code-testing-generator/AGENT.md b/catalog/Testing/Official-DotNet-Test/agents/code-testing-generator/AGENT.md index 3127b7c..1c21e65 100644 --- a/catalog/Testing/Official-DotNet-Test/agents/code-testing-generator/AGENT.md +++ b/catalog/Testing/Official-DotNet-Test/agents/code-testing-generator/AGENT.md @@ -6,7 +6,7 @@ description: >- coverage plateaus or project-wide coverage/CRAP analysis without writing tests (use coverage-analysis); targeted method/class CRAP scores (use crap-score). name: code-testing-generator -tools: ["agent", "skill", "read", "search", "edit", "execute"] +tools: ["agent", "skill", "read", "search", "edit", "execute", "Task", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] agents: - code-testing-researcher - code-testing-planner diff --git a/catalog/Testing/Official-DotNet-Test/agents/code-testing-implementer/AGENT.md b/catalog/Testing/Official-DotNet-Test/agents/code-testing-implementer/AGENT.md index 18ed700..7cb2d09 100644 --- a/catalog/Testing/Official-DotNet-Test/agents/code-testing-implementer/AGENT.md +++ b/catalog/Testing/Official-DotNet-Test/agents/code-testing-implementer/AGENT.md @@ -7,7 +7,7 @@ description: >- running build-test-fix cycle for generated tests. name: code-testing-implementer user-invocable: false -tools: ["agent", "skill", "read", "search", "edit", "execute"] +tools: ["agent", "skill", "read", "search", "edit", "execute", "Task", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] agents: - code-testing-builder - code-testing-tester diff --git a/catalog/Testing/Official-DotNet-Test/agents/code-testing-linter/AGENT.md b/catalog/Testing/Official-DotNet-Test/agents/code-testing-linter/AGENT.md index ff8de44..d81a733 100644 --- a/catalog/Testing/Official-DotNet-Test/agents/code-testing-linter/AGENT.md +++ b/catalog/Testing/Official-DotNet-Test/agents/code-testing-linter/AGENT.md @@ -6,7 +6,7 @@ description: >- applying lint fixes. name: code-testing-linter user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/catalog/Testing/Official-DotNet-Test/agents/code-testing-planner/AGENT.md b/catalog/Testing/Official-DotNet-Test/agents/code-testing-planner/AGENT.md index af02e32..17a47de 100644 --- a/catalog/Testing/Official-DotNet-Test/agents/code-testing-planner/AGENT.md +++ b/catalog/Testing/Official-DotNet-Test/agents/code-testing-planner/AGENT.md @@ -6,7 +6,7 @@ description: >- creating .testagent/plan.md from research. name: code-testing-planner user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/catalog/Testing/Official-DotNet-Test/agents/code-testing-researcher/AGENT.md b/catalog/Testing/Official-DotNet-Test/agents/code-testing-researcher/AGENT.md index 56c277c..a03eadf 100644 --- a/catalog/Testing/Official-DotNet-Test/agents/code-testing-researcher/AGENT.md +++ b/catalog/Testing/Official-DotNet-Test/agents/code-testing-researcher/AGENT.md @@ -6,7 +6,7 @@ description: >- discovering test frameworks and build commands, producing .testagent/research.md. name: code-testing-researcher user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/catalog/Testing/Official-DotNet-Test/agents/code-testing-tester/AGENT.md b/catalog/Testing/Official-DotNet-Test/agents/code-testing-tester/AGENT.md index c9c196a..e34e50e 100644 --- a/catalog/Testing/Official-DotNet-Test/agents/code-testing-tester/AGENT.md +++ b/catalog/Testing/Official-DotNet-Test/agents/code-testing-tester/AGENT.md @@ -6,7 +6,7 @@ description: >- checking test results and failures. name: code-testing-tester user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/catalog/Testing/Official-DotNet-Test/skills/run-tests/SKILL.md b/catalog/Testing/Official-DotNet-Test/skills/run-tests/SKILL.md index d273bd0..2dfb674 100644 --- a/catalog/Testing/Official-DotNet-Test/skills/run-tests/SKILL.md +++ b/catalog/Testing/Official-DotNet-Test/skills/run-tests/SKILL.md @@ -40,7 +40,7 @@ Detect the test platform and framework, run tests, and apply filters using `dotn | Input | Required | Description | |-------|----------|-------------| -| Project or solution path | No | Path to the test project (.csproj) or solution (.sln). Defaults to current directory. | +| Project or solution path | No | Path to the test project (.csproj) or solution (.sln, .slnf, .slnx). Defaults to current directory. | | Filter expression | No | Filter expression to select specific tests | | Target framework | No | Target framework moniker to run against (e.g., `net8.0`) | @@ -154,6 +154,8 @@ dotnet test --project path/to/ # Run all tests in a solution (sln, slnf, slnx) dotnet test --solution path/to/MySolution.sln +dotnet test --solution path/to/MySolution.slnf +dotnet test --solution path/to/MySolution.slnx # Run all tests in a directory containing a solution dotnet test --solution path/to/ diff --git a/catalog/Tools/Official-DotNet-Diagnostics/agents/optimizing-dotnet-performance/AGENT.md b/catalog/Tools/Official-DotNet-Diagnostics/agents/optimizing-dotnet-performance/AGENT.md index a0af47d..6a71cd4 100644 --- a/catalog/Tools/Official-DotNet-Diagnostics/agents/optimizing-dotnet-performance/AGENT.md +++ b/catalog/Tools/Official-DotNet-Diagnostics/agents/optimizing-dotnet-performance/AGENT.md @@ -1,7 +1,7 @@ --- description: "Analyzes .NET code for performance bottlenecks, recommends concrete optimizations, and guides benchmarking. Scans for ~50 anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I/O. Use when reviewing .NET code performance, optimizing hot paths, reducing allocations, or tuning async/concurrency patterns." name: optimizing-dotnet-performance -tools: ['read', 'search', 'edit', 'task', 'skill', 'web_search', 'web_fetch', 'ask_user'] +tools: ['read', 'search', 'edit', 'task', 'skill', 'web_search', 'web_fetch', 'ask_user', 'Read', 'Glob', 'Grep', 'Edit', 'Write', 'Skill', 'read_file', 'replace', 'write_file', 'glob', 'grep_search'] license: MIT --- diff --git a/external-sources/upstreams/dotnet-skills/dotnet-diag/agents/optimizing-dotnet-performance.agent.md b/external-sources/upstreams/dotnet-skills/dotnet-diag/agents/optimizing-dotnet-performance.agent.md index a0af47d..6a71cd4 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-diag/agents/optimizing-dotnet-performance.agent.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-diag/agents/optimizing-dotnet-performance.agent.md @@ -1,7 +1,7 @@ --- description: "Analyzes .NET code for performance bottlenecks, recommends concrete optimizations, and guides benchmarking. Scans for ~50 anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I/O. Use when reviewing .NET code performance, optimizing hot paths, reducing allocations, or tuning async/concurrency patterns." name: optimizing-dotnet-performance -tools: ['read', 'search', 'edit', 'task', 'skill', 'web_search', 'web_fetch', 'ask_user'] +tools: ['read', 'search', 'edit', 'task', 'skill', 'web_search', 'web_fetch', 'ask_user', 'Read', 'Glob', 'Grep', 'Edit', 'Write', 'Skill', 'read_file', 'replace', 'write_file', 'glob', 'grep_search'] license: MIT --- diff --git a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-builder.agent.md b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-builder.agent.md index c333c1a..3155edf 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-builder.agent.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-builder.agent.md @@ -6,7 +6,7 @@ description: >- errors, verifying project builds successfully. name: code-testing-builder user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-fixer.agent.md b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-fixer.agent.md index 9787511..2745add 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-fixer.agent.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-fixer.agent.md @@ -6,7 +6,7 @@ description: >- imports, correcting type mismatches, fixing compilation failures. name: code-testing-fixer user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-generator.agent.md b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-generator.agent.md index 3127b7c..1c21e65 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-generator.agent.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-generator.agent.md @@ -6,7 +6,7 @@ description: >- coverage plateaus or project-wide coverage/CRAP analysis without writing tests (use coverage-analysis); targeted method/class CRAP scores (use crap-score). name: code-testing-generator -tools: ["agent", "skill", "read", "search", "edit", "execute"] +tools: ["agent", "skill", "read", "search", "edit", "execute", "Task", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] agents: - code-testing-researcher - code-testing-planner diff --git a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-implementer.agent.md b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-implementer.agent.md index 18ed700..7cb2d09 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-implementer.agent.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-implementer.agent.md @@ -7,7 +7,7 @@ description: >- running build-test-fix cycle for generated tests. name: code-testing-implementer user-invocable: false -tools: ["agent", "skill", "read", "search", "edit", "execute"] +tools: ["agent", "skill", "read", "search", "edit", "execute", "Task", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] agents: - code-testing-builder - code-testing-tester diff --git a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-linter.agent.md b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-linter.agent.md index ff8de44..d81a733 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-linter.agent.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-linter.agent.md @@ -6,7 +6,7 @@ description: >- applying lint fixes. name: code-testing-linter user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-planner.agent.md b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-planner.agent.md index af02e32..17a47de 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-planner.agent.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-planner.agent.md @@ -6,7 +6,7 @@ description: >- creating .testagent/plan.md from research. name: code-testing-planner user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-researcher.agent.md b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-researcher.agent.md index 56c277c..a03eadf 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-researcher.agent.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-researcher.agent.md @@ -6,7 +6,7 @@ description: >- discovering test frameworks and build commands, producing .testagent/research.md. name: code-testing-researcher user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-tester.agent.md b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-tester.agent.md index c9c196a..e34e50e 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-tester.agent.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-test/agents/code-testing-tester.agent.md @@ -6,7 +6,7 @@ description: >- checking test results and failures. name: code-testing-tester user-invocable: false -tools: ["skill", "read", "search", "edit", "execute"] +tools: ["skill", "read", "search", "edit", "execute", "Skill", "Read", "Glob", "Grep", "Edit", "Write", "Bash", "read_file", "replace", "write_file", "glob", "grep_search", "run_shell_command"] license: MIT --- diff --git a/external-sources/upstreams/dotnet-skills/dotnet-test/skills/run-tests/SKILL.md b/external-sources/upstreams/dotnet-skills/dotnet-test/skills/run-tests/SKILL.md index d273bd0..2dfb674 100644 --- a/external-sources/upstreams/dotnet-skills/dotnet-test/skills/run-tests/SKILL.md +++ b/external-sources/upstreams/dotnet-skills/dotnet-test/skills/run-tests/SKILL.md @@ -40,7 +40,7 @@ Detect the test platform and framework, run tests, and apply filters using `dotn | Input | Required | Description | |-------|----------|-------------| -| Project or solution path | No | Path to the test project (.csproj) or solution (.sln). Defaults to current directory. | +| Project or solution path | No | Path to the test project (.csproj) or solution (.sln, .slnf, .slnx). Defaults to current directory. | | Filter expression | No | Filter expression to select specific tests | | Target framework | No | Target framework moniker to run against (e.g., `net8.0`) | @@ -154,6 +154,8 @@ dotnet test --project path/to/ # Run all tests in a solution (sln, slnf, slnx) dotnet test --solution path/to/MySolution.sln +dotnet test --solution path/to/MySolution.slnf +dotnet test --solution path/to/MySolution.slnx # Run all tests in a directory containing a solution dotnet test --solution path/to/ diff --git a/external-sources/vendir.lock.yml b/external-sources/vendir.lock.yml index 74dfd3b..382cd8a 100644 --- a/external-sources/vendir.lock.yml +++ b/external-sources/vendir.lock.yml @@ -2,9 +2,9 @@ apiVersion: vendir.k14s.io/v1alpha1 directories: - contents: - git: - commitTitle: 'dotnet-test: raise timeouts for chronic-timeout eval scenarios - (#850)...' - sha: ce75c355694bb358162464058081121645767d6f + commitTitle: 'dotnet-test: make code-testing agent tools Claude Code-compatible + + add cross-host portability check (#856)...' + sha: 77154137e87200e9e865b8a72e8674cb70e48ceb tags: - skill-validator-nightly path: dotnet-skills