Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -798,7 +798,7 @@ describe("SettingsControls Accessibility", () => {
expect(activePanelStrip).not.toHaveStyle("--settings-active-panel-top: 9.5rem");
});

it("SettingsContentPanels renders dirty-to-saving-to-saved feedback while keeping values mounted", async () => {
it("SettingsContentPanels renders its standalone sticky active-panel strip while keeping values mounted", async () => {
const { rerender } = render(
<SettingsContentPanels
state={{
Expand Down Expand Up @@ -881,16 +881,24 @@ describe("SettingsControls Accessibility", () => {
expect(screen.getByText("General panel values stay mounted")).toBeInTheDocument();
});

it("SettingsPage keeps scope controls and active panel status in one sticky wrapping bar", () => {
it("SettingsPage keeps scope controls and active panel status in one unified sticky wrapping bar", () => {
const source = readFileSync("dashboard/src/v2/SettingsPage.tsx", "utf8");
const commandStatusBarSource = source.match(
/<div\s+data-settings-sticky="settings-command-status"[\s\S]*?<SettingsCategoryRail/,
)?.[0] ?? "";

expect(source).toContain('import { SettingsScopeControls } from "./components/settings/SettingsScopeControls.js";');
expect(source).toContain('import { SettingsActivePanelStatus } from "./components/settings/SettingsActivePanelStatus.js";');
expect(source).toContain('data-settings-sticky="settings-command-status"');
expect(source).toContain("sticky top-16 z-30");
expect(source).toContain("flex min-w-0 max-w-full flex-wrap");
expect(source).toContain("<SettingsScopeControls");
expect(source).toContain("<SettingsActivePanelStatus");
expect(source).toContain("sticky={false}");
expect(source).toContain("<SettingsContentPanels state={state} showActivePanelStatus={false} />");
expect(commandStatusBarSource).toContain("sticky top-16 z-30");
expect(commandStatusBarSource).toContain("flex min-w-0 max-w-full flex-wrap");
expect(commandStatusBarSource).toContain("<SettingsScopeControls");
expect(commandStatusBarSource).toContain("<SettingsActivePanelStatus");
expect(commandStatusBarSource).toContain("sticky={false}");
expect(commandStatusBarSource).not.toContain("rounded-[");
expect(commandStatusBarSource).not.toContain("bg-[var(--surface-glass)]");
expect(source.match(/<SettingsContentPanels/g) ?? []).toHaveLength(1);
expect(source).toMatch(/<SettingsContentPanels\s+state=\{state\}\s+showActivePanelStatus=\{false\}\s+\/>/);
expect(source).not.toContain("scopeSticky.getBoundingClientRect()");
expect(source).not.toContain("panelStickyTop");
});
Expand Down
2 changes: 2 additions & 0 deletions docs-web/content/docs/user-dashboard-settings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ Switch scope with the selector at the top:
- **Project** — applies to the active project.
- **Sprint** — applies to the selected sprint within the active project.

The sticky command/status bar keeps the System/Project selector, project availability or inheritance context, active panel, and save state visible together while you scroll. It shows the visible-category count only while Smart Find is active; when search is inactive, the visible status stays to a quiet search prompt while the exact category total remains available to assistive technology. The bar uses compact controls and chips instead of one long background card, so focus rings, contrast, wrapping, and saved/dirty cues stay clear on narrow screens.

## Categories

The category rail on the left includes:
Expand Down
2 changes: 1 addition & 1 deletion docs-web/user/dashboard/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Switch scope with the selector at the top:
- **Project** — applies to the active project.
- **Sprint** — applies to the selected sprint within the active project.

The sticky command/status row keeps the System/Project selector, project availability or inheritance context, active panel, and save state visible together while you scroll. It shows the visible-category count only while Smart Find is active; when search is inactive, the visible status stays to a quiet search prompt while the exact category total remains available to assistive technology.
The sticky command/status bar keeps the System/Project selector, project availability or inheritance context, active panel, and save state visible together while you scroll. It shows the visible-category count only while Smart Find is active; when search is inactive, the visible status stays to a quiet search prompt while the exact category total remains available to assistive technology. The bar uses compact controls and chips instead of one long background card, so focus rings, contrast, wrapping, and saved/dirty cues stay clear on narrow screens.

## Categories

Expand Down
2 changes: 1 addition & 1 deletion docs/dashboard/dashboard-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ Runtime scoping:
- Project scope General settings expose the selected project's display name as an immediate metadata edit. Saving calls `PATCH /api/projects/:projectId` with the trimmed `name`, refreshes the project collection, and leaves the project id, settings overrides, tasks, and runtime history unchanged.
- The `/config` page keeps the existing v2 settings shell and categories, but now binds them to real scoped settings instead of draft-only values
- System scope only edits system-owned controls, while project scope only edits project-owned overrides for the selected project
- The Settings scope/status row stays sticky below the app shell while scrolling, keeping the System/Project selector, selected-scope context, project availability or inheritance summary, and save badges visible. The visible-category count appears there only while Smart Find is active; otherwise the visible search status stays to a quiet prompt and keeps the exact category total for screen readers. The active panel/save-state strip is also sticky and uses a measured top offset from the wrapped scope row so long project names and narrow layouts do not create overlapping pinned controls.
- The Settings command/status bar stays sticky below the app shell while scrolling, keeping the System/Project selector, selected-scope context, project availability or inheritance summary, active panel, and save state visible in one wrapping row. The visible-category count is search-only metadata and appears there only while Smart Find is active; otherwise the visible search status stays to a quiet prompt and keeps the exact category total for screen readers. The bar avoids a long background card behind the scope controls; each control or chip carries its own tokenized contrast, focus ring, and reduced-motion-safe status cue.
- The integrations view now owns provider API keys plus GitHub and GitLab tokens and GitHub workflow settings, rather than splitting those across separate categories
- The integrations view uses a registry-style list with per-integration `Add` and `Manage` actions so additional integrations can be added without turning the page into one long form
- Provider integrations are now instance-based:
Expand Down
7 changes: 4 additions & 3 deletions docs/dashboard/design-system-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This document defines the visual patterns and rules for the Settings workspace.
* Destructive actions in the Danger Zone (`Wipe Project`, `Wipe Database`) use the `danger` tone, yielding clear semantic `bg-status-red text-white` presentation. Panels themselves hint at danger via red-tinted borders and backgrounds.

4. **Metadata and Hierarchy**:
* Metadata chips (`visible categories` while Smart Find is active, `unsaved edits`) and badges leverage standard tokens to maintain visual rhythm.
* Metadata chips (`visible categories` while Smart Find is active, `unsaved edits`) and badges leverage standard tokens to maintain visual rhythm. `Visible categories` is search-only metadata and must not appear when Smart Find is idle.
* Headers and contextual information (e.g., `SettingsHeader`) separate sections with thin borders (`--border-hairline`).
* The Quality Assurance section belongs in `Settings > Sprint & Git`, directly below `Merge Gates & Autofix`, even though its persisted settings path remains `agents.qualityAssurance`. QA-labeled project agents remain prominent in the selector ordering, and disabled project selectors still communicate that built-in QA routing remains available.
* Every visible `SectionCard` subcategory exposes card-level help controls in the header action area: an info icon that opens keyboard-accessible guidance and a docs icon that links to the exact `/docs/user/dashboard/settings#<subcategory>` anchor. These controls supplement row-level info affordances and must not replace field-specific help.
Expand All @@ -50,8 +50,9 @@ This document defines the visual patterns and rules for the Settings workspace.
* Always rely on semantic CSS variables from `globals.css` and `tokens.css` via `[var(--variable-name)]` for colors, backgrounds, borders, and shadows instead of hardcoding Tailwind utility colors and shadow values.
* Inline validation and character-counter feedback must use the shared `inlineValidation` and `controlFeedback` interaction tokens. Error text should be announced only after blur or an explicit submit/force-validation path, and helper text should not remain in `aria-describedby` while an error is active.
* Settings page save state lives at the active panel boundary. `SettingsContentPanels` sets `aria-busy` while loading, saving, or resetting and uses `ActionFeedbackRegion` for saved/dirty/saving/loading states. `SettingsActivePanelStatus` provides the durable visible active-panel/save-state line so reduced-motion users see the current category and outcome without relying on panel motion. Blocking errors switch the status stream to assertive alert copy. Do not replace field contents with loading placeholders during saves or background refreshes.
* The Settings command/status bar is sticky below the app shell and includes the System/Project radiogroup, selected-scope context, project-scope availability or inheritance summary, unsaved edits indicator, saved badge, and inline active-panel/save-state status. It shows the visible-category count only while Smart Find is active; otherwise category-count context stays in the search/status regions instead of the sticky bar. The bar must wrap naturally on narrow screens, preserve focus rings while pinned, and keep `settings-scope-context`, `settings-project-scope-disabled`, the polite scope status region, and the active-panel status announcement wired to the controls.
* `SettingsPage` owns the unified sticky command/status bar at `top-16` and renders `SettingsContentPanels` with the separate active-panel strip suppressed. The desktop `SettingsCategoryRail` remains `lg:sticky lg:top-16`; page content starts below the unified bar so the rail, command/status bar, and active panel do not overlap.
* The Settings command/status bar is one unified sticky surface below the app shell. It includes the System/Project radiogroup, selected-scope context, project-scope availability or inheritance summary, search-only visible-category metadata, unsaved edits indicator, saved badge, and inline active-panel/save-state status. It shows the visible-category count only while Smart Find is active; otherwise category-count context stays in the search/status regions instead of the sticky bar. The bar must wrap naturally on narrow screens, preserve focus rings while pinned, and keep `settings-scope-context`, `settings-project-scope-disabled`, the polite scope status region, and the active-panel status announcement wired to the controls.
* The unified command/status bar must not use a long page-level card or pill background behind the scope controls. The child scope selector, context chips, and active-panel status own their tokenized borders, glass fills, focus rings, wrapping, and reduced-motion-safe saved/dirty cues.
* `SettingsPage` owns the unified sticky command/status bar at `top-16` and renders `SettingsContentPanels` with its reusable active-panel strip suppressed. `SettingsContentPanels` may still render that sticky strip by default when used outside the full Settings page. The desktop `SettingsCategoryRail` remains `lg:sticky lg:top-16`; page content starts below the unified bar so the rail, command/status bar, and active panel do not overlap.
* Save and background reload paths must preserve dirty drafts until the affected scope has actually saved or reset. If system settings save while project settings are dirty, project draft values remain mounted and are not replaced by an effective-settings refresh; failed project saves leave the draft visible for correction.
* Category rail buttons expose selected and pending state through active styling plus ARIA (`aria-current`, `aria-selected`, `aria-busy`) without extra visible status badges. Disabled state keeps visible disabled copy plus `aria-disabled`. Category movement uses explicit `selectionMovement` markers; panel entry uses `enterExit`; reduced-motion users receive the same static active styling, validation copy, busy state, and save outcome text without relying on animated movement.
* Scope switches announce the selected system/project context through a polite status region. Project scope must also expose visible inherited/overridden summary text while preserving the existing per-field badge semantics: inherited stays neutral, project overrides stay amber and resettable only when the active scope can clear them.
Expand Down
2 changes: 1 addition & 1 deletion docs/dashboard/interaction-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Current refined dashboard surfaces use the interaction contracts as follows:
| Sprint ledger | `controlFeedback`, `selectionMovement`, `listReorder`, `expansionCollapse`, `asyncFeedback` | Sort, filter, selection, and bulk-action changes are composed into one polite live-region message; selected and pending rows retain static badges; bulk delete uses `ConfirmDialog`; focus returns to the delete trigger or a ledger fallback after dialog teardown. |
| Live runtime | `controlFeedback`, `enterExit`, `expansionCollapse`, `selectionMovement`, `listReveal`, `listReorder`, `asyncFeedback` | Reconnect, stale, refreshing, and recovering states keep the last runtime snapshot visible with polite live regions; disconnected transport and blocking errors are assertive; pending runtime actions remain focus-stable with `aria-disabled` plus activation suppression. Runtime force-complete and sprint pause/stop/delete controls require an explicit named confirmation before their side-effect handlers run. |
| Browser preview, file, and diff workbench | `controlFeedback`, `enterExit`, `selectionMovement`, `listReveal`, `listReorder`, `asyncFeedback` | Preview launch/rebuild/stop/navigation/script/log operations expose visible async status; unavailable links remain keyboard reachable as disabled link controls with persistent reasons; stale iframe/log content remains mounted during refresh when useful content exists. |
| Settings workspace | `controlFeedback`, `selectionMovement`, `enterExit`, `inlineValidation`, `asyncFeedback` | Scope/category changes expose selected, pending, inherited, overridden, and disabled-reason text; saves use active-panel `aria-busy` plus `ActionFeedbackRegion`; provider removals use inline confirmation with cancel and focus restoration; fields preserve current draft values while loading or saving. |
| Settings workspace | `controlFeedback`, `selectionMovement`, `enterExit`, `inlineValidation`, `asyncFeedback` | The unified sticky command/status bar keeps scope selection, scope context, active panel, and save state visible together; visible-category metadata appears only while Smart Find is active. Scope/category changes expose selected, pending, inherited, overridden, and disabled-reason text; saves use active-panel `aria-busy` plus `ActionFeedbackRegion`; provider removals use inline confirmation with cancel and focus restoration; fields preserve current draft values while loading or saving. |
| Global search | `enterExit`, `listReveal`, `controlFeedback`, `selectionMovement` | The input remains the combobox focus owner with `aria-activedescendant`; stale results remain available with `aria-busy`; unavailable rows expose a visible disabled reason and suppress pointer and keyboard activation; active rows are scrolled within the result container only. Running agent and preview-container dots use motion-safe animation only, with badge text, color, and static reduced-motion rings preserving status without pulse or ping motion. |
| Memory workspace | `controlFeedback`, `selectionMovement`, `listReveal`, `listReorder`, `expansionCollapse`, `inlineValidation`, `asyncFeedback` | Search/filter/selection changes announce counts and selected state; background refresh or failed refresh keeps the last useful list visible; batch delete uses confirmation, optimistic feedback, retry, and focus restoration; reduced motion keeps badges, rings, and live-region copy for selected graph/list state. |
| Task cards and active streams | `controlFeedback`, `selectionMovement`, `listReorder`, `asyncFeedback` | Status, dependency blockers, QA review, and PR/live metadata keep stable text equivalents; quick actions sit in the card footer and are visually revealed on hover or keyboard focus while remaining in the keyboard path with task-specific names. Low-value metadata such as the default `Auto` executor and pointer-only drag helper chip are omitted from visible card metadata, while screen-reader drag guidance, pending dispatch, `aria-busy`, disabled state, and reason text remain available. Task-board cards are keyed by stable card view-model identities so unrelated live events and filter announcements preserve mounted card controls instead of rerendering unchanged cards. Sprint selector running dots keep color, shadow, option labels, and selected/loading badges available when reduced motion disables pulse animation. |
Expand Down