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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user