feat(FN-3471): apply tokenized sizing and focus styles to component CSS

Merged CSS changes that apply tokenized sizing and focus styles to InsightsView and DesktopModeChooser components, with the majority of changes in InsightsView.css.

Fusion-Task-Id: FN-3471
This commit is contained in:
Fusion
2026-05-05 00:15:52 -07:00
committed by gsxdsm
parent cd1d44abab
commit bb4a1b7df4
10 changed files with 128 additions and 50 deletions

View File

@@ -25,7 +25,7 @@
.insights-view-title h2 {
margin: 0;
font-size: 18px;
font-size: 1.125rem;
font-weight: 600;
display: flex;
align-items: center;
@@ -33,7 +33,7 @@
}
.insights-view-count {
font-size: 13px;
font-size: 0.8125rem;
color: var(--text-muted);
font-weight: normal;
}
@@ -68,7 +68,7 @@
gap: var(--space-sm);
padding: var(--space-sm) var(--space-md);
border-radius: var(--radius-md);
font-size: 13px;
font-size: 0.8125rem;
}
.insights-status-message--success {
@@ -97,7 +97,7 @@
border: 1px solid color-mix(in srgb, var(--color-error) 30%, transparent);
border-radius: var(--radius-md);
color: var(--color-error);
font-size: 13px;
font-size: 0.8125rem;
flex-shrink: 0;
}
@@ -108,7 +108,7 @@
}
.insights-run-status {
font-size: 12px;
font-size: 0.75rem;
color: var(--text-muted);
}
@@ -153,14 +153,14 @@
.insights-empty h3 {
margin: 0;
font-size: 18px;
font-size: 1.125rem;
color: var(--text);
}
.insights-empty p {
color: var(--text-muted);
margin: 0;
max-width: 400px;
max-width: calc(var(--space-2xl) * 12 + var(--space-lg));
}
/* Two-pane body */
@@ -173,7 +173,7 @@
/* Sidebar (categories) */
.insights-sidebar {
width: 240px;
width: calc(var(--space-2xl) * 7 + var(--space-lg));
flex-shrink: 0;
border-right: 1px solid var(--border);
background: var(--surface);
@@ -187,7 +187,7 @@
padding: var(--space-sm);
display: flex;
flex-direction: column;
gap: 2px;
gap: var(--space-xs);
}
.insights-category-item {
@@ -201,7 +201,7 @@
border-radius: var(--radius-md);
color: var(--text);
font: inherit;
font-size: 13px;
font-size: 0.8125rem;
text-align: left;
cursor: pointer;
transition: background var(--transition-fast), border-color var(--transition-fast);
@@ -233,10 +233,10 @@
.insights-category-count {
flex-shrink: 0;
font-size: 12px;
font-size: 0.75rem;
color: var(--text-muted);
background: var(--surface-elevated);
padding: 2px 8px;
padding: var(--space-xs) var(--space-sm);
border-radius: var(--radius-pill);
border: 1px solid var(--border);
}
@@ -281,7 +281,7 @@
.insights-section-title h3 {
margin: 0;
font-size: 15px;
font-size: 0.9375rem;
font-weight: 600;
}
@@ -290,10 +290,10 @@
}
.insights-section-count {
font-size: 12px;
font-size: 0.75rem;
color: var(--text-muted);
background: var(--surface-elevated);
padding: 2px 8px;
padding: var(--space-xs) var(--space-sm);
border-radius: var(--radius-pill);
border: 1px solid var(--border);
}
@@ -317,7 +317,7 @@
padding: var(--space-md);
background: var(--surface);
border: 1px solid var(--border);
border-left: 3px solid var(--accent);
border-left: var(--space-xs) solid var(--accent);
border-radius: var(--radius-md);
transition: border-color var(--transition-fast);
}
@@ -335,7 +335,7 @@
.insight-item-title {
margin: 0;
font-size: 14px;
font-size: 0.875rem;
font-weight: 600;
color: var(--text);
line-height: 1.4;
@@ -353,8 +353,8 @@
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
width: calc(var(--space-lg) * 2);
height: calc(var(--space-lg) * 2);
padding: 0;
background: transparent;
border: none;
@@ -369,6 +369,11 @@
color: var(--accent);
}
.insight-item-action-btn:focus-visible {
outline: none;
box-shadow: var(--focus-ring-strong);
}
.insight-item-action-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
@@ -376,7 +381,7 @@
.insight-item-content {
margin: var(--space-sm) 0 0;
font-size: 13px;
font-size: 0.8125rem;
color: var(--text-muted);
line-height: 1.5;
white-space: pre-wrap;
@@ -388,12 +393,12 @@
align-items: center;
gap: var(--space-md);
margin-top: var(--space-sm);
font-size: 12px;
font-size: 0.75rem;
color: var(--text-muted);
}
.insight-item-status {
padding: 2px 6px;
padding: var(--space-xs) var(--space-sm);
border-radius: var(--radius-sm);
text-transform: capitalize;
font-weight: 500;
@@ -441,7 +446,7 @@
.insight-item-date {
display: flex;
align-items: center;
gap: 4px;
gap: var(--space-xs);
}
/* Spin animation */
@@ -468,7 +473,7 @@
}
.insights-view-title h2 {
font-size: 16px;
font-size: 1rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@@ -550,27 +555,27 @@
}
.insight-item-action-btn {
width: 40px;
height: 40px;
width: calc(var(--space-2xl) + var(--space-sm));
height: calc(var(--space-2xl) + var(--space-sm));
}
.insights-view-actions .btn {
min-height: 36px;
min-height: calc(var(--space-2xl) + var(--space-xs));
}
.insights-show-archived-toggle {
max-width: 180px;
max-width: calc(var(--space-2xl) * 5 + var(--space-md));
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.insights-view-close {
min-height: 36px;
min-width: 36px;
min-height: calc(var(--space-2xl) + var(--space-xs));
min-width: calc(var(--space-2xl) + var(--space-xs));
}
.insights-view-count {
font-size: 12px;
font-size: 0.75rem;
}
}

View File

@@ -808,6 +808,19 @@ describe("GET /providers/claude-cli/status", () => {
describe("Droid CLI auth routes", () => {
let store: TaskStore;
function setDroidPluginSettings(settings: Record<string, unknown>) {
(store as TaskStore & {
getPluginStore: () => { getPlugin: (id: string) => Promise<{ settings: Record<string, unknown> }> };
}).getPluginStore = vi.fn().mockReturnValue({
getPlugin: vi.fn().mockImplementation(async (id: string) => {
if (id !== "fusion-plugin-droid-runtime") {
throw new Error("not found");
}
return { settings };
}),
});
}
beforeEach(() => {
store = createMockStore({
updateGlobalSettings: vi.fn().mockResolvedValue({ useDroidCli: true }),
@@ -826,6 +839,7 @@ describe("Droid CLI auth routes", () => {
}
it("enables Droid CLI when binary is available", async () => {
setDroidPluginSettings({ droidBinaryPath: "/opt/custom-droid" });
const probeSpy = vi.spyOn(droidCliProbeModule, "probeDroidCli").mockResolvedValue({
available: true,
version: "droid 1.0.0",
@@ -839,6 +853,7 @@ describe("Droid CLI auth routes", () => {
expect(res.status).toBe(200);
expect(res.body).toEqual({ enabled: true, restartRequired: false });
expect(store.updateGlobalSettings).toHaveBeenCalledWith({ useDroidCli: true });
expect(probeSpy).toHaveBeenCalledWith({ settings: { droidBinaryPath: "/opt/custom-droid" } });
probeSpy.mockRestore();
});
@@ -899,7 +914,8 @@ describe("Droid CLI auth routes", () => {
});
it("returns binary + toggle + extension diagnostics and computed readiness", async () => {
vi.spyOn(droidCliProbeModule, "probeDroidCli").mockResolvedValue({
setDroidPluginSettings({ droidBinaryPath: "/opt/custom-droid" });
const probeSpy = vi.spyOn(droidCliProbeModule, "probeDroidCli").mockResolvedValue({
available: true,
version: "droid 1.0.0",
probeDurationMs: 10,
@@ -921,6 +937,7 @@ describe("Droid CLI auth routes", () => {
expect(res.body.ready).toBe(true);
expect(res.body.binary).toMatchObject({ available: true, version: "droid 1.0.0" });
expect(res.body.extension).toMatchObject({ status: "ok" });
expect(probeSpy).toHaveBeenCalledWith({ settings: { droidBinaryPath: "/opt/custom-droid" } });
});
it("returns ready false when binary unavailable", async () => {
@@ -952,7 +969,8 @@ describe("Droid CLI auth routes", () => {
});
it("GET /auth/status includes droid-cli provider with cli type", async () => {
vi.spyOn(droidCliProbeModule, "probeDroidCli").mockResolvedValue({
setDroidPluginSettings({ droidBinaryPath: "/opt/custom-droid" });
const probeSpy = vi.spyOn(droidCliProbeModule, "probeDroidCli").mockResolvedValue({
available: true,
version: "droid 1.0.0",
probeDurationMs: 10,
@@ -974,6 +992,7 @@ describe("Droid CLI auth routes", () => {
}),
]),
);
expect(probeSpy).toHaveBeenCalledWith({ settings: { droidBinaryPath: "/opt/custom-droid" } });
});
it("GET /auth/status marks droid-cli unauthenticated when extension status is not ok", async () => {

View File

@@ -2,6 +2,6 @@ import { probeDroidBinary } from "@fusion-plugin-examples/droid-runtime/probe";
export type DroidCliBinaryStatus = Awaited<ReturnType<typeof probeDroidBinary>>;
export async function probeDroidCli(options: { timeoutMs?: number } = {}): Promise<DroidCliBinaryStatus> {
return probeDroidBinary({ timeoutMs: options.timeoutMs });
export async function probeDroidCli(options: { timeoutMs?: number; settings?: Record<string, unknown> } = {}): Promise<DroidCliBinaryStatus> {
return probeDroidBinary({ timeoutMs: options.timeoutMs, settings: options.settings });
}

View File

@@ -1,3 +1,4 @@
import type { Request } from "express";
import { isGhAvailable, isGhAuthenticated } from "@fusion/core";
import { probeClaudeCli } from "../claude-cli-probe.js";
import { probeDroidCli } from "../droid-cli-probe.js";
@@ -9,7 +10,7 @@ import type { AuthStorageLike } from "../routes.js";
import type { ApiRouteRegistrar } from "./types.js";
export const registerAuthRoutes: ApiRouteRegistrar = (ctx) => {
const { router, options, store, rethrowAsApiError } = ctx;
const { router, options, store, getScopedStore, rethrowAsApiError } = ctx;
const authStorage = options?.authStorage;
// Use injected AuthStorage or fail gracefully if not provided.
@@ -164,6 +165,23 @@ export const registerAuthRoutes: ApiRouteRegistrar = (ctx) => {
};
}
async function probeDroidCliWithEffectiveBinary(req?: Request) {
let pluginSettings: Record<string, unknown> | undefined;
if (req) {
try {
const scopedStore = await getScopedStore(req);
const plugin = await scopedStore.getPluginStore().getPlugin("fusion-plugin-droid-runtime");
if (plugin && typeof plugin.settings === "object" && plugin.settings !== null) {
pluginSettings = plugin.settings as Record<string, unknown>;
}
} catch {
// Missing/unreadable plugin settings: fall back to default droid binary resolution.
}
}
return probeDroidCli({ settings: pluginSettings });
}
function appendManualCodeHint(
instructions: string | undefined,
providerId: string,
@@ -195,7 +213,7 @@ export const registerAuthRoutes: ApiRouteRegistrar = (ctx) => {
* ghCli: { available: boolean, authenticated: boolean }
* }
*/
router.get("/auth/status", async (_req, res) => {
router.get("/auth/status", async (req, res) => {
try {
const storage = getAuthStorage();
storage.reload();
@@ -274,7 +292,7 @@ export const registerAuthRoutes: ApiRouteRegistrar = (ctx) => {
// Unreadable settings — fall through with enabled=false
}
const droidExtension = options?.getDroidCliExtensionStatus?.() ?? null;
const droidBinary = await probeDroidCli();
const droidBinary = await probeDroidCliWithEffectiveBinary(req);
const droidExtensionOk = droidExtension === null || droidExtension.status === "ok";
providers.push({
id: "droid-cli",
@@ -422,7 +440,7 @@ export const registerAuthRoutes: ApiRouteRegistrar = (ctx) => {
}
if (enabled) {
const binary = await probeDroidCli();
const binary = await probeDroidCliWithEffectiveBinary(req);
if (!binary.available) {
throw new ApiError(
400,
@@ -511,9 +529,9 @@ export const registerAuthRoutes: ApiRouteRegistrar = (ctx) => {
}
});
router.get("/providers/droid-cli/status", async (_req, res) => {
router.get("/providers/droid-cli/status", async (req, res) => {
try {
const binary = await probeDroidCli();
const binary = await probeDroidCliWithEffectiveBinary(req);
let enabled = false;
if (store) {
try {

View File

@@ -1,5 +1,5 @@
.desktop-mode-chooser {
max-width: 640px;
max-width: calc(var(--space-2xl) * 20);
margin: 0 auto;
padding: var(--space-2xl);
display: flex;
@@ -13,7 +13,7 @@
.desktop-mode-chooser__title {
margin: 0;
font-size: 1.5rem;
font-size: calc(var(--space-lg) + var(--space-sm));
line-height: 1.2;
color: var(--text);
}