Repository navigation
Restore single-line compact rows in the dashboard resource list - #21880
Conversation
#21727 removed the compact branch of the resource row and stacked compact rows into two lines, so Compact became a shorter Comfortable with "Running" text, and later picked up the availability badges from #21819. It also dropped Spacious's third detail line, leaving the three densities different only in row height and font size. Compact rows are one line again: the name, then aligned type and state columns. In the sidebar, actions replace the columns while a row is hovered or focused or its menu is open; the parent expand toggle stays visible and touch screens always show the actions. The expanded list keeps its actions visible and widens its identity column for the extra columns. Availability is shown by the muted row and its tooltip, and error badges sit before the columns. Spacious sidebar rows show a third line again with an unhealthy health status, the primary endpoint (query values masked) or the source.
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 21880Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 21880" |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
🟡 Changes recommended
The updated layout conflicts with existing browser-test assertions and retains horizontal overflow, keyboard-inaccessible source details, and missing TCP/Redis endpoint details.
5 open issues
Update responsive tests for restored row and sidebar layouts · New Show TCP and Redis endpoints in the Spacious detail line · New Expose truncated source details to keyboard and screen-reader users · New Compact rows overflow at a 960px pane width · New Add bUnit coverage for Spacious detail selection and masking · New
What changed in this PR
Restores distinct dashboard resource-list densities after earlier layout changes made Compact and Spacious resemble Comfortable.
Changes:
- Restores single-line Compact rows with aligned type/state columns and contextual sidebar actions.
- Adds Spacious sidebar details, prioritizing health status, then a masked endpoint, then source.
- Updates component tests for Compact identity, persistence, and availability descriptions.
| File | Description |
|---|---|
tests/Aspire.Dashboard.Components.Tests/Layout/ResourcesLayoutTests.cs |
Updates Compact markup, state, and persistent-type assertions. |
tests/Aspire.Dashboard.Components.Tests/Layout/ResourceRowAvailabilityTests.cs |
Verifies unavailable Compact rows retain explanations without badges. |
src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor.css |
Adjusts density sizing, aligned columns, action visibility, and full-canvas tracks. |
src/Aspire.Dashboard/Components/Layout/ResourcesLayout.razor |
Restores Compact markup and Spacious detail selection. |
Review effort: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
- Fall back to any endpoint (including tcp:// endpoints that aren't links) for the spacious sidebar detail line, matching the full-canvas endpoint column. - Make the spacious endpoint and source detail keyboard-focusable, with the full value in its tooltip and accessible label, and a focus ring. - Add the compact type and state column width only to the full-canvas name track's maximum, so rows no longer overflow at 540-584, 720-730 and 960-998px. - Update the responsive Playwright tests for 32px compact rows, density-keyed icon sizes, the separate spacious sidebar and full-canvas heights, and hover/focus-revealed compact sidebar actions. Add a horizontal-overflow check and 960/990px stages. - Add bUnit coverage for the spacious detail priority: unhealthy status, then endpoint (masked), then source, omitted for parameters and the full canvas.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
🟡 Changes recommended
The new spacious resource detail exposes URL credentials and fragment tokens in its text, tooltip, and accessible label because masking only covers query values.
5 open issues
Mask URL credentials and fragments in resource details · New Update responsive tests for restored row and sidebar layouts Keep the disclosure toggle stationary when revealing row actions · New Preserve type and state details in compact-row tooltips · New Add bUnit coverage for Spacious detail selection and masking
3 resolved since last review
Review effort: Balanced
In compact sidebar rows, the unviewed-error badge sat before the type and state columns at rest, but moved next to the actions while they showed. With a persistent type in the list (a 128px type column) that was a 62px jump, which also slid the badge out from under a pointer that was on it. The badge now takes whichever place is further from the row's end in both states: clear of the columns or clear of the actions. Its spacer moves with it, so the name still stops before the badge. Add a Playwright test that seeds error logs and checks, at rest, on row hover, on badge hover, on focus and with the menu open, that the badge doesn't move, isn't covered, sits over its spacer and overlaps no column or action, with and without a persistent type.
This comment has been minimized.
This comment has been minimized.
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
…act-view-regression-4abee4
…pane - Open on the compact full-page list of all resources, with no tab selected. A reload keeps the view the browser tab showed, and /graph opens the overview with the graph. - Escape closes the resource view like the Close button, whose tooltip names the key. Open popups, popovers, menus and tooltips keep Escape for themselves. - While the content is closed, no tab is shown selected and rows show no unavailable telemetry state. Selecting resources reopens the last tab; selecting a tab opens it for all resources. - Replace the kind toggles with a Telemetry sources option in the filter's resource types, and rename Select all to All resources, with the header using the same icon. - Show when a stopped resource stopped, keep a row's second line on one line with an ellipsis before its error badge, and pin metrics for the selected resources only. - In the full-page list, use the resources grid's URLs column (links and a +N list) and drop the console logs, JSON and +N actions, which stay in the row's menu. - Address review feedback: mask URL credentials in the spacious detail, keep the compact expand toggle in place, and name a compact row's type and state in its tooltip.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
🟡 Changes recommended
The changes expose sensitive URL query values, remove keyboard access to truncated non-link endpoints, and omit selected resources without telemetry from metric pins.
3 open issues
5 resolved since last review
2 files not reviewed
src/Aspire.Dashboard/Resources/ControlsStrings.Designer.cs: Generated filesrc/Aspire.Dashboard/Resources/Layout.Designer.cs: Generated file
Review effort: Balanced
Tests selector15 / 101 PR test projects · 2 PR jobs, from 77 changed files. Selected PR test projects (15 / 101)
Selected PR jobs (2)
How these were chosen — grouped by what changed
🔧 📦 affected project 🔧 🔧 🔧 🧪 🧪 🧪 🧪 🧪 🧪 🧪 🧪 🧪 Job reasons
Selection computed for commit |
There was a problem hiding this comment.
🔵 Needs a closer look
Unpublished findings involving navigation and persistence races, unreliable test readiness, and quadratic metric lookups require human assessment.
No open issues 🎉
3 resolved since last review
2 files not reviewed
src/Aspire.Dashboard/Resources/ControlsStrings.Designer.cs: Generated filesrc/Aspire.Dashboard/Resources/Layout.Designer.cs: Generated file
Review effort: Balanced
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Pull request created: #2088
|
|
📝 Documentation has been drafted in microsoft/aspire.dev#2088 targeting Added a "Resource list density" section to Note This draft PR needs human review before merging. |
…21924) ## Description The scheduled [Outerloop Tests run on main](https://github.com/microsoft/aspire/actions/runs/42437120205) failed in two jobs after #21880: - [Templates (ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874013): all 10 `StarterTemplateWithRedisCacheTests` and `StarterTemplateWithRedisCacheTests_Net11` tests timed out waiting for `.resource-row-sub > .state-dot + span` in `DashboardResourceAssertions.CheckResourcesAsync`. - [Hosting.Testing (ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874062): `DashboardUrlTests.DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint` couldn't find the text "Dashboard test initial" in the resource row. The row itself was there. ## Failure stats (14d, attempt 1) `Outerloop Tests` workflow on microsoft/aspire, runs created 2026-10-25 06:28 → 2026-11-08 06:28 UTC, **attempt 1 only**. Denominator: attempt-1 jobs that ran (success or failure). Hits: the failed job's log matches the signature. Jobs that lost their runner, which have no log, are excluded. | | | |---|---| | Signature | Templates: `.resource-row-sub > .state-dot + span` timeout; Hosting.Testing: `Dashboard test initial` not found | | Job | Outerloop Tests: Templates (ubuntu-latest) and Hosting.Testing (ubuntu-latest), main | | main pushes: hits / runs | 2 / 28 (7.14%) | | main scheduled runs, 30d: hits / jobs | 2 / 60 (3.33%), both on 2026-11-08. Templates also failed on main every scheduled run from 2026-10-10 to 2026-11-04 with other causes | | PR runs: hits / runs | n/a (Outerloop PR runs don't run these jobs) | | Combined rate | 2 / 28 = **7.14%** | | Flake check | main: failed on all 4 attempts of the run (not a flake) | | First / last hit | 2026-11-08 / 2026-11-08 | | Still hitting main? | Hit on the latest scheduled main run (2026-11-08); no main run since | | Fix evidence | Local: default-density helper tests fail with the old helper and pass with the new one. Outerloop dispatched on this PR's head: Templates and Hosting.Testing (ubuntu) passed 2/2 attempts. | Failing attempt-1 jobs, most recent first (2 of 2 log-verified hits): | Date (UTC) | Branch | Job | Signature excerpt | |---|---|---|---| | [2026-11-08 02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604467928) | **main** | Hosting.Testing (ubuntu-latest) | `GetByText("Dashboard test initial", Exact) element(s) not found` | | [2026-11-08 02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604468061) | **main** | Templates (ubuntu-latest) | `.resource-row-sub > .state-dot + span timeout` | **Verdict: STRONG (real break, not a flake).** The 2026-11-08 scheduled main run failed both jobs on **all 4 attempts** with these signatures. The prior scheduled runs (2026-11-06 and 2026-11-07) passed, and the signature never appeared earlier in the window. Earlier Templates failures (2026-10-27 to 2026-11-04) had other causes. <!-- /failure-stats --> **Why:** #21880 deliberately brought back single-line rows for **Compact**, the dashboard's default density. A compact row shows the state as a dot in a `.resource-row-meta-state` column, and the state's name is in that column's `data-tooltip`. There's no `.resource-row-sub` and no visible state text. These browser tests open the dashboard at its default density, so they no longer found the state. `Aspire.Dashboard.Playwright.Tests` passed because `PlaywrightTestsBase` pins those tests to Comfortable unless they opt into the default. This is a deterministic break, not a flake: the previous scheduled runs on main ([42327544779](https://github.com/microsoft/aspire/actions/runs/42327544779), before #21880) passed, and the only dashboard change in between is #21880. **Fix:** the tests now match the intended markup, and no assertions are dropped: - `DashboardResourceAssertions.GetRowStateAsync` reads a row's state at any density. In compact rows it reads the state column's tooltip. In other densities it reads the text after the dot. `RowState(row, state)` finds the element that shows a given state. - `CheckResourcesAsync` uses `GetRowStateAsync`, and keeps waiting while a row hasn't rendered a state yet. - `DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint` waits for the initial and then the updated state with `RowState`. - `TemplateResourceAssertionsTests.TemplateHelper_ValidatesSidebarAndDetailsWithoutCards` now runs at both the default (compact) density and Comfortable, and checks `RowState` directly, so the helper is covered against the real dashboard at the density the template tests use. ## Confidence - Locally, the 6 `TemplateResourceAssertionsTests` pass. With the old helper, the two new default-density cases fail with the same `Timeout ... exceeded` as the Templates job. - [Outerloop Tests dispatched on this branch](https://github.com/microsoft/aspire/actions/runs/42450827141) to cover the Templates and Hosting.Testing jobs end to end. I didn't run those two locally; they need the full template build and container runtime. **How we stop this recurring:** the shared helper's own test now runs at the dashboard's default density, so a markup change to default rows fails that test against the dashboard alone, which is fast to run locally while changing row markup and points straight at the helper. It's still an outerloop test, so it doesn't gate PR CI. ## Checklist - Is this feature complete? - [x] Yes. Ready to ship. - [ ] No. Follow-up changes expected. - Are you including unit tests for the changes and scenario tests if relevant? - [x] Yes - [ ] No - Did you add public API? - [ ] Yes - [x] No - Does the change make any security assumptions or guarantees? - [ ] Yes - [x] No **🤖 Posted by AspireAgent (AI Agent)** Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Brings in main's dashboard changes, including restored single-line compact rows (#21880), and keeps this branch's simplification on top: - Main moved the resource kinds into the filter, as a "Telemetry sources" option after the resource types. That replaces this branch's "Show" section, which is dropped. - The "All resources" row stays in place of main's "All resources" button, so its strings ("All selected") and styles are removed. Tests that used the button use the row, and the Playwright tests are mapped to it too. - Main's row actions (toggle, primary endpoint, commands, menu) and its URLs column with "+N" replace this branch's single clickable URL and fixed slots. The Source column keeps its type prefix and wider tracks. - Main's row time (since stopping, once a resource has stopped) keeps this branch's short "7m ago" form; the tooltip has the full wording. - Rows still have no availability badges, and the page-level close button is still limited to Overview, Parameters and Terminal. - In the full list, a compact row's type column is hidden because the Source column shows the type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
## Description The resource list's **Compact** density lost its one-line rows in #21727. That PR removed the compact branch of the row markup and switched `.density-compact .resource-row-text` to `flex-direction: column`, so compact rows became the same two-line rows as Comfortable ("● Running · 1 minute ago"), only shorter. They then also picked up the "No logs" / "Not applicable" badges added in #21819. #21727 also dropped **Spacious**'s third detail line. That left the three densities differing only in row height and font size, although `ResourceListDensity` still documents one, two and three lines per resource. This PR restores the documented densities: - **Compact**: one line per resource. The icon and name are followed by aligned type and state-dot columns. In the sidebar, a row's actions replace the columns while it's hovered or focused, or while its menu is open, as before #21727. The expand toggle of parent rows stays visible, and touch screens (`hover: none`) always show the actions. Availability badges don't fit on one line: an unavailable row is muted, and the reason stays in its tooltip and accessible description. Unviewed-error badges sit just before the columns, so the columns stay aligned. A badge keeps the same place while the row's actions show, so it never moves out from under the pointer: it takes whichever place is further from the row's end, clear of the columns or clear of the actions. - **Comfortable**: unchanged. - **Spacious**: sidebar rows have a third line again. It shows an unhealthy health status if there is one, otherwise the primary endpoint, otherwise the source. When no endpoint can be opened as a link, such as a `tcp://` endpoint, the first endpoint is used, as in the expanded list's endpoint column. Query values are masked. The endpoint and source can take keyboard focus, which shows the full value in a tooltip and gives it an accessible label. In the expanded resource list (full canvas), compact rows use the same single-line identity. From 960px wide, the identity column can grow by the width of the type and state columns, so names aren't squeezed. That extra width only raises the column's maximum, never its minimum, so rows don't overflow the list at narrower widths. Actions there stay visible all the time. Persistent types such as "RabbitMQ (persistent)" widen every row's type column together, so the columns stay aligned. The column is 128px where rows can be hovered. On touch screens, where the action rail is always showing, it stays at 64px at the default 420px pane width so names keep their room. ### Screenshots / Recordings The resource pane at its default 420px width, using the TestShop playground, before (main) and after: - **First row (before):** all three densities are the same two-line row. - **Second row (after):** Compact is one line with the actions hidden at rest, Comfortable is unchanged, and Spacious has its third line. - **Third row:** compact on Structured logs. Before, rows had "No logs" badges. After, those rows are muted instead. The last panel adds two unviewed-error badges, injected into the page because the sample app logs no errors.  Compact rows with an unviewed error, at rest and hovered. On main, the badge jumped about 62px toward the actions when a row was hovered, with a persistent type in the list. It now keeps its place:  ### Validation - Updated the `ResourcesLayoutTests` that asserted the two-line compact row (`CompactDensity_ShowsOneLineWithTypeAndStateColumns`, `WideRowMetadata_PreservesRuntimeStateAndAccessibleHealthWithRelativeStart`, `AppHost_ContainerLifetimeQualifiesTypeAndBadgesResourceGlyph`). Added `ResourceRowAvailabilityTests.CompactDensity_UnavailableRowKeepsReasonWithoutBadge` and `ResourcesLayoutTests.SpaciousSidebarDetail_PrefersUnhealthyStatusThenEndpointThenSource`. - The 264 tests in `ResourcesLayoutTests`, `ResourceRowAvailabilityTests`, `MainLayoutTests`, `DashboardViewPreferencesTests` and `SettingsDialogTests` pass. - Checked manually against the TestShop playground with Playwright at the default 420px pane width: - In every density, no row element overlaps another or spills out of its row. - Compact: the rest state, hover on a row and on a parent row, keyboard focus, an open "…" menu, an error badge (simulated) and touch emulation. - The expanded list in every density. - Updated `ResourcePaneResponsiveTests` for the restored geometry: - 32px compact rows, and 22px, 32px and 40px icons for Compact, Comfortable and Spacious. - Spacious rows are 76px in the sidebar and 60px in the expanded list. - Compact sidebar rows show only the expand-toggle column at rest, and the full action rail on hover or keyboard focus. - A new horizontal-overflow check, with 960px and 990px stages. With the previous track minimums it fails at 960px (980px of content). - Added `ResourcePaneResponsiveTests.CompactSidebar_ErrorBadgeKeepsItsPlaceWhileActionsShow`. It seeds error logs and checks five states: at rest, the row hovered, the badge hovered, the row focused, and the menu open. In each, the badge must not move or be covered, must line up with its spacer, and must not overlap a column or an action. It runs with and without a persistent type in the list, and fails against the previous styles. - 75 of the 80 `ResourcePaneResponsiveTests` pass locally. 4 failures come from `RowComposition_AlignsStatusAndSingleLineDetailsWithAccessibleContrast` and `Accessibility_PaneAndPopoversHaveNoBlockingWcagViolations` at 390px, which fail the same way on main. `Resizer_DoubleClickRestoresMinimumAcrossPresentationAndNavigation` failed once in the full run because a drag didn't take effect, and passed when rerun on its own. Fixes # (issue) ## Checklist - Is this feature complete? - [x] Yes. Ready to ship. - [ ] No. Follow-up changes expected. - Are you including unit tests for the changes and scenario tests if relevant? - [x] 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 - [x] 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 - [x] No --------- Co-authored-by: Sébastien Ros <1165805+sebastienros@users.noreply.github.com> (cherry picked from commit db27327)
…21924) ## Description The scheduled [Outerloop Tests run on main](https://github.com/microsoft/aspire/actions/runs/42437120205) failed in two jobs after #21880: - [Templates (ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874013): all 10 `StarterTemplateWithRedisCacheTests` and `StarterTemplateWithRedisCacheTests_Net11` tests timed out waiting for `.resource-row-sub > .state-dot + span` in `DashboardResourceAssertions.CheckResourcesAsync`. - [Hosting.Testing (ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874062): `DashboardUrlTests.DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint` couldn't find the text "Dashboard test initial" in the resource row. The row itself was there. ## Failure stats (14d, attempt 1) `Outerloop Tests` workflow on microsoft/aspire, runs created 2026-10-25 06:28 → 2026-11-08 06:28 UTC, **attempt 1 only**. Denominator: attempt-1 jobs that ran (success or failure). Hits: the failed job's log matches the signature. Jobs that lost their runner, which have no log, are excluded. | | | |---|---| | Signature | Templates: `.resource-row-sub > .state-dot + span` timeout; Hosting.Testing: `Dashboard test initial` not found | | Job | Outerloop Tests: Templates (ubuntu-latest) and Hosting.Testing (ubuntu-latest), main | | main pushes: hits / runs | 2 / 28 (7.14%) | | main scheduled runs, 30d: hits / jobs | 2 / 60 (3.33%), both on 2026-11-08. Templates also failed on main every scheduled run from 2026-10-10 to 2026-11-04 with other causes | | PR runs: hits / runs | n/a (Outerloop PR runs don't run these jobs) | | Combined rate | 2 / 28 = **7.14%** | | Flake check | main: failed on all 4 attempts of the run (not a flake) | | First / last hit | 2026-11-08 / 2026-11-08 | | Still hitting main? | Hit on the latest scheduled main run (2026-11-08); no main run since | | Fix evidence | Local: default-density helper tests fail with the old helper and pass with the new one. Outerloop dispatched on this PR's head: Templates and Hosting.Testing (ubuntu) passed 2/2 attempts. | Failing attempt-1 jobs, most recent first (2 of 2 log-verified hits): | Date (UTC) | Branch | Job | Signature excerpt | |---|---|---|---| | [2026-11-08 02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604467928) | **main** | Hosting.Testing (ubuntu-latest) | `GetByText("Dashboard test initial", Exact) element(s) not found` | | [2026-11-08 02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604468061) | **main** | Templates (ubuntu-latest) | `.resource-row-sub > .state-dot + span timeout` | **Verdict: STRONG (real break, not a flake).** The 2026-11-08 scheduled main run failed both jobs on **all 4 attempts** with these signatures. The prior scheduled runs (2026-11-06 and 2026-11-07) passed, and the signature never appeared earlier in the window. Earlier Templates failures (2026-10-27 to 2026-11-04) had other causes. <!-- /failure-stats --> **Why:** #21880 deliberately brought back single-line rows for **Compact**, the dashboard's default density. A compact row shows the state as a dot in a `.resource-row-meta-state` column, and the state's name is in that column's `data-tooltip`. There's no `.resource-row-sub` and no visible state text. These browser tests open the dashboard at its default density, so they no longer found the state. `Aspire.Dashboard.Playwright.Tests` passed because `PlaywrightTestsBase` pins those tests to Comfortable unless they opt into the default. This is a deterministic break, not a flake: the previous scheduled runs on main ([42327544779](https://github.com/microsoft/aspire/actions/runs/42327544779), before #21880) passed, and the only dashboard change in between is #21880. **Fix:** the tests now match the intended markup, and no assertions are dropped: - `DashboardResourceAssertions.GetRowStateAsync` reads a row's state at any density. In compact rows it reads the state column's tooltip. In other densities it reads the text after the dot. `RowState(row, state)` finds the element that shows a given state. - `CheckResourcesAsync` uses `GetRowStateAsync`, and keeps waiting while a row hasn't rendered a state yet. - `DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint` waits for the initial and then the updated state with `RowState`. - `TemplateResourceAssertionsTests.TemplateHelper_ValidatesSidebarAndDetailsWithoutCards` now runs at both the default (compact) density and Comfortable, and checks `RowState` directly, so the helper is covered against the real dashboard at the density the template tests use. ## Confidence - Locally, the 6 `TemplateResourceAssertionsTests` pass. With the old helper, the two new default-density cases fail with the same `Timeout ... exceeded` as the Templates job. - [Outerloop Tests dispatched on this branch](https://github.com/microsoft/aspire/actions/runs/42450827141) to cover the Templates and Hosting.Testing jobs end to end. I didn't run those two locally; they need the full template build and container runtime. **How we stop this recurring:** the shared helper's own test now runs at the dashboard's default density, so a markup change to default rows fails that test against the dashboard alone, which is fast to run locally while changing row markup and points straight at the helper. It's still an outerloop test, so it doesn't gate PR CI. ## Checklist - Is this feature complete? - [x] Yes. Ready to ship. - [ ] No. Follow-up changes expected. - Are you including unit tests for the changes and scenario tests if relevant? - [x] Yes - [ ] No - Did you add public API? - [ ] Yes - [x] No - Does the change make any security assumptions or guarantees? - [ ] Yes - [x] No **🤖 Posted by AspireAgent (AI Agent)** Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 01d9682)
## Description The resource list's **Compact** density lost its one-line rows in #21727. That PR removed the compact branch of the row markup and switched `.density-compact .resource-row-text` to `flex-direction: column`, so compact rows became the same two-line rows as Comfortable ("● Running · 1 minute ago"), only shorter. They then also picked up the "No logs" / "Not applicable" badges added in #21819. #21727 also dropped **Spacious**'s third detail line. That left the three densities differing only in row height and font size, although `ResourceListDensity` still documents one, two and three lines per resource. This PR restores the documented densities: - **Compact**: one line per resource. The icon and name are followed by aligned type and state-dot columns. In the sidebar, a row's actions replace the columns while it's hovered or focused, or while its menu is open, as before #21727. The expand toggle of parent rows stays visible, and touch screens (`hover: none`) always show the actions. Availability badges don't fit on one line: an unavailable row is muted, and the reason stays in its tooltip and accessible description. Unviewed-error badges sit just before the columns, so the columns stay aligned. A badge keeps the same place while the row's actions show, so it never moves out from under the pointer: it takes whichever place is further from the row's end, clear of the columns or clear of the actions. - **Comfortable**: unchanged. - **Spacious**: sidebar rows have a third line again. It shows an unhealthy health status if there is one, otherwise the primary endpoint, otherwise the source. When no endpoint can be opened as a link, such as a `tcp://` endpoint, the first endpoint is used, as in the expanded list's endpoint column. Query values are masked. The endpoint and source can take keyboard focus, which shows the full value in a tooltip and gives it an accessible label. In the expanded resource list (full canvas), compact rows use the same single-line identity. From 960px wide, the identity column can grow by the width of the type and state columns, so names aren't squeezed. That extra width only raises the column's maximum, never its minimum, so rows don't overflow the list at narrower widths. Actions there stay visible all the time. Persistent types such as "RabbitMQ (persistent)" widen every row's type column together, so the columns stay aligned. The column is 128px where rows can be hovered. On touch screens, where the action rail is always showing, it stays at 64px at the default 420px pane width so names keep their room. ### Screenshots / Recordings The resource pane at its default 420px width, using the TestShop playground, before (main) and after: - **First row (before):** all three densities are the same two-line row. - **Second row (after):** Compact is one line with the actions hidden at rest, Comfortable is unchanged, and Spacious has its third line. - **Third row:** compact on Structured logs. Before, rows had "No logs" badges. After, those rows are muted instead. The last panel adds two unviewed-error badges, injected into the page because the sample app logs no errors.  Compact rows with an unviewed error, at rest and hovered. On main, the badge jumped about 62px toward the actions when a row was hovered, with a persistent type in the list. It now keeps its place:  ### Validation - Updated the `ResourcesLayoutTests` that asserted the two-line compact row (`CompactDensity_ShowsOneLineWithTypeAndStateColumns`, `WideRowMetadata_PreservesRuntimeStateAndAccessibleHealthWithRelativeStart`, `AppHost_ContainerLifetimeQualifiesTypeAndBadgesResourceGlyph`). Added `ResourceRowAvailabilityTests.CompactDensity_UnavailableRowKeepsReasonWithoutBadge` and `ResourcesLayoutTests.SpaciousSidebarDetail_PrefersUnhealthyStatusThenEndpointThenSource`. - The 264 tests in `ResourcesLayoutTests`, `ResourceRowAvailabilityTests`, `MainLayoutTests`, `DashboardViewPreferencesTests` and `SettingsDialogTests` pass. - Checked manually against the TestShop playground with Playwright at the default 420px pane width: - In every density, no row element overlaps another or spills out of its row. - Compact: the rest state, hover on a row and on a parent row, keyboard focus, an open "…" menu, an error badge (simulated) and touch emulation. - The expanded list in every density. - Updated `ResourcePaneResponsiveTests` for the restored geometry: - 32px compact rows, and 22px, 32px and 40px icons for Compact, Comfortable and Spacious. - Spacious rows are 76px in the sidebar and 60px in the expanded list. - Compact sidebar rows show only the expand-toggle column at rest, and the full action rail on hover or keyboard focus. - A new horizontal-overflow check, with 960px and 990px stages. With the previous track minimums it fails at 960px (980px of content). - Added `ResourcePaneResponsiveTests.CompactSidebar_ErrorBadgeKeepsItsPlaceWhileActionsShow`. It seeds error logs and checks five states: at rest, the row hovered, the badge hovered, the row focused, and the menu open. In each, the badge must not move or be covered, must line up with its spacer, and must not overlap a column or an action. It runs with and without a persistent type in the list, and fails against the previous styles. - 75 of the 80 `ResourcePaneResponsiveTests` pass locally. 4 failures come from `RowComposition_AlignsStatusAndSingleLineDetailsWithAccessibleContrast` and `Accessibility_PaneAndPopoversHaveNoBlockingWcagViolations` at 390px, which fail the same way on main. `Resizer_DoubleClickRestoresMinimumAcrossPresentationAndNavigation` failed once in the full run because a drag didn't take effect, and passed when rerun on its own. Fixes # (issue) ## Checklist - Is this feature complete? - [x] Yes. Ready to ship. - [ ] No. Follow-up changes expected. - Are you including unit tests for the changes and scenario tests if relevant? - [x] 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 - [x] 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 - [x] No --------- Co-authored-by: Sébastien Ros <1165805+sebastienros@users.noreply.github.com> (cherry picked from commit db27327)
…21924) ## Description The scheduled [Outerloop Tests run on main](https://github.com/microsoft/aspire/actions/runs/42437120205) failed in two jobs after #21880: - [Templates (ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874013): all 10 `StarterTemplateWithRedisCacheTests` and `StarterTemplateWithRedisCacheTests_Net11` tests timed out waiting for `.resource-row-sub > .state-dot + span` in `DashboardResourceAssertions.CheckResourcesAsync`. - [Hosting.Testing (ubuntu-latest)](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128616874062): `DashboardUrlTests.DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint` couldn't find the text "Dashboard test initial" in the resource row. The row itself was there. ## Failure stats (14d, attempt 1) `Outerloop Tests` workflow on microsoft/aspire, runs created 2026-10-25 06:28 → 2026-11-08 06:28 UTC, **attempt 1 only**. Denominator: attempt-1 jobs that ran (success or failure). Hits: the failed job's log matches the signature. Jobs that lost their runner, which have no log, are excluded. | | | |---|---| | Signature | Templates: `.resource-row-sub > .state-dot + span` timeout; Hosting.Testing: `Dashboard test initial` not found | | Job | Outerloop Tests: Templates (ubuntu-latest) and Hosting.Testing (ubuntu-latest), main | | main pushes: hits / runs | 2 / 28 (7.14%) | | main scheduled runs, 30d: hits / jobs | 2 / 60 (3.33%), both on 2026-11-08. Templates also failed on main every scheduled run from 2026-10-10 to 2026-11-04 with other causes | | PR runs: hits / runs | n/a (Outerloop PR runs don't run these jobs) | | Combined rate | 2 / 28 = **7.14%** | | Flake check | main: failed on all 4 attempts of the run (not a flake) | | First / last hit | 2026-11-08 / 2026-11-08 | | Still hitting main? | Hit on the latest scheduled main run (2026-11-08); no main run since | | Fix evidence | Local: default-density helper tests fail with the old helper and pass with the new one. Outerloop dispatched on this PR's head: Templates and Hosting.Testing (ubuntu) passed 2/2 attempts. | Failing attempt-1 jobs, most recent first (2 of 2 log-verified hits): | Date (UTC) | Branch | Job | Signature excerpt | |---|---|---|---| | [2026-11-08 02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604467928) | **main** | Hosting.Testing (ubuntu-latest) | `GetByText("Dashboard test initial", Exact) element(s) not found` | | [2026-11-08 02:06](https://github.com/microsoft/aspire/actions/runs/42437120205/job/128604468061) | **main** | Templates (ubuntu-latest) | `.resource-row-sub > .state-dot + span timeout` | **Verdict: STRONG (real break, not a flake).** The 2026-11-08 scheduled main run failed both jobs on **all 4 attempts** with these signatures. The prior scheduled runs (2026-11-06 and 2026-11-07) passed, and the signature never appeared earlier in the window. Earlier Templates failures (2026-10-27 to 2026-11-04) had other causes. <!-- /failure-stats --> **Why:** #21880 deliberately brought back single-line rows for **Compact**, the dashboard's default density. A compact row shows the state as a dot in a `.resource-row-meta-state` column, and the state's name is in that column's `data-tooltip`. There's no `.resource-row-sub` and no visible state text. These browser tests open the dashboard at its default density, so they no longer found the state. `Aspire.Dashboard.Playwright.Tests` passed because `PlaywrightTestsBase` pins those tests to Comfortable unless they opt into the default. This is a deterministic break, not a flake: the previous scheduled runs on main ([42327544779](https://github.com/microsoft/aspire/actions/runs/42327544779), before #21880) passed, and the only dashboard change in between is #21880. **Fix:** the tests now match the intended markup, and no assertions are dropped: - `DashboardResourceAssertions.GetRowStateAsync` reads a row's state at any density. In compact rows it reads the state column's tooltip. In other densities it reads the text after the dot. `RowState(row, state)` finds the element that shows a given state. - `CheckResourcesAsync` uses `GetRowStateAsync`, and keeps waiting while a row hasn't rendered a state yet. - `DashboardDisplaysResourceUpdatesWhileAppHostIsPausedAtBreakpoint` waits for the initial and then the updated state with `RowState`. - `TemplateResourceAssertionsTests.TemplateHelper_ValidatesSidebarAndDetailsWithoutCards` now runs at both the default (compact) density and Comfortable, and checks `RowState` directly, so the helper is covered against the real dashboard at the density the template tests use. ## Confidence - Locally, the 6 `TemplateResourceAssertionsTests` pass. With the old helper, the two new default-density cases fail with the same `Timeout ... exceeded` as the Templates job. - [Outerloop Tests dispatched on this branch](https://github.com/microsoft/aspire/actions/runs/42450827141) to cover the Templates and Hosting.Testing jobs end to end. I didn't run those two locally; they need the full template build and container runtime. **How we stop this recurring:** the shared helper's own test now runs at the dashboard's default density, so a markup change to default rows fails that test against the dashboard alone, which is fast to run locally while changing row markup and points straight at the helper. It's still an outerloop test, so it doesn't gate PR CI. ## Checklist - Is this feature complete? - [x] Yes. Ready to ship. - [ ] No. Follow-up changes expected. - Are you including unit tests for the changes and scenario tests if relevant? - [x] Yes - [ ] No - Did you add public API? - [ ] Yes - [x] No - Does the change make any security assumptions or guarantees? - [ ] Yes - [x] No **🤖 Posted by AspireAgent (AI Agent)** Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 01d9682)
Addresses the review feedback from #21412 (Modernize the dashboard experience) that was deferred to the tracking issue #21421. Each item was checked against the current code first. Item 17 was already fixed, and every other item is addressed here. The design questions are answered in a comment below. Fixes #21421 | Items | Change | |---|---| | 1, 3, 6 | Replaces `WithDashboardPosition` with `WithDisplayOrder`, backed by its own `DisplayOrderAnnotation` and transported as `resource.displayOrder`. Like `ResourceUrlAnnotation.DisplayOrder`, it's a relative sort key: higher values appear first and resources without one count as 0. The API isn't in a release yet. Polyglot snapshots and AppHosts are updated. | | 2 | Keeps traits as resource properties, as decided in the review, and corrects the comment that described them. | | 4 | Moves tag validation out of the `TraitAnnotation` constructor into `WithTrait`, which `WithTags` goes through. An `int` parameter covers display order validation. | | 5 | Replaces `unresolved_parameter_names` with a generic `related_resource_names` on the notification message, keeping field number 5. The dashboard offers to show the related resources in place of the primary button, and the parameter form still accepts the notification. | | 7 | Restores the `CircularBuffer` removal checks under `#if DEBUG`, for buffers up to 1,024 items, so large console log buffers don't bring back the CPU cost. | | 8, 20 | Recordings don't mount a live terminal. The missing-parameters form is read-only for recordings and doesn't accept the live AppHost's prompt. | | 9 | `ParameterOverview` and `MissingParametersPanel` edit their own copy of the shared command inputs. | | 10 | Keeps generated input names when a prompt has a dynamically loaded input, because callbacks can read `AllInputs` regardless of `DependsOnInputs`. Names that aren't unique are still renamed, because the prompt can't be shown otherwise, and every other input keeps its name. Each prompt input also carries the parameter it is for (`InteractionInput.related_resource_name`), and the parameter form matches inputs by that field rather than by name. A generated name can then match another, already resolved parameter without the form editing the wrong input. | | 11, 19 | The inline form forwards `UpdateStateOnChange`. Without an active interaction, its file button runs the set-parameter command. | | 12 | Sums delta counter observations per bucket. Repeated delta numbers are no longer merged at ingestion, because that lost their times, and the SQLite rollup now sums them by observation time instead of keeping the largest, without overflowing. Delta numbers are selected by their end, so one that arrives out of order isn't lost. Rollups and chart totals stay finite without depending on the order of the values, so a sum too large for a double no longer stops the chart from rendering, and chart intervals compare times in nanoseconds like the rollup query. Cloning metric values keeps their count, and delta counter cards show the latest change. | | 13 | Preserves negative levels. The main metrics chart shares this calculation, so it's corrected too. | | 14, 15 | A recording's activity window ends at its last request, or its last error log if it has no requests, and has its own labels. In the summary layout, the live window now refreshes every 30 seconds. | | 16 | Scopes the overview's pending refresh flag to the selection, and queues a refresh after console filter changes cancel one. Adds an internal `TelemetryRefreshDelayAsync` seam so a test can hold a refresh open. | | 18 | Ctrl-click after "select all" no longer adds a resource kind that's switched off. | | 21, 22 | Display orders come from the requesting run rather than the store, and moves use the displayed tie-break. | | 23 | Localizes the trace back button. `main` has since localized the activity and insights strings, so this PR only adds the recording-specific ones. | - `Aspire.Dashboard.Tests`: 2,655 passed - `Aspire.Dashboard.Components.Tests`: 1,384 passed, after merging `main` - `Aspire.Hosting.Tests`, limited to the trait, parameter processor, dashboard service and interaction service test classes: 257 passed - Code generation tests for TypeScript, Python, Go, Java and Rust: 412 passed, before the last review fixes, which didn't change exported API - `TestShop.AppHost` builds The regression tests for items 14, 15 and 16 were checked by temporarily undoing each fix and confirming they failed. Not covered: the polyglot AppHosts were renamed but not run, and nothing was checked in a browser. - Is this feature complete? - [x] Yes. Ready to ship. - [ ] No. Follow-up changes expected. - Are you including unit tests for the changes and scenario tests if relevant? - [x] Yes - [ ] No - Did you add public API? - [x] Yes - If yes, did you have an API Review for it? - [ ] Yes - [x] No. Not needed for `WithDisplayOrder` and `DisplayOrderAnnotation`, as decided in the review. - Did you add `<remarks />` and `<code />` elements on your triple slash comments? - [ ] Yes - [x] 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 - [x] No --------- Co-authored-by: Sébastien Ros <1165805+sebastienros@users.noreply.github.com> (cherry picked from commit 727eaa7) Adapted for release/17.0, which doesn't have the dashboard changes this built on in main (#21840, #21861, #21880, #21881, #21943): - Tags keep release/17.0's Aspire_Dashboard_Tag name (KnownResourceTraits.DashboardTag); the Aspire_Tag rename is #21881. - ResourceOrderStore keeps its SQLite storage. The AppHost display order semantics, GetPositions(resources) and TryMove(..., resources) are ported onto it, and the store no longer keeps AppHost positions itself. - ResourceOrderStoreTests: the four new display order tests run against the SQLite store, with a new store instance standing in for a restart. - Tests for code that isn't on release/17.0 aren't brought over (ParameterFormReplacedAndPromptShownAgain, Standalone_RemovedTelemetrySource). - Resources.Designer.cs and the Go TwoPassScanningGeneratedAspire snapshot are regenerated against release/17.0.
Combines the stacked dashboard backports into one PR for `release/17.0`. The commits are cherry-picked in stack order, one per source PR, onto the current `release/17.0` tip: 1. #22125: Store dashboard tags, resource order and metric pins in browser local storage (#21840) 2. #22126: Add dashboard resource-selector dropdown (#21861) 3. #22127: Rename the WithTags trait from Aspire_Dashboard_Tag to Aspire_Tag (#21881) 4. #22128: Restore single-line compact rows in the dashboard resource list (#21880) 5. #22129: Allocate the next dashboard run ID when two dashboards start in the same millisecond (#21913) 6. #22130: Simplify the dashboard around its two main jobs (#21943) 7. #22131: Return 400 for OTLP JSON requests with malformed ids (#21436) 8. #22132: Address deferred review feedback for the modern dashboard (#21766) ## Conflict resolution The stack was cut from an older `release/17.0`. Since then, #22123 (Harden Go SDK runtime) landed and regenerated `TwoPassScanningGeneratedAspire.verified.go`. That conflicted with #21766, which replaces `WithDashboardPosition` with `WithDisplayOrder`. The resolution drops `WithDashboardPosition` and regenerates the snapshot with the current Go generator, so `WithDisplayOrder` now uses the hardened `s.Err()` pattern. The other seven commits applied cleanly. ## Validation All `Aspire.Hosting.CodeGeneration.*.Tests` projects pass locally (Go, Java, Python, Rust, TypeScript). --------- Co-authored-by: Sébastien Ros <1165805+sebastienros@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: James Newton-King <james@newtonking.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: David Fowler <davidfowl@gmail.com> Co-authored-by: davidfowl <95136+davidfowl@users.noreply.github.com> Co-authored-by: Maddy Montaquila <maddyleger1@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Maddy Montaquila <maddy@MadBook-Pro-20.local> Co-authored-by: Taha Koçal <tahakocalgs@gmail.com> Co-authored-by: Jose Perez Rodriguez <joperezr@microsoft.com>



Description
The resource list's Compact density lost its one-line rows in #21727. That PR removed the compact branch of the row markup and switched
.density-compact .resource-row-texttoflex-direction: column, so compact rows became the same two-line rows as Comfortable ("● Running · 1 minute ago"), only shorter. They then also picked up the "No logs" / "Not applicable" badges added in #21819. #21727 also dropped Spacious's third detail line. That left the three densities differing only in row height and font size, althoughResourceListDensitystill documents one, two and three lines per resource.This PR restores the documented densities:
hover: none) always show the actions. Availability badges don't fit on one line: an unavailable row is muted, and the reason stays in its tooltip and accessible description. Unviewed-error badges sit just before the columns, so the columns stay aligned. A badge keeps the same place while the row's actions show, so it never moves out from under the pointer: it takes whichever place is further from the row's end, clear of the columns or clear of the actions.tcp://endpoint, the first endpoint is used, as in the expanded list's endpoint column. Query values are masked. The endpoint and source can take keyboard focus, which shows the full value in a tooltip and gives it an accessible label.In the expanded resource list (full canvas), compact rows use the same single-line identity. From 960px wide, the identity column can grow by the width of the type and state columns, so names aren't squeezed. That extra width only raises the column's maximum, never its minimum, so rows don't overflow the list at narrower widths. Actions there stay visible all the time.
Persistent types such as "RabbitMQ (persistent)" widen every row's type column together, so the columns stay aligned. The column is 128px where rows can be hovered. On touch screens, where the action rail is always showing, it stays at 64px at the default 420px pane width so names keep their room.
Screenshots / Recordings
The resource pane at its default 420px width, using the TestShop playground, before (main) and after:
Compact rows with an unviewed error, at rest and hovered. On main, the badge jumped about 62px toward the actions when a row was hovered, with a persistent type in the list. It now keeps its place:
Validation
ResourcesLayoutTeststhat asserted the two-line compact row (CompactDensity_ShowsOneLineWithTypeAndStateColumns,WideRowMetadata_PreservesRuntimeStateAndAccessibleHealthWithRelativeStart,AppHost_ContainerLifetimeQualifiesTypeAndBadgesResourceGlyph). AddedResourceRowAvailabilityTests.CompactDensity_UnavailableRowKeepsReasonWithoutBadgeandResourcesLayoutTests.SpaciousSidebarDetail_PrefersUnhealthyStatusThenEndpointThenSource.ResourcesLayoutTests,ResourceRowAvailabilityTests,MainLayoutTests,DashboardViewPreferencesTestsandSettingsDialogTestspass.ResourcePaneResponsiveTestsfor the restored geometry:ResourcePaneResponsiveTests.CompactSidebar_ErrorBadgeKeepsItsPlaceWhileActionsShow. It seeds error logs and checks five states: at rest, the row hovered, the badge hovered, the row focused, and the menu open. In each, the badge must not move or be covered, must line up with its spacer, and must not overlap a column or an action. It runs with and without a persistent type in the list, and fails against the previous styles.ResourcePaneResponsiveTestspass locally. 4 failures come fromRowComposition_AlignsStatusAndSingleLineDetailsWithAccessibleContrastandAccessibility_PaneAndPopoversHaveNoBlockingWcagViolationsat 390px, which fail the same way on main.Resizer_DoubleClickRestoresMinimumAcrossPresentationAndNavigationfailed once in the full run because a drag didn't take effect, and passed when rerun on its own.Fixes # (issue)
Checklist
<remarks />and<code />elements on your triple slash comments?