diff --git a/apps/ui/package.json b/apps/ui/package.json index eefd7728..ad18d8ac 100644 --- a/apps/ui/package.json +++ b/apps/ui/package.json @@ -48,6 +48,22 @@ "@dnd-kit/core": "6.3.1", "@dnd-kit/sortable": "10.0.0", "@dnd-kit/utilities": "3.2.2", + "@fontsource/cascadia-code": "^5.2.3", + "@fontsource/fira-code": "^5.2.7", + "@fontsource/ibm-plex-mono": "^5.2.7", + "@fontsource/inconsolata": "^5.2.8", + "@fontsource/inter": "^5.2.8", + "@fontsource/iosevka": "^5.2.5", + "@fontsource/jetbrains-mono": "^5.2.8", + "@fontsource/lato": "^5.2.7", + "@fontsource/montserrat": "^5.2.8", + "@fontsource/open-sans": "^5.2.7", + "@fontsource/poppins": "^5.2.7", + "@fontsource/raleway": "^5.2.8", + "@fontsource/roboto": "^5.2.9", + "@fontsource/source-code-pro": "^5.2.7", + "@fontsource/source-sans-3": "^5.2.9", + "@fontsource/work-sans": "^5.2.8", "@lezer/highlight": "1.2.3", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", diff --git a/apps/ui/src/app.tsx b/apps/ui/src/app.tsx index c27cd5e7..814dd38e 100644 --- a/apps/ui/src/app.tsx +++ b/apps/ui/src/app.tsx @@ -8,6 +8,7 @@ import { useCursorStatusInit } from './hooks/use-cursor-status-init'; import { useProviderAuthInit } from './hooks/use-provider-auth-init'; import './styles/global.css'; import './styles/theme-imports'; +import './styles/font-imports'; const logger = createLogger('App'); diff --git a/apps/ui/src/assets/fonts/zed/zed-fonts.css b/apps/ui/src/assets/fonts/zed/zed-fonts.css new file mode 100644 index 00000000..1b62a128 --- /dev/null +++ b/apps/ui/src/assets/fonts/zed/zed-fonts.css @@ -0,0 +1,67 @@ +/* Zed Fonts - https://github.com/zed-industries/zed-fonts */ + +/* Zed Sans - UI Font */ +@font-face { + font-family: 'Zed Sans'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('./zed-sans-extended.ttf') format('truetype'); +} + +@font-face { + font-family: 'Zed Sans'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url('./zed-sans-extendeditalic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Zed Sans'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('./zed-sans-extendedbold.ttf') format('truetype'); +} + +@font-face { + font-family: 'Zed Sans'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url('./zed-sans-extendedbolditalic.ttf') format('truetype'); +} + +/* Zed Mono - Code Font */ +@font-face { + font-family: 'Zed Mono'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('./zed-mono-extended.ttf') format('truetype'); +} + +@font-face { + font-family: 'Zed Mono'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url('./zed-mono-extendeditalic.ttf') format('truetype'); +} + +@font-face { + font-family: 'Zed Mono'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('./zed-mono-extendedbold.ttf') format('truetype'); +} + +@font-face { + font-family: 'Zed Mono'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url('./zed-mono-extendedbolditalic.ttf') format('truetype'); +} diff --git a/apps/ui/src/assets/fonts/zed/zed-mono-extended.ttf b/apps/ui/src/assets/fonts/zed/zed-mono-extended.ttf new file mode 100644 index 00000000..1cab6121 Binary files /dev/null and b/apps/ui/src/assets/fonts/zed/zed-mono-extended.ttf differ diff --git a/apps/ui/src/assets/fonts/zed/zed-mono-extendedbold.ttf b/apps/ui/src/assets/fonts/zed/zed-mono-extendedbold.ttf new file mode 100644 index 00000000..5c027864 Binary files /dev/null and b/apps/ui/src/assets/fonts/zed/zed-mono-extendedbold.ttf differ diff --git a/apps/ui/src/assets/fonts/zed/zed-mono-extendedbolditalic.ttf b/apps/ui/src/assets/fonts/zed/zed-mono-extendedbolditalic.ttf new file mode 100644 index 00000000..47a3a9bc Binary files /dev/null and b/apps/ui/src/assets/fonts/zed/zed-mono-extendedbolditalic.ttf differ diff --git a/apps/ui/src/assets/fonts/zed/zed-mono-extendeditalic.ttf b/apps/ui/src/assets/fonts/zed/zed-mono-extendeditalic.ttf new file mode 100644 index 00000000..85ff2c3c Binary files /dev/null and b/apps/ui/src/assets/fonts/zed/zed-mono-extendeditalic.ttf differ diff --git a/apps/ui/src/assets/fonts/zed/zed-sans-extended.ttf b/apps/ui/src/assets/fonts/zed/zed-sans-extended.ttf new file mode 100644 index 00000000..d5cc91e2 Binary files /dev/null and b/apps/ui/src/assets/fonts/zed/zed-sans-extended.ttf differ diff --git a/apps/ui/src/assets/fonts/zed/zed-sans-extendedbold.ttf b/apps/ui/src/assets/fonts/zed/zed-sans-extendedbold.ttf new file mode 100644 index 00000000..0e1264b1 Binary files /dev/null and b/apps/ui/src/assets/fonts/zed/zed-sans-extendedbold.ttf differ diff --git a/apps/ui/src/assets/fonts/zed/zed-sans-extendedbolditalic.ttf b/apps/ui/src/assets/fonts/zed/zed-sans-extendedbolditalic.ttf new file mode 100644 index 00000000..568d9930 Binary files /dev/null and b/apps/ui/src/assets/fonts/zed/zed-sans-extendedbolditalic.ttf differ diff --git a/apps/ui/src/assets/fonts/zed/zed-sans-extendeditalic.ttf b/apps/ui/src/assets/fonts/zed/zed-sans-extendeditalic.ttf new file mode 100644 index 00000000..d5f10a2e Binary files /dev/null and b/apps/ui/src/assets/fonts/zed/zed-sans-extendeditalic.ttf differ diff --git a/apps/ui/src/components/views/project-settings-view/project-theme-section.tsx b/apps/ui/src/components/views/project-settings-view/project-theme-section.tsx index 2ec89498..003e8b86 100644 --- a/apps/ui/src/components/views/project-settings-view/project-theme-section.tsx +++ b/apps/ui/src/components/views/project-settings-view/project-theme-section.tsx @@ -32,27 +32,56 @@ export function ProjectThemeSection({ project }: ProjectThemeSectionProps) { setProjectFontSans, setProjectFontMono, } = useAppStore(); - const [activeTab, setActiveTab] = useState<'dark' | 'light'>('dark'); - - // Font local state - tracks what's selected when using custom fonts - const [fontSansLocal, setFontSansLocal] = useState( - project.fontFamilySans || DEFAULT_FONT_VALUE - ); - const [fontMonoLocal, setFontMonoLocal] = useState( - project.fontFamilyMono || DEFAULT_FONT_VALUE - ); - - // Sync font state when project changes - useEffect(() => { - setFontSansLocal(project.fontFamilySans || DEFAULT_FONT_VALUE); - setFontMonoLocal(project.fontFamilyMono || DEFAULT_FONT_VALUE); - }, [project]); // Theme state const projectTheme = project.theme as Theme | undefined; const hasCustomTheme = projectTheme !== undefined; const effectiveTheme = projectTheme || globalTheme; + // Determine if current theme is light or dark + const isLightTheme = lightThemes.some((t) => t.value === effectiveTheme); + const [activeTab, setActiveTab] = useState<'dark' | 'light'>(isLightTheme ? 'light' : 'dark'); + + // Helper to validate fonts against available options + const isValidSansFont = (font: string | undefined): boolean => { + if (!font) return false; + return UI_SANS_FONT_OPTIONS.some((opt) => opt.value === font); + }; + const isValidMonoFont = (font: string | undefined): boolean => { + if (!font) return false; + return UI_MONO_FONT_OPTIONS.some((opt) => opt.value === font); + }; + + // Font local state - tracks what's selected when using custom fonts + // Falls back to default if stored font is not in available options + const [fontSansLocal, setFontSansLocal] = useState( + project.fontFamilySans && isValidSansFont(project.fontFamilySans) + ? project.fontFamilySans + : DEFAULT_FONT_VALUE + ); + const [fontMonoLocal, setFontMonoLocal] = useState( + project.fontFamilyMono && isValidMonoFont(project.fontFamilyMono) + ? project.fontFamilyMono + : DEFAULT_FONT_VALUE + ); + + // Sync state when project changes + useEffect(() => { + setFontSansLocal( + project.fontFamilySans && isValidSansFont(project.fontFamilySans) + ? project.fontFamilySans + : DEFAULT_FONT_VALUE + ); + setFontMonoLocal( + project.fontFamilyMono && isValidMonoFont(project.fontFamilyMono) + ? project.fontFamilyMono + : DEFAULT_FONT_VALUE + ); + // Also sync the active tab based on current theme + const currentIsLight = lightThemes.some((t) => t.value === (project.theme || globalTheme)); + setActiveTab(currentIsLight ? 'light' : 'dark'); + }, [project, globalTheme]); + // Font state - check if project has custom fonts set const hasCustomFontSans = project.fontFamilySans !== undefined; const hasCustomFontMono = project.fontFamilyMono !== undefined; @@ -79,10 +108,11 @@ export function ProjectThemeSection({ project }: ProjectThemeSectionProps) { setProjectFontSans(project.id, null); setFontSansLocal(DEFAULT_FONT_VALUE); } else { - // Set to current global font or default + // Set explicit project override - use 'default' value to indicate explicit default choice const fontToSet = globalFontSans || DEFAULT_FONT_VALUE; setFontSansLocal(fontToSet); - setProjectFontSans(project.id, fontToSet === DEFAULT_FONT_VALUE ? null : fontToSet); + // Store the actual value (including 'default') so hasCustomFontSans stays true + setProjectFontSans(project.id, fontToSet); } }; @@ -92,21 +122,24 @@ export function ProjectThemeSection({ project }: ProjectThemeSectionProps) { setProjectFontMono(project.id, null); setFontMonoLocal(DEFAULT_FONT_VALUE); } else { - // Set to current global font or default + // Set explicit project override - use 'default' value to indicate explicit default choice const fontToSet = globalFontMono || DEFAULT_FONT_VALUE; setFontMonoLocal(fontToSet); - setProjectFontMono(project.id, fontToSet === DEFAULT_FONT_VALUE ? null : fontToSet); + // Store the actual value (including 'default') so hasCustomFontMono stays true + setProjectFontMono(project.id, fontToSet); } }; const handleFontSansChange = (value: string) => { setFontSansLocal(value); - setProjectFontSans(project.id, value === DEFAULT_FONT_VALUE ? null : value); + // Store the actual value (including 'default') - only null clears to use global + setProjectFontSans(project.id, value); }; const handleFontMonoChange = (value: string) => { setFontMonoLocal(value); - setProjectFontMono(project.id, value === DEFAULT_FONT_VALUE ? null : value); + // Store the actual value (including 'default') - only null clears to use global + setProjectFontMono(project.id, value); }; // Get display label for global font diff --git a/apps/ui/src/components/views/settings-view/appearance/appearance-section.tsx b/apps/ui/src/components/views/settings-view/appearance/appearance-section.tsx index 651231dd..400d121c 100644 --- a/apps/ui/src/components/views/settings-view/appearance/appearance-section.tsx +++ b/apps/ui/src/components/views/settings-view/appearance/appearance-section.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Label } from '@/components/ui/label'; import { Select, @@ -24,14 +24,34 @@ interface AppearanceSectionProps { } export function AppearanceSection({ effectiveTheme, onThemeChange }: AppearanceSectionProps) { - const [activeTab, setActiveTab] = useState<'dark' | 'light'>('dark'); const { fontFamilySans, fontFamilyMono, setFontSans, setFontMono } = useAppStore(); + // Determine if current theme is light or dark + const isLightTheme = lightThemes.some((t) => t.value === effectiveTheme); + const [activeTab, setActiveTab] = useState<'dark' | 'light'>(isLightTheme ? 'light' : 'dark'); + + // Sync active tab when theme changes + useEffect(() => { + const currentIsLight = lightThemes.some((t) => t.value === effectiveTheme); + setActiveTab(currentIsLight ? 'light' : 'dark'); + }, [effectiveTheme]); + const themesToShow = activeTab === 'dark' ? darkThemes : lightThemes; // Convert null to 'default' for Select component - const fontSansValue = fontFamilySans || DEFAULT_FONT_VALUE; - const fontMonoValue = fontFamilyMono || DEFAULT_FONT_VALUE; + // Also fallback to default if the stored font is not in the available options + const isValidSansFont = (font: string | null): boolean => { + if (!font) return false; + return UI_SANS_FONT_OPTIONS.some((opt) => opt.value === font); + }; + const isValidMonoFont = (font: string | null): boolean => { + if (!font) return false; + return UI_MONO_FONT_OPTIONS.some((opt) => opt.value === font); + }; + const fontSansValue = + fontFamilySans && isValidSansFont(fontFamilySans) ? fontFamilySans : DEFAULT_FONT_VALUE; + const fontMonoValue = + fontFamilyMono && isValidMonoFont(fontFamilyMono) ? fontFamilyMono : DEFAULT_FONT_VALUE; const handleFontSansChange = (value: string) => { setFontSans(value === DEFAULT_FONT_VALUE ? null : value); diff --git a/apps/ui/src/config/terminal-themes.ts b/apps/ui/src/config/terminal-themes.ts index f53d8f2f..e23feba3 100644 --- a/apps/ui/src/config/terminal-themes.ts +++ b/apps/ui/src/config/terminal-themes.ts @@ -542,9 +542,10 @@ const grayTheme: TerminalTheme = { // Theme mapping const terminalThemes: Record = { - light: lightTheme, - dark: darkTheme, + // Special system: darkTheme, // Will be resolved at runtime + // Dark themes + dark: darkTheme, retro: retroTheme, dracula: draculaTheme, nord: nordTheme, @@ -556,9 +557,35 @@ const terminalThemes: Record = { onedark: onedarkTheme, synthwave: synthwaveTheme, red: redTheme, - cream: creamTheme, sunset: sunsetTheme, gray: grayTheme, + forest: gruvboxTheme, // Green-ish theme, gruvbox is close + ocean: nordTheme, // Blue-ish theme, nord is close + ember: monokaiTheme, // Warm orange theme, monokai is close + 'ayu-dark': darkTheme, // Deep dark with warm accents + 'ayu-mirage': darkTheme, // Soft dark with golden accents + matcha: nordTheme, // Calming blue-gray with sage green + // Light themes + light: lightTheme, + cream: creamTheme, + solarizedlight: lightTheme, // TODO: Create dedicated solarized light terminal theme + github: lightTheme, // TODO: Create dedicated github terminal theme + paper: lightTheme, + rose: lightTheme, + mint: lightTheme, + lavender: lightTheme, + sand: creamTheme, // Warm tones like cream + sky: lightTheme, + peach: creamTheme, // Warm tones like cream + snow: lightTheme, + sepia: creamTheme, // Warm tones like cream + gruvboxlight: creamTheme, // Warm light theme + nordlight: lightTheme, // Cool light theme + blossom: lightTheme, + 'ayu-light': lightTheme, // Clean light with orange accents + onelight: lightTheme, // Atom One Light - blue accent + bluloco: lightTheme, // Bluloco - cyan-blue accent + feather: lightTheme, // Feather - orange accent }; /** diff --git a/apps/ui/src/config/theme-options.ts b/apps/ui/src/config/theme-options.ts index 18f0c38c..73ab4831 100644 --- a/apps/ui/src/config/theme-options.ts +++ b/apps/ui/src/config/theme-options.ts @@ -47,7 +47,11 @@ export type Theme = | 'gray' | 'forest' | 'ocean' - // Light themes (16) + | 'ember' + | 'ayu-dark' + | 'ayu-mirage' + | 'matcha' + // Light themes | 'light' | 'cream' | 'solarizedlight' @@ -63,7 +67,11 @@ export type Theme = | 'sepia' | 'gruvboxlight' | 'nordlight' - | 'blossom'; + | 'blossom' + | 'ayu-light' + | 'onelight' + | 'bluloco' + | 'feather'; export interface ThemeOption { value: Theme; @@ -74,9 +82,9 @@ export interface ThemeOption { color: string; // Primary/brand color for icon display } -// All theme options with dark/light categorization +// All theme options with dark/light categorization (alphabetically sorted, Dark/Light first) export const themeOptions: ReadonlyArray = [ - // Dark themes (16) + // Dark themes (20) - alphabetical, Dark first { value: 'dark', label: 'Dark', @@ -86,60 +94,20 @@ export const themeOptions: ReadonlyArray = [ color: '#3b82f6', }, { - value: 'retro', - label: 'Retro', - Icon: Terminal, - testId: 'retro-mode-button', + value: 'ayu-dark', + label: 'Ayu Dark', + Icon: Moon, + testId: 'ayu-dark-mode-button', isDark: true, - color: '#22c55e', + color: '#E6B450', }, { - value: 'dracula', - label: 'Dracula', - Icon: Ghost, - testId: 'dracula-mode-button', - isDark: true, - color: '#bd93f9', - }, - { - value: 'nord', - label: 'Nord', - Icon: Snowflake, - testId: 'nord-mode-button', - isDark: true, - color: '#88c0d0', - }, - { - value: 'monokai', - label: 'Monokai', - Icon: Flame, - testId: 'monokai-mode-button', - isDark: true, - color: '#f92672', - }, - { - value: 'tokyonight', - label: 'Tokyo Night', + value: 'ayu-mirage', + label: 'Ayu Mirage', Icon: Sparkles, - testId: 'tokyonight-mode-button', + testId: 'ayu-mirage-mode-button', isDark: true, - color: '#bb9af7', - }, - { - value: 'solarized', - label: 'Solarized Dark', - Icon: Eclipse, - testId: 'solarized-mode-button', - isDark: true, - color: '#268bd2', - }, - { - value: 'gruvbox', - label: 'Gruvbox', - Icon: Trees, - testId: 'gruvbox-mode-button', - isDark: true, - color: '#fe8019', + color: '#FFCC66', }, { value: 'catppuccin', @@ -150,44 +118,20 @@ export const themeOptions: ReadonlyArray = [ color: '#cba6f7', }, { - value: 'onedark', - label: 'One Dark', - Icon: Atom, - testId: 'onedark-mode-button', + value: 'dracula', + label: 'Dracula', + Icon: Ghost, + testId: 'dracula-mode-button', isDark: true, - color: '#61afef', + color: '#bd93f9', }, { - value: 'synthwave', - label: 'Synthwave', - Icon: Radio, - testId: 'synthwave-mode-button', + value: 'ember', + label: 'Ember', + Icon: Sunrise, + testId: 'ember-mode-button', isDark: true, - color: '#ff7edb', - }, - { - value: 'red', - label: 'Red', - Icon: Heart, - testId: 'red-mode-button', - isDark: true, - color: '#ef4444', - }, - { - value: 'sunset', - label: 'Sunset', - Icon: CloudSun, - testId: 'sunset-mode-button', - isDark: true, - color: '#f97316', - }, - { - value: 'gray', - label: 'Gray', - Icon: Square, - testId: 'gray-mode-button', - isDark: true, - color: '#6b7280', + color: '#fd971f', }, { value: 'forest', @@ -197,6 +141,46 @@ export const themeOptions: ReadonlyArray = [ isDark: true, color: '#22c55e', }, + { + value: 'gray', + label: 'Gray', + Icon: Square, + testId: 'gray-mode-button', + isDark: true, + color: '#6b7280', + }, + { + value: 'gruvbox', + label: 'Gruvbox', + Icon: Trees, + testId: 'gruvbox-mode-button', + isDark: true, + color: '#fe8019', + }, + { + value: 'matcha', + label: 'Matcha', + Icon: Leaf, + testId: 'matcha-mode-button', + isDark: true, + color: '#A4B07E', + }, + { + value: 'monokai', + label: 'Monokai', + Icon: Flame, + testId: 'monokai-mode-button', + isDark: true, + color: '#f92672', + }, + { + value: 'nord', + label: 'Nord', + Icon: Snowflake, + testId: 'nord-mode-button', + isDark: true, + color: '#88c0d0', + }, { value: 'ocean', label: 'Ocean', @@ -205,7 +189,63 @@ export const themeOptions: ReadonlyArray = [ isDark: true, color: '#06b6d4', }, - // Light themes (16) + { + value: 'onedark', + label: 'One Dark', + Icon: Atom, + testId: 'onedark-mode-button', + isDark: true, + color: '#61afef', + }, + { + value: 'red', + label: 'Red', + Icon: Heart, + testId: 'red-mode-button', + isDark: true, + color: '#ef4444', + }, + { + value: 'retro', + label: 'Retro', + Icon: Terminal, + testId: 'retro-mode-button', + isDark: true, + color: '#22c55e', + }, + { + value: 'solarized', + label: 'Solarized Dark', + Icon: Eclipse, + testId: 'solarized-mode-button', + isDark: true, + color: '#268bd2', + }, + { + value: 'sunset', + label: 'Sunset', + Icon: CloudSun, + testId: 'sunset-mode-button', + isDark: true, + color: '#f97316', + }, + { + value: 'synthwave', + label: 'Synthwave', + Icon: Radio, + testId: 'synthwave-mode-button', + isDark: true, + color: '#ff7edb', + }, + { + value: 'tokyonight', + label: 'Tokyo Night', + Icon: Sparkles, + testId: 'tokyonight-mode-button', + isDark: true, + color: '#bb9af7', + }, + // Light themes (20) - alphabetical, Light first { value: 'light', label: 'Light', @@ -214,6 +254,30 @@ export const themeOptions: ReadonlyArray = [ isDark: false, color: '#3b82f6', }, + { + value: 'ayu-light', + label: 'Ayu Light', + Icon: Sun, + testId: 'ayu-light-mode-button', + isDark: false, + color: '#F29718', + }, + { + value: 'blossom', + label: 'Blossom', + Icon: Cherry, + testId: 'blossom-mode-button', + isDark: false, + color: '#ec4899', + }, + { + value: 'bluloco', + label: 'Bluloco', + Icon: Waves, + testId: 'bluloco-mode-button', + isDark: false, + color: '#0099e1', + }, { value: 'cream', label: 'Cream', @@ -223,12 +287,12 @@ export const themeOptions: ReadonlyArray = [ color: '#b45309', }, { - value: 'solarizedlight', - label: 'Solarized Light', - Icon: Sunrise, - testId: 'solarizedlight-mode-button', + value: 'feather', + label: 'Feather', + Icon: Feather, + testId: 'feather-mode-button', isDark: false, - color: '#268bd2', + color: '#FF7B2E', }, { value: 'github', @@ -239,28 +303,12 @@ export const themeOptions: ReadonlyArray = [ color: '#0969da', }, { - value: 'paper', - label: 'Paper', - Icon: Scroll, - testId: 'paper-mode-button', + value: 'gruvboxlight', + label: 'Gruvbox Light', + Icon: Trees, + testId: 'gruvboxlight-mode-button', isDark: false, - color: '#374151', - }, - { - value: 'rose', - label: 'Rose', - Icon: Flower2, - testId: 'rose-mode-button', - isDark: false, - color: '#e11d48', - }, - { - value: 'mint', - label: 'Mint', - Icon: Wind, - testId: 'mint-mode-button', - isDark: false, - color: '#0d9488', + color: '#d65d0e', }, { value: 'lavender', @@ -271,52 +319,12 @@ export const themeOptions: ReadonlyArray = [ color: '#8b5cf6', }, { - value: 'sand', - label: 'Sand', - Icon: Palmtree, - testId: 'sand-mode-button', + value: 'mint', + label: 'Mint', + Icon: Wind, + testId: 'mint-mode-button', isDark: false, - color: '#d97706', - }, - { - value: 'sky', - label: 'Sky', - Icon: Sun, - testId: 'sky-mode-button', - isDark: false, - color: '#0284c7', - }, - { - value: 'peach', - label: 'Peach', - Icon: Cherry, - testId: 'peach-mode-button', - isDark: false, - color: '#ea580c', - }, - { - value: 'snow', - label: 'Snow', - Icon: Snowflake, - testId: 'snow-mode-button', - isDark: false, - color: '#3b82f6', - }, - { - value: 'sepia', - label: 'Sepia', - Icon: Coffee, - testId: 'sepia-mode-button', - isDark: false, - color: '#92400e', - }, - { - value: 'gruvboxlight', - label: 'Gruvbox Light', - Icon: Trees, - testId: 'gruvboxlight-mode-button', - isDark: false, - color: '#d65d0e', + color: '#0d9488', }, { value: 'nordlight', @@ -327,12 +335,76 @@ export const themeOptions: ReadonlyArray = [ color: '#5e81ac', }, { - value: 'blossom', - label: 'Blossom', - Icon: Cherry, - testId: 'blossom-mode-button', + value: 'onelight', + label: 'One Light', + Icon: Atom, + testId: 'onelight-mode-button', isDark: false, - color: '#ec4899', + color: '#526FFF', + }, + { + value: 'paper', + label: 'Paper', + Icon: Scroll, + testId: 'paper-mode-button', + isDark: false, + color: '#374151', + }, + { + value: 'peach', + label: 'Peach', + Icon: Cherry, + testId: 'peach-mode-button', + isDark: false, + color: '#ea580c', + }, + { + value: 'rose', + label: 'Rose', + Icon: Flower2, + testId: 'rose-mode-button', + isDark: false, + color: '#e11d48', + }, + { + value: 'sand', + label: 'Sand', + Icon: Palmtree, + testId: 'sand-mode-button', + isDark: false, + color: '#d97706', + }, + { + value: 'sepia', + label: 'Sepia', + Icon: Coffee, + testId: 'sepia-mode-button', + isDark: false, + color: '#92400e', + }, + { + value: 'sky', + label: 'Sky', + Icon: Sun, + testId: 'sky-mode-button', + isDark: false, + color: '#0284c7', + }, + { + value: 'snow', + label: 'Snow', + Icon: Snowflake, + testId: 'snow-mode-button', + isDark: false, + color: '#3b82f6', + }, + { + value: 'solarizedlight', + label: 'Solarized Light', + Icon: Sunrise, + testId: 'solarizedlight-mode-button', + isDark: false, + color: '#268bd2', }, ]; diff --git a/apps/ui/src/config/ui-font-options.ts b/apps/ui/src/config/ui-font-options.ts index 98b77151..5a1309a9 100644 --- a/apps/ui/src/config/ui-font-options.ts +++ b/apps/ui/src/config/ui-font-options.ts @@ -1,8 +1,9 @@ /** * Font options for per-project font customization * - * These are system fonts (no bundled @fontsource packages required). - * Users must have the fonts installed on their system for them to work. + * All fonts listed here are bundled with the app via @fontsource packages + * or custom font files (Zed fonts from zed-industries/zed-fonts). + * They are self-hosted and will work without any system installation. */ // Sentinel value for "use default font" - Radix Select doesn't allow empty strings @@ -14,34 +15,53 @@ export interface UIFontOption { } /** - * Sans/UI fonts for headings, labels, and general text + * Sans/UI fonts for headings, labels, and general text (Top 10) * * 'default' value means "use the theme default" (Geist Sans for all themes) */ export const UI_SANS_FONT_OPTIONS: readonly UIFontOption[] = [ { value: DEFAULT_FONT_VALUE, label: 'Default (Geist Sans)' }, - { value: "'Inter', system-ui, sans-serif", label: 'Inter' }, - { value: "'SF Pro', system-ui, sans-serif", label: 'SF Pro' }, + // Sans fonts (alphabetical) + { value: 'Inter, system-ui, sans-serif', label: 'Inter' }, + { value: 'Lato, system-ui, sans-serif', label: 'Lato' }, + { value: 'Montserrat, system-ui, sans-serif', label: 'Montserrat' }, + { value: "'Open Sans', system-ui, sans-serif", label: 'Open Sans' }, + { value: 'Poppins, system-ui, sans-serif', label: 'Poppins' }, + { value: 'Raleway, system-ui, sans-serif', label: 'Raleway' }, + { value: 'Roboto, system-ui, sans-serif', label: 'Roboto' }, { value: "'Source Sans 3', system-ui, sans-serif", label: 'Source Sans' }, - { value: "'IBM Plex Sans', system-ui, sans-serif", label: 'IBM Plex Sans' }, - { value: "'Roboto', system-ui, sans-serif", label: 'Roboto' }, - { value: 'system-ui, sans-serif', label: 'System Default' }, + { value: "'Work Sans', system-ui, sans-serif", label: 'Work Sans' }, + { value: "'Zed Sans', system-ui, sans-serif", label: 'Zed Sans' }, + // Monospace fonts (alphabetical, for users who prefer mono UI) + { value: "'Cascadia Code', monospace", label: 'Cascadia Code' }, + { value: "'Fira Code', monospace", label: 'Fira Code' }, + { value: "'IBM Plex Mono', monospace", label: 'IBM Plex Mono' }, + { value: 'Inconsolata, monospace', label: 'Inconsolata' }, + { value: 'Iosevka, monospace', label: 'Iosevka' }, + { value: "'JetBrains Mono', monospace", label: 'JetBrains Mono' }, + { value: "'Source Code Pro', monospace", label: 'Source Code Pro' }, + { value: "'Zed Mono', monospace", label: 'Zed Mono' }, ] as const; /** - * Mono/code fonts for code blocks, terminals, and monospaced text + * Mono/code fonts for code blocks, terminals, and monospaced text (Top 10) * * 'default' value means "use the theme default" (Geist Mono for all themes) + * Many of these support ligatures for coding symbols (-> => != etc.) */ export const UI_MONO_FONT_OPTIONS: readonly UIFontOption[] = [ { value: DEFAULT_FONT_VALUE, label: 'Default (Geist Mono)' }, - { value: "'JetBrains Mono', monospace", label: 'JetBrains Mono' }, - { value: "'Fira Code', monospace", label: 'Fira Code' }, - { value: "'SF Mono', Menlo, Monaco, monospace", label: 'SF Mono' }, - { value: "'Source Code Pro', monospace", label: 'Source Code Pro' }, - { value: "'IBM Plex Mono', monospace", label: 'IBM Plex Mono' }, - { value: "Menlo, Monaco, 'Courier New', monospace", label: 'Menlo / Monaco' }, + // Bundled fonts (alphabetical) { value: "'Cascadia Code', monospace", label: 'Cascadia Code' }, + { value: "'Fira Code', monospace", label: 'Fira Code' }, + { value: "'IBM Plex Mono', monospace", label: 'IBM Plex Mono' }, + { value: 'Inconsolata, monospace', label: 'Inconsolata' }, + { value: 'Iosevka, monospace', label: 'Iosevka' }, + { value: "'JetBrains Mono', monospace", label: 'JetBrains Mono' }, + { value: "'Source Code Pro', monospace", label: 'Source Code Pro' }, + { value: "'Zed Mono', monospace", label: 'Zed Mono' }, + // System fonts + { value: 'Menlo, Monaco, monospace', label: 'Menlo / Monaco (macOS)' }, ] as const; /** diff --git a/apps/ui/src/hooks/use-settings-migration.ts b/apps/ui/src/hooks/use-settings-migration.ts index 836daf15..5789886b 100644 --- a/apps/ui/src/hooks/use-settings-migration.ts +++ b/apps/ui/src/hooks/use-settings-migration.ts @@ -533,6 +533,8 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void { path: ref.path, lastOpened: ref.lastOpened, theme: ref.theme, + fontFamilySans: ref.fontFamilySans, + fontFamilyMono: ref.fontFamilyMono, isFavorite: ref.isFavorite, icon: ref.icon, customIconPath: ref.customIconPath, diff --git a/apps/ui/src/routes/__root.tsx b/apps/ui/src/routes/__root.tsx index 4e643eee..ec09be70 100644 --- a/apps/ui/src/routes/__root.tsx +++ b/apps/ui/src/routes/__root.tsx @@ -160,6 +160,10 @@ function RootLayoutContent() { getEffectiveTheme, getEffectiveFontSans, getEffectiveFontMono, + // Subscribe to theme and font state to trigger re-renders when they change + theme, + fontFamilySans, + fontFamilyMono, skipSandboxWarning, setSkipSandboxWarning, fetchCodexModels, @@ -250,7 +254,14 @@ function RootLayoutContent() { // Defer the theme value to keep UI responsive during rapid hover changes const deferredTheme = useDeferredValue(effectiveTheme); - // Get effective fonts for the current project + // Get effective theme and fonts for the current project + // Note: theme/fontFamilySans/fontFamilyMono are destructured above to ensure re-renders when they change + // eslint-disable-next-line @typescript-eslint/no-unused-vars + void theme; // Used for subscription + // eslint-disable-next-line @typescript-eslint/no-unused-vars + void fontFamilySans; // Used for subscription + // eslint-disable-next-line @typescript-eslint/no-unused-vars + void fontFamilyMono; // Used for subscription const effectiveFontSans = getEffectiveFontSans(); const effectiveFontMono = getEffectiveFontMono(); diff --git a/apps/ui/src/store/app-store.ts b/apps/ui/src/store/app-store.ts index c154ef39..121fb8cd 100644 --- a/apps/ui/src/store/app-store.ts +++ b/apps/ui/src/store/app-store.ts @@ -4,6 +4,11 @@ import type { Project, TrashedProject } from '@/lib/electron'; import { getElectronAPI } from '@/lib/electron'; import { createLogger } from '@automaker/utils/logger'; import { setItem, getItem } from '@/lib/storage'; +import { + UI_SANS_FONT_OPTIONS, + UI_MONO_FONT_OPTIONS, + DEFAULT_FONT_VALUE, +} from '@/config/ui-font-options'; import type { Feature as BaseFeature, FeatureImagePath, @@ -65,9 +70,10 @@ export type ViewMode = | 'ideation'; export type ThemeMode = - | 'light' - | 'dark' + // Special modes | 'system' + // Dark themes + | 'dark' | 'retro' | 'dracula' | 'nord' @@ -79,12 +85,40 @@ export type ThemeMode = | 'onedark' | 'synthwave' | 'red' - | 'cream' | 'sunset' - | 'gray'; + | 'gray' + | 'forest' + | 'ocean' + | 'ember' + | 'ayu-dark' + | 'ayu-mirage' + | 'matcha' + // Light themes + | 'light' + | 'cream' + | 'solarizedlight' + | 'github' + | 'paper' + | 'rose' + | 'mint' + | 'lavender' + | 'sand' + | 'sky' + | 'peach' + | 'snow' + | 'sepia' + | 'gruvboxlight' + | 'nordlight' + | 'blossom' + | 'ayu-light' + | 'onelight' + | 'bluloco' + | 'feather'; -// LocalStorage key for theme persistence (fallback when server settings aren't available) +// LocalStorage keys for persistence (fallback when server settings aren't available) export const THEME_STORAGE_KEY = 'automaker:theme'; +export const FONT_SANS_STORAGE_KEY = 'automaker:font-sans'; +export const FONT_MONO_STORAGE_KEY = 'automaker:font-mono'; // Maximum number of output lines to keep in init script state (prevents unbounded memory growth) export const MAX_INIT_OUTPUT_LINES = 500; @@ -123,6 +157,40 @@ function saveThemeToStorage(theme: ThemeMode): void { setItem(THEME_STORAGE_KEY, theme); } +/** + * Get fonts from localStorage as a fallback + * Used before server settings are loaded (e.g., on login/setup pages) + */ +export function getStoredFontSans(): string | null { + return getItem(FONT_SANS_STORAGE_KEY); +} + +export function getStoredFontMono(): string | null { + return getItem(FONT_MONO_STORAGE_KEY); +} + +/** + * Save fonts to localStorage for immediate persistence + * This is used as a fallback when server settings can't be loaded + */ +function saveFontSansToStorage(fontFamily: string | null): void { + if (fontFamily) { + setItem(FONT_SANS_STORAGE_KEY, fontFamily); + } else { + // Remove from storage if null (using default) + localStorage.removeItem(FONT_SANS_STORAGE_KEY); + } +} + +function saveFontMonoToStorage(fontFamily: string | null): void { + if (fontFamily) { + setItem(FONT_MONO_STORAGE_KEY, fontFamily); + } else { + // Remove from storage if null (using default) + localStorage.removeItem(FONT_MONO_STORAGE_KEY); + } +} + function persistEffectiveThemeForProject(project: Project | null, fallbackTheme: ThemeMode): void { const projectTheme = project?.theme as ThemeMode | undefined; const themeToStore = projectTheme ?? fallbackTheme; @@ -1270,8 +1338,8 @@ const initialState: AppState = { mobileSidebarHidden: false, // Sidebar visible by default on mobile lastSelectedSessionByProject: {}, theme: getStoredTheme() || 'dark', // Use localStorage theme as initial value, fallback to 'dark' - fontFamilySans: null, // Global UI font (null = use default Geist Sans) - fontFamilyMono: null, // Global code font (null = use default Geist Mono) + fontFamilySans: getStoredFontSans(), // Use localStorage font as initial value (null = use default Geist Sans) + fontFamilyMono: getStoredFontMono(), // Use localStorage font as initial value (null = use default Geist Mono) features: [], appSpec: '', ipcConnected: false, @@ -1748,12 +1816,21 @@ export const useAppStore = create()((set, get) => ({ setPreviewTheme: (theme) => set({ previewTheme: theme }), // Font actions (global + per-project override) - setFontSans: (fontFamily) => set({ fontFamilySans: fontFamily }), + setFontSans: (fontFamily) => { + // Save to localStorage for fallback when server settings aren't available + saveFontSansToStorage(fontFamily); + set({ fontFamilySans: fontFamily }); + }, - setFontMono: (fontFamily) => set({ fontFamilyMono: fontFamily }), + setFontMono: (fontFamily) => { + // Save to localStorage for fallback when server settings aren't available + saveFontMonoToStorage(fontFamily); + set({ fontFamilyMono: fontFamily }); + }, setProjectFontSans: (projectId, fontFamily) => { // Update the project's fontFamilySans property + // null means "clear to use global", any string (including 'default') means explicit override const projects = get().projects.map((p) => p.id === projectId ? { ...p, fontFamilySans: fontFamily === null ? undefined : fontFamily } @@ -1775,6 +1852,7 @@ export const useAppStore = create()((set, get) => ({ setProjectFontMono: (projectId, fontFamily) => { // Update the project's fontFamilyMono property + // null means "clear to use global", any string (including 'default') means explicit override const projects = get().projects.map((p) => p.id === projectId ? { ...p, fontFamilyMono: fontFamily === null ? undefined : fontFamily } @@ -1797,19 +1875,41 @@ export const useAppStore = create()((set, get) => ({ getEffectiveFontSans: () => { const currentProject = get().currentProject; // Return project override if set, otherwise global, otherwise null for default + // 'default' value means explicitly using default font, so return null for CSS + // Also validate that the font is in the available options list + const isValidFont = (font: string | null | undefined): boolean => { + if (!font || font === DEFAULT_FONT_VALUE) return true; + return UI_SANS_FONT_OPTIONS.some((opt) => opt.value === font); + }; + if (currentProject?.fontFamilySans) { - return currentProject.fontFamilySans; + const font = currentProject.fontFamilySans; + if (!isValidFont(font)) return null; // Fallback to default if font not in list + return font === DEFAULT_FONT_VALUE ? null : font; } - return get().fontFamilySans; + const globalFont = get().fontFamilySans; + if (!isValidFont(globalFont)) return null; // Fallback to default if font not in list + return globalFont === DEFAULT_FONT_VALUE ? null : globalFont; }, getEffectiveFontMono: () => { const currentProject = get().currentProject; // Return project override if set, otherwise global, otherwise null for default + // 'default' value means explicitly using default font, so return null for CSS + // Also validate that the font is in the available options list + const isValidFont = (font: string | null | undefined): boolean => { + if (!font || font === DEFAULT_FONT_VALUE) return true; + return UI_MONO_FONT_OPTIONS.some((opt) => opt.value === font); + }; + if (currentProject?.fontFamilyMono) { - return currentProject.fontFamilyMono; + const font = currentProject.fontFamilyMono; + if (!isValidFont(font)) return null; // Fallback to default if font not in list + return font === DEFAULT_FONT_VALUE ? null : font; } - return get().fontFamilyMono; + const globalFont = get().fontFamilyMono; + if (!isValidFont(globalFont)) return null; // Fallback to default if font not in list + return globalFont === DEFAULT_FONT_VALUE ? null : globalFont; }, // Feature actions diff --git a/apps/ui/src/styles/font-imports.ts b/apps/ui/src/styles/font-imports.ts new file mode 100644 index 00000000..64b1dedf --- /dev/null +++ b/apps/ui/src/styles/font-imports.ts @@ -0,0 +1,113 @@ +/** + * Bundles all web font packages so they're available + * for use in the font customization settings. + * + * These fonts are self-hosted with the app, so users don't need + * to have them installed on their system. + */ + +// Zed Fonts (from zed-industries/zed-fonts) +import '@/assets/fonts/zed/zed-fonts.css'; + +// ============================================ +// Sans-serif / UI Fonts (Top 10) +// ============================================ + +// Inter - Designed specifically for screens; excellent legibility at small sizes +import '@fontsource/inter/400.css'; +import '@fontsource/inter/500.css'; +import '@fontsource/inter/600.css'; +import '@fontsource/inter/700.css'; + +// Roboto - Highly versatile and clean; the standard for Google-based interfaces +import '@fontsource/roboto/400.css'; +import '@fontsource/roboto/500.css'; +import '@fontsource/roboto/700.css'; + +// Open Sans - Neutral and friendly; optimized for web and mobile readability +import '@fontsource/open-sans/400.css'; +import '@fontsource/open-sans/500.css'; +import '@fontsource/open-sans/600.css'; +import '@fontsource/open-sans/700.css'; + +// Montserrat - Geometric and modern; best for high-impact titles and branding +import '@fontsource/montserrat/400.css'; +import '@fontsource/montserrat/500.css'; +import '@fontsource/montserrat/600.css'; +import '@fontsource/montserrat/700.css'; + +// Lato - Blends professionalism with warmth; ideal for longer body text +import '@fontsource/lato/400.css'; +import '@fontsource/lato/700.css'; + +// Poppins - Geometric and energetic; popular for modern, friendly brand identities +import '@fontsource/poppins/400.css'; +import '@fontsource/poppins/500.css'; +import '@fontsource/poppins/600.css'; +import '@fontsource/poppins/700.css'; + +// Raleway - Elegant with unique characteristics; great for creative portfolios +import '@fontsource/raleway/400.css'; +import '@fontsource/raleway/500.css'; +import '@fontsource/raleway/600.css'; +import '@fontsource/raleway/700.css'; + +// Work Sans - Optimized for screen readability; feels clean and contemporary +import '@fontsource/work-sans/400.css'; +import '@fontsource/work-sans/500.css'; +import '@fontsource/work-sans/600.css'; +import '@fontsource/work-sans/700.css'; + +// Source Sans 3 - Adobe's first open-source font; highly functional for complex interfaces +import '@fontsource/source-sans-3/400.css'; +import '@fontsource/source-sans-3/500.css'; +import '@fontsource/source-sans-3/600.css'; +import '@fontsource/source-sans-3/700.css'; + +// ============================================ +// Monospace / Code Fonts (Top 10) +// ============================================ + +// Fira Code - Excellent legibility and stylish ligatures (=>, !=, etc.) +import '@fontsource/fira-code/400.css'; +import '@fontsource/fira-code/500.css'; +import '@fontsource/fira-code/600.css'; +import '@fontsource/fira-code/700.css'; + +// JetBrains Mono - Designed by JetBrains for developers, focusing on readability +import '@fontsource/jetbrains-mono/400.css'; +import '@fontsource/jetbrains-mono/500.css'; +import '@fontsource/jetbrains-mono/600.css'; +import '@fontsource/jetbrains-mono/700.css'; + +// Cascadia Code - Microsoft's font, popular in Windows Terminal, with ligatures +import '@fontsource/cascadia-code/400.css'; +import '@fontsource/cascadia-code/600.css'; +import '@fontsource/cascadia-code/700.css'; + +// Iosevka - Highly customizable, slender sans-serif/slab-serif font +import '@fontsource/iosevka/400.css'; +import '@fontsource/iosevka/500.css'; +import '@fontsource/iosevka/600.css'; +import '@fontsource/iosevka/700.css'; + +// Inconsolata - Popular, clean, and highly readable choice for screens +import '@fontsource/inconsolata/400.css'; +import '@fontsource/inconsolata/500.css'; +import '@fontsource/inconsolata/600.css'; +import '@fontsource/inconsolata/700.css'; + +// Source Code Pro - Adobe's clean, geometric, open-source font +import '@fontsource/source-code-pro/400.css'; +import '@fontsource/source-code-pro/500.css'; +import '@fontsource/source-code-pro/600.css'; +import '@fontsource/source-code-pro/700.css'; + +// IBM Plex Mono - Clean, modern monospaced font from IBM +import '@fontsource/ibm-plex-mono/400.css'; +import '@fontsource/ibm-plex-mono/500.css'; +import '@fontsource/ibm-plex-mono/600.css'; +import '@fontsource/ibm-plex-mono/700.css'; + +// Note: Monaco/Menlo are macOS system fonts (not bundled) +// Note: Hack font is not available via @fontsource diff --git a/apps/ui/src/styles/global.css b/apps/ui/src/styles/global.css index 9ffd4978..e0159e25 100644 --- a/apps/ui/src/styles/global.css +++ b/apps/ui/src/styles/global.css @@ -18,6 +18,10 @@ @custom-variant gray (&:is(.gray *)); @custom-variant forest (&:is(.forest *)); @custom-variant ocean (&:is(.ocean *)); +@custom-variant ember (&:is(.ember *)); +@custom-variant ayu-dark (&:is(.ayu-dark *)); +@custom-variant ayu-mirage (&:is(.ayu-mirage *)); +@custom-variant matcha (&:is(.matcha *)); /* Light themes */ @custom-variant cream (&:is(.cream *)); @@ -35,6 +39,10 @@ @custom-variant gruvboxlight (&:is(.gruvboxlight *)); @custom-variant nordlight (&:is(.nordlight *)); @custom-variant blossom (&:is(.blossom *)); +@custom-variant ayu-light (&:is(.ayu-light *)); +@custom-variant onelight (&:is(.onelight *)); +@custom-variant bluloco (&:is(.bluloco *)); +@custom-variant feather (&:is(.feather *)); @theme inline { --color-background: var(--background); @@ -372,6 +380,15 @@ body { @apply bg-background text-foreground; background-color: var(--background); + font-family: var(--font-sans); + } + + /* Apply monospace font to code elements */ + code, + pre, + kbd, + samp { + font-family: var(--font-mono); } /* Text selection styling for readability */ diff --git a/apps/ui/src/styles/theme-imports.ts b/apps/ui/src/styles/theme-imports.ts index 6560b47b..a1b24797 100644 --- a/apps/ui/src/styles/theme-imports.ts +++ b/apps/ui/src/styles/theme-imports.ts @@ -3,7 +3,7 @@ * doesn't tree-shake their CSS when imported dynamically. */ -// Dark themes (16) +// Dark themes (20) import './themes/dark.css'; import './themes/retro.css'; import './themes/dracula.css'; @@ -20,8 +20,12 @@ import './themes/sunset.css'; import './themes/gray.css'; import './themes/forest.css'; import './themes/ocean.css'; +import './themes/ember.css'; +import './themes/ayu-dark.css'; +import './themes/ayu-mirage.css'; +import './themes/matcha.css'; -// Light themes (16) +// Light themes (20) import './themes/light.css'; import './themes/cream.css'; import './themes/solarizedlight.css'; @@ -38,3 +42,7 @@ import './themes/sepia.css'; import './themes/gruvboxlight.css'; import './themes/nordlight.css'; import './themes/blossom.css'; +import './themes/ayu-light.css'; +import './themes/onelight.css'; +import './themes/bluloco.css'; +import './themes/feather.css'; diff --git a/apps/ui/src/styles/themes/ayu-dark.css b/apps/ui/src/styles/themes/ayu-dark.css new file mode 100644 index 00000000..a4a4309b --- /dev/null +++ b/apps/ui/src/styles/themes/ayu-dark.css @@ -0,0 +1,151 @@ +/* Ayu Dark Theme - Deep dark with warm orange accents */ + +.ayu-dark { + /* Backgrounds - from editor.bg #10141C, ui.bg #0D1017 */ + --background: oklch(0.12 0.02 250); /* #0D1017 */ + --background-50: oklch(0.12 0.02 250 / 0.5); + --background-80: oklch(0.12 0.02 250 / 0.8); + + /* Text - from editor.fg #BFBDB6, ui.fg #5A6378 - boosted for contrast */ + --foreground: oklch(0.95 0.01 90); /* Brighter for better readability */ + --foreground-secondary: oklch(0.75 0.02 240); + --foreground-muted: oklch(0.6 0.03 240); + + /* Card/Popover - from panel.bg #141821, popup.bg #0F131A */ + --card: oklch(0.15 0.02 250); /* #141821 */ + --card-foreground: oklch(0.95 0.01 90); + --popover: oklch(0.13 0.02 250); /* #0F131A */ + --popover-foreground: oklch(0.95 0.01 90); + + /* Primary/Brand - from accent.tint #E6B450, orange #FF8F40 */ + --primary: oklch(0.78 0.15 75); /* #E6B450 golden */ + --primary-foreground: oklch(0.12 0.02 250); + + --brand-400: oklch(0.82 0.14 75); + --brand-500: oklch(0.78 0.15 75); /* #E6B450 */ + --brand-600: oklch(0.72 0.17 70); + + /* Secondary - from ui.line #1B1F29 */ + --secondary: oklch(0.18 0.02 250); + --secondary-foreground: oklch(0.95 0.01 90); + + /* Muted */ + --muted: oklch(0.18 0.02 250); + --muted-foreground: oklch(0.45 0.04 240); + + /* Accent */ + --accent: oklch(0.22 0.03 250); + --accent-foreground: oklch(0.95 0.01 90); + + /* Destructive - from error #D95757 */ + --destructive: oklch(0.55 0.2 25); /* #D95757 */ + + /* Borders - from editor.line #161A24 */ + --border: oklch(0.2 0.02 250); + --border-glass: oklch(0.78 0.15 75 / 0.3); + + --input: oklch(0.15 0.02 250); + --ring: oklch(0.78 0.15 75); + + /* Charts - using Ayu palette */ + --chart-1: oklch(0.78 0.15 75); /* Gold accent */ + --chart-2: oklch(0.75 0.2 130); /* Green #AAD94C */ + --chart-3: oklch(0.7 0.15 220); /* Blue #59C2FF */ + --chart-4: oklch(0.7 0.2 320); /* Purple #D2A6FF */ + --chart-5: oklch(0.65 0.2 15); /* Red #F07178 */ + + /* Sidebar - from ui.bg #0D1017 */ + --sidebar: oklch(0.1 0.02 250); + --sidebar-foreground: oklch(0.95 0.01 90); + --sidebar-primary: oklch(0.78 0.15 75); + --sidebar-primary-foreground: oklch(0.12 0.02 250); + --sidebar-accent: oklch(0.18 0.02 250); + --sidebar-accent-foreground: oklch(0.95 0.01 90); + --sidebar-border: oklch(0.2 0.02 250); + --sidebar-ring: oklch(0.78 0.15 75); + + /* Action buttons */ + --action-view: oklch(0.78 0.15 75); /* Gold */ + --action-view-hover: oklch(0.72 0.17 70); + --action-followup: oklch(0.7 0.15 220); /* Blue #59C2FF */ + --action-followup-hover: oklch(0.65 0.17 220); + --action-commit: oklch(0.75 0.2 130); /* Green #AAD94C */ + --action-commit-hover: oklch(0.7 0.22 130); + --action-verify: oklch(0.75 0.2 130); + --action-verify-hover: oklch(0.7 0.22 130); + + /* Running indicator */ + --running-indicator: oklch(0.78 0.15 75); + --running-indicator-text: oklch(0.82 0.14 75); +} + +/* Theme-specific overrides */ + +.ayu-dark .animated-outline-gradient { + background: conic-gradient(from 90deg at 50% 50%, #e6b450 0%, #ff8f40 50%, #e6b450 100%); +} + +.ayu-dark .animated-outline-inner { + background: oklch(0.12 0.02 250) !important; + color: #e6b450 !important; +} + +.ayu-dark [data-slot='button'][class*='animated-outline']:hover .animated-outline-inner { + background: oklch(0.18 0.02 250) !important; + color: #ffb454 !important; +} + +.ayu-dark .slider-track { + background: oklch(0.18 0.02 250); +} + +.ayu-dark .slider-range { + background: linear-gradient(to right, #e6b450, #ffb454); +} + +.ayu-dark .slider-thumb { + background: oklch(0.12 0.02 250); + border-color: #e6b450; +} + +/* XML Syntax Highlighting */ +.ayu-dark .xml-highlight { + color: oklch(0.95 0.01 90); +} + +.ayu-dark .xml-tag-bracket { + color: #39bae6; /* Indigo */ +} + +.ayu-dark .xml-tag-name { + color: #39bae6; +} + +.ayu-dark .xml-attribute-name { + color: #ffb454; /* Yellow */ +} + +.ayu-dark .xml-attribute-equals { + color: oklch(0.95 0.01 90); +} + +.ayu-dark .xml-attribute-value { + color: #aad94c; /* Green */ +} + +.ayu-dark .xml-comment { + color: #5a6673; /* Gray */ + font-style: italic; +} + +.ayu-dark .xml-cdata { + color: #95e6cb; /* Teal */ +} + +.ayu-dark .xml-doctype { + color: #d2a6ff; /* Purple */ +} + +.ayu-dark .xml-text { + color: oklch(0.95 0.01 90); +} diff --git a/apps/ui/src/styles/themes/ayu-light.css b/apps/ui/src/styles/themes/ayu-light.css new file mode 100644 index 00000000..755e7af8 --- /dev/null +++ b/apps/ui/src/styles/themes/ayu-light.css @@ -0,0 +1,151 @@ +/* Ayu Light Theme - Clean light with warm orange accents */ + +.ayu-light { + /* Backgrounds - from editor.bg #FCFCFC, ui.bg #F8F9FA */ + --background: oklch(0.98 0.005 90); /* #FCFCFC */ + --background-50: oklch(0.98 0.005 90 / 0.5); + --background-80: oklch(0.98 0.005 90 / 0.8); + + /* Text - from editor.fg #5C6166, ui.fg #828E9F */ + --foreground: oklch(0.42 0.02 240); /* #5C6166 */ + --foreground-secondary: oklch(0.52 0.02 240); + --foreground-muted: oklch(0.58 0.03 240); /* #828E9F */ + + /* Card/Popover - from panel.bg #FAFAFA, popup.bg #FFFFFF */ + --card: oklch(0.98 0.003 90); /* #FAFAFA */ + --card-foreground: oklch(0.42 0.02 240); + --popover: oklch(1 0 0); /* #FFFFFF */ + --popover-foreground: oklch(0.42 0.02 240); + + /* Primary/Brand - from accent.tint #F29718 */ + --primary: oklch(0.72 0.18 55); /* #F29718 orange */ + --primary-foreground: oklch(1 0 0); + + --brand-400: oklch(0.78 0.16 55); + --brand-500: oklch(0.72 0.18 55); /* #F29718 */ + --brand-600: oklch(0.65 0.2 50); + + /* Secondary */ + --secondary: oklch(0.96 0.005 90); + --secondary-foreground: oklch(0.42 0.02 240); + + /* Muted */ + --muted: oklch(0.96 0.005 90); + --muted-foreground: oklch(0.58 0.03 240); + + /* Accent */ + --accent: oklch(0.94 0.01 90); + --accent-foreground: oklch(0.42 0.02 240); + + /* Destructive - from error #E65050 */ + --destructive: oklch(0.55 0.22 25); /* #E65050 */ + + /* Borders */ + --border: oklch(0.9 0.01 90); + --border-glass: oklch(0.42 0.02 240 / 0.1); + + --input: oklch(1 0 0); + --ring: oklch(0.72 0.18 55); + + /* Charts - using Ayu Light palette */ + --chart-1: oklch(0.72 0.18 55); /* Orange accent */ + --chart-2: oklch(0.6 0.18 130); /* Green #86B300 */ + --chart-3: oklch(0.65 0.15 210); /* Blue #22A4E6 */ + --chart-4: oklch(0.55 0.18 310); /* Purple #A37ACC */ + --chart-5: oklch(0.58 0.2 20); /* Red #F07171 */ + + /* Sidebar */ + --sidebar: oklch(0.97 0.005 90); /* #F8F9FA */ + --sidebar-foreground: oklch(0.42 0.02 240); + --sidebar-primary: oklch(0.72 0.18 55); + --sidebar-primary-foreground: oklch(1 0 0); + --sidebar-accent: oklch(0.94 0.01 90); + --sidebar-accent-foreground: oklch(0.42 0.02 240); + --sidebar-border: oklch(0.9 0.01 90); + --sidebar-ring: oklch(0.72 0.18 55); + + /* Action buttons */ + --action-view: oklch(0.72 0.18 55); /* Orange */ + --action-view-hover: oklch(0.65 0.2 50); + --action-followup: oklch(0.65 0.15 210); /* Blue */ + --action-followup-hover: oklch(0.58 0.17 210); + --action-commit: oklch(0.6 0.18 130); /* Green */ + --action-commit-hover: oklch(0.55 0.2 130); + --action-verify: oklch(0.6 0.18 130); + --action-verify-hover: oklch(0.55 0.2 130); + + /* Running indicator */ + --running-indicator: oklch(0.72 0.18 55); + --running-indicator-text: oklch(0.65 0.2 50); +} + +/* Theme-specific overrides */ + +.ayu-light .animated-outline-gradient { + background: conic-gradient(from 90deg at 50% 50%, #f29718 0%, #fa8532 50%, #f29718 100%); +} + +.ayu-light .animated-outline-inner { + background: oklch(0.98 0.005 90) !important; + color: #f29718 !important; +} + +.ayu-light [data-slot='button'][class*='animated-outline']:hover .animated-outline-inner { + background: oklch(0.96 0.01 90) !important; + color: #fa8532 !important; +} + +.ayu-light .slider-track { + background: oklch(0.92 0.01 90); +} + +.ayu-light .slider-range { + background: linear-gradient(to right, #f29718, #fa8532); +} + +.ayu-light .slider-thumb { + background: oklch(1 0 0); + border-color: #f29718; +} + +/* XML Syntax Highlighting */ +.ayu-light .xml-highlight { + color: oklch(0.42 0.02 240); +} + +.ayu-light .xml-tag-bracket { + color: #55b4d4; /* Indigo/Cyan */ +} + +.ayu-light .xml-tag-name { + color: #55b4d4; +} + +.ayu-light .xml-attribute-name { + color: #f29718; /* Orange */ +} + +.ayu-light .xml-attribute-equals { + color: oklch(0.42 0.02 240); +} + +.ayu-light .xml-attribute-value { + color: #86b300; /* Green */ +} + +.ayu-light .xml-comment { + color: #adaeb1; /* Gray */ + font-style: italic; +} + +.ayu-light .xml-cdata { + color: #4cbf99; /* Teal */ +} + +.ayu-light .xml-doctype { + color: #a37acc; /* Purple */ +} + +.ayu-light .xml-text { + color: oklch(0.42 0.02 240); +} diff --git a/apps/ui/src/styles/themes/ayu-mirage.css b/apps/ui/src/styles/themes/ayu-mirage.css new file mode 100644 index 00000000..076eebaf --- /dev/null +++ b/apps/ui/src/styles/themes/ayu-mirage.css @@ -0,0 +1,151 @@ +/* Ayu Mirage Theme - Soft dark with golden yellow accents */ + +.ayu-mirage { + /* Backgrounds - from editor.bg #242936, ui.bg #1F2430 */ + --background: oklch(0.2 0.02 260); /* #1F2430 */ + --background-50: oklch(0.2 0.02 260 / 0.5); + --background-80: oklch(0.2 0.02 260 / 0.8); + + /* Text - from editor.fg #CCCAC2, ui.fg #707A8C */ + --foreground: oklch(0.82 0.01 90); /* #CCCAC2 */ + --foreground-secondary: oklch(0.58 0.03 240); + --foreground-muted: oklch(0.52 0.04 240); /* #707A8C */ + + /* Card/Popover - from panel.bg #282E3B, popup.bg #1C212C */ + --card: oklch(0.24 0.02 260); /* #282E3B */ + --card-foreground: oklch(0.82 0.01 90); + --popover: oklch(0.18 0.02 260); /* #1C212C */ + --popover-foreground: oklch(0.82 0.01 90); + + /* Primary/Brand - from accent.tint #FFCC66 */ + --primary: oklch(0.85 0.14 85); /* #FFCC66 golden yellow */ + --primary-foreground: oklch(0.18 0.02 260); + + --brand-400: oklch(0.88 0.12 85); + --brand-500: oklch(0.85 0.14 85); /* #FFCC66 */ + --brand-600: oklch(0.78 0.16 80); + + /* Secondary - from ui.line #171B24 */ + --secondary: oklch(0.22 0.02 260); + --secondary-foreground: oklch(0.82 0.01 90); + + /* Muted */ + --muted: oklch(0.22 0.02 260); + --muted-foreground: oklch(0.52 0.04 240); + + /* Accent */ + --accent: oklch(0.28 0.03 260); + --accent-foreground: oklch(0.82 0.01 90); + + /* Destructive - from error #FF6666 */ + --destructive: oklch(0.62 0.22 20); /* #FF6666 */ + + /* Borders - from editor.line #1A1F29 */ + --border: oklch(0.22 0.02 260); + --border-glass: oklch(0.85 0.14 85 / 0.3); + + --input: oklch(0.2 0.02 260); + --ring: oklch(0.85 0.14 85); + + /* Charts - using Ayu Mirage palette */ + --chart-1: oklch(0.85 0.14 85); /* Yellow accent */ + --chart-2: oklch(0.85 0.2 120); /* Green #D5FF80 */ + --chart-3: oklch(0.78 0.12 200); /* Blue #73D0FF */ + --chart-4: oklch(0.82 0.15 310); /* Purple #DFBFFF */ + --chart-5: oklch(0.68 0.18 20); /* Red #F28779 */ + + /* Sidebar - slightly darker than main bg */ + --sidebar: oklch(0.16 0.02 260); + --sidebar-foreground: oklch(0.82 0.01 90); + --sidebar-primary: oklch(0.85 0.14 85); + --sidebar-primary-foreground: oklch(0.18 0.02 260); + --sidebar-accent: oklch(0.22 0.02 260); + --sidebar-accent-foreground: oklch(0.82 0.01 90); + --sidebar-border: oklch(0.22 0.02 260); + --sidebar-ring: oklch(0.85 0.14 85); + + /* Action buttons */ + --action-view: oklch(0.85 0.14 85); /* Yellow */ + --action-view-hover: oklch(0.78 0.16 80); + --action-followup: oklch(0.78 0.12 200); /* Blue #73D0FF */ + --action-followup-hover: oklch(0.72 0.14 200); + --action-commit: oklch(0.85 0.2 120); /* Green #D5FF80 */ + --action-commit-hover: oklch(0.78 0.22 120); + --action-verify: oklch(0.85 0.2 120); + --action-verify-hover: oklch(0.78 0.22 120); + + /* Running indicator */ + --running-indicator: oklch(0.85 0.14 85); + --running-indicator-text: oklch(0.88 0.12 85); +} + +/* Theme-specific overrides */ + +.ayu-mirage .animated-outline-gradient { + background: conic-gradient(from 90deg at 50% 50%, #ffcc66 0%, #ffa659 50%, #ffcc66 100%); +} + +.ayu-mirage .animated-outline-inner { + background: oklch(0.2 0.02 260) !important; + color: #ffcc66 !important; +} + +.ayu-mirage [data-slot='button'][class*='animated-outline']:hover .animated-outline-inner { + background: oklch(0.24 0.02 260) !important; + color: #ffcd66 !important; +} + +.ayu-mirage .slider-track { + background: oklch(0.22 0.02 260); +} + +.ayu-mirage .slider-range { + background: linear-gradient(to right, #ffcc66, #ffa659); +} + +.ayu-mirage .slider-thumb { + background: oklch(0.2 0.02 260); + border-color: #ffcc66; +} + +/* XML Syntax Highlighting */ +.ayu-mirage .xml-highlight { + color: oklch(0.82 0.01 90); +} + +.ayu-mirage .xml-tag-bracket { + color: #5ccfe6; /* Indigo/Cyan */ +} + +.ayu-mirage .xml-tag-name { + color: #5ccfe6; +} + +.ayu-mirage .xml-attribute-name { + color: #ffcd66; /* Yellow */ +} + +.ayu-mirage .xml-attribute-equals { + color: oklch(0.82 0.01 90); +} + +.ayu-mirage .xml-attribute-value { + color: #d5ff80; /* Green */ +} + +.ayu-mirage .xml-comment { + color: #6e7c8f; /* Gray */ + font-style: italic; +} + +.ayu-mirage .xml-cdata { + color: #95e6cb; /* Teal */ +} + +.ayu-mirage .xml-doctype { + color: #dfbfff; /* Purple */ +} + +.ayu-mirage .xml-text { + color: oklch(0.82 0.01 90); +} diff --git a/apps/ui/src/styles/themes/bluloco.css b/apps/ui/src/styles/themes/bluloco.css new file mode 100644 index 00000000..c99ddbfc --- /dev/null +++ b/apps/ui/src/styles/themes/bluloco.css @@ -0,0 +1,93 @@ +/* Bluloco Light Theme - Clean light with cyan-blue accent */ + +.bluloco { + /* Backgrounds */ + --background: oklch(0.98 0.002 250); + --background-50: oklch(0.98 0.002 250 / 0.5); + --background-80: oklch(0.98 0.002 250 / 0.8); + + /* Text */ + --foreground: oklch(0.3 0.02 250); + --foreground-secondary: oklch(0.45 0.02 250); + --foreground-muted: oklch(0.55 0.015 250); + + /* Card/Popover */ + --card: oklch(0.96 0.003 250); + --card-foreground: oklch(0.3 0.02 250); + --popover: oklch(0.98 0.002 250); + --popover-foreground: oklch(0.3 0.02 250); + + /* Primary/Brand - cyan-blue #0099e1 */ + --primary: oklch(0.62 0.18 220); + --primary-foreground: oklch(1 0 0); + + --brand-400: oklch(0.67 0.16 220); + --brand-500: oklch(0.62 0.18 220); + --brand-600: oklch(0.55 0.2 220); + + /* Secondary */ + --secondary: oklch(0.92 0.005 250); + --secondary-foreground: oklch(0.3 0.02 250); + + /* Muted */ + --muted: oklch(0.93 0.005 250); + --muted-foreground: oklch(0.5 0.015 250); + + /* Accent */ + --accent: oklch(0.94 0.005 250); + --accent-foreground: oklch(0.3 0.02 250); + + /* Destructive - red #d52753 */ + --destructive: oklch(0.5 0.22 15); + + /* Borders */ + --border: oklch(0.88 0.005 250); + --border-glass: oklch(0.3 0.02 250 / 0.1); + + --input: oklch(0.98 0.002 250); + --ring: oklch(0.62 0.18 220); + + /* Charts */ + --chart-1: oklch(0.62 0.18 220); + --chart-2: oklch(0.55 0.15 145); + --chart-3: oklch(0.5 0.22 15); + --chart-4: oklch(0.6 0.2 320); + --chart-5: oklch(0.7 0.15 85); + + /* Sidebar */ + --sidebar: oklch(0.93 0.005 250); + --sidebar-foreground: oklch(0.3 0.02 250); + --sidebar-primary: oklch(0.62 0.18 220); + --sidebar-primary-foreground: oklch(1 0 0); + --sidebar-accent: oklch(0.9 0.005 250); + --sidebar-accent-foreground: oklch(0.3 0.02 250); + --sidebar-border: oklch(0.88 0.005 250); + --sidebar-ring: oklch(0.62 0.18 220); + + /* Action buttons */ + --action-view: oklch(0.62 0.18 220); + --action-view-hover: oklch(0.55 0.2 220); + --action-followup: oklch(0.55 0.15 265); + --action-followup-hover: oklch(0.5 0.17 265); + --action-commit: oklch(0.55 0.15 145); + --action-commit-hover: oklch(0.5 0.17 145); + --action-verify: oklch(0.55 0.15 145); + --action-verify-hover: oklch(0.5 0.17 145); + + /* Running indicator */ + --running-indicator: oklch(0.62 0.18 220); + --running-indicator-text: oklch(0.55 0.2 220); + + /* Status colors */ + --status-success: oklch(0.55 0.15 145); + --status-success-bg: oklch(0.55 0.15 145 / 0.15); + --status-warning: oklch(0.7 0.15 85); + --status-warning-bg: oklch(0.7 0.15 85 / 0.15); + --status-error: oklch(0.5 0.22 15); + --status-error-bg: oklch(0.5 0.22 15 / 0.15); + --status-info: oklch(0.62 0.18 220); + --status-info-bg: oklch(0.62 0.18 220 / 0.15); + --status-backlog: oklch(0.5 0 0); + --status-in-progress: oklch(0.7 0.15 85); + --status-waiting: oklch(0.65 0.18 50); +} diff --git a/apps/ui/src/styles/themes/ember.css b/apps/ui/src/styles/themes/ember.css new file mode 100644 index 00000000..a7236ff0 --- /dev/null +++ b/apps/ui/src/styles/themes/ember.css @@ -0,0 +1,151 @@ +/* Ember Theme - Warm orange accent on dark background */ + +.ember { + /* Backgrounds - from bgMain #272822 */ + --background: oklch(0.2 0.02 100); /* #272822 */ + --background-50: oklch(0.2 0.02 100 / 0.5); + --background-80: oklch(0.2 0.02 100 / 0.8); + + /* Text - from textMain #f8f8f2, textDim #8f908a */ + --foreground: oklch(0.97 0.01 100); /* #f8f8f2 */ + --foreground-secondary: oklch(0.65 0.02 100); + --foreground-muted: oklch(0.6 0.02 100); /* #8f908a */ + + /* Card/Popover - derived from bgActivity #3e3d32 */ + --card: oklch(0.28 0.02 100); /* #3e3d32 */ + --card-foreground: oklch(0.97 0.01 100); + --popover: oklch(0.24 0.02 100); + --popover-foreground: oklch(0.97 0.01 100); + + /* Primary/Brand - from accent #fd971f (orange) */ + --primary: oklch(0.75 0.18 60); /* #fd971f */ + --primary-foreground: oklch(0.18 0.02 100); /* #1e1f1c */ + + --brand-400: oklch(0.8 0.16 60); /* #fdbf6f lighter */ + --brand-500: oklch(0.75 0.18 60); /* #fd971f */ + --brand-600: oklch(0.68 0.2 55); + + /* Secondary - from bgActivity #3e3d32 */ + --secondary: oklch(0.28 0.02 100); + --secondary-foreground: oklch(0.97 0.01 100); + + /* Muted */ + --muted: oklch(0.28 0.02 100); + --muted-foreground: oklch(0.6 0.02 100); + + /* Accent - from accentDim */ + --accent: oklch(0.32 0.04 80); + --accent-foreground: oklch(0.97 0.01 100); + + /* Destructive - from error #f92672 */ + --destructive: oklch(0.6 0.28 350); /* #f92672 */ + + /* Borders - from border #49483e */ + --border: oklch(0.35 0.03 90); /* #49483e */ + --border-glass: oklch(0.75 0.18 60 / 0.3); + + --input: oklch(0.24 0.02 100); + --ring: oklch(0.75 0.18 60); + + /* Charts - using theme colors */ + --chart-1: oklch(0.75 0.18 60); /* Orange accent */ + --chart-2: oklch(0.75 0.22 130); /* Green #a6e22e */ + --chart-3: oklch(0.85 0.15 95); /* Yellow #e6db74 */ + --chart-4: oklch(0.6 0.28 350); /* Pink #f92672 */ + --chart-5: oklch(0.7 0.2 200); /* Blue for variety */ + + /* Sidebar - from bgSidebar #1e1f1c */ + --sidebar: oklch(0.16 0.02 100); /* #1e1f1c */ + --sidebar-foreground: oklch(0.97 0.01 100); + --sidebar-primary: oklch(0.75 0.18 60); + --sidebar-primary-foreground: oklch(0.18 0.02 100); + --sidebar-accent: oklch(0.28 0.02 100); + --sidebar-accent-foreground: oklch(0.97 0.01 100); + --sidebar-border: oklch(0.35 0.03 90); + --sidebar-ring: oklch(0.75 0.18 60); + + /* Action buttons - orange/green theme */ + --action-view: oklch(0.75 0.18 60); /* Orange */ + --action-view-hover: oklch(0.7 0.2 55); + --action-followup: oklch(0.7 0.2 200); /* Blue */ + --action-followup-hover: oklch(0.65 0.22 200); + --action-commit: oklch(0.75 0.22 130); /* Green #a6e22e */ + --action-commit-hover: oklch(0.7 0.24 130); + --action-verify: oklch(0.75 0.22 130); + --action-verify-hover: oklch(0.7 0.24 130); + + /* Running indicator - Orange */ + --running-indicator: oklch(0.75 0.18 60); + --running-indicator-text: oklch(0.8 0.16 60); +} + +/* Theme-specific overrides */ + +.ember .animated-outline-gradient { + background: conic-gradient(from 90deg at 50% 50%, #fd971f 0%, #f92672 50%, #fd971f 100%); +} + +.ember .animated-outline-inner { + background: oklch(0.2 0.02 100) !important; + color: #fd971f !important; +} + +.ember [data-slot='button'][class*='animated-outline']:hover .animated-outline-inner { + background: oklch(0.28 0.02 100) !important; + color: #fdbf6f !important; +} + +.ember .slider-track { + background: oklch(0.28 0.02 100); +} + +.ember .slider-range { + background: linear-gradient(to right, #fd971f, #fdbf6f); +} + +.ember .slider-thumb { + background: oklch(0.2 0.02 100); + border-color: #fd971f; +} + +/* XML Syntax Highlighting */ +.ember .xml-highlight { + color: oklch(0.97 0.01 100); +} + +.ember .xml-tag-bracket { + color: oklch(0.6 0.28 350); /* Pink */ +} + +.ember .xml-tag-name { + color: oklch(0.6 0.28 350); /* Pink */ +} + +.ember .xml-attribute-name { + color: oklch(0.75 0.22 130); /* Green */ +} + +.ember .xml-attribute-equals { + color: oklch(0.97 0.01 100); +} + +.ember .xml-attribute-value { + color: oklch(0.85 0.15 95); /* Yellow */ +} + +.ember .xml-comment { + color: oklch(0.6 0.02 100); + font-style: italic; +} + +.ember .xml-cdata { + color: oklch(0.7 0.2 200); /* Blue */ +} + +.ember .xml-doctype { + color: oklch(0.75 0.18 60); /* Orange */ +} + +.ember .xml-text { + color: oklch(0.97 0.01 100); +} diff --git a/apps/ui/src/styles/themes/feather.css b/apps/ui/src/styles/themes/feather.css new file mode 100644 index 00000000..f0e3a054 --- /dev/null +++ b/apps/ui/src/styles/themes/feather.css @@ -0,0 +1,93 @@ +/* Feather Theme - Clean white with orange accent */ + +.feather { + /* Backgrounds */ + --background: oklch(1 0 0); + --background-50: oklch(1 0 0 / 0.5); + --background-80: oklch(1 0 0 / 0.8); + + /* Text - dark blue-gray #395063 */ + --foreground: oklch(0.38 0.03 230); + --foreground-secondary: oklch(0.5 0.025 230); + --foreground-muted: oklch(0.6 0.02 230); + + /* Card/Popover */ + --card: oklch(0.99 0.001 250); + --card-foreground: oklch(0.38 0.03 230); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.38 0.03 230); + + /* Primary/Brand - orange #FF7B2E */ + --primary: oklch(0.7 0.2 45); + --primary-foreground: oklch(1 0 0); + + --brand-400: oklch(0.75 0.18 45); + --brand-500: oklch(0.7 0.2 45); + --brand-600: oklch(0.62 0.22 40); + + /* Secondary */ + --secondary: oklch(0.95 0.003 250); + --secondary-foreground: oklch(0.38 0.03 230); + + /* Muted */ + --muted: oklch(0.96 0.003 250); + --muted-foreground: oklch(0.55 0.02 230); + + /* Accent */ + --accent: oklch(0.97 0.003 250); + --accent-foreground: oklch(0.38 0.03 230); + + /* Destructive - orange-red */ + --destructive: oklch(0.55 0.22 30); + + /* Borders */ + --border: oklch(0.9 0.003 250); + --border-glass: oklch(0.38 0.03 230 / 0.1); + + --input: oklch(1 0 0); + --ring: oklch(0.7 0.2 45); + + /* Charts */ + --chart-1: oklch(0.7 0.2 45); + --chart-2: oklch(0.6 0.15 175); + --chart-3: oklch(0.6 0.18 320); + --chart-4: oklch(0.55 0.15 220); + --chart-5: oklch(0.55 0.22 30); + + /* Sidebar */ + --sidebar: oklch(0.99 0.001 250); + --sidebar-foreground: oklch(0.38 0.03 230); + --sidebar-primary: oklch(0.7 0.2 45); + --sidebar-primary-foreground: oklch(1 0 0); + --sidebar-accent: oklch(0.96 0.003 250); + --sidebar-accent-foreground: oklch(0.38 0.03 230); + --sidebar-border: oklch(0.92 0.003 250); + --sidebar-ring: oklch(0.7 0.2 45); + + /* Action buttons */ + --action-view: oklch(0.7 0.2 45); + --action-view-hover: oklch(0.62 0.22 40); + --action-followup: oklch(0.55 0.15 220); + --action-followup-hover: oklch(0.5 0.17 220); + --action-commit: oklch(0.6 0.15 175); + --action-commit-hover: oklch(0.55 0.17 175); + --action-verify: oklch(0.6 0.15 175); + --action-verify-hover: oklch(0.55 0.17 175); + + /* Running indicator */ + --running-indicator: oklch(0.7 0.2 45); + --running-indicator-text: oklch(0.62 0.22 40); + + /* Status colors */ + --status-success: oklch(0.6 0.15 175); + --status-success-bg: oklch(0.6 0.15 175 / 0.15); + --status-warning: oklch(0.7 0.2 45); + --status-warning-bg: oklch(0.7 0.2 45 / 0.15); + --status-error: oklch(0.55 0.22 30); + --status-error-bg: oklch(0.55 0.22 30 / 0.15); + --status-info: oklch(0.55 0.15 220); + --status-info-bg: oklch(0.55 0.15 220 / 0.15); + --status-backlog: oklch(0.5 0 0); + --status-in-progress: oklch(0.7 0.2 45); + --status-waiting: oklch(0.65 0.18 50); +} diff --git a/apps/ui/src/styles/themes/matcha.css b/apps/ui/src/styles/themes/matcha.css new file mode 100644 index 00000000..ca47d5df --- /dev/null +++ b/apps/ui/src/styles/themes/matcha.css @@ -0,0 +1,103 @@ +/** + * Matcha Theme + * A calming dark theme with sage green and warm beige accents + * Inspired by: https://github.com/lucafalasco/matcha + */ + +.matcha { + /* Background layers - dark with subtle green undertone */ + --background: oklch(20% 0.015 145); + --background-secondary: oklch(24% 0.015 145); + --background-tertiary: oklch(16% 0.015 145); + --background-50: oklch(20% 0.015 145 / 0.5); + --background-80: oklch(20% 0.015 145 / 0.8); + + /* Foreground / text - light with green tint */ + --foreground: oklch(90% 0.025 145); + --foreground-secondary: oklch(70% 0.02 145); + --foreground-muted: oklch(60% 0.015 145); + + /* Brand / accent - sage green */ + --brand-400: oklch(72% 0.12 135); + --brand-500: oklch(68% 0.14 135); + --brand-600: oklch(62% 0.16 135); + --primary: oklch(68% 0.14 135); + --primary-foreground: oklch(15% 0.02 135); + + /* Sidebar - slightly lighter with green tint */ + --sidebar: oklch(22% 0.02 145); + --sidebar-foreground: oklch(90% 0.025 145); + --sidebar-primary: oklch(68% 0.14 135); + --sidebar-primary-foreground: oklch(15% 0.02 135); + --sidebar-accent: oklch(28% 0.025 145); + --sidebar-accent-foreground: oklch(90% 0.025 145); + --sidebar-border: oklch(30% 0.02 145); + --sidebar-ring: oklch(68% 0.14 135); + + /* Cards */ + --card: oklch(22% 0.02 145); + --card-foreground: oklch(90% 0.025 145); + + /* Inputs */ + --input: oklch(18% 0.015 145); + + /* Borders */ + --border: oklch(32% 0.025 145); + --border-glass: oklch(90% 0.02 145 / 0.1); + + /* Popover / Dropdown */ + --popover: oklch(20% 0.02 145); + --popover-foreground: oklch(90% 0.025 145); + + /* Secondary */ + --secondary: oklch(28% 0.025 145); + --secondary-foreground: oklch(90% 0.025 145); + + /* Muted */ + --muted: oklch(26% 0.02 145); + --muted-foreground: oklch(65% 0.02 145); + + /* Accent */ + --accent: oklch(30% 0.03 145); + --accent-foreground: oklch(90% 0.025 145); + + /* Destructive */ + --destructive: oklch(60% 0.18 25); + + /* Ring / Focus */ + --ring: oklch(68% 0.14 135); + + /* Action buttons - warm beige */ + --action-view: oklch(78% 0.06 90); + --action-view-hover: oklch(72% 0.07 90); + --action-followup: oklch(68% 0.14 135); + --action-followup-hover: oklch(62% 0.16 135); + --action-commit: oklch(68% 0.14 135); + --action-commit-hover: oklch(62% 0.16 135); + --action-verify: oklch(68% 0.14 135); + --action-verify-hover: oklch(62% 0.16 135); + + /* Running indicator - sage green */ + --running-indicator: oklch(68% 0.14 135); + --running-indicator-text: oklch(72% 0.12 135); + + /* Status colors */ + --status-success: oklch(68% 0.14 135); + --status-success-bg: oklch(68% 0.14 135 / 0.2); + --status-warning: oklch(78% 0.12 85); + --status-warning-bg: oklch(78% 0.12 85 / 0.2); + --status-error: oklch(65% 0.18 25); + --status-error-bg: oklch(65% 0.18 25 / 0.2); + --status-info: oklch(68% 0.14 135); + --status-info-bg: oklch(68% 0.14 135 / 0.2); + --status-backlog: oklch(55% 0 0); + --status-in-progress: oklch(78% 0.12 85); + --status-waiting: oklch(70% 0.15 55); + + /* Chart colors */ + --chart-1: oklch(68% 0.14 135); + --chart-2: oklch(70% 0.1 170); + --chart-3: oklch(78% 0.12 85); + --chart-4: oklch(65% 0.12 280); + --chart-5: oklch(60% 0.15 25); +} diff --git a/apps/ui/src/styles/themes/onelight.css b/apps/ui/src/styles/themes/onelight.css new file mode 100644 index 00000000..a8cc491b --- /dev/null +++ b/apps/ui/src/styles/themes/onelight.css @@ -0,0 +1,93 @@ +/* Atom One Light Theme - Clean light with blue accent */ + +.onelight { + /* Backgrounds */ + --background: oklch(0.98 0.002 250); + --background-50: oklch(0.98 0.002 250 / 0.5); + --background-80: oklch(0.98 0.002 250 / 0.8); + + /* Text */ + --foreground: oklch(0.3 0.02 250); + --foreground-secondary: oklch(0.45 0.02 250); + --foreground-muted: oklch(0.55 0.015 250); + + /* Card/Popover */ + --card: oklch(0.97 0.002 250); + --card-foreground: oklch(0.3 0.02 250); + --popover: oklch(0.98 0.002 250); + --popover-foreground: oklch(0.3 0.02 250); + + /* Primary/Brand - blue #526FFF */ + --primary: oklch(0.55 0.22 265); + --primary-foreground: oklch(1 0 0); + + --brand-400: oklch(0.6 0.2 265); + --brand-500: oklch(0.55 0.22 265); + --brand-600: oklch(0.5 0.24 265); + + /* Secondary */ + --secondary: oklch(0.92 0.005 250); + --secondary-foreground: oklch(0.3 0.02 250); + + /* Muted */ + --muted: oklch(0.93 0.005 250); + --muted-foreground: oklch(0.5 0.015 250); + + /* Accent */ + --accent: oklch(0.94 0.005 250); + --accent-foreground: oklch(0.3 0.02 250); + + /* Destructive - red #E45649 */ + --destructive: oklch(0.55 0.2 25); + + /* Borders */ + --border: oklch(0.88 0.005 250); + --border-glass: oklch(0.3 0.02 250 / 0.1); + + --input: oklch(0.98 0.002 250); + --ring: oklch(0.55 0.22 265); + + /* Charts */ + --chart-1: oklch(0.55 0.22 265); + --chart-2: oklch(0.55 0.15 145); + --chart-3: oklch(0.55 0.2 25); + --chart-4: oklch(0.55 0.18 320); + --chart-5: oklch(0.55 0.15 70); + + /* Sidebar */ + --sidebar: oklch(0.93 0.005 250); + --sidebar-foreground: oklch(0.3 0.02 250); + --sidebar-primary: oklch(0.55 0.22 265); + --sidebar-primary-foreground: oklch(1 0 0); + --sidebar-accent: oklch(0.9 0.005 250); + --sidebar-accent-foreground: oklch(0.3 0.02 250); + --sidebar-border: oklch(0.88 0.005 250); + --sidebar-ring: oklch(0.55 0.22 265); + + /* Action buttons */ + --action-view: oklch(0.55 0.22 265); + --action-view-hover: oklch(0.5 0.24 265); + --action-followup: oklch(0.55 0.18 230); + --action-followup-hover: oklch(0.5 0.2 230); + --action-commit: oklch(0.55 0.15 145); + --action-commit-hover: oklch(0.5 0.17 145); + --action-verify: oklch(0.55 0.15 145); + --action-verify-hover: oklch(0.5 0.17 145); + + /* Running indicator */ + --running-indicator: oklch(0.55 0.22 265); + --running-indicator-text: oklch(0.5 0.24 265); + + /* Status colors */ + --status-success: oklch(0.55 0.15 145); + --status-success-bg: oklch(0.55 0.15 145 / 0.15); + --status-warning: oklch(0.7 0.15 70); + --status-warning-bg: oklch(0.7 0.15 70 / 0.15); + --status-error: oklch(0.55 0.2 25); + --status-error-bg: oklch(0.55 0.2 25 / 0.15); + --status-info: oklch(0.55 0.22 265); + --status-info-bg: oklch(0.55 0.22 265 / 0.15); + --status-backlog: oklch(0.5 0 0); + --status-in-progress: oklch(0.7 0.15 70); + --status-waiting: oklch(0.65 0.18 50); +} diff --git a/libs/types/src/settings.ts b/libs/types/src/settings.ts index 06670cc6..1ccc2026 100644 --- a/libs/types/src/settings.ts +++ b/libs/types/src/settings.ts @@ -396,6 +396,10 @@ export interface ProjectRef { lastOpened?: string; /** Project-specific theme override (or undefined to use global) */ theme?: string; + /** Project-specific UI/sans font override (or undefined to use global) */ + fontFamilySans?: string; + /** Project-specific code/mono font override (or undefined to use global) */ + fontFamilyMono?: string; /** Whether project is pinned to favorites on dashboard */ isFavorite?: boolean; /** Lucide icon name for project identification */ diff --git a/package-lock.json b/package-lock.json index c542b728..bd5fbb43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,13 @@ "libs/*" ], "dependencies": { + "@fontsource/cascadia-code": "^5.2.3", + "@fontsource/iosevka": "^5.2.5", + "@fontsource/lato": "^5.2.7", + "@fontsource/montserrat": "^5.2.8", + "@fontsource/playfair-display": "^5.2.8", + "@fontsource/raleway": "^5.2.8", + "@fontsource/source-sans-3": "^5.2.9", "cross-spawn": "7.0.6", "rehype-sanitize": "6.0.0", "tree-kill": "1.2.2" @@ -93,6 +100,22 @@ "@dnd-kit/core": "6.3.1", "@dnd-kit/sortable": "10.0.0", "@dnd-kit/utilities": "3.2.2", + "@fontsource/cascadia-code": "^5.2.3", + "@fontsource/fira-code": "^5.2.7", + "@fontsource/ibm-plex-mono": "^5.2.7", + "@fontsource/inconsolata": "^5.2.8", + "@fontsource/inter": "^5.2.8", + "@fontsource/iosevka": "^5.2.5", + "@fontsource/jetbrains-mono": "^5.2.8", + "@fontsource/lato": "^5.2.7", + "@fontsource/montserrat": "^5.2.8", + "@fontsource/open-sans": "^5.2.7", + "@fontsource/poppins": "^5.2.7", + "@fontsource/raleway": "^5.2.8", + "@fontsource/roboto": "^5.2.9", + "@fontsource/source-code-pro": "^5.2.7", + "@fontsource/source-sans-3": "^5.2.9", + "@fontsource/work-sans": "^5.2.8", "@lezer/highlight": "1.2.3", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", @@ -1480,7 +1503,7 @@ }, "node_modules/@electron/node-gyp": { "version": "10.2.0-electron.1", - "resolved": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", "integrity": "sha512-4MSBTT8y07YUDqf69/vSh80Hh791epYqGtWHO3zSKhYFwQg+gx9wi1PqbqP6YqC4WMsNxZ5l9oDmnWdK5pfCKQ==", "dev": true, "license": "MIT", @@ -2814,6 +2837,159 @@ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", "license": "MIT" }, + "node_modules/@fontsource/cascadia-code": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@fontsource/cascadia-code/-/cascadia-code-5.2.3.tgz", + "integrity": "sha512-jnKfdZ+0UL25XUqYV7iq9gDlaUMWXxZNA7KnYzO4v8De6jBJmTkLHxDXs+1iZwl/8w7eKlDrTJWcyalOHdE2eg==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/fira-code": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@fontsource/fira-code/-/fira-code-5.2.7.tgz", + "integrity": "sha512-tnB9NNund9TwIym8/7DMJe573nlPEQb+fKUV5GL8TBYXjIhDvL0D7mgmNVNQUPhXp+R7RylQeiBdkA4EbOHPGQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/ibm-plex-mono": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.2.7.tgz", + "integrity": "sha512-MKAb8qV+CaiMQn2B0dIi1OV3565NYzp3WN5b4oT6LTkk+F0jR6j0ZN+5BKJiIhffDC3rtBULsYZE65+0018z9w==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/inconsolata": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/inconsolata/-/inconsolata-5.2.8.tgz", + "integrity": "sha512-lIZW+WOZYpUH91g9r6rYYhfTmptF3YPPM54ZOs8IYVeeL4SeiAu4tfj7mdr8llYEq31DLYgi6JtGIJa192gB0Q==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/inter": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.8.tgz", + "integrity": "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/iosevka": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/@fontsource/iosevka/-/iosevka-5.2.5.tgz", + "integrity": "sha512-Zv/UHJodDug1LcnWv2u2+GPp3oWP3U6Xp16cJOsqqZQNsCu8sA/ttT331N0NypxBZ+7c8szlSRlYDcy9liZ8pw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/jetbrains-mono": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/jetbrains-mono/-/jetbrains-mono-5.2.8.tgz", + "integrity": "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/lato": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@fontsource/lato/-/lato-5.2.7.tgz", + "integrity": "sha512-k5mum1ADbDW5cTw1Ett1eQVWeoZ6gq0ct6SFBibEhB4LRxhniChJZTBgd6ph5yBxLkN1fcnsnmicBNA4S/3nbw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/montserrat": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/montserrat/-/montserrat-5.2.8.tgz", + "integrity": "sha512-xTjLxSbSfCycDB0pwmNsfNvdfWPaDaRQ2LC6yt/ZI7SdvXG52zHnzNYC/09mzuAuWNJyShkteutfCoDgym56hQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/open-sans": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@fontsource/open-sans/-/open-sans-5.2.7.tgz", + "integrity": "sha512-8yfgDYjE5O0vmTPdrcjV35y4yMnctsokmi9gN49Gcsr0sjzkMkR97AnKDe6OqZh2SFkYlR28fxOvi21bYEgMSw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/playfair-display": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/playfair-display/-/playfair-display-5.2.8.tgz", + "integrity": "sha512-fUEhib70SszNhQVsGbUMSsWJhr7Je0rdeuZdtGpDNu0GKF1xJM8QhpI/y0pckU25GcChXm9TLOmeZupkvvZo2g==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/poppins": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@fontsource/poppins/-/poppins-5.2.7.tgz", + "integrity": "sha512-6uQyPmseo4FgI97WIhA4yWRlNaoLk4vSDK/PyRwdqqZb5zAEuc+Kunt8JTMcsHYUEGYBtN15SNkMajMdqUSUmg==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/raleway": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/raleway/-/raleway-5.2.8.tgz", + "integrity": "sha512-OATNM+J4XniKQRlI1e67v3euOnCUiXwpqgMofomKc5JNYOq0JPE6E5uTXMnFaZejKN1Z7s05JrHpmYB7/tg1Fw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/roboto": { + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.2.9.tgz", + "integrity": "sha512-ZTkyHiPk74B/aj8BZWbsxD5Yu+Lq+nR64eV4wirlrac2qXR7jYk2h6JlLYuOuoruTkGQWNw2fMuKNavw7/rg0w==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/source-code-pro": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@fontsource/source-code-pro/-/source-code-pro-5.2.7.tgz", + "integrity": "sha512-7papq9TH94KT+S5VSY8cU7tFmwuGkIe3qxXRMscuAXH6AjMU+KJI75f28FzgBVDrlMfA0jjlTV4/x5+H5o/5EQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/source-sans-3": { + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/@fontsource/source-sans-3/-/source-sans-3-5.2.9.tgz", + "integrity": "sha512-u3ymIq4rfmCCyB9MEw/sFR5lPVJ1yTNXmIMbUz+9kVCFIHvNtfzXOEBuvkg3Tk0zhmioPeJ28ZK5smZ7TurezQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, + "node_modules/@fontsource/work-sans": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@fontsource/work-sans/-/work-sans-5.2.8.tgz", + "integrity": "sha512-6LaHjVVgts+rnrcqvEkP2+iUB/jw1oDSYsGO0+TltAhnWki9Hnf/UGpgMQh2jcm0GEH8VqCPnq4PpmHLFzxXtQ==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", diff --git a/package.json b/package.json index 5418b71b..f550b786 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,13 @@ ] }, "dependencies": { + "@fontsource/cascadia-code": "^5.2.3", + "@fontsource/iosevka": "^5.2.5", + "@fontsource/lato": "^5.2.7", + "@fontsource/montserrat": "^5.2.8", + "@fontsource/playfair-display": "^5.2.8", + "@fontsource/raleway": "^5.2.8", + "@fontsource/source-sans-3": "^5.2.9", "cross-spawn": "7.0.6", "rehype-sanitize": "6.0.0", "tree-kill": "1.2.2"