Skip to content

Add survey example app demonstrating Terminal.Gui + Spectre.Console#9

Open
tig wants to merge 24 commits into
developfrom
claude/spectre-console-tg-cli-example-Z7cWS
Open

Add survey example app demonstrating Terminal.Gui + Spectre.Console#9
tig wants to merge 24 commits into
developfrom
claude/spectre-console-tg-cli-example-Z7cWS

Conversation

@tig
Copy link
Copy Markdown
Member

@tig tig commented May 26, 2026

Summary

Adds a complete example application (survey) that demonstrates the collaboration between Terminal.Gui and Spectre.Console, along with the scriptable surfaces provided by Terminal.Gui.Cli (JSON output, --cat rendering, OpenCLI metadata, and agent guides).

The survey app collects a user profile through either an interactive Terminal.Gui form or headless command-line options, then renders it as a rich Spectre.Console card. It showcases how Terminal.Gui handles interaction and navigation while Spectre.Console provides rich rendering, with the host layer adding JSON serialization, structured output, and AI-agent discoverability.

Key Changes

  • New example project (examples/survey/Terminal.Gui.Cli.Survey.csproj):

    • SurveyCommand — input command with interactive Terminal.Gui form and headless mode
    • CardCommand — viewer command rendering profiles as Spectre.Console cards
    • SurveyAnswers — structured result type (record)
    • ProfileInput — shared option parsing and validation
    • SpectreProfile — Spectre.Console renderable builder
    • SurveyJsonContext — source-generated JSON serialization context
    • SurveyApp — host configuration and command registration
    • Embedded markdown help resources and agent guide
  • Library enhancements to support consumer-defined result types:

    • CliHostOptions.ResultJsonResolver — allows registering a consumer IJsonTypeInfoResolver for custom result type serialization
    • JsonEnvelope.ToJson(IJsonTypeInfoResolver?) — overload that combines consumer and built-in JSON contexts
    • ResultWriter.Write() — updated to pass resolver through the serialization pipeline
    • CliHost.ExecuteCommandAsync() — passes resolver to result writer
  • Test coverage:

    • SurveyExampleTests — integration tests covering headless input, JSON output, validation, and Spectre rendering
  • Documentation:

    • specs/library-spec.md — updated to document result value JSON serialization contract

Implementation Details

  • The survey form uses Terminal.Gui TextField, Label, and StatusBar with F2 to submit and validation feedback
  • Headless mode validates options (age 1-120, required name) and returns structured results
  • The card command renders to stdout via Spectre.Console when --cat is used, or shows markdown in a Terminal.Gui window otherwise
  • Consumer result types serialize without reflection by registering a source-generated JsonSerializerContext on the host
  • The example includes an agent guide (embedded resource) describing command structure and JSON envelope format for AI integration

https://claude.ai/code/session_014gpZxbbFxi3SNNqqHPc4Wi

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c189c4ff4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread examples/survey/CardCommand.cs Outdated
tig pushed a commit that referenced this pull request May 26, 2026
The --cat path in CliHost returned the cat result's exit code but never
wrote its message, so a viewer command that fails validation (e.g.
card --age 999 --cat) exited 65 with no diagnostic output. Route
non-success cat results through ResultWriter so the error reaches stderr
(or the error envelope under --json) while successful renders are
untouched. Adds a regression test.

Addresses Codex review feedback on PR #9.

https://claude.ai/code/session_014gpZxbbFxi3SNNqqHPc4Wi
claude and others added 13 commits May 28, 2026 09:40
Ports Spectre.Console's Prompt example to Terminal.Gui.Cli as a TG/Spectre
collaboration (spectre.console#2128): Terminal.Gui handles interaction, the
host adds structured --json, --cat, OpenCLI, and an agent guide, and
Spectre.Console renders the resulting profile card (Panel, Table, BarChart).

To emit a structured result value under source-generated JSON (constitution
C4), add CliHostOptions.ResultJsonResolver plus a JsonEnvelope.ToJson overload
that combines a consumer JsonSerializerContext with the built-in context via
JsonTypeInfoResolver.Combine. This keeps serialization reflection-free and
AOT-compatible; update specs/library-spec.md accordingly.

The in-TUI Spectre rendering (SpectreView) will be wired once the
Terminal.Gui.Interop.Spectre bridge package is published.

https://claude.ai/code/session_014gpZxbbFxi3SNNqqHPc4Wi
The --cat path in CliHost returned the cat result's exit code but never
wrote its message, so a viewer command that fails validation (e.g.
card --age 999 --cat) exited 65 with no diagnostic output. Route
non-success cat results through ResultWriter so the error reaches stderr
(or the error envelope under --json) while successful renders are
untouched. Adds a regression test.

Addresses Codex review feedback on PR #9.

https://claude.ai/code/session_014gpZxbbFxi3SNNqqHPc4Wi
- Remove CardCommand and card.md; survey is the sole default command
- Replace flat form with a 7-step Wizard (Name, Fruits, FavoriteFruit,
  Sport, Age, Password, Color) matching the Spectre Prompt example
- Use AppModel.Inline for inline terminal rendering
- Wizard is the main runnable (no wrapper Window/Runnable)
- Title: 'Survey - Enter to accept, Esc to quit'
- Rounded border with top-only thickness (clet-style)
- Multi-select ListView with hierarchical berries (indented sub-items)
- Conditional 'pick one' step only shown when >1 fruit selected
- OptionSelector for sport with linked TextField for custom input
- ColorPicker with text fields and color name enabled
- Secret TextField for password entry
- Render Spectre card to stdout after wizard completes
- Update SurveyAnswers to include FavoriteFruit and Password fields
- Update ProfileInput, SpectreProfile, JSON context, help files
- Update integration tests for new command structure

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add Label views before each input control (e.g. _Name:, _Age:)
- Fix wizard.Accepting to not set args.Handled (allows natural close)
- Add --confirm option with final confirmation step showing results
- Use explicit heights instead of Dim.Fill for Dim.Auto compatibility
- Extract BuildAnswers and FormatPreview helper methods
- Route empty args to default command in CliHost

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add Accepting += args.Handled = true on all TextFields to prevent
  Enter from bubbling up to the Wizard and closing it prematurely
- Move labels inline with fields (X = Pos.Right(label) + 1, Y = 0)
- Use Height = Dim.Fill() on ListView, ColorPicker, and confirm label
  so all wizard steps share the same height as the tallest
- Add tests: default command routing and --confirm option acceptance

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Major cleanup addressing review feedback:

- Replace 4 parallel string arrays with single tuple array for fruit data
- Remove TextField.Accepting overrides (filed gui-cs/Terminal.Gui#5430)
- Set explicit wizard Height based on tallest step (fruits list)
- Add age validation via wizard.MovingNext that prevents advancement
- Use vendored SpectreView for --confirm step (renders Spectre card)
- Add --confirm to help documentation
- Simplify sport TextField.TextChanged logic

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use BuildTable() instead of Build() for the confirm step's SpectreView
so the results render without the outer Panel/Double border (the
WizardStep already provides its own border).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Build() now returns just the rounded Table (no outer Panel with
BoxBorder.Double). Removed the duplicate BuildTable() method since
Build() is now identical.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- All TextFields now use Width = Dim.Percent(50) instead of Dim.Fill()
- Confirm step extracts background color from the WizardStep via
  GetAttributeForRole(VisualRole.Normal) and passes it to
  SpectreProfile.Build() so the table border blends with the wizard
- Final stdout output still uses default colors (no backgroundColor)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Set wizard SchemeName to SchemeManager.SchemesToSchemeName(Schemes.Accent)
- Changed fruitsList and favFruitList Width from Dim.Fill() to Dim.Auto()

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The VS debugger's internal console doesn't properly support the
virtual terminal escape sequences needed by Terminal.Gui's Inline
AppModel. Setting useExternalConsole=true launches an external
terminal (Windows Terminal) which handles VT sequences correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AppModel.Inline was causing a blank/hung console in the VS debugger.
Clet doesn't set AppModel either - it uses the default (FullScreen)
which the CliHost handles via Application.Create().Init().

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- TextField.Text is non-null in current TG API, remove ?? string.Empty
- Add using aliases for Attribute and Color to avoid verbose qualifiers
- Use 'and' pattern instead of separate >= check
- Use ?? instead of ternary for null coalescing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tig tig force-pushed the claude/spectre-console-tg-cli-example-Z7cWS branch from 9cbb2f5 to 324c0d2 Compare May 28, 2026 15:42
tig and others added 3 commits May 28, 2026 09:58
The #15 fix skipped Init() for inline commands, but Terminal.Gui
requires Init before Run/RunAsync. Call Init() unconditionally after
setting AppModel.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Setting app.AppModel on the instance after Create() was too late -
the constructor copies from the static Application.AppModel. Set the
static property before Create() so the instance picks it up correctly
and the driver initializes in inline mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Spectre card was being written to Console.Out inside RunAsync
while TG was still active, causing it to render in the wrong position.

Now the command returns CommandStatus.Ok with the result, and
SurveyAnswers.ToString() renders the Spectre table. ResultWriter
outputs it after the IApplication is disposed and the console is
properly restored.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tig tig force-pushed the claude/spectre-console-tg-cli-example-Z7cWS branch from d1167ba to 5be0b2e Compare May 28, 2026 16:54
Terminal.Gui changes Console.OutputEncoding during its session, which
replaces Console.Out internally. After shutdown the caller's captured
stdout reference is stale (points to the old writer with wrong encoding).

After RunWithTerminalGuiAsync returns, ensure UTF-8 and refresh
stdout/stderr to Console.Out/Error so both Spectre's Unicode detection
in ToString() and the actual write use the correct encoding and writer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tig tig force-pushed the claude/spectre-console-tg-cli-example-Z7cWS branch from 5be0b2e to 135fe25 Compare May 28, 2026 16:59
tig and others added 7 commits May 28, 2026 11:08
Each step now has a right-side help panel demonstrating various markdown
features: headings, bold/italic, bullet lists, numbered lists, tables,
blockquotes, inline code, and links.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use the new TreeView CheckboxMode (from the copilot-add-built-in-checkbox-
mode-for-treeview branch) instead of the ListView hack with manual [x]/[ ]
prefixes. Berries are now a proper parent node with child items, and
checking/unchecking cascades through the hierarchy.

Switched local TG reference to the checkbox mode branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The StepChanged handler was calling GoNext() whenever favFruitStep had
<=1 selected fruits, regardless of direction. This caused pressing Back
from the sport step to immediately bounce forward again.

Track direction via MovingBack/MovingNext events and call GoBack() when
navigating backward through an irrelevant step.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ctre

- Update TerminalGuiVersion to 2.4.3-develop.57 (includes wizard fix,
  TreeView CheckboxMode)
- Add Terminal.Gui.Interop.Spectre package reference to survey project
- Remove vendored SpectreView.cs and SpectreMarkupBridge.cs (now in package)
- Remove UseLocalTerminalGui/LocalTerminalGuiPath from Directory.Build.props
- Remove conditional ProjectReference from Terminal.Gui.Cli.csproj

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove driver buffer content assertions that are unreliable with
TextMateSyntaxHighlighter in headless CI environments. The Markdown
view requires multiple main loop iterations to render content which
is non-deterministic in CI. Tests still validate command correctness
via CommandStatus.Ok assertion.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tig tig force-pushed the claude/spectre-console-tg-cli-example-Z7cWS branch from 8659a5a to a18e942 Compare May 28, 2026 23:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants