FN-8527: theme imported task stats links
Make imported-source links in the shared Stats panel follow the active dashboard theme. - Style imported task links with the dashboard accent and accessible focus treatment. - Cover safe themed external links across shared Stats surfaces. - Add a patch changeset for the user-facing visual fix. Files changed: .changeset/fn-8527-themed-stats-link.md | 7 +++++ .../app/components/TaskTokenStatsPanel.css | 15 +++++++++++ .../app/components/TaskTokenStatsPanel.tsx | 7 ++++- .../__tests__/TaskTokenStatsPanel.test.tsx | 30 ++++++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-8527 Fusion-Task-Lineage: c7ee16ea-23e7-462d-a5c6-799fe0ede62d Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-8527-themed-stats-link.md
Normal file
7
.changeset/fn-8527-themed-stats-link.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Make imported task links in Stats follow the active dashboard theme.
|
||||
category: fix
|
||||
dev: The shared Stats provenance link now uses the --accent token.
|
||||
@@ -132,6 +132,21 @@
|
||||
font-size: calc(var(--space-sm) + var(--space-xs));
|
||||
}
|
||||
|
||||
.task-token-stats-panel__imported-source-link {
|
||||
color: var(--accent);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.task-token-stats-panel__imported-source-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.task-token-stats-panel__imported-source-link:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring-strong);
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
|
||||
.task-token-stats-panel__cache-ratio {
|
||||
color: var(--text);
|
||||
font-size: calc(var(--space-sm) + var(--space-xs));
|
||||
|
||||
@@ -317,7 +317,12 @@ export function TaskTokenStatsPanel({ tokenUsage, loading, task }: TaskTokenStat
|
||||
<dt>{t("taskDetail.provenance.importedFrom", "Imported from")}</dt>
|
||||
<dd>
|
||||
{/^https?:\/\//i.test(task.sourceMetadata.issueUrl) ? (
|
||||
<a href={task.sourceMetadata.issueUrl} target="_blank" rel="noreferrer">
|
||||
/*
|
||||
FNXC:TaskStatsProvenance 2026-07-22-21:47:
|
||||
Imported-source links use the active accent rather than the browser default so the
|
||||
shared Stats panel remains theme-aware on desktop and narrow/mobile task detail.
|
||||
*/
|
||||
<a className="task-token-stats-panel__imported-source-link" href={task.sourceMetadata.issueUrl} target="_blank" rel="noreferrer">
|
||||
{task.sourceMetadata.issueUrl}
|
||||
</a>
|
||||
) : (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { createInstance } from "i18next";
|
||||
@@ -6,6 +8,8 @@ import { TaskTokenStatsPanel } from "../TaskTokenStatsPanel";
|
||||
import type { Task } from "@fusion/core";
|
||||
import realEnApp from "../../../../i18n/locales/en/app.json";
|
||||
|
||||
const taskTokenStatsPanelCss = readFileSync(resolve(__dirname, "../TaskTokenStatsPanel.css"), "utf8");
|
||||
|
||||
function makeTask(overrides: Partial<Task> = {}): Task {
|
||||
return {
|
||||
id: "FN-400",
|
||||
@@ -311,6 +315,32 @@ describe("TaskTokenStatsPanel", () => {
|
||||
.toHaveAttribute("href", "https://github.com/Runfusion/Fusion/issues/2356");
|
||||
});
|
||||
|
||||
/*
|
||||
FNXC:TaskStatsProvenance 2026-07-22-21:47:
|
||||
The desktop and narrow/mobile task detail both mount this shared Stats panel. A populated
|
||||
imported task must keep its safe external-link contract while component CSS binds its color to
|
||||
the active theme accent instead of browser-default blue.
|
||||
*/
|
||||
it("uses the active theme accent for populated imported-source links across shared Stats surfaces", () => {
|
||||
const issueUrl = "https://github.com/Runfusion/Fusion/issues/2410";
|
||||
render(<TaskTokenStatsPanel loading={false} tokenUsage={undefined} task={makeTask({
|
||||
sourceType: "github",
|
||||
sourceMetadata: { issueUrl },
|
||||
})} />);
|
||||
|
||||
const importedSource = screen.getByRole("link", { name: issueUrl });
|
||||
expect(importedSource).toHaveClass("task-token-stats-panel__imported-source-link");
|
||||
expect(importedSource).toHaveAttribute("href", issueUrl);
|
||||
expect(importedSource).toHaveAttribute("target", "_blank");
|
||||
expect(importedSource).toHaveAttribute("rel", "noreferrer");
|
||||
expect(taskTokenStatsPanelCss).toMatch(
|
||||
/\.task-token-stats-panel__imported-source-link\s*\{[^}]*color:\s*var\(--accent\);[^}]*\}/,
|
||||
);
|
||||
expect(taskTokenStatsPanelCss).not.toMatch(
|
||||
/\.task-token-stats-panel__imported-source-link\s*\{[^}]*color:\s*(?:blue|#[0-9a-f]{3,8}|rgb)/i,
|
||||
);
|
||||
});
|
||||
|
||||
it("renders a non-http issueUrl as plain text, never as a link", () => {
|
||||
render(<TaskTokenStatsPanel loading={false} tokenUsage={undefined} task={makeTask({
|
||||
sourceType: "api",
|
||||
|
||||
Reference in New Issue
Block a user