From 3c6db08270a65e4ed31c40bc494fc11358671e01 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sat, 20 Jun 2026 21:16:16 -0700 Subject: [PATCH 1/6] FN-6805: color workflow counts by column Align workflow switcher status count badges with their board column colors. - Map Todo, In Progress, and Done workflow count badge text to the matching board column CSS tokens. - Cover the badge color contract with a CSS fixture regression test. - Document the color-token behavior for board and list workflow dropdown counts. Files changed: docs/dashboard-guide.md | 2 +- .../dashboard/app/components/WorkflowSwitcher.css | 10 +++++++--- .../components/__tests__/WorkflowSwitcher.test.tsx | 22 ++++++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) Fusion-Task-Id: FN-6805 Fusion-Task-Lineage: aab7140c-ad37-4bd8-b73d-199534285b37 --- docs/dashboard-guide.md | 2 +- .../app/components/WorkflowSwitcher.css | 10 ++++++--- .../__tests__/WorkflowSwitcher.test.tsx | 22 +++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index 08715b2f21..057dd6dfe2 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -77,7 +77,7 @@ Features: - Task card header meta badges group priority, fast mode, agent-created provenance, and elapsed/created-time chips into one wrapping row; agent labels prefer `sourceMetadata.agentName` over raw agent IDs - Column ordering semantics: `todo` mirrors scheduler pickup order (priority descending, then oldest `createdAt`, then task ID); `triage`, `in-progress`, `in-review`, and `archived` remain priority-first with task-ID tie-breaks; `done` is ordered by most recent completion first (`columnMovedAt`, then `updatedAt`, then `createdAt` fallback) - On mobile, both default and workflow-mode boards fill the project viewport while the column strip remains the internal horizontal scroller with contained edge overscroll. -- Board and List workflow switchers use a themed dropdown instead of a native select. The closed trigger shows the workflow name and chevron only; compact Todo / In Progress / Done counts derived from workflow column flags (excluding archived columns) appear while the dropdown is expanded, including on each workflow option. +- Board and List workflow switchers use a themed dropdown instead of a native select. The closed trigger shows the workflow name and chevron only; compact Todo / In Progress / Done counts derived from workflow column flags (excluding archived columns) appear while the dropdown is expanded, including on each workflow option. Those inline count badges intentionally use the same board column color tokens as cards: `--todo`, `--in-progress`, and `--done`. - When workflow columns are enabled, Board and List hydrate the last successful workflow-lane payload from a per-project session cache; cold loads show a neutral skeleton until settings and workflow metadata are known, avoiding a legacy single-lane flash. ![Board view](./screenshots/dashboard-overview.png) diff --git a/packages/dashboard/app/components/WorkflowSwitcher.css b/packages/dashboard/app/components/WorkflowSwitcher.css index fa3a507360..d2b2ab5f3a 100644 --- a/packages/dashboard/app/components/WorkflowSwitcher.css +++ b/packages/dashboard/app/components/WorkflowSwitcher.css @@ -91,16 +91,20 @@ line-height: calc(var(--space-md) / var(--space-sm)); } +/* +FNXC:WorkflowSwitcher 2026-06-20-00:00: +The switcher's inline Todo, In Progress, and Done count badges intentionally mirror the board column color tokens so each count reads as the same color as the column it summarizes. +*/ .workflow-switcher-count--todo { - color: var(--text-muted); + color: var(--todo); } .workflow-switcher-count--in-progress { - color: var(--color-warning); + color: var(--in-progress); } .workflow-switcher-count--done { - color: var(--color-success); + color: var(--done); } .workflow-switcher-count-separator { diff --git a/packages/dashboard/app/components/__tests__/WorkflowSwitcher.test.tsx b/packages/dashboard/app/components/__tests__/WorkflowSwitcher.test.tsx index b18af3819b..0625f4f7f7 100644 --- a/packages/dashboard/app/components/__tests__/WorkflowSwitcher.test.tsx +++ b/packages/dashboard/app/components/__tests__/WorkflowSwitcher.test.tsx @@ -1,6 +1,7 @@ import { fireEvent, render, screen, within } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import type { BoardWorkflowDefinition } from "../../api"; +import { loadAllAppCssBaseOnly } from "../../test/cssFixture"; import { WorkflowSwitcher } from "../WorkflowSwitcher"; import type { WorkflowStatusCounts } from "../workflowStatusCounts"; @@ -21,6 +22,11 @@ function countMap(entries: Array<[string, WorkflowStatusCounts]> = []) { return new Map(entries); } +function cssRuleFor(css: string, selector: string) { + const escaped = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + return css.match(new RegExp(`${escaped}\\s*\\{([^}]*)\\}`))?.[1] ?? ""; +} + describe("WorkflowSwitcher", () => { it("renders the active workflow without compact counts while collapsed", () => { render( @@ -109,4 +115,20 @@ describe("WorkflowSwitcher", () => { expect(within(designOption).getByText("0", { selector: ".workflow-switcher-count--in-progress" })).toBeInTheDocument(); expect(within(designOption).getByText("0", { selector: ".workflow-switcher-count--done" })).toBeInTheDocument(); }); + + it("colors status counts with board column color tokens", () => { + const css = loadAllAppCssBaseOnly(); + const badgeRules = [ + [".workflow-switcher-count--todo", "--todo"], + [".workflow-switcher-count--in-progress", "--in-progress"], + [".workflow-switcher-count--done", "--done"], + ] as const; + + for (const [selector, token] of badgeRules) { + const rule = cssRuleFor(css, selector); + expect(rule).toMatch(new RegExp(`color:\\s*var\\(${token}\\)`)); + expect(rule).not.toMatch(/var\(--(?:text-muted|color-warning|color-success)\)/); + expect(rule).not.toMatch(/#[0-9a-fA-F]{3,8}|rgba?\(/); + } + }); }); From 185ff70d863312c934279650af58259ae3f4ad20 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sat, 20 Jun 2026 21:23:17 -0700 Subject: [PATCH 2/6] FN-6819: keep sidebar settings above footer Keep the experimental left sidebar Settings control clear of the fixed executor footer while preserving readable project selector fallbacks. - Share the executor footer visibility state with the left sidebar and project content. - Reserve executor-footer height on the sidebar when the status footer is visible so Settings stays clickable in expanded and collapsed modes. - Add project-selector translation fallbacks for incomplete locale fixtures. - Cover footer-clearance behavior in left sidebar tests and add a patch changeset. Files changed: .changeset/fn-6819-sidebar-footer-clearance.md | 5 +++ packages/dashboard/app/App.tsx | 8 +++-- .../dashboard/app/components/LeftSidebarNav.css | 9 ++++++ .../dashboard/app/components/LeftSidebarNav.tsx | 4 ++- .../dashboard/app/components/ProjectSelector.css | 2 +- .../dashboard/app/components/ProjectSelector.tsx | 11 +++++-- .../components/__tests__/LeftSidebarNav.test.tsx | 36 ++++++++++++++++++++++ 7 files changed, 67 insertions(+), 8 deletions(-) Fusion-Task-Id: FN-6819 Fusion-Task-Lineage: 950da852-8dcf-4379-a91b-0188b12d1a6c --- .../fn-6819-sidebar-footer-clearance.md | 5 +++ packages/dashboard/app/App.tsx | 8 +++-- .../app/components/LeftSidebarNav.css | 9 +++++ .../app/components/LeftSidebarNav.tsx | 4 ++- .../app/components/ProjectSelector.css | 2 +- .../app/components/ProjectSelector.tsx | 11 ++++-- .../__tests__/LeftSidebarNav.test.tsx | 36 +++++++++++++++++++ 7 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 .changeset/fn-6819-sidebar-footer-clearance.md diff --git a/.changeset/fn-6819-sidebar-footer-clearance.md b/.changeset/fn-6819-sidebar-footer-clearance.md new file mode 100644 index 0000000000..5758d00f30 --- /dev/null +++ b/.changeset/fn-6819-sidebar-footer-clearance.md @@ -0,0 +1,5 @@ +--- +"@runfusion/fusion": patch +--- + +Fix the experimental left sidebar Settings button so it remains clear of the fixed executor status footer, and keep project-selector fallback labels readable when translations are incomplete. diff --git a/packages/dashboard/app/App.tsx b/packages/dashboard/app/App.tsx index fed57307fd..b6c4f87b6d 100644 --- a/packages/dashboard/app/App.tsx +++ b/packages/dashboard/app/App.tsx @@ -1036,7 +1036,8 @@ function AppInner() { Experimental left sidebar navigation replaces the Header view shortcuts with a persistent sidebar on non-mobile project screens, while mobile continues to use the bottom navigation bar as the only primary navigation surface. */ const leftSidebarNavEnabled = experimentalFeatures.leftSidebarNav === true; - const sidebarActive = leftSidebarNavEnabled && !isMobile && viewMode === "project" && !!currentProject; + const executorFooterVisible = viewMode === "project" && !!currentProject; + const sidebarActive = leftSidebarNavEnabled && !isMobile && executorFooterVisible; const agentOnboardingEnabled = experimentalFeatures.agentOnboarding === true; const agentsEnabled = true; @@ -2136,15 +2137,16 @@ function AppInner() { currentProject={currentProject} onSelectProject={handleSelectProject} onViewAllProjects={handleViewAllProjects} + footerVisible={executorFooterVisible} /> )}
{renderMainContent()}
- {viewMode === "project" && currentProject && ( + {executorFooterVisible && currentProject && ( 0 ? remoteData.tasks : tasks} projectId={currentProject.id} diff --git a/packages/dashboard/app/components/LeftSidebarNav.css b/packages/dashboard/app/components/LeftSidebarNav.css index 04edbcc453..064dd9f7d8 100644 --- a/packages/dashboard/app/components/LeftSidebarNav.css +++ b/packages/dashboard/app/components/LeftSidebarNav.css @@ -8,6 +8,7 @@ The experimental sidebar is a persistent desktop/tablet navigation replacement f position: relative; display: flex; flex-direction: column; + box-sizing: border-box; width: var(--left-sidebar-nav-width); min-width: var(--left-sidebar-nav-width); min-height: 0; @@ -16,6 +17,14 @@ The experimental sidebar is a persistent desktop/tablet navigation replacement f color: var(--text); } +/* +FNXC:Navigation 2026-06-20-00:00: +The left sidebar is a sibling of project-content, so it does not inherit project-content footer padding. When the fixed executor status bar is rendered, reserve the shared --executor-footer-height on the aside so the bottom Settings button remains visible and clickable above the footer. +*/ +.left-sidebar-nav--with-footer { + padding-bottom: var(--executor-footer-height); +} + .left-sidebar-nav__collapse-toggle { flex-shrink: 0; } diff --git a/packages/dashboard/app/components/LeftSidebarNav.tsx b/packages/dashboard/app/components/LeftSidebarNav.tsx index b2272e60d3..4f4daf30da 100644 --- a/packages/dashboard/app/components/LeftSidebarNav.tsx +++ b/packages/dashboard/app/components/LeftSidebarNav.tsx @@ -115,6 +115,7 @@ export interface LeftSidebarNavProps { currentProject?: ProjectInfo | null; onSelectProject?: (project: ProjectInfo) => void; onViewAllProjects?: () => void; + footerVisible?: boolean; } function formatCount(count: number): string { @@ -160,6 +161,7 @@ export function LeftSidebarNav({ pluginDashboardViews = [], showAgentsTab = false, showSkillsTab = false, + footerVisible = false, }: LeftSidebarNavProps) { const { t } = useTranslation("app"); const [sidebarWidth, setSidebarWidth] = useState(readStoredSidebarWidth); @@ -390,7 +392,7 @@ export function LeftSidebarNav({ return (