Files
fusion/packages/dashboard/app/components/EngineControlMenu.css
gsxdsm 2db8ead842 FN-7071: show concurrency utilization in the footer panel
Expose live running-agent utilization in the footer concurrency controls.

- Show all-project and current-project running counts beside the matching concurrency sliders.
- Add clamped current-use markers on global and project slider tracks.
- Preserve shared global-concurrency hook state with utilization counts and cover the new UI behavior with tests.
- Document the footer panel utilization indicators and add a release changeset.

Files changed:
 .changeset/fn-7071-concurrency-running-counts.md   |   7 ++
 docs/dashboard-guide.md                            |   2 +-
 .../dashboard/app/components/EngineControlMenu.css |  23 ++++
 .../dashboard/app/components/EngineControlMenu.tsx | 103 +++++++++++++-----
 .../__tests__/EngineControlMenu.test.tsx           | 117 ++++++++++++++++++++-
 .../hooks/__tests__/useGlobalConcurrency.test.ts   |  96 +++++++++++++++++
 .../dashboard/app/hooks/useGlobalConcurrency.ts    |  41 +++++++-
 packages/i18n/locales/en/app.json                  |   4 +-
 packages/i18n/locales/es/app.json                  |   4 +-
 packages/i18n/locales/fr/app.json                  |   4 +-
 packages/i18n/locales/ko/app.json                  |   4 +-
 packages/i18n/locales/zh-CN/app.json               |   4 +-
 packages/i18n/locales/zh-TW/app.json               |   4 +-
 13 files changed, 373 insertions(+), 40 deletions(-)

Fusion-Task-Id: FN-7071

Fusion-Task-Lineage: db95d50a-c042-4972-a462-1987b7bca61e
2026-06-26 13:57:00 -07:00

156 lines
3.8 KiB
CSS

.engine-control-menu {
position: relative;
display: inline-flex;
align-items: center;
}
.engine-control-menu__trigger {
color: var(--text-muted);
}
.engine-control-menu__trigger:hover {
color: var(--text);
}
.engine-control-menu__popover {
position: absolute;
right: 0;
bottom: calc(100% + var(--space-xs));
z-index: 70;
width: min(24rem, calc(100vw - (var(--space-lg) * 2)));
max-height: min(32rem, calc(100vh - var(--space-2xl)));
overflow: auto;
padding: var(--space-md);
display: flex;
flex-direction: column;
gap: var(--space-md);
background: var(--card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg);
color: var(--text);
}
.engine-control-menu__section {
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.engine-control-menu__section--actions {
gap: var(--space-xs);
}
.engine-control-menu__action {
width: 100%;
justify-content: flex-start;
gap: var(--space-sm);
}
.engine-control-menu__action:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.engine-control-menu__section-header,
.engine-control-menu__slider-label {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-md);
}
.engine-control-menu__section-header {
color: var(--text);
font-weight: 600;
}
.engine-control-menu__save-state {
color: var(--text-muted);
font-size: var(--font-size-xs);
font-weight: 500;
}
/* FNXC:GlobalConcurrencyControls 2026-06-25-22:45: "All projects" scope caption sits between the global-cap title and its save-state indicator; muted so the save-state remains the emphasized signal. */
.engine-control-menu__scope-caption {
margin-inline-start: auto;
color: var(--text-muted);
font-size: var(--font-size-xs);
font-weight: 500;
}
.engine-control-menu__save-state--saving {
color: var(--color-warning);
}
.engine-control-menu__save-state--saved {
color: var(--color-success);
}
.engine-control-menu__save-state--error,
.engine-control-menu__error {
color: var(--color-error);
}
/* FNXC:GlobalConcurrencyControls 2026-06-25-14:10: The global cap is a cross-project setting; separate it visually from the per-project sliders below with a divider and an "All projects" caption. */
.engine-control-menu__section--global {
padding-bottom: var(--space-sm);
border-bottom: 1px solid var(--border);
}
.engine-control-menu__slider {
display: flex;
flex-direction: column;
gap: var(--space-xs);
color: var(--text-muted);
}
.engine-control-menu__slider-label strong {
color: var(--text);
font-family: var(--font-mono);
}
.engine-control-menu__slider-meta {
color: var(--text-muted);
font-size: var(--font-size-xs);
}
.engine-control-menu__range-wrap {
--engine-control-range-thumb-size: var(--space-md);
position: relative;
display: flex;
align-items: center;
}
.engine-control-menu__range {
width: 100%;
accent-color: var(--accent);
}
/* FNXC:GlobalConcurrencyControls 2026-06-26-06:26: The current-use marker reuses the global .status-dot convention and is positioned with logical inset properties so utilization is visible on LTR/RTL slider tracks without intercepting drag input. */
.engine-control-menu__use-marker {
--use-pct: 0%;
--use-offset: 0%;
position: absolute;
inset-block-start: 50%;
inset-inline-start: var(--use-offset, var(--use-pct));
transform: translate(-50%, -50%);
pointer-events: none;
}
.engine-control-menu__error {
margin: 0;
font-size: var(--font-size-xs);
}
@media (max-width: 768px) {
.engine-control-menu__popover {
position: fixed;
left: var(--space-sm);
right: var(--space-sm);
bottom: calc(var(--mobile-nav-height) + max(env(safe-area-inset-bottom, 0px), var(--space-md)) + var(--space-2xl));
width: auto;
max-height: min(28rem, calc(100vh - var(--mobile-nav-height) - var(--space-2xl) - var(--space-lg)));
}
}