feat(FN-2685): remove TaskCard token usage badge

- Remove token usage badge rendering and compact token formatting from TaskCard
- Drop token usage fields from TaskCard memo equality checks now that the badge is gone
- Delete obsolete TaskCard token badge CSS rules and icon import
- Simplify TaskCard tests by removing token badge assertions and related fixtures
This commit is contained in:
Fusion
2026-04-27 07:59:42 -07:00
committed by gsxdsm
parent 0a5dcf12bc
commit 747a8a91b6
3 changed files with 3 additions and 117 deletions

View File

@@ -478,21 +478,6 @@
opacity: 0.7; opacity: 0.7;
} }
.card-token-usage {
display: inline-flex;
align-items: center;
gap: var(--space-xs);
color: var(--text-muted);
font-size: calc(var(--space-sm) + var(--space-xs) * 0.75);
line-height: 1;
white-space: nowrap;
}
.card-token-usage-value {
font-family: var(--font-mono);
font-weight: 600;
}
.card-time-indicator { .card-time-indicator {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -1004,10 +989,6 @@
gap: var(--space-xs); gap: var(--space-xs);
} }
.card-token-usage {
font-size: calc(var(--space-sm) + var(--space-xs) * 0.5);
}
.card-time-indicator { .card-time-indicator {
padding: var(--space-xs) var(--space-sm); padding: var(--space-xs) var(--space-sm);
font-size: 10px; font-size: 10px;

View File

@@ -1,6 +1,6 @@
import "./TaskCard.css"; import "./TaskCard.css";
import { memo, useCallback, useState, useRef, useEffect, useMemo } from "react"; import { memo, useCallback, useState, useRef, useEffect, useMemo } from "react";
import { Link, Clock, Layers, Pencil, ChevronDown, Folder, Target, Bot, Trash2, Zap } from "lucide-react"; import { Link, Clock, Layers, Pencil, ChevronDown, Folder, Target, Bot, Trash2 } from "lucide-react";
import type { Task, TaskDetail, Column, PrInfo, IssueInfo, TaskPriority } from "@fusion/core"; import type { Task, TaskDetail, Column, PrInfo, IssueInfo, TaskPriority } from "@fusion/core";
import { COLUMN_LABELS, DEFAULT_TASK_PRIORITY, TASK_PRIORITIES, VALID_TRANSITIONS, getErrorMessage } from "@fusion/core"; import { COLUMN_LABELS, DEFAULT_TASK_PRIORITY, TASK_PRIORITIES, VALID_TRANSITIONS, getErrorMessage } from "@fusion/core";
import { fetchTaskDetail, uploadAttachment, fetchMission, fetchAgent } from "../api"; import { fetchTaskDetail, uploadAttachment, fetchMission, fetchAgent } from "../api";
@@ -165,23 +165,6 @@ function formatElapsedDuration(elapsedMs: number): string {
return `${elapsedDays}d`; return `${elapsedDays}d`;
} }
function formatCompactTokenCount(value: number): string {
if (!Number.isFinite(value) || value < 0) {
return "0";
}
if (value < 1_000) {
return String(value);
}
const trimTrailingDecimal = (formatted: string) => formatted.replace(/\.0$/, "");
if (value < 1_000_000) {
return `${trimTrailingDecimal((value / 1_000).toFixed(1))}k`;
}
return `${trimTrailingDecimal((value / 1_000_000).toFixed(1))}M`;
}
interface TaskCardProps { interface TaskCardProps {
task: Task; task: Task;
@@ -371,8 +354,6 @@ function areTaskCardPropsEqual(previous: TaskCardProps, next: TaskCardProps): bo
previousTask.missionId === nextTask.missionId && previousTask.missionId === nextTask.missionId &&
previousTask.assignedAgentId === nextTask.assignedAgentId && previousTask.assignedAgentId === nextTask.assignedAgentId &&
previousTask.mergeRetries === nextTask.mergeRetries && previousTask.mergeRetries === nextTask.mergeRetries &&
previousTask.tokenUsage?.totalTokens === nextTask.tokenUsage?.totalTokens &&
previousTask.tokenUsage?.lastUsedAt === nextTask.tokenUsage?.lastUsedAt &&
areAttachmentsEqual(previousTask.attachments, nextTask.attachments) && areAttachmentsEqual(previousTask.attachments, nextTask.attachments) &&
areCommentsEqual(previousTask.comments, nextTask.comments) && areCommentsEqual(previousTask.comments, nextTask.comments) &&
areTaskDependenciesEqual(previousTask.dependencies, nextTask.dependencies) && areTaskDependenciesEqual(previousTask.dependencies, nextTask.dependencies) &&
@@ -1131,18 +1112,6 @@ function TaskCardComponent({
return null; return null;
})(); })();
const tokenUsageIndicator = task.tokenUsage ? (
<span
className="card-token-usage"
title={`${task.tokenUsage.totalTokens.toLocaleString()} tokens`}
aria-label={`Token usage ${formatCompactTokenCount(task.tokenUsage.totalTokens)} tokens`}
>
<Zap size={12} />
<span className="card-token-usage-value">{formatCompactTokenCount(task.tokenUsage.totalTokens)}</span>
<span>tokens</span>
</span>
) : null;
if (isEditing) { if (isEditing) {
return ( return (
<div <div
@@ -1415,10 +1384,9 @@ function TaskCardComponent({
</> </>
); );
})()} })()}
{(filesChangedButton || timeIndicator || tokenUsageIndicator) && ( {(filesChangedButton || timeIndicator) && (
<div className="card-footer-row"> <div className="card-footer-row">
{filesChangedButton} {filesChangedButton}
{tokenUsageIndicator}
{timeIndicator && ( {timeIndicator && (
<span <span
className="card-time-indicator" className="card-time-indicator"

View File

@@ -1,6 +1,6 @@
import { afterEach, describe, it, expect, vi } from "vitest"; import { afterEach, describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react"; import { render, screen, fireEvent, waitFor, act } from "@testing-library/react";
import { TaskCard, __test_areTaskCardPropsEqual } from "../TaskCard"; import { TaskCard } from "../TaskCard";
import type { Task } from "@fusion/core"; import type { Task } from "@fusion/core";
// Mock lucide-react to avoid SVG rendering issues in test env // Mock lucide-react to avoid SVG rendering issues in test env
@@ -16,7 +16,6 @@ vi.mock("lucide-react", () => ({
Target: () => null, Target: () => null,
Bot: () => null, Bot: () => null,
Trash2: () => null, Trash2: () => null,
Zap: () => null,
})); }));
vi.mock("../ProviderIcon", () => ({ vi.mock("../ProviderIcon", () => ({
@@ -48,15 +47,6 @@ function makeTask(overrides: Partial<Task> = {}): Task {
const noop = () => {}; const noop = () => {};
const tokenUsageFixture = {
inputTokens: 50_000,
outputTokens: 30_000,
cachedTokens: 10_000,
totalTokens: 90_000,
firstUsedAt: "2026-04-26T10:00:00.000Z",
lastUsedAt: "2026-04-26T10:30:00.000Z",
} as const;
afterEach(() => { afterEach(() => {
vi.useRealTimers(); vi.useRealTimers();
}); });
@@ -67,33 +57,6 @@ describe("TaskCard", () => {
expect(screen.getByText("FN-001")).toBeDefined(); expect(screen.getByText("FN-001")).toBeDefined();
}); });
it("renders token usage indicator when task has token usage", () => {
const { container } = render(
<TaskCard
task={makeTask({ tokenUsage: { ...tokenUsageFixture } })}
onOpenDetail={noop}
addToast={noop}
/>,
);
const tokenUsage = container.querySelector(".card-token-usage");
expect(tokenUsage).not.toBeNull();
expect(tokenUsage?.textContent).toContain("90k");
expect(tokenUsage?.textContent).toContain("tokens");
});
it("does not render token usage indicator when tokenUsage is undefined", () => {
const { container } = render(
<TaskCard
task={makeTask({ tokenUsage: undefined })}
onOpenDetail={noop}
addToast={noop}
/>,
);
expect(container.querySelector(".card-token-usage")).toBeNull();
});
it("renders the status badge when task.status is set", () => { it("renders the status badge when task.status is set", () => {
render( render(
<TaskCard <TaskCard
@@ -740,32 +703,6 @@ describe("TaskCard", () => {
}); });
}); });
describe("TaskCard memo comparator", () => {
it("detects token usage changes", () => {
type ComparatorProps = Parameters<typeof __test_areTaskCardPropsEqual>[0];
const baseTask = makeTask({ tokenUsage: { ...tokenUsageFixture } });
const baseProps: ComparatorProps = {
task: baseTask,
onOpenDetail: noop,
addToast: noop,
};
const totalTokenChange: ComparatorProps = {
...baseProps,
task: makeTask({ tokenUsage: { ...tokenUsageFixture, totalTokens: 95_000 } }),
};
const lastUsedAtChange: ComparatorProps = {
...baseProps,
task: makeTask({ tokenUsage: { ...tokenUsageFixture, lastUsedAt: "2026-04-26T10:35:00.000Z" } }),
};
expect(__test_areTaskCardPropsEqual(baseProps, totalTokenChange)).toBe(false);
expect(__test_areTaskCardPropsEqual(baseProps, lastUsedAtChange)).toBe(false);
});
});
describe("TaskCard provider icons on agent row", () => { describe("TaskCard provider icons on agent row", () => {
it("renders provider icons when task has model overrides", () => { it("renders provider icons when task has model overrides", () => {
render( render(