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:
@@ -478,21 +478,6 @@
|
||||
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 {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -1004,10 +989,6 @@
|
||||
gap: var(--space-xs);
|
||||
}
|
||||
|
||||
.card-token-usage {
|
||||
font-size: calc(var(--space-sm) + var(--space-xs) * 0.5);
|
||||
}
|
||||
|
||||
.card-time-indicator {
|
||||
padding: var(--space-xs) var(--space-sm);
|
||||
font-size: 10px;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "./TaskCard.css";
|
||||
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 { COLUMN_LABELS, DEFAULT_TASK_PRIORITY, TASK_PRIORITIES, VALID_TRANSITIONS, getErrorMessage } from "@fusion/core";
|
||||
import { fetchTaskDetail, uploadAttachment, fetchMission, fetchAgent } from "../api";
|
||||
@@ -165,23 +165,6 @@ function formatElapsedDuration(elapsedMs: number): string {
|
||||
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 {
|
||||
task: Task;
|
||||
@@ -371,8 +354,6 @@ function areTaskCardPropsEqual(previous: TaskCardProps, next: TaskCardProps): bo
|
||||
previousTask.missionId === nextTask.missionId &&
|
||||
previousTask.assignedAgentId === nextTask.assignedAgentId &&
|
||||
previousTask.mergeRetries === nextTask.mergeRetries &&
|
||||
previousTask.tokenUsage?.totalTokens === nextTask.tokenUsage?.totalTokens &&
|
||||
previousTask.tokenUsage?.lastUsedAt === nextTask.tokenUsage?.lastUsedAt &&
|
||||
areAttachmentsEqual(previousTask.attachments, nextTask.attachments) &&
|
||||
areCommentsEqual(previousTask.comments, nextTask.comments) &&
|
||||
areTaskDependenciesEqual(previousTask.dependencies, nextTask.dependencies) &&
|
||||
@@ -1131,18 +1112,6 @@ function TaskCardComponent({
|
||||
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) {
|
||||
return (
|
||||
<div
|
||||
@@ -1415,10 +1384,9 @@ function TaskCardComponent({
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
{(filesChangedButton || timeIndicator || tokenUsageIndicator) && (
|
||||
{(filesChangedButton || timeIndicator) && (
|
||||
<div className="card-footer-row">
|
||||
{filesChangedButton}
|
||||
{tokenUsageIndicator}
|
||||
{timeIndicator && (
|
||||
<span
|
||||
className="card-time-indicator"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { afterEach, describe, it, expect, vi } from "vitest";
|
||||
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";
|
||||
|
||||
// Mock lucide-react to avoid SVG rendering issues in test env
|
||||
@@ -16,7 +16,6 @@ vi.mock("lucide-react", () => ({
|
||||
Target: () => null,
|
||||
Bot: () => null,
|
||||
Trash2: () => null,
|
||||
Zap: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("../ProviderIcon", () => ({
|
||||
@@ -48,15 +47,6 @@ function makeTask(overrides: Partial<Task> = {}): Task {
|
||||
|
||||
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(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
@@ -67,33 +57,6 @@ describe("TaskCard", () => {
|
||||
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", () => {
|
||||
render(
|
||||
<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", () => {
|
||||
it("renders provider icons when task has model overrides", () => {
|
||||
render(
|
||||
|
||||
Reference in New Issue
Block a user