FN-8005: normalize Quick Add action icon sizes

Unify the Quick Add primary action cluster around the shared icon-only sizing treatment.

- Apply btn-icon styling and 14px SVG sizing to GitHub, session advisor, priority, and Fast controls.
- Cover primary cluster uniformity across mobile, toggle, and priority states.
- Add a patch changeset for the Quick Add visual fix.

Files changed:
 .changeset/fn-8005-quick-add-icon-size-parity.md   |  7 +++
 .../dashboard/app/components/QuickEntryBox.tsx     | 19 ++++---
 .../components/__tests__/QuickEntryBox.test.tsx    | 63 ++++++++++++++++++++--
 3 files changed, 79 insertions(+), 10 deletions(-)

Fusion-Task-Id: FN-8005

Fusion-Task-Lineage: c912fb90-1e49-4c4d-8664-4caf1489f890

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-15 21:25:51 -07:00
parent d1bda3683c
commit a646ae3f36
3 changed files with 79 additions and 10 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Quick Add overseer, priority, fast, GitHub, and attach icons now render at one uniform size.
category: fix
dev: QuickEntryBox primary-group icon-only buttons (Eye/EyeOff, PriorityIcon, Zap, GitHub ProviderIcon, Paperclip) all use the icon-only btn-icon treatment (--icon-size-sm) for a uniform 14px cluster; ProviderIcon sizeMap unchanged.

View File

@@ -2181,6 +2181,11 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
FNXC:QuickAddAttachments 2026-06-30-00:00 (relocated 2026-07-10): the attachment affordance
stays adjacent to Save (now immediately to its LEFT) preserving the icon-only label, hidden
file input trigger, and pending-count badge.
FNXC:QuickAddActionRow 2026-07-15-00:00:
Attach, GitHub, session advisor, Priority, and Fast are one icon-only cluster. Every control
uses `btn-icon` so its SVG resolves to the shared `--icon-size-sm` token; do not fork
ProviderIcon's shared size map to size this one GitHub use case.
*/}
<div className="quick-entry-primary-group" data-testid="quick-entry-primary-group">
<button
@@ -2200,7 +2205,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
<button
type="button"
className={`btn btn-sm ${effectiveGithubTracking ? "btn-primary" : ""}`}
className={`btn btn-icon btn-sm ${effectiveGithubTracking ? "btn-primary" : ""}`}
onClick={() => {
setGithubTrackingOverride((prev) => !(prev ?? projectGithubTrackingDefault));
}}
@@ -2225,7 +2230,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
*/}
<button
type="button"
className={`btn btn-sm ${effectiveSessionAdvisor ? "btn-primary" : ""}`}
className={`btn btn-icon btn-sm ${effectiveSessionAdvisor ? "btn-primary" : ""}`}
onClick={() => {
setSessionAdvisorOverride((prev) => {
const currentEffective = prev ?? projectSessionAdvisorDefault;
@@ -2239,14 +2244,14 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
title={sessionAdvisorToggleLabel}
aria-label={sessionAdvisorToggleLabel}
>
{effectiveSessionAdvisor ? <Eye size={12} aria-hidden="true" /> : <EyeOff size={12} aria-hidden="true" />}
{effectiveSessionAdvisor ? <Eye size={14} aria-hidden="true" /> : <EyeOff size={14} aria-hidden="true" />}
</button>
<div className="priority-trigger-wrap" ref={priorityPickerRef}>
<button
type="button"
onMouseDown={(e) => e.preventDefault()}
className="btn btn-sm dep-trigger"
className="btn btn-icon btn-sm dep-trigger"
data-testid="quick-entry-priority-button"
title={priorityButtonLabel}
aria-label={priorityButtonLabel}
@@ -2271,7 +2276,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
}}
>
{/* FNXC:PriorityColorCoding 2026-07-11-00:00: The quick-add icon-only priority trigger must preview urgency color from priorityIndicator without changing its label, test id, or picker behavior. */}
<PriorityIcon size={12} aria-hidden="true" style={{ color: getPriorityColorVar(priority) }} />
<PriorityIcon size={14} aria-hidden="true" style={{ color: getPriorityColorVar(priority) }} />
</button>
</div>
@@ -2316,7 +2321,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
<button
type="button"
className={`btn btn-sm ${isFastMode ? "btn-primary" : ""}`}
className={`btn btn-icon btn-sm ${isFastMode ? "btn-primary" : ""}`}
onClick={toggleFastMode}
onMouseDown={(e) => e.preventDefault()}
aria-pressed={isFastMode}
@@ -2324,7 +2329,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
title={fastToggleLabel}
aria-label={fastToggleLabel}
>
<Zap size={12} aria-hidden="true" />
<Zap size={14} aria-hidden="true" />
</button>
<button

View File

@@ -416,7 +416,7 @@ function mockMobileViewport() {
FNXC:BoardComposer 2026-07-10-12:00:
DOM order mirrors the reorganized composer action row: the options group (subtask, deps,
models, node, agent) comes first, followed by the right-aligned primary group (attach, GitHub,
Priority, Fast, Save) with Save as the LAST control.
session advisor, Priority, Fast, Save) with Save as the LAST control.
*/
const QUICK_ENTRY_ACTION_BUTTONS = [
["Subtask", "subtask-button"],
@@ -426,11 +426,20 @@ const QUICK_ENTRY_ACTION_BUTTONS = [
["Agent", "quick-entry-agent-button"],
["Attach", "quick-entry-attach"],
["GitHub", "quick-entry-github-toggle"],
["Session advisor", "quick-entry-session-advisor-toggle"],
["Priority", "quick-entry-priority-button"],
["Fast", "quick-entry-fast-toggle"],
["Save", "quick-entry-save"],
] as const;
const QUICK_ENTRY_PRIMARY_ICON_BUTTON_IDS = [
"quick-entry-attach",
"quick-entry-github-toggle",
"quick-entry-session-advisor-toggle",
"quick-entry-priority-button",
"quick-entry-fast-toggle",
] as const;
const QUICK_ENTRY_PRIORITY_ICON_CLASS: Record<TaskPriority, string> = {
low: "lucide-arrow-down",
normal: "lucide-flag",
@@ -456,6 +465,20 @@ function expectPriorityOptionColor(priority: TaskPriority) {
expect(icon?.getAttribute("style")).toContain(`color: ${getPriorityColorVar(priority)}`);
}
/**
* FNXC:QuickAddActionRow 2026-07-15-00:00:
* The primary action cluster is visually uniform only when every icon control opts into btn-icon.
* Assert the shared treatment rather than SVG attributes because ProviderIcon's public sm size is
* intentionally broader than this local CSS override.
*/
function expectQuickEntryPrimaryIconCluster() {
for (const testId of QUICK_ENTRY_PRIMARY_ICON_BUTTON_IDS) {
const button = screen.getByTestId(testId);
expect(button).toHaveClass("btn-icon");
expect(button.querySelector("svg")).not.toBeNull();
}
}
describe("QuickEntryBox", () => {
beforeEach(() => {
vi.clearAllMocks();
@@ -894,16 +917,17 @@ describe("QuickEntryBox", () => {
expandQuickEntry();
const actionButtonTestIds = getActionButtonTestIdsInDomOrder();
expect(actionButtonTestIds.slice(-5)).toEqual([
expect(actionButtonTestIds.slice(-6)).toEqual([
"quick-entry-attach",
"quick-entry-github-toggle",
"quick-entry-session-advisor-toggle",
"quick-entry-priority-button",
"quick-entry-fast-toggle",
"quick-entry-save",
]);
const primaryGroup = screen.getByTestId("quick-entry-primary-group");
for (const testId of ["quick-entry-attach", "quick-entry-github-toggle", "quick-entry-priority-button", "quick-entry-fast-toggle", "quick-entry-save"]) {
for (const testId of ["quick-entry-attach", "quick-entry-github-toggle", "quick-entry-session-advisor-toggle", "quick-entry-priority-button", "quick-entry-fast-toggle", "quick-entry-save"]) {
expect(primaryGroup.contains(screen.getByTestId(testId))).toBe(true);
}
const optionsGroup = screen.getByTestId("quick-entry-options-group");
@@ -1004,6 +1028,18 @@ describe("QuickEntryBox", () => {
});
}
it("keeps the uniform icon cluster in the shared mobile button touch-target contract", async () => {
await renderMobileQuickEntryWithEnabledActions();
const actions = screen.getByTestId("quick-entry-actions");
expectQuickEntryPrimaryIconCluster();
for (const testId of QUICK_ENTRY_PRIMARY_ICON_BUTTON_IDS) {
const button = screen.getByTestId(testId);
expect(button).toHaveClass("btn");
expect(actions.contains(button)).toBe(true);
}
});
it("captures an SVG touch target inside the priority button and opens the picker", async () => {
await renderMobileQuickEntryWithEnabledActions();
const priorityButton = screen.getByTestId("quick-entry-priority-button");
@@ -1241,6 +1277,9 @@ describe("QuickEntryBox", () => {
case "quick-entry-github-toggle":
expect(screen.getByTestId(testId)).toHaveAttribute("aria-pressed", "false");
break;
case "quick-entry-session-advisor-toggle":
expect(screen.getByTestId(testId)).toHaveAttribute("aria-pressed", "true");
break;
case "quick-entry-priority-button":
expect(await screen.findByTestId("quick-entry-priority-option-normal")).toBeTruthy();
break;
@@ -2162,6 +2201,24 @@ describe("QuickEntryBox", () => {
expectQuickEntryPriorityButton("normal");
});
it("keeps every primary icon control in one btn-icon cluster across toggle and priority states", () => {
renderQuickEntryBox({});
expandQuickEntry();
expectQuickEntryPrimaryIconCluster();
fireEvent.click(screen.getByTestId("quick-entry-session-advisor-toggle"));
fireEvent.click(screen.getByTestId("quick-entry-github-toggle"));
fireEvent.click(screen.getByTestId("quick-entry-fast-toggle"));
expectQuickEntryPrimaryIconCluster();
for (const taskPriority of TASK_PRIORITIES) {
openPriorityMenu();
fireEvent.click(screen.getByTestId(`quick-entry-priority-option-${taskPriority}`));
expectQuickEntryPriorityButton(taskPriority);
expectQuickEntryPrimaryIconCluster();
}
});
it("renders urgency-colored priority glyphs in the trigger and picker for every level", () => {
renderQuickEntryBox({});
expandQuickEntry();