diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index 113812b3c7..75c934dbbb 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -663,6 +663,7 @@ Navigation: Features: - Global date-range picker in the header scopes the analytics tabs; **Mission Control** remains live rather than historical. +- **Overview controls dashboard** sits at the top of the Overview landing surface on desktop and mobile. It includes a read-only agent org chart, heartbeat pause/resume backed by the existing `enginePaused` setting, AI engine stop/start backed by `globalPause`, live scheduler status from executor stats, range sliders for `maxConcurrent`, `maxTriageConcurrent`, and `maxWorktrees` that persist through `/api/settings`, and a compact theme dropdown with the same color-chip swatches as Settings. These controls reuse existing APIs and App-level theme setters; they do not add a new heartbeat interval setting, backend route, or second theme owner. - **Overview** summarizes token usage/cost, autonomy, active nodes, sessions, agent runs, tasks done, model breadth, and real open signals, and includes the SDLC throughput funnel for the selected range at the bottom of the Overview content in loading, error, empty, and populated states. Its token total and Live activity snapshot token metric refresh on a bounded live cadence and animate number changes while preserving reduced-motion preferences. The sessions card uses the selected-range `ActivityAnalytics.sessions` value already loaded for the overview. The Live activity snapshot also shows the current board-state count for tasks in progress, independent of the selected analytics date range. Overview includes a graph-rich software-factory snapshot with the existing tokens-by-model bar, tool-category bar, real recharts token-share pie, and the daily activity multi-series line chart placed before the daily activity sparkline/trend so the richer line graph sits higher in the chart grid. These reuse the already-loaded tokens, tools, activity, and signals analytics; the signals count comes from `/api/command-center/signals` and renders unavailable (`—`) while the incidents-backed response is loading or unavailable. The chart reveal/glow accents are decorative and disabled when reduced-motion preferences are active. The SDLC completion rate is shown as a radial gauge and is calculated as cohort conversion from in-range triage entrants, so the rate is capped at 100% even when older tasks finish during the range. - **Tokens** breaks down token totals, estimated cost, tasks, and per-model usage. Per-model and per-provider breakdowns use the task's analytics-only actually-used model snapshot when available, so usage from settings-resolved runs appears under the real runtime model instead of `(unknown)` without changing future model resolution; estimated cost uses the same snapshot-first, legacy-fallback model identity so those resolved runs price normally when the model is in the pricing table. It includes the existing token-usage-over-time chart, an additive recharts multi-series line graph, and a token-share pie backed by the same grouped token analytics; use the granularity control to switch the time-series request between hourly, daily, and weekly buckets. The token total and charts poll on a bounded cadence, keep the previous data visible during refresh, animate decorative count/bar transitions, and disable those animations for reduced-motion users. - **Tools** shows autonomy ratio, tool-call volume, intervention counts, sessions, and tool categories. The area keeps the existing category bar and adds a recharts category-share pie from `ToolAnalytics.byCategory`. There is intentionally no tools line chart yet because `ToolAnalytics` does not expose a per-day tool trend; the dashboard does not fabricate one or call a new endpoint. diff --git a/docs/settings-reference.md b/docs/settings-reference.md index 13727f31bc..8d630133b7 100644 --- a/docs/settings-reference.md +++ b/docs/settings-reference.md @@ -299,10 +299,10 @@ Defaults from `DEFAULT_PROJECT_SETTINGS`; key scope from `PROJECT_SETTINGS_KEYS` | `globalPause` | `boolean` | `false` | Hard stop: terminate active engine sessions and pause scheduling immediately. | | `globalPauseReason` | `string` | `undefined` | Optional reason for `globalPause` (`"rate-limit"` for automatic pauses, `"manual"` for user-triggered pauses). Cleared on unpause. | | `enginePaused` | `boolean` | `false` | Soft pause: stop dispatching new work while letting active sessions finish. While paused (including shared pause windows with `globalPause`), stuck-task polling/timers are suspended so paused wall-clock time does not count against `taskStuckTimeoutMs`. Clearing pause state resumes runtime scheduling and gives tracked active sessions a fresh stuck-task grace window before normal detection resumes; when `autoMerge` is enabled, eligible `in-review` tasks are re-swept into the auto-merge queue (paused/blocked/failed review tasks remain skipped). | -| `maxConcurrent` | `number` | `2` | Max concurrent task-lane AI agents (planning, executor, merge). | -| `maxTriageConcurrent` | `number` | `2` | Max concurrent planning agents. | +| `maxConcurrent` | `number` | `2` | Max concurrent task-lane AI agents (planning, executor, merge). Editable from Settings and the Command Center Overview controls dashboard. | +| `maxTriageConcurrent` | `number` | `2` | Max concurrent planning agents. Editable from Settings and the Command Center Overview controls dashboard. | | `globalMaxConcurrent` | `number` | `4` | System-wide max concurrent agents across all projects. | -| `maxWorktrees` | `number` | `4` | Max git worktrees. | +| `maxWorktrees` | `number` | `4` | Max git worktrees. Editable from Settings and the Command Center Overview controls dashboard. | | `pollIntervalMs` | `number` | `15000` | Scheduler poll interval (ms). | | `heartbeatMultiplier` | `number` | `1` | Global multiplier applied to agent heartbeat timing: both heartbeat intervals and unresponsive timeout bases. Configured from the Agents screen (not Settings). | | `heartbeatScopeDiscipline` | `"strict" \| "lite" \| "off"` | `"strict"` | Heartbeat prompt procedure mode. `strict` keeps coordination-heavy scope discipline, `lite` restores pre-2026-05-11 wording, and `off` uses a minimal procedure. Per-agent `runtimeConfig.heartbeatScopeDiscipline` can override this default. | diff --git a/packages/dashboard/app/App.tsx b/packages/dashboard/app/App.tsx index 01ba53439d..9bd9d80d6b 100644 --- a/packages/dashboard/app/App.tsx +++ b/packages/dashboard/app/App.tsx @@ -1819,7 +1819,13 @@ function AppInner() { return ( - + ); diff --git a/packages/dashboard/app/components/ThemeDropdown.css b/packages/dashboard/app/components/ThemeDropdown.css new file mode 100644 index 0000000000..04aaf8f256 --- /dev/null +++ b/packages/dashboard/app/components/ThemeDropdown.css @@ -0,0 +1,96 @@ +.theme-dropdown { + position: relative; + display: flex; + flex-direction: column; + gap: var(--space-sm); + min-inline-size: 0; +} + +.theme-dropdown-trigger { + justify-content: space-between; + gap: var(--space-sm); + inline-size: 100%; + min-inline-size: 0; +} + +.theme-dropdown-trigger .theme-option-swatch, +.theme-dropdown-option .theme-option-swatch { + flex: 0 0 auto; +} + +.theme-dropdown-trigger-label, +.theme-dropdown-option-label { + min-inline-size: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.theme-dropdown-modes { + display: flex; + flex-wrap: wrap; + gap: var(--space-xs); +} + +.theme-dropdown-mode-btn { + gap: var(--space-xs); +} + +.theme-dropdown-mode-btn.active { + border-color: var(--accent); + color: var(--accent); +} + +.theme-dropdown-popover { + position: absolute; + z-index: 10; + inset-block-start: calc(100% + var(--space-xs)); + inset-inline: 0; + padding: var(--space-sm); + border: 1px solid var(--border-subtle); + border-radius: var(--radius-md); + background: var(--surface-1); + box-shadow: var(--shadow-lg); +} + +.theme-dropdown-list { + display: grid; + gap: var(--space-xs); + max-block-size: min(60vh, calc(var(--space-2xl) * 12)); + overflow-y: auto; + overscroll-behavior: contain; +} + +.theme-dropdown-option { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; + gap: var(--space-sm); + inline-size: 100%; + padding: var(--space-sm); + border: 1px solid transparent; + border-radius: var(--radius-sm); + background: transparent; + color: var(--text); + cursor: pointer; + text-align: start; +} + +.theme-dropdown-option:hover, +.theme-dropdown-option:focus-visible, +.theme-dropdown-option.active { + border-color: var(--accent); + background: color-mix(in srgb, var(--accent) 10%, transparent); + outline: none; +} + +@media (max-width: 768px) { + .theme-dropdown-popover { + position: static; + margin-block-start: var(--space-xs); + } + + .theme-dropdown-list { + max-block-size: min(50vh, calc(var(--space-2xl) * 10)); + } +} diff --git a/packages/dashboard/app/components/ThemeDropdown.tsx b/packages/dashboard/app/components/ThemeDropdown.tsx new file mode 100644 index 0000000000..54ce3c07cb --- /dev/null +++ b/packages/dashboard/app/components/ThemeDropdown.tsx @@ -0,0 +1,175 @@ +import { useEffect, useMemo, useRef, useState, type KeyboardEvent } from "react"; +import { useTranslation } from "react-i18next"; +import { Check, ChevronDown } from "lucide-react"; +import type { ColorTheme, ThemeMode } from "@fusion/core"; +import { COLOR_THEMES, THEME_MODES } from "./themeOptions"; +import "./ThemeSelector.css"; +import "./ThemeDropdown.css"; + +interface ThemeDropdownProps { + colorTheme: ColorTheme; + onColorThemeChange: (theme: ColorTheme) => void; + themeMode?: ThemeMode; + onThemeModeChange?: (mode: ThemeMode) => void; +} + +function ThemeSwatch({ className }: { className: string }) { + return ( +