From 2899b6d41655d5079e73abb0d286e41e389420da Mon Sep 17 00:00:00 2001 From: Stefan de Vogelaere Date: Fri, 16 Jan 2026 22:28:56 +0100 Subject: [PATCH 1/5] feat: separate project settings from global settings This PR introduces a new dedicated Project Settings screen accessible from the sidebar, clearly separating project-specific settings from global application settings. - Added new route `/project-settings` with dedicated view - Sidebar navigation item "Settings" in Tools section (Shift+S shortcut) - Sidebar-based navigation matching global Settings pattern - Sections: Identity, Worktrees, Theme, Danger Zone **Moved to Project Settings:** - Project name and icon customization - Project-specific theme override - Worktree isolation enable/disable (per-project override) - Init script indicator visibility and auto-dismiss - Delete branch by default preference - Initialization script editor - Delete project (Danger Zone) **Remains in Global Settings:** - Global theme (default for all projects) - Global worktree isolation (default for new projects) - Feature Defaults, Model Defaults - API Keys, AI Providers, MCP Servers - Terminal, Keyboard Shortcuts, Audio - Account, Security, Developer settings Both Theme and Worktree Isolation now follow a consistent override pattern: 1. Global Settings defines the default value 2. New projects inherit the global value 3. Project Settings can override for that specific project 4. Changing global setting doesn't affect projects with overrides - Fixed: Changing global theme was incorrectly overwriting project themes - Fixed: Project worktree setting not persisting across sessions - Project settings now properly load from server on component mount - Shell syntax editor: improved background contrast (bg-background) - Shell syntax editor: removed distracting active line highlight - Project Settings header matches Context/Memory views pattern - `apps/ui/src/routes/project-settings.tsx` - `apps/ui/src/components/views/project-settings-view/` (9 files) - Global settings simplified (removed project-specific options) - Sidebar navigation updated with project settings link - App store: added project-specific useWorktrees state/actions - Types: added projectSettings keyboard shortcut - HTTP client: added missing project settings response fields --- .../layout/sidebar/hooks/use-navigation.ts | 8 + .../src/components/ui/shell-syntax-editor.tsx | 5 +- .../project-settings-navigation.tsx | 122 ++ .../config/navigation.ts | 16 + .../project-settings-view/hooks/index.ts | 1 + .../hooks/use-project-settings-view.ts | 22 + .../views/project-settings-view/index.ts | 6 + .../project-identity-section.tsx | 199 ++++ .../project-settings-view.tsx | 174 +++ .../project-theme-section.tsx | 164 +++ .../worktree-preferences-section.tsx | 450 +++++++ .../ui/src/components/views/settings-view.tsx | 58 +- .../appearance/appearance-section.tsx | 192 +-- .../components/settings-navigation.tsx | 27 +- .../views/settings-view/config/navigation.ts | 9 +- .../worktrees/worktrees-section.tsx | 377 +----- apps/ui/src/lib/http-api-client.ts | 3 + apps/ui/src/routes/project-settings.tsx | 6 + apps/ui/src/store/app-store.ts | 37 + libs/types/src/settings.ts | 3 + package-lock.json | 1051 +---------------- 21 files changed, 1249 insertions(+), 1681 deletions(-) create mode 100644 apps/ui/src/components/views/project-settings-view/components/project-settings-navigation.tsx create mode 100644 apps/ui/src/components/views/project-settings-view/config/navigation.ts create mode 100644 apps/ui/src/components/views/project-settings-view/hooks/index.ts create mode 100644 apps/ui/src/components/views/project-settings-view/hooks/use-project-settings-view.ts create mode 100644 apps/ui/src/components/views/project-settings-view/index.ts create mode 100644 apps/ui/src/components/views/project-settings-view/project-identity-section.tsx create mode 100644 apps/ui/src/components/views/project-settings-view/project-settings-view.tsx create mode 100644 apps/ui/src/components/views/project-settings-view/project-theme-section.tsx create mode 100644 apps/ui/src/components/views/project-settings-view/worktree-preferences-section.tsx create mode 100644 apps/ui/src/routes/project-settings.tsx diff --git a/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts b/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts index 110fa26c..2e22537e 100644 --- a/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts +++ b/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts @@ -11,6 +11,7 @@ import { Lightbulb, Brain, Network, + Settings, } from 'lucide-react'; import type { NavSection, NavItem } from '../types'; import type { KeyboardShortcut } from '@/hooks/use-keyboard-shortcuts'; @@ -32,6 +33,7 @@ interface UseNavigationProps { agent: string; terminal: string; settings: string; + projectSettings: string; ideation: string; githubIssues: string; githubPrs: string; @@ -121,6 +123,12 @@ export function useNavigation({ icon: Brain, shortcut: shortcuts.memory, }, + { + id: 'project-settings', + label: 'Settings', + icon: Settings, + shortcut: shortcuts.projectSettings, + }, ]; // Filter out hidden items diff --git a/apps/ui/src/components/ui/shell-syntax-editor.tsx b/apps/ui/src/components/ui/shell-syntax-editor.tsx index 159123c4..c405309a 100644 --- a/apps/ui/src/components/ui/shell-syntax-editor.tsx +++ b/apps/ui/src/components/ui/shell-syntax-editor.tsx @@ -70,8 +70,7 @@ const editorTheme = EditorView.theme({ backgroundColor: 'oklch(0.55 0.25 265 / 0.3)', }, '.cm-activeLine': { - backgroundColor: 'var(--accent)', - opacity: '0.3', + backgroundColor: 'transparent', }, '.cm-line': { padding: '0 0.25rem', @@ -114,7 +113,7 @@ export function ShellSyntaxEditor({ }: ShellSyntaxEditorProps) { return (
diff --git a/apps/ui/src/components/views/project-settings-view/components/project-settings-navigation.tsx b/apps/ui/src/components/views/project-settings-view/components/project-settings-navigation.tsx new file mode 100644 index 00000000..1c06dad3 --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/components/project-settings-navigation.tsx @@ -0,0 +1,122 @@ +import { X } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { Button } from '@/components/ui/button'; +import { PROJECT_SETTINGS_NAV_ITEMS } from '../config/navigation'; +import type { ProjectSettingsViewId } from '../hooks/use-project-settings-view'; + +interface ProjectSettingsNavigationProps { + activeSection: ProjectSettingsViewId; + onNavigate: (sectionId: ProjectSettingsViewId) => void; + isOpen?: boolean; + onClose?: () => void; +} + +export function ProjectSettingsNavigation({ + activeSection, + onNavigate, + isOpen = true, + onClose, +}: ProjectSettingsNavigationProps) { + return ( + <> + {/* Mobile backdrop overlay - only shown when isOpen is true on mobile */} + {isOpen && ( +
+ )} + + {/* Navigation sidebar */} + + + ); +} diff --git a/apps/ui/src/components/views/project-settings-view/config/navigation.ts b/apps/ui/src/components/views/project-settings-view/config/navigation.ts new file mode 100644 index 00000000..7f052ef5 --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/config/navigation.ts @@ -0,0 +1,16 @@ +import type { LucideIcon } from 'lucide-react'; +import { User, GitBranch, Palette, AlertTriangle } from 'lucide-react'; +import type { ProjectSettingsViewId } from '../hooks/use-project-settings-view'; + +export interface ProjectNavigationItem { + id: ProjectSettingsViewId; + label: string; + icon: LucideIcon; +} + +export const PROJECT_SETTINGS_NAV_ITEMS: ProjectNavigationItem[] = [ + { id: 'identity', label: 'Identity', icon: User }, + { id: 'worktrees', label: 'Worktrees', icon: GitBranch }, + { id: 'theme', label: 'Theme', icon: Palette }, + { id: 'danger', label: 'Danger Zone', icon: AlertTriangle }, +]; diff --git a/apps/ui/src/components/views/project-settings-view/hooks/index.ts b/apps/ui/src/components/views/project-settings-view/hooks/index.ts new file mode 100644 index 00000000..023eca9e --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/hooks/index.ts @@ -0,0 +1 @@ +export { useProjectSettingsView, type ProjectSettingsViewId } from './use-project-settings-view'; diff --git a/apps/ui/src/components/views/project-settings-view/hooks/use-project-settings-view.ts b/apps/ui/src/components/views/project-settings-view/hooks/use-project-settings-view.ts new file mode 100644 index 00000000..19faf5e3 --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/hooks/use-project-settings-view.ts @@ -0,0 +1,22 @@ +import { useState, useCallback } from 'react'; + +export type ProjectSettingsViewId = 'identity' | 'theme' | 'worktrees' | 'danger'; + +interface UseProjectSettingsViewOptions { + initialView?: ProjectSettingsViewId; +} + +export function useProjectSettingsView({ + initialView = 'identity', +}: UseProjectSettingsViewOptions = {}) { + const [activeView, setActiveView] = useState(initialView); + + const navigateTo = useCallback((viewId: ProjectSettingsViewId) => { + setActiveView(viewId); + }, []); + + return { + activeView, + navigateTo, + }; +} diff --git a/apps/ui/src/components/views/project-settings-view/index.ts b/apps/ui/src/components/views/project-settings-view/index.ts new file mode 100644 index 00000000..bc16ffaf --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/index.ts @@ -0,0 +1,6 @@ +export { ProjectSettingsView } from './project-settings-view'; +export { ProjectIdentitySection } from './project-identity-section'; +export { ProjectThemeSection } from './project-theme-section'; +export { WorktreePreferencesSection } from './worktree-preferences-section'; +export { useProjectSettingsView, type ProjectSettingsViewId } from './hooks'; +export { ProjectSettingsNavigation } from './components/project-settings-navigation'; diff --git a/apps/ui/src/components/views/project-settings-view/project-identity-section.tsx b/apps/ui/src/components/views/project-settings-view/project-identity-section.tsx new file mode 100644 index 00000000..d938ee73 --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/project-identity-section.tsx @@ -0,0 +1,199 @@ +import { useState, useRef, useEffect } from 'react'; +import { Label } from '@/components/ui/label'; +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { Palette, Upload, X, ImageIcon } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { useAppStore } from '@/store/app-store'; +import { IconPicker } from '@/components/layout/project-switcher/components/icon-picker'; +import { getAuthenticatedImageUrl } from '@/lib/api-fetch'; +import { getHttpApiClient } from '@/lib/http-api-client'; +import type { Project } from '@/lib/electron'; + +interface ProjectIdentitySectionProps { + project: Project; +} + +export function ProjectIdentitySection({ project }: ProjectIdentitySectionProps) { + const { setProjectIcon, setProjectName, setProjectCustomIcon } = useAppStore(); + const [projectName, setProjectNameLocal] = useState(project.name || ''); + const [projectIcon, setProjectIconLocal] = useState(project.icon || null); + const [customIconPath, setCustomIconPathLocal] = useState( + project.customIconPath || null + ); + const [isUploadingIcon, setIsUploadingIcon] = useState(false); + const fileInputRef = useRef(null); + + // Sync local state when project changes + useEffect(() => { + setProjectNameLocal(project.name || ''); + setProjectIconLocal(project.icon || null); + setCustomIconPathLocal(project.customIconPath || null); + }, [project]); + + // Auto-save when values change + const handleNameChange = (name: string) => { + setProjectNameLocal(name); + if (name.trim() && name.trim() !== project.name) { + setProjectName(project.id, name.trim()); + } + }; + + const handleIconChange = (icon: string | null) => { + setProjectIconLocal(icon); + setProjectIcon(project.id, icon); + }; + + const handleCustomIconChange = (path: string | null) => { + setCustomIconPathLocal(path); + setProjectCustomIcon(project.id, path); + // Clear Lucide icon when custom icon is set + if (path) { + setProjectIconLocal(null); + setProjectIcon(project.id, null); + } + }; + + const handleCustomIconUpload = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + + // Validate file type + const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; + if (!validTypes.includes(file.type)) { + return; + } + + // Validate file size (max 2MB for icons) + if (file.size > 2 * 1024 * 1024) { + return; + } + + setIsUploadingIcon(true); + try { + // Convert to base64 + const reader = new FileReader(); + reader.onload = async () => { + const base64Data = reader.result as string; + const result = await getHttpApiClient().saveImageToTemp( + base64Data, + `project-icon-${file.name}`, + file.type, + project.path + ); + if (result.success && result.path) { + handleCustomIconChange(result.path); + } + setIsUploadingIcon(false); + }; + reader.readAsDataURL(file); + } catch { + setIsUploadingIcon(false); + } + }; + + const handleRemoveCustomIcon = () => { + handleCustomIconChange(null); + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }; + + return ( +
+
+
+
+ +
+

Project Identity

+
+

+ Customize how your project appears in the sidebar and project switcher. +

+
+
+ {/* Project Name */} +
+ + handleNameChange(e.target.value)} + placeholder="Enter project name" + /> +
+ + {/* Project Icon */} +
+ +

+ Choose a preset icon or upload a custom image +

+ + {/* Custom Icon Upload */} +
+
+ {customIconPath ? ( +
+ Custom project icon + +
+ ) : ( +
+ +
+ )} +
+ + +

+ PNG, JPG, GIF or WebP. Max 2MB. +

+
+
+
+ + {/* Preset Icon Picker - only show if no custom icon */} + {!customIconPath && ( + + )} +
+
+
+ ); +} diff --git a/apps/ui/src/components/views/project-settings-view/project-settings-view.tsx b/apps/ui/src/components/views/project-settings-view/project-settings-view.tsx new file mode 100644 index 00000000..f441cc72 --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/project-settings-view.tsx @@ -0,0 +1,174 @@ +import { useState, useEffect } from 'react'; +import { useAppStore } from '@/store/app-store'; +import { Settings, FolderOpen, Menu } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { ProjectIdentitySection } from './project-identity-section'; +import { ProjectThemeSection } from './project-theme-section'; +import { WorktreePreferencesSection } from './worktree-preferences-section'; +import { DangerZoneSection } from '../settings-view/danger-zone/danger-zone-section'; +import { DeleteProjectDialog } from '../settings-view/components/delete-project-dialog'; +import { ProjectSettingsNavigation } from './components/project-settings-navigation'; +import { useProjectSettingsView } from './hooks/use-project-settings-view'; +import type { Project as ElectronProject } from '@/lib/electron'; + +// Breakpoint constant for mobile (matches Tailwind lg breakpoint) +const LG_BREAKPOINT = 1024; + +// Convert to the shared types used by components +interface SettingsProject { + id: string; + name: string; + path: string; + theme?: string; + icon?: string | null; + customIconPath?: string | null; +} + +export function ProjectSettingsView() { + const { currentProject, moveProjectToTrash } = useAppStore(); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + + // Use project settings view navigation hook + const { activeView, navigateTo } = useProjectSettingsView(); + + // Mobile navigation state - default to showing on desktop, hidden on mobile + const [showNavigation, setShowNavigation] = useState(() => { + if (typeof window !== 'undefined') { + return window.innerWidth >= LG_BREAKPOINT; + } + return true; + }); + + // Auto-close navigation on mobile when a section is selected + useEffect(() => { + if (typeof window !== 'undefined' && window.innerWidth < LG_BREAKPOINT) { + setShowNavigation(false); + } + }, [activeView]); + + // Handle window resize to show/hide navigation appropriately + useEffect(() => { + const handleResize = () => { + if (window.innerWidth >= LG_BREAKPOINT) { + setShowNavigation(true); + } + }; + + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + // Convert electron Project to settings-view Project type + const convertProject = (project: ElectronProject | null): SettingsProject | null => { + if (!project) return null; + return { + id: project.id, + name: project.name, + path: project.path, + theme: project.theme, + icon: project.icon, + customIconPath: project.customIconPath, + }; + }; + + const settingsProject = convertProject(currentProject); + + // Render the active section based on current view + const renderActiveSection = () => { + if (!currentProject) return null; + + switch (activeView) { + case 'identity': + return ; + case 'theme': + return ; + case 'worktrees': + return ; + case 'danger': + return ( + setShowDeleteDialog(true)} + /> + ); + default: + return ; + } + }; + + // Show message if no project is selected + if (!currentProject) { + return ( +
+
+
+
+ +
+

No Project Selected

+

+ Select a project from the sidebar to configure project-specific settings. +

+
+
+
+ ); + } + + return ( +
+ {/* Header */} +
+
+ {/* Mobile menu button */} + + +
+

Project Settings

+

+ Configure settings for {currentProject.name} +

+
+
+
+ + {/* Content Area with Sidebar */} +
+ {/* Side Navigation */} + setShowNavigation(false)} + /> + + {/* Content Panel - Shows only the active section */} +
+
{renderActiveSection()}
+
+
+ + {/* Delete Project Confirmation Dialog */} + +
+ ); +} 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 new file mode 100644 index 00000000..d9293df2 --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/project-theme-section.tsx @@ -0,0 +1,164 @@ +import { useState } from 'react'; +import { Label } from '@/components/ui/label'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Palette, Moon, Sun } from 'lucide-react'; +import { darkThemes, lightThemes, type Theme } from '@/config/theme-options'; +import { cn } from '@/lib/utils'; +import { useAppStore } from '@/store/app-store'; +import type { Project } from '@/lib/electron'; + +interface ProjectThemeSectionProps { + project: Project; +} + +export function ProjectThemeSection({ project }: ProjectThemeSectionProps) { + const { theme: globalTheme, setProjectTheme } = useAppStore(); + const [activeTab, setActiveTab] = useState<'dark' | 'light'>('dark'); + + const projectTheme = project.theme as Theme | undefined; + const hasCustomTheme = projectTheme !== undefined; + const effectiveTheme = projectTheme || globalTheme; + + const themesToShow = activeTab === 'dark' ? darkThemes : lightThemes; + + 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); + } + }; + + return ( +
+
+
+
+ +
+

Theme

+
+

+ Customize the theme for this project. +

+
+
+ {/* Use Global Theme Toggle */} +
+ +
+ +

+ When enabled, this project will use the global theme setting. Disable to set a + project-specific theme. +

+
+
+ + {/* Theme Selection - only show if not using global theme */} + {hasCustomTheme && ( +
+
+ + {/* Dark/Light Tabs */} +
+ + +
+
+
+ {themesToShow.map(({ value, label, Icon, testId, color }) => { + const isActive = effectiveTheme === value; + return ( + + ); + })} +
+
+ )} + + {/* Info when using global theme */} + {!hasCustomTheme && ( +
+

+ This project is using the global theme:{' '} + {globalTheme} +

+
+ )} +
+
+ ); +} diff --git a/apps/ui/src/components/views/project-settings-view/worktree-preferences-section.tsx b/apps/ui/src/components/views/project-settings-view/worktree-preferences-section.tsx new file mode 100644 index 00000000..af85eb03 --- /dev/null +++ b/apps/ui/src/components/views/project-settings-view/worktree-preferences-section.tsx @@ -0,0 +1,450 @@ +import { useState, useEffect, useCallback } from 'react'; +import { Label } from '@/components/ui/label'; +import { Checkbox } from '@/components/ui/checkbox'; +import { Button } from '@/components/ui/button'; +import { ShellSyntaxEditor } from '@/components/ui/shell-syntax-editor'; +import { + GitBranch, + Terminal, + FileCode, + Save, + RotateCcw, + Trash2, + Loader2, + PanelBottomClose, +} from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { apiGet, apiPut, apiDelete } from '@/lib/api-fetch'; +import { toast } from 'sonner'; +import { useAppStore } from '@/store/app-store'; +import { getHttpApiClient } from '@/lib/http-api-client'; +import type { Project } from '@/lib/electron'; + +interface WorktreePreferencesSectionProps { + project: Project; +} + +interface InitScriptResponse { + success: boolean; + exists: boolean; + content: string; + path: string; + error?: string; +} + +export function WorktreePreferencesSection({ project }: WorktreePreferencesSectionProps) { + const globalUseWorktrees = useAppStore((s) => s.useWorktrees); + const getProjectUseWorktrees = useAppStore((s) => s.getProjectUseWorktrees); + const setProjectUseWorktrees = useAppStore((s) => s.setProjectUseWorktrees); + const getShowInitScriptIndicator = useAppStore((s) => s.getShowInitScriptIndicator); + const setShowInitScriptIndicator = useAppStore((s) => s.setShowInitScriptIndicator); + const getDefaultDeleteBranch = useAppStore((s) => s.getDefaultDeleteBranch); + const setDefaultDeleteBranch = useAppStore((s) => s.setDefaultDeleteBranch); + const getAutoDismissInitScriptIndicator = useAppStore((s) => s.getAutoDismissInitScriptIndicator); + const setAutoDismissInitScriptIndicator = useAppStore((s) => s.setAutoDismissInitScriptIndicator); + + // Get effective worktrees setting (project override or global fallback) + const projectUseWorktrees = getProjectUseWorktrees(project.path); + const effectiveUseWorktrees = projectUseWorktrees ?? globalUseWorktrees; + + const [scriptContent, setScriptContent] = useState(''); + const [originalContent, setOriginalContent] = useState(''); + const [scriptExists, setScriptExists] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + + // Get the current settings for this project + const showIndicator = getShowInitScriptIndicator(project.path); + const defaultDeleteBranch = getDefaultDeleteBranch(project.path); + const autoDismiss = getAutoDismissInitScriptIndicator(project.path); + + // Check if there are unsaved changes + const hasChanges = scriptContent !== originalContent; + + // Load project settings (including useWorktrees) when project changes + useEffect(() => { + const loadProjectSettings = async () => { + try { + const httpClient = getHttpApiClient(); + const response = await httpClient.settings.getProject(project.path); + if (response.success && response.settings) { + // Sync useWorktrees to store if it has a value + if (response.settings.useWorktrees !== undefined) { + setProjectUseWorktrees(project.path, response.settings.useWorktrees); + } + // Also sync other settings to store + if (response.settings.showInitScriptIndicator !== undefined) { + setShowInitScriptIndicator(project.path, response.settings.showInitScriptIndicator); + } + if (response.settings.defaultDeleteBranchWithWorktree !== undefined) { + setDefaultDeleteBranch(project.path, response.settings.defaultDeleteBranchWithWorktree); + } + if (response.settings.autoDismissInitScriptIndicator !== undefined) { + setAutoDismissInitScriptIndicator( + project.path, + response.settings.autoDismissInitScriptIndicator + ); + } + } + } catch (error) { + console.error('Failed to load project settings:', error); + } + }; + + loadProjectSettings(); + }, [ + project.path, + setProjectUseWorktrees, + setShowInitScriptIndicator, + setDefaultDeleteBranch, + setAutoDismissInitScriptIndicator, + ]); + + // Load init script content when project changes + useEffect(() => { + const loadInitScript = async () => { + setIsLoading(true); + try { + const response = await apiGet( + `/api/worktree/init-script?projectPath=${encodeURIComponent(project.path)}` + ); + if (response.success) { + const content = response.content || ''; + setScriptContent(content); + setOriginalContent(content); + setScriptExists(response.exists); + } + } catch (error) { + console.error('Failed to load init script:', error); + } finally { + setIsLoading(false); + } + }; + + loadInitScript(); + }, [project.path]); + + // Save script + const handleSave = useCallback(async () => { + setIsSaving(true); + try { + const response = await apiPut<{ success: boolean; error?: string }>( + '/api/worktree/init-script', + { + projectPath: project.path, + content: scriptContent, + } + ); + if (response.success) { + setOriginalContent(scriptContent); + setScriptExists(true); + toast.success('Init script saved'); + } else { + toast.error('Failed to save init script', { + description: response.error, + }); + } + } catch (error) { + console.error('Failed to save init script:', error); + toast.error('Failed to save init script'); + } finally { + setIsSaving(false); + } + }, [project.path, scriptContent]); + + // Reset to original content + const handleReset = useCallback(() => { + setScriptContent(originalContent); + }, [originalContent]); + + // Delete script + const handleDelete = useCallback(async () => { + setIsDeleting(true); + try { + const response = await apiDelete<{ success: boolean; error?: string }>( + '/api/worktree/init-script', + { + body: { projectPath: project.path }, + } + ); + if (response.success) { + setScriptContent(''); + setOriginalContent(''); + setScriptExists(false); + toast.success('Init script deleted'); + } else { + toast.error('Failed to delete init script', { + description: response.error, + }); + } + } catch (error) { + console.error('Failed to delete init script:', error); + toast.error('Failed to delete init script'); + } finally { + setIsDeleting(false); + } + }, [project.path]); + + // Handle content change (no auto-save) + const handleContentChange = useCallback((value: string) => { + setScriptContent(value); + }, []); + + return ( +
+
+
+
+ +
+

+ Worktree Preferences +

+
+

+ Configure worktree behavior for this project. +

+
+
+ {/* Enable Git Worktree Isolation Toggle */} +
+ { + const value = checked === true; + setProjectUseWorktrees(project.path, value); + try { + const httpClient = getHttpApiClient(); + await httpClient.settings.updateProject(project.path, { + useWorktrees: value, + }); + } catch (error) { + console.error('Failed to persist useWorktrees:', error); + } + }} + className="mt-1" + data-testid="project-use-worktrees-checkbox" + /> +
+ +

+ Creates isolated git branches for each feature in this project. When disabled, agents + work directly in the main project directory. +

+
+
+ + {/* Separator */} +
+ + {/* Show Init Script Indicator Toggle */} +
+ { + const value = checked === true; + setShowInitScriptIndicator(project.path, value); + // Persist to server + try { + const httpClient = getHttpApiClient(); + await httpClient.settings.updateProject(project.path, { + showInitScriptIndicator: value, + }); + } catch (error) { + console.error('Failed to persist showInitScriptIndicator:', error); + } + }} + className="mt-1" + /> +
+ +

+ Display a floating panel in the bottom-right corner showing init script execution + status and output when a worktree is created. +

+
+
+ + {/* Auto-dismiss Init Script Indicator Toggle */} + {showIndicator && ( +
+ { + const value = checked === true; + setAutoDismissInitScriptIndicator(project.path, value); + // Persist to server + try { + const httpClient = getHttpApiClient(); + await httpClient.settings.updateProject(project.path, { + autoDismissInitScriptIndicator: value, + }); + } catch (error) { + console.error('Failed to persist autoDismissInitScriptIndicator:', error); + } + }} + className="mt-1" + /> +
+ +

+ Automatically hide the indicator 5 seconds after the script completes. +

+
+
+ )} + + {/* Default Delete Branch Toggle */} +
+ { + const value = checked === true; + setDefaultDeleteBranch(project.path, value); + // Persist to server + try { + const httpClient = getHttpApiClient(); + await httpClient.settings.updateProject(project.path, { + defaultDeleteBranch: value, + }); + } catch (error) { + console.error('Failed to persist defaultDeleteBranch:', error); + } + }} + className="mt-1" + /> +
+ +

+ When deleting a worktree, automatically check the "Also delete the branch" option. +

+
+
+ + {/* Separator */} +
+ + {/* Init Script Section */} +
+
+
+ + +
+
+

+ Shell commands to run after a worktree is created. Runs once per worktree. Uses Git Bash + on Windows for cross-platform compatibility. +

+ + {/* File path indicator */} +
+ + .automaker/worktree-init.sh + {hasChanges && (unsaved changes)} +
+ + {isLoading ? ( +
+ +
+ ) : ( + <> + + + {/* Action buttons */} +
+ + + +
+ + )} +
+
+
+ ); +} diff --git a/apps/ui/src/components/views/settings-view.tsx b/apps/ui/src/components/views/settings-view.tsx index 1ddf0a39..3bcec3bb 100644 --- a/apps/ui/src/components/views/settings-view.tsx +++ b/apps/ui/src/components/views/settings-view.tsx @@ -6,7 +6,6 @@ import { useSettingsView, type SettingsViewId } from './settings-view/hooks'; import { NAV_ITEMS } from './settings-view/config/navigation'; import { SettingsHeader } from './settings-view/components/settings-header'; import { KeyboardMapDialog } from './settings-view/components/keyboard-map-dialog'; -import { DeleteProjectDialog } from './settings-view/components/delete-project-dialog'; import { SettingsNavigation } from './settings-view/components/settings-navigation'; import { ApiKeysSection } from './settings-view/api-keys/api-keys-section'; import { ModelDefaultsSection } from './settings-view/model-defaults'; @@ -16,7 +15,6 @@ import { AudioSection } from './settings-view/audio/audio-section'; import { KeyboardShortcutsSection } from './settings-view/keyboard-shortcuts/keyboard-shortcuts-section'; import { FeatureDefaultsSection } from './settings-view/feature-defaults/feature-defaults-section'; import { WorktreesSection } from './settings-view/worktrees'; -import { DangerZoneSection } from './settings-view/danger-zone/danger-zone-section'; import { AccountSection } from './settings-view/account'; import { SecuritySection } from './settings-view/security'; import { DeveloperSection } from './settings-view/developer/developer-section'; @@ -30,8 +28,7 @@ import { MCPServersSection } from './settings-view/mcp-servers'; import { PromptCustomizationSection } from './settings-view/prompts'; import { EventHooksSection } from './settings-view/event-hooks'; import { ImportExportDialog } from './settings-view/components/import-export-dialog'; -import type { Project as SettingsProject, Theme } from './settings-view/shared/types'; -import type { Project as ElectronProject } from '@/lib/electron'; +import type { Theme } from './settings-view/shared/types'; // Breakpoint constant for mobile (matches Tailwind lg breakpoint) const LG_BREAKPOINT = 1024; @@ -40,7 +37,6 @@ export function SettingsView() { const { theme, setTheme, - setProjectTheme, defaultSkipTests, setDefaultSkipTests, enableDependencyBlocking, @@ -54,7 +50,6 @@ export function SettingsView() { muteDoneSound, setMuteDoneSound, currentProject, - moveProjectToTrash, defaultPlanningMode, setDefaultPlanningMode, defaultRequirePlanApproval, @@ -69,34 +64,8 @@ export function SettingsView() { setSkipSandboxWarning, } = useAppStore(); - // Convert electron Project to settings-view Project type - const convertProject = (project: ElectronProject | null): SettingsProject | null => { - if (!project) return null; - return { - id: project.id, - name: project.name, - path: project.path, - theme: project.theme as Theme | undefined, - icon: project.icon, - customIconPath: project.customIconPath, - }; - }; - - const settingsProject = convertProject(currentProject); - - // Compute the effective theme for the current project - const effectiveTheme = (settingsProject?.theme || theme) as Theme; - - // Handler to set theme - always updates global theme (user's preference), - // and also sets per-project theme if a project is selected - const handleSetTheme = (newTheme: typeof theme) => { - // Always update global theme so user's preference persists across all projects - setTheme(newTheme); - // Also set per-project theme if a project is selected - if (currentProject) { - setProjectTheme(currentProject.id, newTheme); - } - }; + // Global theme (project-specific themes are managed in Project Settings) + const globalTheme = theme as Theme; // Get initial view from URL search params const { view: initialView } = useSearch({ from: '/settings' }); @@ -113,7 +82,6 @@ export function SettingsView() { } }; - const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [showKeyboardMapDialog, setShowKeyboardMapDialog] = useState(false); const [showImportExportDialog, setShowImportExportDialog] = useState(false); @@ -172,9 +140,8 @@ export function SettingsView() { case 'appearance': return ( handleSetTheme(theme as any)} + effectiveTheme={globalTheme} + onThemeChange={(newTheme) => setTheme(newTheme as typeof theme)} /> ); case 'terminal': @@ -223,13 +190,6 @@ export function SettingsView() { ); case 'developer': return ; - case 'danger': - return ( - setShowDeleteDialog(true)} - /> - ); default: return ; } @@ -265,14 +225,6 @@ export function SettingsView() { {/* Keyboard Map Dialog */} - {/* Delete Project Confirmation Dialog */} - - {/* Import/Export Settings Dialog */}
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 003501f9..47646287 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,118 +1,20 @@ -import { useState, useRef, useEffect } from 'react'; +import { useState } from 'react'; import { Label } from '@/components/ui/label'; -import { Input } from '@/components/ui/input'; -import { Button } from '@/components/ui/button'; -import { Palette, Moon, Sun, Upload, X, ImageIcon } from 'lucide-react'; +import { Palette, Moon, Sun } from 'lucide-react'; import { darkThemes, lightThemes } from '@/config/theme-options'; import { cn } from '@/lib/utils'; -import { useAppStore } from '@/store/app-store'; -import { IconPicker } from '@/components/layout/project-switcher/components/icon-picker'; -import { getAuthenticatedImageUrl } from '@/lib/api-fetch'; -import { getHttpApiClient } from '@/lib/http-api-client'; -import type { Theme, Project } from '../shared/types'; +import type { Theme } from '../shared/types'; interface AppearanceSectionProps { effectiveTheme: Theme; - currentProject: Project | null; onThemeChange: (theme: Theme) => void; } -export function AppearanceSection({ - effectiveTheme, - currentProject, - onThemeChange, -}: AppearanceSectionProps) { - const { setProjectIcon, setProjectName, setProjectCustomIcon } = useAppStore(); +export function AppearanceSection({ effectiveTheme, onThemeChange }: AppearanceSectionProps) { const [activeTab, setActiveTab] = useState<'dark' | 'light'>('dark'); - const [projectName, setProjectNameLocal] = useState(currentProject?.name || ''); - const [projectIcon, setProjectIconLocal] = useState(currentProject?.icon || null); - const [customIconPath, setCustomIconPathLocal] = useState( - currentProject?.customIconPath || null - ); - const [isUploadingIcon, setIsUploadingIcon] = useState(false); - const fileInputRef = useRef(null); - - // Sync local state when currentProject changes - useEffect(() => { - setProjectNameLocal(currentProject?.name || ''); - setProjectIconLocal(currentProject?.icon || null); - setCustomIconPathLocal(currentProject?.customIconPath || null); - }, [currentProject]); const themesToShow = activeTab === 'dark' ? darkThemes : lightThemes; - // Auto-save when values change - const handleNameChange = (name: string) => { - setProjectNameLocal(name); - if (currentProject && name.trim() && name.trim() !== currentProject.name) { - setProjectName(currentProject.id, name.trim()); - } - }; - - const handleIconChange = (icon: string | null) => { - setProjectIconLocal(icon); - if (currentProject) { - setProjectIcon(currentProject.id, icon); - } - }; - - const handleCustomIconChange = (path: string | null) => { - setCustomIconPathLocal(path); - if (currentProject) { - setProjectCustomIcon(currentProject.id, path); - // Clear Lucide icon when custom icon is set - if (path) { - setProjectIconLocal(null); - setProjectIcon(currentProject.id, null); - } - } - }; - - const handleCustomIconUpload = async (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file || !currentProject) return; - - // Validate file type - const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; - if (!validTypes.includes(file.type)) { - return; - } - - // Validate file size (max 2MB for icons) - if (file.size > 2 * 1024 * 1024) { - return; - } - - setIsUploadingIcon(true); - try { - // Convert to base64 - const reader = new FileReader(); - reader.onload = async () => { - const base64Data = reader.result as string; - const result = await getHttpApiClient().saveImageToTemp( - base64Data, - `project-icon-${file.name}`, - file.type, - currentProject.path - ); - if (result.success && result.path) { - handleCustomIconChange(result.path); - } - setIsUploadingIcon(false); - }; - reader.readAsDataURL(file); - } catch { - setIsUploadingIcon(false); - } - }; - - const handleRemoveCustomIcon = () => { - handleCustomIconChange(null); - if (fileInputRef.current) { - fileInputRef.current.value = ''; - } - }; - return (
- {/* Project Details Section */} - {currentProject && ( -
-
-
- - handleNameChange(e.target.value)} - placeholder="Enter project name" - /> -
- -
- -

- Choose a preset icon or upload a custom image -

- - {/* Custom Icon Upload */} -
-
- {customIconPath ? ( -
- Custom project icon - -
- ) : ( -
- -
- )} -
- - -

- PNG, JPG, GIF or WebP. Max 2MB. -

-
-
-
- - {/* Preset Icon Picker - only show if no custom icon */} - {!customIconPath && ( - - )} -
-
-
- )} - {/* Theme Section */}
- + {/* Dark/Light Tabs */}
))} - - {/* Project Settings - only show when a project is selected */} - {currentProject && ( - <> - {/* Divider */} -
- - {/* Project Settings Label */} -
- Project Settings -
- - {/* Project Settings Items */} -
- {PROJECT_NAV_ITEMS.map((item) => ( - - ))} -
- - )}
diff --git a/apps/ui/src/components/views/settings-view/config/navigation.ts b/apps/ui/src/components/views/settings-view/config/navigation.ts index c5d5d362..107d8678 100644 --- a/apps/ui/src/components/views/settings-view/config/navigation.ts +++ b/apps/ui/src/components/views/settings-view/config/navigation.ts @@ -8,13 +8,11 @@ import { Settings2, Volume2, FlaskConical, - Trash2, Workflow, Plug, MessageSquareText, User, Shield, - Cpu, GitBranch, Code2, Webhook, @@ -84,10 +82,5 @@ export const GLOBAL_NAV_GROUPS: NavigationGroup[] = [ // Flat list of all global nav items for backwards compatibility export const GLOBAL_NAV_ITEMS: NavigationItem[] = GLOBAL_NAV_GROUPS.flatMap((group) => group.items); -// Project-specific settings - only visible when a project is selected -export const PROJECT_NAV_ITEMS: NavigationItem[] = [ - { id: 'danger', label: 'Danger Zone', icon: Trash2 }, -]; - // Legacy export for backwards compatibility -export const NAV_ITEMS: NavigationItem[] = [...GLOBAL_NAV_ITEMS, ...PROJECT_NAV_ITEMS]; +export const NAV_ITEMS: NavigationItem[] = GLOBAL_NAV_ITEMS; diff --git a/apps/ui/src/components/views/settings-view/worktrees/worktrees-section.tsx b/apps/ui/src/components/views/settings-view/worktrees/worktrees-section.tsx index 2d232a65..062d2d0d 100644 --- a/apps/ui/src/components/views/settings-view/worktrees/worktrees-section.tsx +++ b/apps/ui/src/components/views/settings-view/worktrees/worktrees-section.tsx @@ -1,172 +1,14 @@ -import { useState, useEffect, useCallback } from 'react'; import { Label } from '@/components/ui/label'; import { Checkbox } from '@/components/ui/checkbox'; -import { Button } from '@/components/ui/button'; -import { ShellSyntaxEditor } from '@/components/ui/shell-syntax-editor'; -import { - GitBranch, - Terminal, - FileCode, - Save, - RotateCcw, - Trash2, - Loader2, - PanelBottomClose, -} from 'lucide-react'; +import { GitBranch } from 'lucide-react'; import { cn } from '@/lib/utils'; -import { apiGet, apiPut, apiDelete } from '@/lib/api-fetch'; -import { toast } from 'sonner'; -import { useAppStore } from '@/store/app-store'; -import { getHttpApiClient } from '@/lib/http-api-client'; interface WorktreesSectionProps { useWorktrees: boolean; onUseWorktreesChange: (value: boolean) => void; } -interface InitScriptResponse { - success: boolean; - exists: boolean; - content: string; - path: string; - error?: string; -} - export function WorktreesSection({ useWorktrees, onUseWorktreesChange }: WorktreesSectionProps) { - const currentProject = useAppStore((s) => s.currentProject); - const getShowInitScriptIndicator = useAppStore((s) => s.getShowInitScriptIndicator); - const setShowInitScriptIndicator = useAppStore((s) => s.setShowInitScriptIndicator); - const getDefaultDeleteBranch = useAppStore((s) => s.getDefaultDeleteBranch); - const setDefaultDeleteBranch = useAppStore((s) => s.setDefaultDeleteBranch); - const getAutoDismissInitScriptIndicator = useAppStore((s) => s.getAutoDismissInitScriptIndicator); - const setAutoDismissInitScriptIndicator = useAppStore((s) => s.setAutoDismissInitScriptIndicator); - const [scriptContent, setScriptContent] = useState(''); - const [originalContent, setOriginalContent] = useState(''); - const [scriptExists, setScriptExists] = useState(false); - const [isLoading, setIsLoading] = useState(true); - const [isSaving, setIsSaving] = useState(false); - const [isDeleting, setIsDeleting] = useState(false); - - // Get the current show indicator setting - const showIndicator = currentProject?.path - ? getShowInitScriptIndicator(currentProject.path) - : true; - - // Get the default delete branch setting - const defaultDeleteBranch = currentProject?.path - ? getDefaultDeleteBranch(currentProject.path) - : false; - - // Get the auto-dismiss setting - const autoDismiss = currentProject?.path - ? getAutoDismissInitScriptIndicator(currentProject.path) - : true; - - // Check if there are unsaved changes - const hasChanges = scriptContent !== originalContent; - - // Load init script content when project changes - useEffect(() => { - if (!currentProject?.path) { - setScriptContent(''); - setOriginalContent(''); - setScriptExists(false); - setIsLoading(false); - return; - } - - const loadInitScript = async () => { - setIsLoading(true); - try { - const response = await apiGet( - `/api/worktree/init-script?projectPath=${encodeURIComponent(currentProject.path)}` - ); - if (response.success) { - const content = response.content || ''; - setScriptContent(content); - setOriginalContent(content); - setScriptExists(response.exists); - } - } catch (error) { - console.error('Failed to load init script:', error); - } finally { - setIsLoading(false); - } - }; - - loadInitScript(); - }, [currentProject?.path]); - - // Save script - const handleSave = useCallback(async () => { - if (!currentProject?.path) return; - - setIsSaving(true); - try { - const response = await apiPut<{ success: boolean; error?: string }>( - '/api/worktree/init-script', - { - projectPath: currentProject.path, - content: scriptContent, - } - ); - if (response.success) { - setOriginalContent(scriptContent); - setScriptExists(true); - toast.success('Init script saved'); - } else { - toast.error('Failed to save init script', { - description: response.error, - }); - } - } catch (error) { - console.error('Failed to save init script:', error); - toast.error('Failed to save init script'); - } finally { - setIsSaving(false); - } - }, [currentProject?.path, scriptContent]); - - // Reset to original content - const handleReset = useCallback(() => { - setScriptContent(originalContent); - }, [originalContent]); - - // Delete script - const handleDelete = useCallback(async () => { - if (!currentProject?.path) return; - - setIsDeleting(true); - try { - const response = await apiDelete<{ success: boolean; error?: string }>( - '/api/worktree/init-script', - { - body: { projectPath: currentProject.path }, - } - ); - if (response.success) { - setScriptContent(''); - setOriginalContent(''); - setScriptExists(false); - toast.success('Init script deleted'); - } else { - toast.error('Failed to delete init script', { - description: response.error, - }); - } - } catch (error) { - console.error('Failed to delete init script:', error); - toast.error('Failed to delete init script'); - } finally { - setIsDeleting(false); - } - }, [currentProject?.path]); - - // Handle content change (no auto-save) - const handleContentChange = useCallback((value: string) => { - setScriptContent(value); - }, []); - return (
Worktrees

- Configure git worktree isolation and initialization scripts. + Configure git worktree isolation for feature development.

@@ -212,217 +54,12 @@ export function WorktreesSection({ useWorktrees, onUseWorktreesChange }: Worktre
- {/* Show Init Script Indicator Toggle */} - {currentProject && ( -
- { - if (currentProject?.path) { - const value = checked === true; - setShowInitScriptIndicator(currentProject.path, value); - // Persist to server - try { - const httpClient = getHttpApiClient(); - await httpClient.settings.updateProject(currentProject.path, { - showInitScriptIndicator: value, - }); - } catch (error) { - console.error('Failed to persist showInitScriptIndicator:', error); - } - } - }} - className="mt-1" - /> -
- -

- Display a floating panel in the bottom-right corner showing init script execution - status and output when a worktree is created. -

-
-
- )} - - {/* Auto-dismiss Init Script Indicator Toggle */} - {currentProject && showIndicator && ( -
- { - if (currentProject?.path) { - const value = checked === true; - setAutoDismissInitScriptIndicator(currentProject.path, value); - // Persist to server - try { - const httpClient = getHttpApiClient(); - await httpClient.settings.updateProject(currentProject.path, { - autoDismissInitScriptIndicator: value, - }); - } catch (error) { - console.error('Failed to persist autoDismissInitScriptIndicator:', error); - } - } - }} - className="mt-1" - /> -
- -

- Automatically hide the indicator 5 seconds after the script completes. -

-
-
- )} - - {/* Default Delete Branch Toggle */} - {currentProject && ( -
- { - if (currentProject?.path) { - const value = checked === true; - setDefaultDeleteBranch(currentProject.path, value); - // Persist to server - try { - const httpClient = getHttpApiClient(); - await httpClient.settings.updateProject(currentProject.path, { - defaultDeleteBranch: value, - }); - } catch (error) { - console.error('Failed to persist defaultDeleteBranch:', error); - } - } - }} - className="mt-1" - /> -
- -

- When deleting a worktree, automatically check the "Also delete the branch" option. -

-
-
- )} - - {/* Separator */} -
- - {/* Init Script Section */} -
-
-
- - -
-
-

- Shell commands to run after a worktree is created. Runs once per worktree. Uses Git Bash - on Windows for cross-platform compatibility. + {/* Info about project-specific settings */} +

+

+ Project-specific worktree preferences (init script, delete branch behavior) can be + configured in each project's settings via the sidebar.

- - {currentProject ? ( - <> - {/* File path indicator */} -
- - .automaker/worktree-init.sh - {hasChanges && ( - (unsaved changes) - )} -
- - {isLoading ? ( -
- -
- ) : ( - <> - - - {/* Action buttons */} -
- - - -
- - )} - - ) : ( -
- Select a project to configure the init script. -
- )}
diff --git a/apps/ui/src/lib/http-api-client.ts b/apps/ui/src/lib/http-api-client.ts index 90781b59..26e1f308 100644 --- a/apps/ui/src/lib/http-api-client.ts +++ b/apps/ui/src/lib/http-api-client.ts @@ -2171,6 +2171,9 @@ export class HttpApiClient implements ElectronAPI { hideScrollbar: boolean; }; worktreePanelVisible?: boolean; + showInitScriptIndicator?: boolean; + defaultDeleteBranchWithWorktree?: boolean; + autoDismissInitScriptIndicator?: boolean; lastSelectedSessionId?: string; }; error?: string; diff --git a/apps/ui/src/routes/project-settings.tsx b/apps/ui/src/routes/project-settings.tsx new file mode 100644 index 00000000..e933d58d --- /dev/null +++ b/apps/ui/src/routes/project-settings.tsx @@ -0,0 +1,6 @@ +import { createFileRoute } from '@tanstack/react-router'; +import { ProjectSettingsView } from '@/components/views/project-settings-view'; + +export const Route = createFileRoute('/project-settings')({ + component: ProjectSettingsView, +}); diff --git a/apps/ui/src/store/app-store.ts b/apps/ui/src/store/app-store.ts index 23fa5371..23886ab6 100644 --- a/apps/ui/src/store/app-store.ts +++ b/apps/ui/src/store/app-store.ts @@ -231,6 +231,7 @@ export interface KeyboardShortcuts { context: string; memory: string; settings: string; + projectSettings: string; terminal: string; ideation: string; githubIssues: string; @@ -266,6 +267,7 @@ export const DEFAULT_KEYBOARD_SHORTCUTS: KeyboardShortcuts = { context: 'C', memory: 'Y', settings: 'S', + projectSettings: 'Shift+S', terminal: 'T', ideation: 'I', githubIssues: 'G', @@ -730,6 +732,10 @@ export interface AppState { // Whether to auto-dismiss the indicator after completion (default: true) autoDismissInitScriptIndicatorByProject: Record; + // Use Worktrees Override (per-project, keyed by project path) + // undefined = use global setting, true/false = project-specific override + useWorktreesByProject: Record; + // UI State (previously in localStorage, now synced via API) /** Whether worktree panel is collapsed in board view */ worktreePanelCollapsed: boolean; @@ -1183,6 +1189,11 @@ export interface AppActions { setAutoDismissInitScriptIndicator: (projectPath: string, autoDismiss: boolean) => void; getAutoDismissInitScriptIndicator: (projectPath: string) => boolean; + // Use Worktrees Override actions (per-project) + setProjectUseWorktrees: (projectPath: string, useWorktrees: boolean | null) => void; // null = use global + getProjectUseWorktrees: (projectPath: string) => boolean | undefined; // undefined = using global + getEffectiveUseWorktrees: (projectPath: string) => boolean; // Returns actual value (project or global fallback) + // UI State actions (previously in localStorage, now synced via API) setWorktreePanelCollapsed: (collapsed: boolean) => void; setLastProjectDir: (dir: string) => void; @@ -1343,6 +1354,7 @@ const initialState: AppState = { showInitScriptIndicatorByProject: {}, defaultDeleteBranchByProject: {}, autoDismissInitScriptIndicatorByProject: {}, + useWorktreesByProject: {}, // UI State (previously in localStorage, now synced via API) worktreePanelCollapsed: false, lastProjectDir: '', @@ -3526,6 +3538,31 @@ export const useAppStore = create()((set, get) => ({ return get().autoDismissInitScriptIndicatorByProject[projectPath] ?? true; }, + // Use Worktrees Override actions (per-project) + setProjectUseWorktrees: (projectPath, useWorktrees) => { + const newValue = useWorktrees === null ? undefined : useWorktrees; + set({ + useWorktreesByProject: { + ...get().useWorktreesByProject, + [projectPath]: newValue, + }, + }); + }, + + getProjectUseWorktrees: (projectPath) => { + // Returns undefined if using global setting, true/false if project-specific + return get().useWorktreesByProject[projectPath]; + }, + + getEffectiveUseWorktrees: (projectPath) => { + // Returns the actual value to use (project override or global fallback) + const projectSetting = get().useWorktreesByProject[projectPath]; + if (projectSetting !== undefined) { + return projectSetting; + } + return get().useWorktrees; + }, + // UI State actions (previously in localStorage, now synced via API) setWorktreePanelCollapsed: (collapsed) => set({ worktreePanelCollapsed: collapsed }), setLastProjectDir: (dir) => set({ lastProjectDir: dir }), diff --git a/libs/types/src/settings.ts b/libs/types/src/settings.ts index 6e807f66..0715cfc1 100644 --- a/libs/types/src/settings.ts +++ b/libs/types/src/settings.ts @@ -296,6 +296,8 @@ export interface KeyboardShortcuts { context: string; /** Open settings */ settings: string; + /** Open project settings */ + projectSettings: string; /** Open terminal */ terminal: string; /** Toggle sidebar visibility */ @@ -799,6 +801,7 @@ export const DEFAULT_KEYBOARD_SHORTCUTS: KeyboardShortcuts = { spec: 'D', context: 'C', settings: 'S', + projectSettings: 'Shift+S', terminal: 'T', toggleSidebar: '`', addFeature: 'N', diff --git a/package-lock.json b/package-lock.json index 1f9e8037..66065929 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "automaker", - "version": "0.12.0rc", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "automaker", - "version": "0.12.0rc", + "version": "1.0.0", "hasInstallScript": true, "workspaces": [ "apps/*", @@ -29,7 +29,7 @@ }, "apps/server": { "name": "@automaker/server", - "version": "0.12.0", + "version": "0.11.0", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@anthropic-ai/claude-agent-sdk": "0.1.76", @@ -80,7 +80,7 @@ }, "apps/ui": { "name": "@automaker/ui", - "version": "0.12.0", + "version": "0.11.0", "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", "dependencies": { @@ -2127,76 +2127,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": { @@ -2950,17 +2885,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", @@ -3069,57 +2993,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", @@ -3212,75 +3085,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", @@ -3347,66 +3151,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", @@ -3795,149 +3539,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", @@ -4031,7 +3632,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" @@ -5499,16 +5100,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 +5996,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 +6006,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" @@ -8074,6 +7666,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", @@ -8297,13 +7890,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", @@ -8601,15 +8187,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", @@ -8646,6 +8223,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": { @@ -8922,7 +8500,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" @@ -9212,19 +8790,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", @@ -9325,44 +8890,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", @@ -10810,16 +10337,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hono": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.3.tgz", - "integrity": "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==", - "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", @@ -11585,7 +11102,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11607,7 +11123,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11629,7 +11144,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11651,7 +11165,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11673,7 +11186,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11695,7 +11207,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11717,7 +11228,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11739,7 +11249,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11761,7 +11270,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11783,7 +11291,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11805,7 +11312,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -13363,6 +12869,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", @@ -13393,59 +12900,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", @@ -13975,6 +13429,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": { @@ -14016,7 +13471,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" @@ -14035,7 +13490,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" @@ -14074,65 +13529,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", @@ -14677,21 +14073,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", @@ -14808,7 +14189,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" @@ -14914,352 +14295,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", @@ -15476,6 +14511,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" @@ -15716,30 +14752,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", @@ -15885,21 +14897,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", @@ -15949,20 +14946,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", From 4e53215104f19d18bfcdd8f377d8ddf81ce9db83 Mon Sep 17 00:00:00 2001 From: Stefan de Vogelaere Date: Fri, 16 Jan 2026 22:55:53 +0100 Subject: [PATCH 2/5] chore: reset package-lock.json to match base branch --- package-lock.json | 1038 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 1023 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 66065929..dd96e672 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ }, "apps/server": { "name": "@automaker/server", - "version": "0.11.0", + "version": "0.10.0", "license": "SEE LICENSE IN LICENSE", "dependencies": { "@anthropic-ai/claude-agent-sdk": "0.1.76", @@ -80,7 +80,7 @@ }, "apps/ui": { "name": "@automaker/ui", - "version": "0.11.0", + "version": "0.10.0", "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", "dependencies": { @@ -2127,11 +2127,76 @@ "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": { @@ -2885,6 +2950,17 @@ "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", @@ -2993,6 +3069,57 @@ "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", @@ -3085,6 +3212,75 @@ "@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", @@ -3151,6 +3347,66 @@ "@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", @@ -3539,6 +3795,149 @@ "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", @@ -3632,7 +4031,7 @@ "version": "1.57.0", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "playwright": "1.57.0" @@ -5100,6 +5499,16 @@ "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", @@ -5996,7 +6405,6 @@ "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" @@ -6006,7 +6414,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==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -7666,7 +8074,6 @@ "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", @@ -7890,6 +8297,13 @@ "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", @@ -8187,6 +8601,15 @@ "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", @@ -8223,7 +8646,6 @@ "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": { @@ -8500,7 +8922,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -8790,6 +9212,19 @@ "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", @@ -8890,6 +9325,44 @@ "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", @@ -10337,6 +10810,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hono": { + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.3.tgz", + "integrity": "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==", + "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", @@ -11102,6 +11585,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -11165,6 +11649,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -12869,7 +13354,6 @@ "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", @@ -12900,6 +13384,59 @@ "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", @@ -13429,7 +13966,6 @@ "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": { @@ -13471,7 +14007,7 @@ "version": "1.57.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "playwright-core": "1.57.0" @@ -13490,7 +14026,7 @@ "version": "1.57.0", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -13529,6 +14065,65 @@ "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", @@ -14073,6 +14668,21 @@ "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", @@ -14189,7 +14799,7 @@ "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, + "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -14295,6 +14905,352 @@ "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", @@ -14511,7 +15467,6 @@ "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" @@ -14752,6 +15707,30 @@ "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", @@ -14897,6 +15876,21 @@ "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", @@ -14946,6 +15940,20 @@ "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", From 6a23e6ce78f6184941162646cce373742e04e1dd Mon Sep 17 00:00:00 2001 From: Stefan de Vogelaere Date: Fri, 16 Jan 2026 23:00:47 +0100 Subject: [PATCH 3/5] fix: address PR review feedback - Fix race conditions when rapidly switching projects - Added cancellation logic to prevent stale responses from updating state - Both project settings and init script loading now properly cancelled on unmount - Improve error handling in custom icon upload - Added toast notifications for validation errors (file type, file size) - Added toast notifications for upload success/failure - Handle network errors gracefully with user feedback - Handle file reader errors --- .../project-identity-section.tsx | 44 ++++++++++++++---- .../worktree-preferences-section.tsx | 46 +++++++++++++++---- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/apps/ui/src/components/views/project-settings-view/project-identity-section.tsx b/apps/ui/src/components/views/project-settings-view/project-identity-section.tsx index d938ee73..669b7879 100644 --- a/apps/ui/src/components/views/project-settings-view/project-identity-section.tsx +++ b/apps/ui/src/components/views/project-settings-view/project-identity-section.tsx @@ -8,6 +8,7 @@ import { useAppStore } from '@/store/app-store'; import { IconPicker } from '@/components/layout/project-switcher/components/icon-picker'; import { getAuthenticatedImageUrl } from '@/lib/api-fetch'; import { getHttpApiClient } from '@/lib/http-api-client'; +import { toast } from 'sonner'; import type { Project } from '@/lib/electron'; interface ProjectIdentitySectionProps { @@ -61,11 +62,17 @@ export function ProjectIdentitySection({ project }: ProjectIdentitySectionProps) // Validate file type const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (!validTypes.includes(file.type)) { + toast.error('Invalid file type', { + description: 'Please upload a PNG, JPG, GIF, or WebP image.', + }); return; } // Validate file size (max 2MB for icons) if (file.size > 2 * 1024 * 1024) { + toast.error('File too large', { + description: 'Please upload an image smaller than 2MB.', + }); return; } @@ -74,20 +81,39 @@ export function ProjectIdentitySection({ project }: ProjectIdentitySectionProps) // Convert to base64 const reader = new FileReader(); reader.onload = async () => { - const base64Data = reader.result as string; - const result = await getHttpApiClient().saveImageToTemp( - base64Data, - `project-icon-${file.name}`, - file.type, - project.path - ); - if (result.success && result.path) { - handleCustomIconChange(result.path); + try { + const base64Data = reader.result as string; + const result = await getHttpApiClient().saveImageToTemp( + base64Data, + `project-icon-${file.name}`, + file.type, + project.path + ); + if (result.success && result.path) { + handleCustomIconChange(result.path); + toast.success('Icon uploaded successfully'); + } else { + toast.error('Failed to upload icon', { + description: result.error || 'Please try again.', + }); + } + } catch (error) { + toast.error('Failed to upload icon', { + description: 'Network error. Please try again.', + }); + } finally { + setIsUploadingIcon(false); } + }; + reader.onerror = () => { + toast.error('Failed to read file', { + description: 'Please try again with a different file.', + }); setIsUploadingIcon(false); }; reader.readAsDataURL(file); } catch { + toast.error('Failed to upload icon'); setIsUploadingIcon(false); } }; diff --git a/apps/ui/src/components/views/project-settings-view/worktree-preferences-section.tsx b/apps/ui/src/components/views/project-settings-view/worktree-preferences-section.tsx index af85eb03..c289d382 100644 --- a/apps/ui/src/components/views/project-settings-view/worktree-preferences-section.tsx +++ b/apps/ui/src/components/views/project-settings-view/worktree-preferences-section.tsx @@ -64,35 +64,48 @@ export function WorktreePreferencesSection({ project }: WorktreePreferencesSecti // Load project settings (including useWorktrees) when project changes useEffect(() => { + let isCancelled = false; + const currentPath = project.path; + const loadProjectSettings = async () => { try { const httpClient = getHttpApiClient(); - const response = await httpClient.settings.getProject(project.path); + const response = await httpClient.settings.getProject(currentPath); + + // Avoid updating state if component unmounted or project changed + if (isCancelled) return; + if (response.success && response.settings) { // Sync useWorktrees to store if it has a value if (response.settings.useWorktrees !== undefined) { - setProjectUseWorktrees(project.path, response.settings.useWorktrees); + setProjectUseWorktrees(currentPath, response.settings.useWorktrees); } // Also sync other settings to store if (response.settings.showInitScriptIndicator !== undefined) { - setShowInitScriptIndicator(project.path, response.settings.showInitScriptIndicator); + setShowInitScriptIndicator(currentPath, response.settings.showInitScriptIndicator); } if (response.settings.defaultDeleteBranchWithWorktree !== undefined) { - setDefaultDeleteBranch(project.path, response.settings.defaultDeleteBranchWithWorktree); + setDefaultDeleteBranch(currentPath, response.settings.defaultDeleteBranchWithWorktree); } if (response.settings.autoDismissInitScriptIndicator !== undefined) { setAutoDismissInitScriptIndicator( - project.path, + currentPath, response.settings.autoDismissInitScriptIndicator ); } } } catch (error) { - console.error('Failed to load project settings:', error); + if (!isCancelled) { + console.error('Failed to load project settings:', error); + } } }; loadProjectSettings(); + + return () => { + isCancelled = true; + }; }, [ project.path, setProjectUseWorktrees, @@ -103,12 +116,19 @@ export function WorktreePreferencesSection({ project }: WorktreePreferencesSecti // Load init script content when project changes useEffect(() => { + let isCancelled = false; + const currentPath = project.path; + const loadInitScript = async () => { setIsLoading(true); try { const response = await apiGet( - `/api/worktree/init-script?projectPath=${encodeURIComponent(project.path)}` + `/api/worktree/init-script?projectPath=${encodeURIComponent(currentPath)}` ); + + // Avoid updating state if component unmounted or project changed + if (isCancelled) return; + if (response.success) { const content = response.content || ''; setScriptContent(content); @@ -116,13 +136,21 @@ export function WorktreePreferencesSection({ project }: WorktreePreferencesSecti setScriptExists(response.exists); } } catch (error) { - console.error('Failed to load init script:', error); + if (!isCancelled) { + console.error('Failed to load init script:', error); + } } finally { - setIsLoading(false); + if (!isCancelled) { + setIsLoading(false); + } } }; loadInitScript(); + + return () => { + isCancelled = true; + }; }, [project.path]); // Save script From 8b7700364dd779c0410de761984de01784a08597 Mon Sep 17 00:00:00 2001 From: Stefan de Vogelaere Date: Fri, 16 Jan 2026 23:17:50 +0100 Subject: [PATCH 4/5] refactor: move project settings to Project section, rename global settings - Move "Settings" from Tools section to Project section in sidebar - Rename bottom settings link from "Settings" to "Global Settings" - Update keyboard shortcut description accordingly --- .../sidebar/components/sidebar-footer.tsx | 6 +++--- .../layout/sidebar/hooks/use-navigation.ts | 18 ++++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/apps/ui/src/components/layout/sidebar/components/sidebar-footer.tsx b/apps/ui/src/components/layout/sidebar/components/sidebar-footer.tsx index 24cdafbf..4f864eea 100644 --- a/apps/ui/src/components/layout/sidebar/components/sidebar-footer.tsx +++ b/apps/ui/src/components/layout/sidebar/components/sidebar-footer.tsx @@ -151,7 +151,7 @@ export function SidebarFooter({ sidebarOpen ? 'justify-start' : 'justify-center', 'hover:scale-[1.02] active:scale-[0.97]' )} - title={!sidebarOpen ? 'Settings' : undefined} + title={!sidebarOpen ? 'Global Settings' : undefined} data-testid="settings-button" > - Settings + Global Settings {sidebarOpen && ( - Settings + Global Settings {formatShortcut(shortcuts.settings, true)} diff --git a/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts b/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts index 2e22537e..cb1399c1 100644 --- a/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts +++ b/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts @@ -123,12 +123,6 @@ export function useNavigation({ icon: Brain, shortcut: shortcuts.memory, }, - { - id: 'project-settings', - label: 'Settings', - icon: Settings, - shortcut: shortcuts.projectSettings, - }, ]; // Filter out hidden items @@ -174,6 +168,14 @@ export function useNavigation({ }); } + // Add Project Settings to Project section + projectItems.push({ + id: 'project-settings', + label: 'Settings', + icon: Settings, + shortcut: shortcuts.projectSettings, + }); + const sections: NavSection[] = [ { label: 'Project', @@ -265,11 +267,11 @@ export function useNavigation({ }); }); - // Add settings shortcut + // Add global settings shortcut shortcutsList.push({ key: shortcuts.settings, action: () => navigate({ to: '/settings' }), - description: 'Navigate to Settings', + description: 'Navigate to Global Settings', }); } From 5436b18f7098ab69c6320c1a166213124e7afe5a Mon Sep 17 00:00:00 2001 From: Stefan de Vogelaere Date: Fri, 16 Jan 2026 23:26:50 +0100 Subject: [PATCH 5/5] refactor: move Project Settings below Tools section in sidebar - Remove Project Settings from Project section - Add Project Settings as standalone section below Tools/GitHub - Use empty label for visual separation without header - Add horizontal separator line above sections without labels - Rename to "Project Settings" for clarity - Keep "Global Settings" at bottom of sidebar --- .../sidebar/components/sidebar-navigation.tsx | 8 ++++++- .../layout/sidebar/hooks/use-navigation.ts | 21 ++++++++++++------- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/apps/ui/src/components/layout/sidebar/components/sidebar-navigation.tsx b/apps/ui/src/components/layout/sidebar/components/sidebar-navigation.tsx index f1671a78..d95f0c3a 100644 --- a/apps/ui/src/components/layout/sidebar/components/sidebar-navigation.tsx +++ b/apps/ui/src/components/layout/sidebar/components/sidebar-navigation.tsx @@ -41,7 +41,13 @@ export function SidebarNavigation({
)} - {section.label && !sidebarOpen &&
} + {/* Separator for sections without label (visual separation) */} + {!section.label && sectionIdx > 0 && sidebarOpen && ( +
+ )} + {(section.label || sectionIdx > 0) && !sidebarOpen && ( +
+ )} {/* Nav Items */}
diff --git a/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts b/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts index cb1399c1..79462ab7 100644 --- a/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts +++ b/apps/ui/src/components/layout/sidebar/hooks/use-navigation.ts @@ -168,14 +168,6 @@ export function useNavigation({ }); } - // Add Project Settings to Project section - projectItems.push({ - id: 'project-settings', - label: 'Settings', - icon: Settings, - shortcut: shortcuts.projectSettings, - }); - const sections: NavSection[] = [ { label: 'Project', @@ -209,6 +201,19 @@ export function useNavigation({ }); } + // Add Project Settings as a standalone section (no label for visual separation) + sections.push({ + label: '', + items: [ + { + id: 'project-settings', + label: 'Project Settings', + icon: Settings, + shortcut: shortcuts.projectSettings, + }, + ], + }); + return sections; }, [ shortcuts,