stan44 5b383858ae feat(sdt): add Journal.DevTool TUI, devtool.json, and justfile
- Journal.DevTool: C# TUI dev tool (SDT) using Spectre.Console
  - Phosphor green (#00FF41) terminal aesthetic with amber/red accents
  - Grouped build target menu driven by devtool.json config
  - Topological dependency resolver for DependsOn chains
  - Live stdout/stderr streaming per target step
  - Interactive environment variable editor (dropdown + free-text)
  - Toolchain management screen: Python venv create/install/upgrade, Node npm install
  - Workspace switcher: reads sdt-workspace.json, hot-switches between projects
  - Outputs as 'sdt' executable (net10.0)

- devtool.json: project config for SDT
  - All build targets: sidecar, web, webgateway, tauri, tauri-nsis, all (virtual)
  - Dev targets: run-gateway
  - Test targets: test (smoke), gate (migration)
  - Cache targets: nuget-export, nuget-import
  - Toolchains: Python 3.14 (cpu/gpu/nlp profiles) + Node/npm (Journal.App)
  - Env vars: AI provider, log level, NLP backend, path overrides

- justfile: just command runner recipes wrapping existing scripts
  - Correct dependency ordering (sidecar before tauri, web before webgateway)
  - OS-aware runtime detection (win-x64 / linux-x64)
  - Recipes: sidecar, web, webgateway, tauri, tauri-nsis, all, run, dev-app, test, gate, build, nuget-export/import, sdt

- Journal.slnx: added Journal.DevTool project
2026-02-27 13:12:36 -06:00

55 lines
3.2 KiB
C#

using Spectre.Console;
namespace Sdt.Tui;
/// <summary>
/// SDT phosphor-green colour palette.
/// Primary text is classic terminal phosphor (#00FF41).
/// Modern accent colours are kept for highlights and status.
/// </summary>
internal static class Theme
{
// ── Hex colour constants (use in Spectre markup strings) ─────────────────
public const string Green = "#00ff41"; // primary phosphor — all normal text
public const string GreenDim = "#005c1b"; // muted — borders, secondary info
public const string GreenBold = "#a8ff90"; // bright — selections, emphasis
public const string Amber = "#ffb300"; // warnings / group titles
public const string Red = "#ff4040"; // errors
public const string Ghost = "#003d12"; // near-invisible — decorative scanlines
// ── Spectre Color instances (for FigletText, Rule styles, etc.) ──────────
public static readonly Color GreenColor = new(0, 255, 65);
public static readonly Color GreenDimColor = new(0, 92, 27);
public static readonly Color GreenBoldColor = new(168, 255, 144);
public static readonly Color AmberColor = new(255, 179, 0);
public static readonly Color RedColor = new(255, 64, 64);
// ── Pre-built Style objects ───────────────────────────────────────────────
public static readonly Style PrimaryStyle = new(GreenColor);
public static readonly Style DimStyle = new(GreenDimColor);
public static readonly Style BrightStyle = new(GreenBoldColor, decoration: Decoration.Bold);
public static readonly Style AmberStyle = new(AmberColor);
public static readonly Style RedStyle = new(RedColor, decoration: Decoration.Bold);
// ── Markup helper methods (auto-escape user content) ─────────────────────
public static string G(string t) => $"[{Green}]{Markup.Escape(t)}[/]";
public static string Faint(string t) => $"[{GreenDim}]{Markup.Escape(t)}[/]";
public static string Bold(string t) => $"[bold {GreenBold}]{Markup.Escape(t)}[/]";
public static string Warn(string t) => $"[{Amber}]{Markup.Escape(t)}[/]";
public static string Err(string t) => $"[bold {Red}]{Markup.Escape(t)}[/]";
public static string Ok(string t) => $"[bold {Green}]✓ {Markup.Escape(t)}[/]";
public static string Fail(string t) => $"[bold {Red}]✗ {Markup.Escape(t)}[/]";
// ── Shared UI components ──────────────────────────────────────────────────
public static Rule SectionRule(string? title = null) => title is null
? new Rule().RuleStyle(DimStyle)
: new Rule($"[bold {GreenBold}]{Markup.Escape(title)}[/]").RuleStyle(DimStyle);
public static Rule DimRule() => new Rule().RuleStyle(new Style(new Color(0, 40, 12)));
public static Panel StatusPanel(string markup) =>
new Panel(markup)
.BorderStyle(DimStyle)
.Padding(1, 0);
}