FN-8632: align Command Center concurrency controls

Align Command Center capacity controls so their slider tracks remain visually synchronized.

- Use a two-column grid for the surviving per-project capacity sliders.
- Stretch slider cards and bottom-align range inputs despite optional running-count captions.
- Add regression coverage and a patch changeset for the layout correction.

Files changed:
 .changeset/fn-8632-concurrency-layout.md           |  7 ++++
 .../command-center/CommandCenterControls.css       | 22 ++++++----
 .../__tests__/CommandCenterControls.test.tsx       | 47 +++++++++++++++++++++-
 3 files changed, 67 insertions(+), 9 deletions(-)

Fusion-Task-Id: FN-8632

Fusion-Task-Lineage: c59f52fc-0e5b-4633-98fa-64b8a60621d0

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-31 14:21:52 -07:00
parent 478b15d7ec
commit cde02b423d
3 changed files with 67 additions and 9 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Align Command Center concurrency controls and remove the unused slider column.
category: fix
dev: The two per-project capacity tracks now share a baseline when live running counts are shown.

View File

@@ -75,14 +75,22 @@
color: var(--color-error);
}
/*
FNXC:CommandCenter 2026-07-31-20:57:
FN-8632 keeps the grid equal to the two per-project capacity sliders that exist.
Stretch each slider and bottom-align its range control so optional running-count captions
cannot desynchronize the range tracks at desktop or tablet widths.
*/
.cc-controls-sliders {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-columns: repeat(2, minmax(0, 1fr));
align-items: stretch;
gap: var(--space-md);
}
.cc-controls-slider {
display: flex;
align-self: stretch;
min-inline-size: 0;
flex-direction: column;
gap: var(--space-sm);
@@ -90,13 +98,6 @@
font-size: 0.8125rem;
}
/* FNXC:GlobalConcurrencyControls 2026-06-25-14:10: The global cap is a cross-project setting; span it full-width at the top of the Concurrency card and separate it from the per-project sliders with a divider plus an "Across all projects" caption. */
.cc-controls-slider--global {
grid-column: 1 / -1;
padding-bottom: var(--space-md);
border-bottom: 1px solid var(--border);
}
.cc-controls-slider-caption {
color: var(--text-muted);
font-size: 0.75rem;
@@ -125,6 +126,11 @@ FN-8007 makes the current-use dot's half-thumb edge inset match the native deskt
align-items: center;
}
.cc-controls-range-wrap,
.cc-controls-slider > input[type="range"] {
margin-block-start: auto;
}
/* FNXC:GlobalConcurrencyControls 2026-06-26-00:00: The Command Center current-use marker mirrors the footer marker, reuses the status-dot convention, uses logical inline positioning for RTL, and never intercepts range drags. */
.cc-controls-use-marker {
--use-pct: 0%;

View File

@@ -1,7 +1,7 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { CommandCenterControls } from "../CommandCenterControls";
import { ConfirmDialogProvider } from "../../../hooks/useConfirm";
import { readAppFile } from "../../../test/cssFixture";
@@ -68,6 +68,13 @@ function expectCommandCenterUseOffset(testId: string, ratio: number) {
);
}
function cssRule(css: string, selector: string) {
const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const match = css.match(new RegExp(`${escapedSelector}\\s*\\{([\\s\\S]*?)\\}`));
expect(match, `Expected CSS rule for ${selector}`).not.toBeNull();
return match?.[1] ?? "";
}
// FNXC:Theme 2026-07-16-14:30: FN-8146 pins the historical Settings-grid set, including restored shadcn-mono, so a removal from COLOR_THEMES cannot make the all-themes checks pass circularly.
const EXPECTED_THEME_IDS = ['default', 'ocean', 'forest', 'sunset', 'zen', 'berry', 'high-contrast', 'industrial', 'monochrome', 'slate', 'ash', 'air', 'graphite', 'silver', 'solarized', 'factory', 'factory-mono', 'ayu', 'one-dark', 'nord', 'dracula', 'gruvbox', 'tokyo-night', 'catppuccin-mocha', 'github-dark', 'everforest', 'rose-pine', 'kanagawa', 'night-owl', 'palenight', 'monokai-pro', 'slime', 'brutalist', 'neon-city', 'parchment', 'terminal', 'glass', 'glass-silver', 'horizon', 'vitesse', 'outrun', 'snazzy', 'porple', 'espresso', 'mars', 'poimandres', 'ember', 'rust', 'copper', 'foundry', 'carbon', 'sandstone', 'lagoon', 'frost', 'lavender', 'neon-bloom', 'sepia', 'cobalt', 'clay', 'moss', 'aurora', 'calm', 'dawn', 'factory-dark', 'shadcn', 'shadcn-ember', 'shadcn-custom', 'shadcn-blue', 'shadcn-green', 'shadcn-red', 'shadcn-purple', 'shadcn-pink', 'shadcn-orange', 'shadcn-yellow', 'shadcn-mono', 'shadcn-mono-red', 'shadcn-mono-blue', 'shadcn-mono-green', 'shadcn-mono-purple', 'shadcn-mono-pink', 'shadcn-mono-orange', 'shadcn-mono-yellow', 'shadcn-black', 'shadcn-gray', 'shadcn-gray-blue'] as const;
@@ -120,6 +127,44 @@ describe("CommandCenterControls concurrency markers", () => {
/*
FNXC:CommandCenter 2026-07-31-20:57:
FN-8632 requires the desktop slider grid to contain only the two surviving capacity
controls. Each label stretches and its range control consumes remaining block space
so loaded running-count captions cannot place either track above the shared baseline.
*/
it("keeps concurrency tracks baseline-aligned with and without loaded counts", async () => {
const slidersRule = cssRule(commandCenterControlsCss, ".cc-controls-sliders");
const sliderRule = cssRule(commandCenterControlsCss, ".cc-controls-slider");
const rangeBaselineRule = cssRule(
commandCenterControlsCss,
".cc-controls-range-wrap,\n.cc-controls-slider > input[type=\"range\"]",
);
expect(slidersRule).toContain("grid-template-columns: repeat(2, minmax(0, 1fr));");
expect(slidersRule).toContain("align-items: stretch;");
expect(sliderRule).toContain("align-self: stretch;");
expect(rangeBaselineRule).toContain("margin-block-start: auto;");
expect(commandCenterControlsCss).not.toContain(".cc-controls-slider--global");
let resolveCounts!: (value: { currentlyActive: number; queuedCount: number; projectsActive: Record<string, number> }) => void;
legacyMocks.fetchGlobalConcurrency.mockReturnValue(new Promise((resolve) => {
resolveCounts = resolve;
}));
renderControls();
expect(screen.getAllByRole("slider")).toHaveLength(2);
expect(screen.queryByTestId("cc-project-running")).not.toBeInTheDocument();
expect(screen.queryByTestId("cc-global-running")).not.toBeInTheDocument();
resolveCounts({ currentlyActive: 10, queuedCount: 0, projectsActive: { proj_123: 10 } });
await screen.findByTestId("cc-project-running");
expect(screen.getAllByRole("slider")).toHaveLength(2);
expect(screen.getByTestId("cc-global-running")).toBeInTheDocument();
for (const slider of screen.getAllByRole("slider")) {
expect(slider.closest(".cc-controls-slider")).toBeTruthy();
}
});
it("matches the desktop and mobile native thumb-size CSS contract", () => {
expect(commandCenterControlsCss).toContain(
"--cc-controls-range-thumb-size: calc(var(--space-lg) + var(--space-xs) / 2);",