-
{type.label}
+
{type.label}
{type.percentage > 0 && (
-
+
{type.percentage.toFixed(1)}%
)}
{formatNumber(type.value)}
@@ -295,21 +325,21 @@ export interface ProfileStatsProps {
export function ProfileStats({ stats, currentStreak = 0, longestStreak = 0, favoriteModel }: ProfileStatsProps) {
const statItems = [
- { label: "Active Days", value: stats.activeDays.toString(), color: "#53d1f3" },
- { label: "Current Streak", value: `${currentStreak} days`, color: "#53d1f3" },
- { label: "Longest Streak", value: `${longestStreak} days`, color: "#53d1f3" },
- { label: "Submissions", value: (stats.submissionCount ?? 0).toString(), color: "#53d1f3" },
+ { label: "Active Days", value: stats.activeDays.toString(), color: "var(--color-primary)" },
+ { label: "Current Streak", value: `${currentStreak} days`, color: "var(--color-primary)" },
+ { label: "Longest Streak", value: `${longestStreak} days`, color: "var(--color-primary)" },
+ { label: "Submissions", value: (stats.submissionCount ?? 0).toString(), color: "var(--color-primary)" },
];
return (
{statItems.map((item) => (
-
{item.label}
+
{item.label}
{favoriteModel && (
-
+
-
Favorite Model:
+
Favorite Model:
{favoriteModel}
@@ -381,11 +411,11 @@ export function ProfileModels({ models, modelUsage }: ProfileModelsProps) {
return (
Model
Tokens
@@ -393,13 +423,13 @@ export function ProfileModels({ models, modelUsage }: ProfileModelsProps) {
%
-
+
{sortedUsage.map((usage, index) => (
@@ -409,23 +439,23 @@ export function ProfileModels({ models, modelUsage }: ProfileModelsProps) {
/>
{usage.model}
-
+
{formatNumber(usage.tokens)}
-
+
{formatCurrency(usage.cost)}
-
+
{usage.percentage.toFixed(1)}%
@@ -439,14 +469,14 @@ export function ProfileModels({ models, modelUsage }: ProfileModelsProps) {
return (
{filteredModels.map((model) => (
-
+
No contribution data available yet.
diff --git a/packages/frontend/src/lib/providers/PrimerProvider.tsx b/packages/frontend/src/lib/providers/PrimerProvider.tsx
index 948cb1043..1072c719b 100644
--- a/packages/frontend/src/lib/providers/PrimerProvider.tsx
+++ b/packages/frontend/src/lib/providers/PrimerProvider.tsx
@@ -2,37 +2,14 @@
import React from "react";
import { ThemeProvider, BaseStyles } from "@primer/react";
-import { useSettings } from "@/lib/useSettings";
-// Import Primer CSS primitives for theming
-import "@primer/primitives/dist/css/functional/themes/light.css";
import "@primer/primitives/dist/css/functional/themes/dark.css";
-/**
- * Primer Theme Provider wrapper
- *
- * Integrates Primer's theming system with our existing theme management.
- * - Uses our useSettings hook to determine current theme
- * - Maps our theme values to Primer's colorMode
- * - Uses preventSSRMismatch for proper SSR/hydration
- */
export function PrimerProvider({ children }: { children: React.ReactNode }) {
- const { theme } = useSettings();
-
- // Map our theme preference to Primer's colorMode
- // Primer uses 'day' | 'night' | 'auto'
- const colorMode = theme === "system" ? "auto" : theme === "dark" ? "night" : "day";
-
- // Force the resolved scheme based on our existing theme system
- // This ensures consistency with our existing dark/light mode implementation
- const dayScheme = "light";
- const nightScheme = "dark";
-
return (
@@ -42,13 +19,9 @@ export function PrimerProvider({ children }: { children: React.ReactNode }) {
);
}
-/**
- * Wrapper component to use Primer components outside of the theme context
- * Useful for components that need to render before the theme is loaded
- */
export function PrimerWrapper({ children }: { children: React.ReactNode }) {
return (
-
+
{children}
);
diff --git a/packages/frontend/src/lib/themes.ts b/packages/frontend/src/lib/themes.ts
index 681cbc4c9..5fe2e7a16 100644
--- a/packages/frontend/src/lib/themes.ts
+++ b/packages/frontend/src/lib/themes.ts
@@ -18,7 +18,7 @@ export type ColorPaletteName =
| "monochrome"
| "YlGnBu";
-const GRAPH_EMPTY = "#1F1F20";
+const GRAPH_EMPTY = "var(--color-graph-empty)";
export const colorPalettes: Record = {
green: {
@@ -114,9 +114,9 @@ export type Theme = GraphColorPalette & { background: string; text: string; meta
export const DEFAULT_THEME = DEFAULT_PALETTE;
export const getThemeNames = getPaletteNames;
-const CANVAS_DEFAULT = "#141415";
-const FG_DEFAULT = "#FFFFFF";
-const FG_MUTED = "#696969";
+const CANVAS_DEFAULT = "var(--color-canvas-default)";
+const FG_DEFAULT = "var(--color-fg-default)";
+const FG_MUTED = "var(--color-fg-muted)";
export const getTheme = (name: ThemeName): Theme => ({
...getPalette(name),
diff --git a/packages/frontend/src/lib/useSettings.ts b/packages/frontend/src/lib/useSettings.ts
index 3baa6f01b..2e6337eb6 100644
--- a/packages/frontend/src/lib/useSettings.ts
+++ b/packages/frontend/src/lib/useSettings.ts
@@ -4,15 +4,11 @@ import { useState, useEffect, useCallback, useRef } from "react";
import type { ColorPaletteName } from "./themes";
import { DEFAULT_PALETTE } from "./themes";
-export type ThemePreference = "light" | "dark" | "system";
-
export interface Settings {
- theme: ThemePreference;
paletteName: ColorPaletteName;
}
const DEFAULT_SETTINGS: Settings = {
- theme: "system",
paletteName: DEFAULT_PALETTE,
};
@@ -26,7 +22,6 @@ function getStoredSettings(): Settings {
if (stored) {
const parsed = JSON.parse(stored);
return {
- theme: parsed.theme || DEFAULT_SETTINGS.theme,
paletteName: parsed.paletteName || DEFAULT_SETTINGS.paletteName,
};
}
@@ -46,16 +41,11 @@ function saveSettings(settings: Settings): void {
}
}
-function getSystemTheme(): "light" | "dark" {
- if (typeof window === "undefined") return "light";
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
-}
-
-function applyThemeToDocument(resolvedTheme: "light" | "dark"): void {
+function applyDarkModeToDocument(): void {
if (typeof document === "undefined") return;
const root = document.documentElement;
- root.classList.remove("light", "dark");
- root.classList.add(resolvedTheme);
+ root.classList.remove("light");
+ root.classList.add("dark");
}
export function useSettings() {
@@ -64,61 +54,13 @@ export function useSettings() {
return getStoredSettings();
});
- const [resolvedTheme, setResolvedTheme] = useState<"light" | "dark">(() => {
- if (typeof window === "undefined") return "light";
- const stored = getStoredSettings();
- return stored.theme === "system" ? getSystemTheme() : stored.theme;
- });
-
const mountedRef = useRef(false);
const [mounted, setMounted] = useState(false);
useEffect(() => {
- applyThemeToDocument(resolvedTheme);
+ applyDarkModeToDocument();
mountedRef.current = true;
- // eslint-disable-next-line react-hooks/set-state-in-effect -- Intentional: track mount state for SSR hydration
setMounted(true);
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
-
- useEffect(() => {
- if (!mountedRef.current) return;
-
- const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
-
- const handleChange = (e: MediaQueryListEvent) => {
- if (settings.theme === "system") {
- const newResolved = e.matches ? "dark" : "light";
- setResolvedTheme(() => {
- applyThemeToDocument(newResolved);
- return newResolved;
- });
- }
- };
-
- mediaQuery.addEventListener("change", handleChange);
- return () => mediaQuery.removeEventListener("change", handleChange);
- }, [settings.theme]);
-
- useEffect(() => {
- if (!mountedRef.current) return;
-
- const resolved = settings.theme === "system" ? getSystemTheme() : settings.theme;
- // eslint-disable-next-line react-hooks/set-state-in-effect -- Intentional: sync resolved theme when settings change
- setResolvedTheme((prev) => {
- if (prev !== resolved) {
- applyThemeToDocument(resolved);
- return resolved;
- }
- return prev;
- });
- }, [settings.theme]);
-
- const setTheme = useCallback((theme: ThemePreference) => {
- setSettings((prev) => {
- const newSettings = { ...prev, theme };
- saveSettings(newSettings);
- return newSettings;
- });
}, []);
const setPalette = useCallback((paletteName: ColorPaletteName) => {
@@ -130,10 +72,7 @@ export function useSettings() {
}, []);
return {
- theme: settings.theme,
paletteName: settings.paletteName,
- resolvedTheme,
- setTheme,
setPalette,
mounted,
};