Skip to content

Improve terminal sizing controls and defaults - #19811

Open
Mitch Denny (mitchdenny) wants to merge 2 commits into
mainfrom
mitchdenny-terminal-font-controls
Open

Improve terminal sizing controls and defaults#19811
Mitch Denny (mitchdenny) wants to merge 2 commits into
mainfrom
mitchdenny-terminal-font-controls

Conversation

@mitchdenny

Copy link
Copy Markdown
Member

Description

Terminal sizing in the dashboard currently requires opening the page toolbar, and interacting with a terminal can unexpectedly switch its resolution to Fit. New terminals can also start at 80x24 even when WithTerminal specifies another size.

This change:

  • Moves font decrease/current size/increase and resolution selection into the terminal footer.
  • Keeps sizing controls on the bottom-right and adds an F6 focus hint on the bottom-left.
  • Uses F6 and Shift+F6 to move focus between the terminal and dashboard controls without consuming Escape.
  • Preserves the producer's current resolution when a dashboard or peer session takes control.
  • Defaults terminals to 132x50, while continuing to honor explicit WithTerminal column and row overrides.
  • Initializes both the DCP PTY and HMP consumer handshake with the configured dimensions instead of Hex1b's 80x24 presentation default.

User-facing usage

Terminals now start at 132x50 by default:

builder.AddExecutable("shell", "/bin/bash", ".")
    .WithTerminal();

Existing overrides remain authoritative:

builder.AddProject<Projects.Terminals_Repl>("repl")
    .WithTerminal(options =>
    {
        options.Columns = 120;
        options.Rows = 32;
    });

Screenshots / Recordings

This PR includes UI changes. Please add screenshots or screen recordings so reviewers can evaluate the visual changes without running locally.

  • For before/after comparisons, place them side-by-side or label them clearly.
  • For interactive changes (animations, transitions, new flows), prefer a short screen recording (GIF or video).
  • If you cannot capture visuals now, note what scenario to test and mark this section as TODO.

TODO: Capture the terminal footer showing the F6 hint, resolution selector, and font controls.

Validation

  • Aspire.Hosting.Tests WithTerminalTests: 46 passed, 1 platform skip.
  • Aspire.TerminalHost.Tests: 27 passed.
  • Aspire.Dashboard.Components.Tests ConsoleLogsTerminalTests: 32 passed.
  • Built the Terminals playground AppHost successfully.
  • Confirmed the live shell PTY reports 50 132 by default and both overridden REPL PTYs report 32 120.

Fixes # (issue)

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
Move terminal sizing controls into the footer, preserve the active resolution across terminal peers, and default new terminals to 132x50. Ensure configured dimensions reach both DCP PTYs and HMP consumers.

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

Copilot-Session: 395e0f6a-f4c1-4db8-a3dd-e0fbf5392773
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19811

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19811"
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

Review tier: Balanced
Findings: 2 Low severity

New issues introduced by this change (2)
Severity Finding
Low severity src/​Aspire.Dashboard/​Components/​Controls/​TerminalView.razor.js — This keyboard-only behavior is not exercised by the updated bUnit tests; those tests mock…
Low severity src/​Aspire.Dashboard/​Components/​Controls/​TerminalView.razor.js — The core resolution-preservation fix is currently untested on the dashboard side. The new…
What changed in this PR

Improves terminal sizing defaults, preserves producer dimensions, and moves localized sizing/focus controls into the terminal footer.

Changes:

  • Changes default terminal dimensions to 132×50.
  • Initializes HMP/DCP sessions with configured dimensions.
  • Adds footer sizing controls, F6 focus navigation, localization, and tests.
File Description
tests/​Aspire.TerminalHost.Tests/​TerminalHostArgsTests.cs Updates default-size assertions.
tests/​Aspire.TerminalHost.Tests/​TerminalHostAppTests.cs Tests configured handshake and PTY dimensions.
tests/​Aspire.Hosting.Tests/​WithTerminalTests.cs Updates hosting default assertions.
tests/​Aspire.Dashboard.Components.Tests/​Pages/​ConsoleLogsTerminalTests.cs Updates menu and localization interop tests.
src/​Aspire.TerminalHost/​TerminalReplica.cs Configures HMP presentation dimensions directly.
src/​Aspire.TerminalHost/​TerminalHostArgs.cs Changes defaults to 132×50.
src/​Aspire.TerminalHost/​Hmp1UdsServerListenerFilter.cs Adds the custom HMP consumer listener.
src/​Aspire.Hosting/​TerminalResourceBuilderExtensions.cs Updates documented polyglot defaults.
src/​Aspire.Hosting/​ApplicationModel/​TerminalAnnotation.cs Updates terminal option defaults and docs.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.zh-Hant.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.zh-Hans.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.tr.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.ru.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.pt-BR.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.pl.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.ko.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.ja.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.it.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.fr.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.es.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.de.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​xlf/​ConsoleLogs.cs.xlf Adds the focus hint resource.
src/​Aspire.Dashboard/​Resources/​ConsoleLogs.resx Defines the focus hint text.
src/​Aspire.Dashboard/​Resources/​ConsoleLogs.Designer.cs Exposes the new localized resource.
src/​Aspire.Dashboard/​Components/​Pages/​ConsoleLogs.razor.cs Removes page-menu terminal controls.
src/​Aspire.Dashboard/​Components/​Pages/​ConsoleLogs.razor Passes localized footer labels.
src/​Aspire.Dashboard/​Components/​Controls/​TerminalView.razor.js Implements footer controls, focus navigation, and sizing preservation.
src/​Aspire.Dashboard/​Components/​Controls/​TerminalView.razor.cs Adds localized label parameters and JS wiring.
docs/​specs/​with-terminal.md Documents sizing and focus behavior.
Files not reviewed (1)
  • src/Aspire.Dashboard/Resources/ConsoleLogs.Designer.cs: Generated file
Comment thread src/Aspire.Dashboard/Components/Controls/TerminalView.razor.js
Comment thread src/Aspire.Dashboard/Components/Controls/TerminalView.razor.js
@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Cover terminal focus navigation and producer-dimension preservation with Playwright using a lightweight HMP test peer.

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

Copilot-Session: 395e0f6a-f4c1-4db8-a3dd-e0fbf5392773
Copilot AI review requested due to automatic review settings August 31, 2026 04:34
@github-actions

Copy link
Copy Markdown
Contributor

Tests selector

54 / 99 PR test projects · 3 PR jobs · 2 advisory-only targets, from 32 changed files.

Selected PR test projects (54 / 99)

Aspire.Cli.EndToEnd.Tests, Aspire.Dashboard.Components.Tests, Aspire.Dashboard.Tests, Aspire.Hosting.Analyzers.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Kusto.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Browsers.Tests, Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.EntityFrameworkCore.Tests, Aspire.Hosting.Foundry.Tests, Aspire.Hosting.Garnet.Tests, Aspire.Hosting.GitHub.Models.Tests, Aspire.Hosting.Go.Tests, Aspire.Hosting.Java.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Kafka.Tests, Aspire.Hosting.Keycloak.Tests, Aspire.Hosting.Kubernetes.Tests, Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests, Aspire.Hosting.MongoDB.Tests, Aspire.Hosting.MySql.Tests, Aspire.Hosting.Nats.Tests, Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests, Aspire.Hosting.Orleans.Tests, Aspire.Hosting.PostgreSQL.Tests, Aspire.Hosting.Python.Tests, Aspire.Hosting.Qdrant.Tests, Aspire.Hosting.RabbitMQ.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Redis.Tests, Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Rust.Tests, Aspire.Hosting.Seq.Tests, Aspire.Hosting.SqlServer.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests, Aspire.Hosting.Yarp.Tests, Aspire.Managed.Tests, Aspire.Playground.Tests, Aspire.TerminalHost.Tests

Selected PR jobs (3)

extension-e2e, polyglot, typescript-api-compat

Advisory workflow impact (2)

  • Aspire.EndToEnd.Tests (outerloop-only)
  • deployment-e2e (schedule/dispatch-only)

How these were chosen — grouped by what changed

⚠️ 43 of the 55 selected test projects come from a single change — src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs.

🔧 src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs (changed source)
43 via the project graph

show 43

Aspire.Hosting.Analyzers.Tests (2 hops), Aspire.Hosting.Azure.Kubernetes.Tests (2 hops), Aspire.Hosting.Azure.Kusto.Tests (2 hops), Aspire.Hosting.Azure.Tests, Aspire.Hosting.Browsers.Tests (2 hops), Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests (2 hops), Aspire.Hosting.DevTunnels.Tests (2 hops), Aspire.Hosting.Docker.Tests (2 hops), Aspire.Hosting.EntityFrameworkCore.Tests (2 hops), Aspire.Hosting.Foundry.Tests (2 hops), Aspire.Hosting.Garnet.Tests (2 hops), Aspire.Hosting.GitHub.Models.Tests (2 hops), Aspire.Hosting.Go.Tests (2 hops), Aspire.Hosting.Java.Tests (2 hops), Aspire.Hosting.JavaScript.Tests (2 hops), Aspire.Hosting.Kafka.Tests (2 hops), Aspire.Hosting.Keycloak.Tests (2 hops), Aspire.Hosting.Kubernetes.Tests (2 hops), Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests (2 hops), Aspire.Hosting.MongoDB.Tests (2 hops), Aspire.Hosting.MySql.Tests (2 hops), Aspire.Hosting.Nats.Tests (2 hops), Aspire.Hosting.OpenAI.Tests (2 hops), Aspire.Hosting.Oracle.Tests (2 hops), Aspire.Hosting.Orleans.Tests (2 hops), Aspire.Hosting.PostgreSQL.Tests (2 hops), Aspire.Hosting.Python.Tests (2 hops), Aspire.Hosting.Qdrant.Tests (2 hops), Aspire.Hosting.RabbitMQ.Tests (2 hops), Aspire.Hosting.Redis.Tests (2 hops), Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Rust.Tests (2 hops), Aspire.Hosting.Seq.Tests (2 hops), Aspire.Hosting.SqlServer.Tests (2 hops), Aspire.Hosting.Valkey.Tests (2 hops), Aspire.Hosting.Yarp.Tests (2 hops), Aspire.Playground.Tests

🧪 tests/Aspire.Hosting.Tests/WithTerminalTests.cs (changed test)
1 directly: Aspire.Hosting.Tests
3 via the project graph: Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.Radius.Tests

🔧 src/Aspire.Dashboard/Components/Controls/TerminalView.razor.cs (changed source)
3 via the project graph: Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.Testing.Tests, Aspire.Managed.Tests (2 hops)

📦 affected project Aspire.Hosting
1 test: Aspire.EndToEnd.Tests

📦 affected project Aspire.Managed
1 test: Aspire.Cli.EndToEnd.Tests

🧪 tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTerminalTests.cs (changed test)
1 directly: Aspire.Dashboard.Components.Tests

🧪 tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/DashboardServerFixture.cs (changed test)
1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/MockDashboardClient.cs (changed test)
1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/TestTerminalConnectionResolver.cs (changed test)
1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.Dashboard.Tests/Integration/Playwright/TerminalTests.cs (changed test)
1 directly: Aspire.Dashboard.Tests

🧪 tests/Aspire.TerminalHost.Tests/TerminalHostAppTests.cs (changed test)
1 directly: Aspire.TerminalHost.Tests

🧪 tests/Aspire.TerminalHost.Tests/TerminalHostArgsTests.cs (changed test)
1 directly: Aspire.TerminalHost.Tests

Job reasons

Job Triggered by
deployment-e2e affected project Aspire.Managed
extension-e2e src/Aspire.Dashboard/Components/Controls/TerminalView.razor.cs, src/Aspire.Dashboard/Components/Controls/TerminalView.razor.js, src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor, src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs, src/Aspire.Dashboard/Resources/ConsoleLogs.Designer.cs, src/Aspire.Dashboard/Resources/ConsoleLogs.resx, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.cs.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.de.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.es.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.fr.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.it.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.ja.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.ko.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.pl.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.pt-BR.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.ru.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.tr.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.zh-Hans.xlf, src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.zh-Hant.xlf, src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs, src/Aspire.Hosting/TerminalResourceBuilderExtensions.cs
• affected project Aspire.Dashboard
polyglot affected project Aspire.Managed
typescript-api-compat affected project Aspire.Hosting

Selection computed for commit cc999f9.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

Review tier: Balanced
Findings: None

Issues resolved since last review (2)
Severity Finding
Low severity src/​Aspire.Dashboard/​Components/​Controls/​TerminalView.razor.js — The core resolution-preservation fix is currently untested on the dashboard side. The new… View resolved comment
Low severity src/​Aspire.Dashboard/​Components/​Controls/​TerminalView.razor.js — This keyboard-only behavior is not exercised by the updated bUnit tests; those tests mock… View resolved comment
Files not reviewed (1)
  • src/Aspire.Dashboard/Resources/ConsoleLogs.Designer.cs: Generated file
@github-actions

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@adamint Adam Ratzman (adamint) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I found lifecycle and sizing paths that can leave the terminal unavailable or overwrite the configured grid.

CancellationToken ct = default)
{
_listenerCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
_listenerTask = Task.Run(() => RunListenerAsync(_listenerCts.Token), _listenerCts.Token);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

OnSessionStartAsync reports success before ListenUnixSocket has bound. A bind/listen exception faults this detached task, but nothing observes it until session teardown, so the producer can stay live while the consumer endpoint is unusable. Could startup await an explicit listener-ready/failure signal and monitor later listener faults during the session?

{
await foreach (var stream in Hmp1Transports.ListenUnixSocket(socketPath, ct).ConfigureAwait(false))
{
_ = AddClientAsync(stream, ct);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Every accepted client is detached, while session teardown waits only for the listener. Active callbacks and stream cleanup can therefore outlive the presentation and race the next replica cycle; unexpected client faults are also unobserved. Please track these tasks, cancel and await them before OnSessionEndAsync returns, and dispose each stream in finally.


private Hmp1PresentationAdapter CreateDownstream(DcpUpstreamAdapter upstream)
{
var downstream = new Hmp1PresentationAdapter(Columns, Rows);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

_currentColumns and _currentRows track the negotiated live grid, but every rebuilt terminal seeds both adapters from the immutable configured Columns/Rows. A producer recycle therefore undoes an explicit user resize. Could BuildTerminal() snapshot the current dimensions under _gate for both the terminal and presentation, with a resize-then-reconnect regression test?

// to Fit or selects another preset.
function adoptProducerDimensions(state) {
const client = state.client;
if (!client || client.isPrimary || client.width <= 0 || client.height <= 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

An initial Hello can already make this dashboard the primary peer. This guard then leaves fixedDims at the 132x50 default, and the following primary fixed-mode layout sends that size upstream, overwriting an explicit producer size such as 137x41. Initial Hello should seed fixedDims from every valid producer grid before applying role-aware layout; the secondary-only rule can remain for later resize events.


function attachTerminalFocusNavigation(state, term) {
term.attachCustomKeyEventHandler((event) => {
if (event.key !== 'F6') {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This also consumes Ctrl+F6, Alt+F6, and Meta+F6 as focus navigation. Could this handle only exact F6 and Shift+F6, returning true whenever Ctrl, Alt, or Meta is present, and add a modified-key regression case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

3 participants