diff --git a/apps/ui/package.json b/apps/ui/package.json index 2515632e..72755463 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 d9293df2..933521fd 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 @@ -1,8 +1,20 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Label } from '@/components/ui/label'; import { Checkbox } from '@/components/ui/checkbox'; -import { Palette, Moon, Sun } from 'lucide-react'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Palette, Moon, Sun, Type } from 'lucide-react'; import { darkThemes, lightThemes, type Theme } from '@/config/theme-options'; +import { + UI_SANS_FONT_OPTIONS, + UI_MONO_FONT_OPTIONS, + DEFAULT_FONT_VALUE, +} from '@/config/ui-font-options'; import { cn } from '@/lib/utils'; import { useAppStore } from '@/store/app-store'; import type { Project } from '@/lib/electron'; @@ -12,29 +24,125 @@ interface ProjectThemeSectionProps { } export function ProjectThemeSection({ project }: ProjectThemeSectionProps) { - const { theme: globalTheme, setProjectTheme } = useAppStore(); - const [activeTab, setActiveTab] = useState<'dark' | 'light'>('dark'); + const { + theme: globalTheme, + fontFamilySans: globalFontSans, + fontFamilyMono: globalFontMono, + setProjectTheme, + setProjectFontSans, + setProjectFontMono, + } = useAppStore(); + // 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): boolean => + !!font && UI_SANS_FONT_OPTIONS.some((opt) => opt.value === font); + const isValidMonoFont = (font?: string): boolean => + !!font && UI_MONO_FONT_OPTIONS.some((opt) => opt.value === font); + + // Helper to get initial font value with validation + const getInitialFontValue = (font: string | undefined, validator: (f?: string) => boolean) => + font && validator(font) ? font : DEFAULT_FONT_VALUE; + + // 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(() => + getInitialFontValue(project.fontFamilySans, isValidSansFont) + ); + const [fontMonoLocal, setFontMonoLocal] = useState(() => + getInitialFontValue(project.fontFamilyMono, isValidMonoFont) + ); + + // Sync state when project changes + useEffect(() => { + setFontSansLocal(getInitialFontValue(project.fontFamilySans, isValidSansFont)); + setFontMonoLocal(getInitialFontValue(project.fontFamilyMono, isValidMonoFont)); + // 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; + const themesToShow = activeTab === 'dark' ? darkThemes : lightThemes; + // Theme handlers const handleThemeChange = (theme: Theme) => { setProjectTheme(project.id, theme); }; const handleUseGlobalTheme = (checked: boolean) => { if (checked) { - // Clear project theme to use global setProjectTheme(project.id, null); } else { - // Set project theme to current global theme setProjectTheme(project.id, globalTheme); } }; + // Font handlers + const handleUseGlobalFontSans = (checked: boolean) => { + if (checked) { + // Clear project font to use global + setProjectFontSans(project.id, null); + setFontSansLocal(DEFAULT_FONT_VALUE); + } else { + // Set explicit project override - use 'default' value to indicate explicit default choice + const fontToSet = globalFontSans || DEFAULT_FONT_VALUE; + setFontSansLocal(fontToSet); + // Store the actual value (including 'default') so hasCustomFontSans stays true + setProjectFontSans(project.id, fontToSet); + } + }; + + const handleUseGlobalFontMono = (checked: boolean) => { + if (checked) { + // Clear project font to use global + setProjectFontMono(project.id, null); + setFontMonoLocal(DEFAULT_FONT_VALUE); + } else { + // Set explicit project override - use 'default' value to indicate explicit default choice + const fontToSet = globalFontMono || DEFAULT_FONT_VALUE; + setFontMonoLocal(fontToSet); + // Store the actual value (including 'default') so hasCustomFontMono stays true + setProjectFontMono(project.id, fontToSet); + } + }; + + const handleFontSansChange = (value: string) => { + setFontSansLocal(value); + // Store the actual value (including 'default') - only null clears to use global + setProjectFontSans(project.id, value); + }; + + const handleFontMonoChange = (value: string) => { + setFontMonoLocal(value); + // Store the actual value (including 'default') - only null clears to use global + setProjectFontMono(project.id, value); + }; + + // Get display label for global font + const getGlobalFontSansLabel = () => { + if (!globalFontSans) return 'Default (Geist Sans)'; + const option = UI_SANS_FONT_OPTIONS.find((o) => o.value === globalFontSans); + return option?.label || globalFontSans; + }; + + const getGlobalFontMonoLabel = () => { + if (!globalFontMono) return 'Default (Geist Mono)'; + const option = UI_MONO_FONT_OPTIONS.find((o) => o.value === globalFontMono); + return option?.label || globalFontMono; + }; + return (
-

Theme

+

Theme & Fonts

- Customize the theme for this project. + Customize the appearance for this project.

@@ -158,6 +266,122 @@ export function ProjectThemeSection({ project }: ProjectThemeSectionProps) {

)} + + {/* Fonts Section */} +
+
+ + +
+ +
+ {/* UI Font */} +
+
+ +
+ + {!hasCustomFontSans && ( +

+ Currently using:{' '} + {getGlobalFontSansLabel()} +

+ )} +
+
+ + {hasCustomFontSans && ( +
+ + +
+ )} +
+ + {/* Code Font */} +
+
+ +
+ + {!hasCustomFontMono && ( +

+ Currently using:{' '} + {getGlobalFontMonoLabel()} +

+ )} +
+
+ + {hasCustomFontMono && ( +
+ + +
+ )} +
+
+
); 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 47646287..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,8 +1,21 @@ -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { Label } from '@/components/ui/label'; -import { Palette, Moon, Sun } from 'lucide-react'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Palette, Moon, Sun, Type } from 'lucide-react'; import { darkThemes, lightThemes } from '@/config/theme-options'; +import { + UI_SANS_FONT_OPTIONS, + UI_MONO_FONT_OPTIONS, + DEFAULT_FONT_VALUE, +} from '@/config/ui-font-options'; import { cn } from '@/lib/utils'; +import { useAppStore } from '@/store/app-store'; import type { Theme } from '../shared/types'; interface AppearanceSectionProps { @@ -11,10 +24,43 @@ 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 + // 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); + }; + + const handleFontMonoChange = (value: string) => { + setFontMono(value === DEFAULT_FONT_VALUE ? null : value); + }; + return (
+ + {/* Fonts Section */} +
+
+ + +
+

+ Set default fonts for all projects. Individual projects can override these settings. +

+ +
+ {/* UI Font Selector */} +
+ + +

+ Used for headings, labels, and UI text +

+
+ + {/* Code Font Selector */} +
+ + +

+ Used for code blocks and monospaced text +

+
+
+
); diff --git a/apps/ui/src/components/views/settings-view/shared/types.ts b/apps/ui/src/components/views/settings-view/shared/types.ts index 9bb2cd48..b0bdb977 100644 --- a/apps/ui/src/components/views/settings-view/shared/types.ts +++ b/apps/ui/src/components/views/settings-view/shared/types.ts @@ -26,6 +26,8 @@ export interface Project { name: string; path: string; theme?: string; + fontFamilySans?: string; + fontFamilyMono?: string; icon?: string; customIconPath?: string; } diff --git a/apps/ui/src/components/views/settings-view/terminal/terminal-section.tsx b/apps/ui/src/components/views/settings-view/terminal/terminal-section.tsx index 154ebcef..f1cebb10 100644 --- a/apps/ui/src/components/views/settings-view/terminal/terminal-section.tsx +++ b/apps/ui/src/components/views/settings-view/terminal/terminal-section.tsx @@ -2,11 +2,19 @@ import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import { Slider } from '@/components/ui/slider'; import { Input } from '@/components/ui/input'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; import { SquareTerminal } from 'lucide-react'; import { cn } from '@/lib/utils'; import { useAppStore } from '@/store/app-store'; import { toast } from 'sonner'; import { TERMINAL_FONT_OPTIONS } from '@/config/terminal-themes'; +import { DEFAULT_FONT_VALUE } from '@/config/ui-font-options'; export function TerminalSection() { const { @@ -53,27 +61,32 @@ export function TerminalSection() { {/* Font Family */}
- { + setTerminalFontFamily(value); toast.info('Font family changed', { description: 'Restart terminal for changes to take effect', }); }} - className={cn( - 'w-full px-3 py-2 rounded-lg', - 'bg-accent/30 border border-border/50', - 'text-foreground text-sm', - 'focus:outline-none focus:ring-2 focus:ring-green-500/30' - )} > - {TERMINAL_FONT_OPTIONS.map((font) => ( - - ))} - + + + + + {TERMINAL_FONT_OPTIONS.map((option) => ( + + + {option.label} + + + ))} + +
{/* Default Font Size */} diff --git a/apps/ui/src/components/views/terminal-view.tsx b/apps/ui/src/components/views/terminal-view.tsx index 0cad0408..328afc21 100644 --- a/apps/ui/src/components/views/terminal-view.tsx +++ b/apps/ui/src/components/views/terminal-view.tsx @@ -25,8 +25,17 @@ import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Slider } from '@/components/ui/slider'; +import { Switch } from '@/components/ui/switch'; import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; import { TERMINAL_FONT_OPTIONS } from '@/config/terminal-themes'; +import { DEFAULT_FONT_VALUE } from '@/config/ui-font-options'; import { toast } from 'sonner'; import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import { TerminalPanel } from './terminal-view/terminal-panel'; @@ -232,6 +241,8 @@ export function TerminalView() { setTerminalDefaultRunScript, setTerminalFontFamily, setTerminalLineHeight, + setTerminalScrollbackLines, + setTerminalScreenReaderMode, updateTerminalPanelSizes, } = useAppStore(); @@ -1457,9 +1468,9 @@ export function TerminalView() { - +
-
+

Terminal Settings

Configure global terminal appearance @@ -1469,15 +1480,15 @@ export function TerminalView() { {/* Default Font Size */}

- - + + {terminalState.defaultFontSize}px
setTerminalDefaultFontSize(value)} onValueCommit={() => { @@ -1488,37 +1499,79 @@ export function TerminalView() { />
+ {/* Default Run Script */} +
+ + setTerminalDefaultRunScript(e.target.value)} + placeholder="e.g., claude" + className="h-7 text-xs" + /> +

+ Command to run when creating a new terminal +

+
+ {/* Font Family */}
- - { + setTerminalFontFamily(value); toast.info('Font family changed', { description: 'Restart terminal for changes to take effect', }); }} - className={cn( - 'w-full px-2 py-1.5 rounded-md text-sm', - 'bg-accent/50 border border-border', - 'text-foreground', - 'focus:outline-none focus:ring-2 focus:ring-ring' - )} > - {TERMINAL_FONT_OPTIONS.map((font) => ( - - ))} - + + + + + {TERMINAL_FONT_OPTIONS.map((option) => ( + + + {option.label} + + + ))} + + +
+ + {/* Scrollback */} +
+
+ + + {(terminalState.scrollbackLines / 1000).toFixed(0)}k lines + +
+ setTerminalScrollbackLines(value)} + onValueCommit={() => { + toast.info('Scrollback changed', { + description: 'Restart terminal for changes to take effect', + }); + }} + />
{/* Line Height */}
- - + + {terminalState.lineHeight.toFixed(1)}
@@ -1536,18 +1589,21 @@ export function TerminalView() { />
- {/* Default Run Script */} -
- - setTerminalDefaultRunScript(e.target.value)} - placeholder="e.g., claude, npm run dev" - className="h-8 text-sm" + {/* Screen Reader */} +
+
+ +

Enable accessibility mode

+
+ { + setTerminalScreenReaderMode(checked); + toast.info(checked ? 'Screen reader enabled' : 'Screen reader disabled', { + description: 'Restart terminal for changes to take effect', + }); + }} /> -

- Command to run when opening new terminals -

diff --git a/apps/ui/src/components/views/terminal-view/terminal-panel.tsx b/apps/ui/src/components/views/terminal-view/terminal-panel.tsx index 24a91f46..481ee6b4 100644 --- a/apps/ui/src/components/views/terminal-view/terminal-panel.tsx +++ b/apps/ui/src/components/views/terminal-view/terminal-panel.tsx @@ -30,6 +30,13 @@ import { Slider } from '@/components/ui/slider'; import { Label } from '@/components/ui/label'; import { Input } from '@/components/ui/input'; import { Switch } from '@/components/ui/switch'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; import { useDraggable, useDroppable } from '@dnd-kit/core'; import { useAppStore, DEFAULT_KEYBOARD_SHORTCUTS, type KeyboardShortcuts } from '@/store/app-store'; import { useShallow } from 'zustand/react/shallow'; @@ -38,7 +45,9 @@ import { getTerminalTheme, TERMINAL_FONT_OPTIONS, DEFAULT_TERMINAL_FONT, + getTerminalFontFamily, } from '@/config/terminal-themes'; +import { DEFAULT_FONT_VALUE } from '@/config/ui-font-options'; import { toast } from 'sonner'; import { getElectronAPI } from '@/lib/electron'; import { getApiKey, getSessionToken, getServerUrlSync } from '@/lib/http-api-client'; @@ -567,7 +576,7 @@ export function TerminalPanel({ // Get settings from store (read at initialization time) const terminalSettings = useAppStore.getState().terminalState; const screenReaderEnabled = terminalSettings.screenReaderMode; - const terminalFontFamily = terminalSettings.fontFamily || DEFAULT_TERMINAL_FONT; + const terminalFontFamily = getTerminalFontFamily(terminalSettings.fontFamily); const terminalScrollback = terminalSettings.scrollbackLines || 5000; const terminalLineHeight = terminalSettings.lineHeight || 1.0; @@ -1269,7 +1278,7 @@ export function TerminalPanel({ useEffect(() => { if (xtermRef.current && isTerminalReady) { - xtermRef.current.options.fontFamily = fontFamily; + xtermRef.current.options.fontFamily = getTerminalFontFamily(fontFamily); fitAddonRef.current?.fit(); } }, [fontFamily, isTerminalReady]); @@ -1902,22 +1911,33 @@ export function TerminalPanel({
- { + setTerminalFontFamily(value); toast.info('Font family changed', { description: 'Restart terminal for changes to take effect', }); }} - className="w-full h-7 text-xs bg-background border border-input rounded-md px-2" > - {TERMINAL_FONT_OPTIONS.map((font) => ( - - ))} - + + + + + {TERMINAL_FONT_OPTIONS.map((option) => ( + + + {option.label} + + + ))} + +
diff --git a/apps/ui/src/config/terminal-themes.ts b/apps/ui/src/config/terminal-themes.ts index f53d8f2f..4573bc01 100644 --- a/apps/ui/src/config/terminal-themes.ts +++ b/apps/ui/src/config/terminal-themes.ts @@ -4,6 +4,11 @@ */ import type { ThemeMode } from '@/store/app-store'; +import { + UI_MONO_FONT_OPTIONS, + DEFAULT_FONT_VALUE, + type UIFontOption, +} from '@/config/ui-font-options'; export interface TerminalTheme { background: string; @@ -37,27 +42,44 @@ export interface TerminalTheme { /** * Terminal font options for user selection - * These are monospace fonts commonly available on different platforms + * + * Uses the same fonts as UI_MONO_FONT_OPTIONS for consistency across the app. + * All fonts listed here are bundled with the app via @fontsource packages + * or are system fonts with appropriate fallbacks. */ -export interface TerminalFontOption { - value: string; - label: string; -} -export const TERMINAL_FONT_OPTIONS: TerminalFontOption[] = [ - { value: "Menlo, Monaco, 'Courier New', monospace", label: 'Menlo / Monaco' }, - { value: "'SF Mono', Menlo, Monaco, monospace", label: 'SF Mono' }, - { value: "'JetBrains Mono', monospace", label: 'JetBrains Mono' }, - { value: "'Fira Code', monospace", label: 'Fira Code' }, - { value: "'Source Code Pro', monospace", label: 'Source Code Pro' }, - { value: "Consolas, 'Courier New', monospace", label: 'Consolas' }, - { value: "'Ubuntu Mono', monospace", label: 'Ubuntu Mono' }, -]; +// Re-export for backwards compatibility +export type TerminalFontOption = UIFontOption; /** - * Default terminal font family (first option) + * Terminal font options - reuses UI_MONO_FONT_OPTIONS with terminal-specific default + * + * The 'default' value means "use the default terminal font" (Menlo/Monaco) */ -export const DEFAULT_TERMINAL_FONT = TERMINAL_FONT_OPTIONS[0].value; +export const TERMINAL_FONT_OPTIONS: readonly UIFontOption[] = UI_MONO_FONT_OPTIONS.map((option) => { + // Replace the UI default label with terminal-specific default + if (option.value === DEFAULT_FONT_VALUE) { + return { value: option.value, label: 'Default (Menlo / Monaco)' }; + } + return option; +}); + +/** + * Default terminal font family + * Uses the DEFAULT_FONT_VALUE sentinel which maps to Menlo/Monaco + */ +export const DEFAULT_TERMINAL_FONT = DEFAULT_FONT_VALUE; + +/** + * Get the actual font family CSS value for terminal + * Converts DEFAULT_FONT_VALUE to the actual Menlo/Monaco font stack + */ +export function getTerminalFontFamily(fontValue: string | undefined): string { + if (!fontValue || fontValue === DEFAULT_FONT_VALUE) { + return "Menlo, Monaco, 'Courier New', monospace"; + } + return fontValue; +} // Dark theme (default) const darkTheme: TerminalTheme = { @@ -542,9 +564,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 +579,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 new file mode 100644 index 00000000..5a1309a9 --- /dev/null +++ b/apps/ui/src/config/ui-font-options.ts @@ -0,0 +1,77 @@ +/** + * Font options for per-project font customization + * + * 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 +export const DEFAULT_FONT_VALUE = 'default'; + +export interface UIFontOption { + value: string; // CSS font-family value ('default' means "use default") + label: string; // Display label for the dropdown +} + +/** + * 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)' }, + // 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: "'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 (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)' }, + // 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; + +/** + * Get the display label for a font value + */ +export function getFontLabel( + fontValue: string | undefined, + options: readonly UIFontOption[] +): string { + if (!fontValue || fontValue === DEFAULT_FONT_VALUE) return options[0].label; + const option = options.find((o) => o.value === fontValue); + return option?.label ?? fontValue; +} diff --git a/apps/ui/src/hooks/use-settings-migration.ts b/apps/ui/src/hooks/use-settings-migration.ts index 79135bb2..07119b85 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, @@ -556,6 +558,8 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void { useAppStore.setState({ theme: settings.theme as unknown as import('@/store/app-store').ThemeMode, + fontFamilySans: settings.fontFamilySans ?? null, + fontFamilyMono: settings.fontFamilyMono ?? null, sidebarOpen: settings.sidebarOpen ?? true, chatHistoryOpen: settings.chatHistoryOpen ?? false, maxConcurrency: settings.maxConcurrency ?? 3, @@ -596,6 +600,13 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void { worktreePanelCollapsed: settings.worktreePanelCollapsed ?? false, lastProjectDir: settings.lastProjectDir ?? '', recentFolders: settings.recentFolders ?? [], + // Terminal font (nested in terminalState) + ...(settings.terminalFontFamily && { + terminalState: { + ...current.terminalState, + fontFamily: settings.terminalFontFamily, + }, + }), }); // Hydrate setup wizard state from global settings (API-backed) @@ -649,6 +660,7 @@ function buildSettingsUpdateFromStore(): Record { worktreePanelCollapsed: state.worktreePanelCollapsed, lastProjectDir: state.lastProjectDir, recentFolders: state.recentFolders, + terminalFontFamily: state.terminalState.fontFamily, }; } diff --git a/apps/ui/src/hooks/use-settings-sync.ts b/apps/ui/src/hooks/use-settings-sync.ts index ca6c62c7..c9430684 100644 --- a/apps/ui/src/hooks/use-settings-sync.ts +++ b/apps/ui/src/hooks/use-settings-sync.ts @@ -33,6 +33,9 @@ const SYNC_DEBOUNCE_MS = 1000; // Fields to sync to server (subset of AppState that should be persisted) const SETTINGS_FIELDS_TO_SYNC = [ 'theme', + 'fontFamilySans', + 'fontFamilyMono', + 'terminalFontFamily', // Maps to terminalState.fontFamily 'sidebarOpen', 'chatHistoryOpen', 'maxConcurrency', @@ -157,6 +160,9 @@ export function useSettingsSync(): SettingsSyncState { if (field === 'currentProjectId') { // Special handling: extract ID from currentProject object updates[field] = appState.currentProject?.id ?? null; + } else if (field === 'terminalFontFamily') { + // Special handling: map terminalState.fontFamily to terminalFontFamily + updates[field] = appState.terminalState.fontFamily; } else { updates[field] = appState[field as keyof typeof appState]; } @@ -258,6 +264,8 @@ export function useSettingsSync(): SettingsSyncState { for (const field of SETTINGS_FIELDS_TO_SYNC) { if (field === 'currentProjectId') { updates[field] = appState.currentProject?.id ?? null; + } else if (field === 'terminalFontFamily') { + updates[field] = appState.terminalState.fontFamily; } else { updates[field] = appState[field as keyof typeof appState]; } @@ -320,6 +328,12 @@ export function useSettingsSync(): SettingsSyncState { changed = true; break; } + } else if (field === 'terminalFontFamily') { + // Special handling: compare terminalState.fontFamily + if (newState.terminalState.fontFamily !== prevState.terminalState.fontFamily) { + changed = true; + break; + } } else { const key = field as keyof typeof newState; if (newState[key] !== prevState[key]) { @@ -401,6 +415,8 @@ export async function forceSyncSettingsToServer(): Promise { for (const field of SETTINGS_FIELDS_TO_SYNC) { if (field === 'currentProjectId') { updates[field] = appState.currentProject?.id ?? null; + } else if (field === 'terminalFontFamily') { + updates[field] = appState.terminalState.fontFamily; } else { updates[field] = appState[field as keyof typeof appState]; } @@ -503,6 +519,13 @@ export async function refreshSettingsFromServer(): Promise { worktreePanelCollapsed: serverSettings.worktreePanelCollapsed ?? false, lastProjectDir: serverSettings.lastProjectDir ?? '', recentFolders: serverSettings.recentFolders ?? [], + // Terminal font (nested in terminalState) + ...(serverSettings.terminalFontFamily && { + terminalState: { + ...currentAppState.terminalState, + fontFamily: serverSettings.terminalFontFamily, + }, + }), }); // Also refresh setup wizard state diff --git a/apps/ui/src/lib/electron.ts b/apps/ui/src/lib/electron.ts index 66bbd537..f6eb6f2e 100644 --- a/apps/ui/src/lib/electron.ts +++ b/apps/ui/src/lib/electron.ts @@ -3292,6 +3292,8 @@ export interface Project { path: string; lastOpened?: string; theme?: string; // Per-project theme override (uses ThemeMode from app-store) + fontFamilySans?: string; // Per-project UI/sans font override + fontFamilyMono?: string; // Per-project code/mono font override isFavorite?: boolean; // Pin project to top of dashboard icon?: string; // Lucide icon name for project identification customIconPath?: string; // Path to custom uploaded icon image in .automaker/images/ diff --git a/apps/ui/src/routes/__root.tsx b/apps/ui/src/routes/__root.tsx index f3a662e8..ec09be70 100644 --- a/apps/ui/src/routes/__root.tsx +++ b/apps/ui/src/routes/__root.tsx @@ -158,6 +158,12 @@ function RootLayoutContent() { projectHistory, upsertAndSetCurrentProject, getEffectiveTheme, + getEffectiveFontSans, + getEffectiveFontMono, + // Subscribe to theme and font state to trigger re-renders when they change + theme, + fontFamilySans, + fontFamilyMono, skipSandboxWarning, setSkipSandboxWarning, fetchCodexModels, @@ -248,6 +254,17 @@ function RootLayoutContent() { // Defer the theme value to keep UI responsive during rapid hover changes const deferredTheme = useDeferredValue(effectiveTheme); + // 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(); + useEffect(() => { setIsMounted(true); }, []); @@ -727,6 +744,23 @@ function RootLayoutContent() { } }, [deferredTheme]); + // Apply font CSS variables for project-specific font overrides + useEffect(() => { + const root = document.documentElement; + + if (effectiveFontSans) { + root.style.setProperty('--font-sans', effectiveFontSans); + } else { + root.style.removeProperty('--font-sans'); + } + + if (effectiveFontMono) { + root.style.setProperty('--font-mono', effectiveFontMono); + } else { + root.style.removeProperty('--font-mono'); + } + }, [effectiveFontSans, effectiveFontMono]); + // Show sandbox rejection screen if user denied the risk warning if (sandboxStatus === 'denied') { return ; diff --git a/apps/ui/src/store/app-store.ts b/apps/ui/src/store/app-store.ts index b4fbb89b..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; @@ -510,6 +578,10 @@ export interface AppState { // Theme theme: ThemeMode; + // Fonts (global defaults) + fontFamilySans: string | null; // null = use default Geist Sans + fontFamilyMono: string | null; // null = use default Geist Mono + // Features/Kanban features: Feature[]; @@ -920,6 +992,14 @@ export interface AppActions { getEffectiveTheme: () => ThemeMode; // Get the effective theme (project, global, or preview if set) setPreviewTheme: (theme: ThemeMode | null) => void; // Set preview theme for hover preview (null to clear) + // Font actions (global + per-project override) + setFontSans: (fontFamily: string | null) => void; // Set global UI/sans font (null to clear) + setFontMono: (fontFamily: string | null) => void; // Set global code/mono font (null to clear) + setProjectFontSans: (projectId: string, fontFamily: string | null) => void; // Set per-project UI/sans font override (null = use global) + setProjectFontMono: (projectId: string, fontFamily: string | null) => void; // Set per-project code/mono font override (null = use global) + getEffectiveFontSans: () => string | null; // Get effective UI font (project override -> global -> null for default) + getEffectiveFontMono: () => string | null; // Get effective code font (project override -> global -> null for default) + // Feature actions setFeatures: (features: Feature[]) => void; updateFeature: (id: string, updates: Partial) => void; @@ -1258,6 +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: 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, @@ -1733,6 +1815,103 @@ export const useAppStore = create()((set, get) => ({ setPreviewTheme: (theme) => set({ previewTheme: theme }), + // Font actions (global + per-project override) + setFontSans: (fontFamily) => { + // Save to localStorage for fallback when server settings aren't available + saveFontSansToStorage(fontFamily); + set({ fontFamilySans: 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 } + : p + ); + set({ projects }); + + // Also update currentProject if it's the same project + const currentProject = get().currentProject; + if (currentProject?.id === projectId) { + set({ + currentProject: { + ...currentProject, + fontFamilySans: fontFamily === null ? undefined : fontFamily, + }, + }); + } + }, + + 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 } + : p + ); + set({ projects }); + + // Also update currentProject if it's the same project + const currentProject = get().currentProject; + if (currentProject?.id === projectId) { + set({ + currentProject: { + ...currentProject, + fontFamilyMono: fontFamily === null ? undefined : fontFamily, + }, + }); + } + }, + + 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) { + const font = currentProject.fontFamilySans; + if (!isValidFont(font)) return null; // Fallback to default if font not in list + return font === DEFAULT_FONT_VALUE ? null : font; + } + 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) { + const font = currentProject.fontFamilyMono; + if (!isValidFont(font)) return null; // Fallback to default if font not in list + return font === DEFAULT_FONT_VALUE ? null : font; + } + 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 setFeatures: (features) => set({ features }), 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..a8a6e53a 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 */ @@ -863,6 +880,11 @@ background: var(--muted-foreground); } +/* Terminal padding for better readability */ +.xterm { + padding: 12px 16px; +} + /* ======================================== DEPENDENCY GRAPH STYLES Theme-aware styling for React Flow graph 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 844abf1e..a48504a8 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 */ @@ -463,6 +467,14 @@ export interface GlobalSettings { /** Currently selected theme */ theme: ThemeMode; + // Font Configuration + /** Global UI/Sans font family (undefined = use default Geist Sans) */ + fontFamilySans?: string; + /** Global Code/Mono font family (undefined = use default Geist Mono) */ + fontFamilyMono?: string; + /** Terminal font family (undefined = use default Menlo/Monaco) */ + terminalFontFamily?: string; + // UI State Preferences /** Whether sidebar is currently open */ sidebarOpen: boolean; @@ -718,6 +730,12 @@ export interface ProjectSettings { /** Project theme (undefined = use global setting) */ theme?: ThemeMode; + // Font Configuration (project-specific override) + /** UI/Sans font family override (undefined = use default Geist Sans) */ + fontFamilySans?: string; + /** Code/Mono font family override (undefined = use default Geist Mono) */ + fontFamilyMono?: string; + // Worktree Management /** Project-specific worktree preference override */ useWorktrees?: boolean; diff --git a/package-lock.json b/package-lock.json index d40c4f73..8fc7b149 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "automaker", - "version": "1.0.0", + "version": "0.12.0rc", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "automaker", - "version": "1.0.0", + "version": "0.12.0rc", "hasInstallScript": true, "workspaces": [ "apps/*", @@ -30,7 +30,7 @@ }, "apps/server": { "name": "@automaker/server", - "version": "0.11.0", + "version": "0.12.0", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@anthropic-ai/claude-agent-sdk": "0.1.76", @@ -81,7 +81,7 @@ }, "apps/ui": { "name": "@automaker/ui", - "version": "0.11.0", + "version": "0.12.0", "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", "dependencies": { @@ -94,6 +94,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", @@ -2128,76 +2144,11 @@ "node": ">= 10.0.0" } }, - "node_modules/@electron/windows-sign": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", - "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "cross-dirname": "^0.1.0", - "debug": "^4.3.4", - "fs-extra": "^11.1.1", - "minimist": "^1.2.8", - "postject": "^1.0.0-alpha.6" - }, - "bin": { - "electron-windows-sign": "bin/electron-windows-sign.js" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/@electron/windows-sign/node_modules/fs-extra": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", - "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/@electron/windows-sign/node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/@electron/windows-sign/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/@emnapi/runtime": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2880,6 +2831,150 @@ "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/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", @@ -2951,17 +3046,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@img/colour": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", - "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", @@ -3070,57 +3154,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linux-x64": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", @@ -3213,75 +3246,6 @@ "@img/sharp-libvips-linux-arm64": "1.0.4" } }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, "node_modules/@img/sharp-linux-x64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", @@ -3348,66 +3312,6 @@ "@img/sharp-libvips-linuxmusl-x64": "1.0.4" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-win32-x64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", @@ -3796,149 +3700,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, - "node_modules/@next/env": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz", - "integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==", - "license": "MIT", - "peer": true - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.0.10.tgz", - "integrity": "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.10.tgz", - "integrity": "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.10.tgz", - "integrity": "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.10.tgz", - "integrity": "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.10.tgz", - "integrity": "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.10.tgz", - "integrity": "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.10.tgz", - "integrity": "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.10.tgz", - "integrity": "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/@npmcli/agent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", @@ -4032,7 +3793,7 @@ "version": "1.57.0", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "playwright": "1.57.0" @@ -5500,16 +5261,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "tslib": "^2.8.0" - } - }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -6405,6 +6156,7 @@ "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -6414,7 +6166,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "devOptional": true, + "dev": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -8071,6 +7823,7 @@ "version": "1.0.30001760", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -8293,13 +8046,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", - "license": "MIT", - "peer": true - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -8595,15 +8341,6 @@ "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", "license": "MIT" }, - "node_modules/cross-dirname": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cross-dirname/-/cross-dirname-0.1.0.tgz", - "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/cross-env": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", @@ -8640,6 +8377,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, "license": "MIT" }, "node_modules/d3-color": { @@ -8916,7 +8654,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -8950,9 +8688,9 @@ } }, "node_modules/diff": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", - "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", + "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -9205,19 +8943,6 @@ "node": ">=14.0.0" } }, - "node_modules/electron-builder-squirrel-windows": { - "version": "26.0.12", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-26.0.12.tgz", - "integrity": "sha512-kpwXM7c/ayRUbYVErQbsZ0nQZX4aLHQrPEG9C4h9vuJCXylwFH8a7Jgi2VpKIObzCXO7LKHiCw4KdioFLFOgqA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "app-builder-lib": "26.0.12", - "builder-util": "26.0.11", - "electron-winstaller": "5.4.0" - } - }, "node_modules/electron-builder/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -9318,44 +9043,6 @@ "dev": true, "license": "ISC" }, - "node_modules/electron-winstaller": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/electron-winstaller/-/electron-winstaller-5.4.0.tgz", - "integrity": "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@electron/asar": "^3.2.1", - "debug": "^4.1.1", - "fs-extra": "^7.0.1", - "lodash": "^4.17.21", - "temp": "^0.9.0" - }, - "engines": { - "node": ">=8.0.0" - }, - "optionalDependencies": { - "@electron/windows-sign": "^1.1.2" - } - }, - "node_modules/electron-winstaller/node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, "node_modules/electron/node_modules/@types/node": { "version": "22.19.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", @@ -10802,16 +10489,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hono": { - "version": "4.11.4", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.4.tgz", - "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=16.9.0" - } - }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -11576,7 +11253,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11642,7 +11318,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13354,6 +13029,7 @@ "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, "funding": [ { "type": "github", @@ -13384,59 +13060,6 @@ "node": ">= 0.6" } }, - "node_modules/next": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/next/-/next-16.0.10.tgz", - "integrity": "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==", - "license": "MIT", - "peer": true, - "dependencies": { - "@next/env": "16.0.10", - "@swc/helpers": "0.5.15", - "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", - "styled-jsx": "5.1.6" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=20.9.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "16.0.10", - "@next/swc-darwin-x64": "16.0.10", - "@next/swc-linux-arm64-gnu": "16.0.10", - "@next/swc-linux-arm64-musl": "16.0.10", - "@next/swc-linux-x64-gnu": "16.0.10", - "@next/swc-linux-x64-musl": "16.0.10", - "@next/swc-win32-arm64-msvc": "16.0.10", - "@next/swc-win32-x64-msvc": "16.0.10", - "sharp": "^0.34.4" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "@playwright/test": "^1.51.1", - "babel-plugin-react-compiler": "*", - "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@playwright/test": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, "node_modules/node-abi": { "version": "4.24.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.24.0.tgz", @@ -13965,6 +13588,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -14006,7 +13630,7 @@ "version": "1.57.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "playwright-core": "1.57.0" @@ -14025,7 +13649,7 @@ "version": "1.57.0", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -14064,65 +13688,6 @@ "node": ">=10.4.0" } }, - "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postject": { - "version": "1.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", - "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "commander": "^9.4.0" - }, - "bin": { - "postject": "dist/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/postject/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": "^12.20.0 || >=14" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -14667,21 +14232,6 @@ "dev": true, "license": "MIT" }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -14798,7 +14348,7 @@ "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "devOptional": true, + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -14904,352 +14454,6 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, - "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/sharp/node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/sharp/node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/sharp/node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/sharp/node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/sharp/node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/sharp/node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/sharp/node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/sharp/node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -15465,6 +14669,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -15705,30 +14910,6 @@ "inline-style-parser": "0.2.7" } }, - "node_modules/styled-jsx": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", - "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", - "license": "MIT", - "peer": true, - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -15874,21 +15055,6 @@ "dev": true, "license": "ISC" }, - "node_modules/temp": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", - "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "mkdirp": "^0.5.1", - "rimraf": "~2.6.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/temp-file": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", @@ -15938,20 +15104,6 @@ "node": ">= 10.0.0" } }, - "node_modules/temp/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/tiny-async-pool": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz",