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 */}
+
+ {/* Mobile close button */}
+
+ Navigation
+
+
+
+
+
+
+ {PROJECT_SETTINGS_NAV_ITEMS.map((item) => {
+ const Icon = item.icon;
+ const isActive = activeSection === item.id;
+ const isDanger = item.id === 'danger';
+
+ return (
+
onNavigate(item.id)}
+ className={cn(
+ 'group w-full flex items-center gap-2.5 px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-200 ease-out text-left relative overflow-hidden',
+ isActive
+ ? [
+ isDanger
+ ? 'bg-gradient-to-r from-red-500/15 via-red-500/10 to-red-600/5'
+ : 'bg-gradient-to-r from-brand-500/15 via-brand-500/10 to-brand-600/5',
+ 'text-foreground',
+ isDanger ? 'border border-red-500/25' : 'border border-brand-500/25',
+ isDanger ? 'shadow-sm shadow-red-500/5' : 'shadow-sm shadow-brand-500/5',
+ ]
+ : [
+ 'text-muted-foreground hover:text-foreground',
+ 'hover:bg-accent/50',
+ 'border border-transparent hover:border-border/40',
+ ],
+ 'hover:scale-[1.01] active:scale-[0.98]'
+ )}
+ >
+ {/* Active indicator bar */}
+ {isActive && (
+
+ )}
+
+ {item.label}
+
+ );
+ })}
+
+
+ >
+ );
+}
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 (
+
+
+
+
+ Customize how your project appears in the sidebar and project switcher.
+
+
+
+ {/* Project Name */}
+
+ Project Name
+ handleNameChange(e.target.value)}
+ placeholder="Enter project name"
+ />
+
+
+ {/* Project Icon */}
+
+
Project Icon
+
+ Choose a preset icon or upload a custom image
+
+
+ {/* Custom Icon Upload */}
+
+
+ {customIconPath ? (
+
+
+
+
+
+
+ ) : (
+
+
+
+ )}
+
+
+
fileInputRef.current?.click()}
+ disabled={isUploadingIcon}
+ className="gap-1.5"
+ >
+
+ {isUploadingIcon ? 'Uploading...' : 'Upload Custom 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 */}
+
setShowNavigation(!showNavigation)}
+ className="lg:hidden h-8 w-8 p-0"
+ aria-label="Toggle navigation menu"
+ >
+
+
+
+
+
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 (
+
+
+
+
+ Customize the theme for this project.
+
+
+
+ {/* Use Global Theme Toggle */}
+
+
+
+
+
+ Use Global Theme
+
+
+ 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 && (
+
+
+
Project Theme
+ {/* Dark/Light Tabs */}
+
+ setActiveTab('dark')}
+ className={cn(
+ 'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all duration-200',
+ activeTab === 'dark'
+ ? 'bg-brand-500 text-white shadow-sm'
+ : 'text-muted-foreground hover:text-foreground'
+ )}
+ >
+
+ Dark
+
+ setActiveTab('light')}
+ className={cn(
+ 'flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-all duration-200',
+ activeTab === 'light'
+ ? 'bg-brand-500 text-white shadow-sm'
+ : 'text-muted-foreground hover:text-foreground'
+ )}
+ >
+
+ Light
+
+
+
+
+ {themesToShow.map(({ value, label, Icon, testId, color }) => {
+ const isActive = effectiveTheme === value;
+ return (
+ handleThemeChange(value)}
+ className={cn(
+ 'group flex items-center justify-center gap-2.5 px-4 py-3.5 rounded-xl',
+ 'text-sm font-medium transition-all duration-200 ease-out',
+ isActive
+ ? [
+ 'bg-gradient-to-br from-brand-500/15 to-brand-600/10',
+ 'border-2 border-brand-500/40',
+ 'text-foreground',
+ 'shadow-md shadow-brand-500/10',
+ ]
+ : [
+ 'bg-accent/30 hover:bg-accent/50',
+ 'border border-border/50 hover:border-border',
+ 'text-muted-foreground hover:text-foreground',
+ 'hover:shadow-sm',
+ ],
+ 'hover:scale-[1.02] active:scale-[0.98]'
+ )}
+ data-testid={`project-${testId}`}
+ >
+
+ {label}
+
+ );
+ })}
+
+
+ )}
+
+ {/* 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"
+ />
+
+
+
+ Enable Git Worktree Isolation
+
+
+ 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"
+ />
+
+
+
+ Show Init Script Indicator
+
+
+ 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"
+ />
+
+
+ Auto-dismiss After Completion
+
+
+ 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"
+ />
+
+
+
+ Delete Branch by Default
+
+
+ When deleting a worktree, automatically check the "Also delete the branch" option.
+
+
+
+
+ {/* Separator */}
+
+
+ {/* Init Script Section */}
+
+
+
+
+ Initialization Script
+
+
+
+ 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 */}
+
+
+
+ Reset
+
+
+ {isDeleting ? (
+
+ ) : (
+
+ )}
+ Delete
+
+
+ {isSaving ? (
+
+ ) : (
+
+ )}
+ Save
+
+
+ >
+ )}
+
+
+
+ );
+}
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 && (
-
-
-
- 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 ? (
-
-
-
-
-
-
- ) : (
-
-
-
- )}
-
-
-
fileInputRef.current?.click()}
- disabled={isUploadingIcon}
- className="gap-1.5"
- >
-
- {isUploadingIcon ? 'Uploading...' : 'Upload Custom Icon'}
-
-
- PNG, JPG, GIF or WebP. Max 2MB.
-
-
-
-
-
- {/* Preset Icon Picker - only show if no custom icon */}
- {!customIconPath && (
-
- )}
-
-
-
- )}
-
{/* Theme Section */}
-
- Theme{' '}
-
- {currentProject ? `(for ${currentProject.name})` : '(Global)'}
-
-
+
Theme
{/* 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"
- />
-
-
-
- Show Init Script Indicator
-
-
- 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"
- />
-
-
- Auto-dismiss After Completion
-
-
- 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"
- />
-
-
-
- Delete Branch by Default
-
-
- When deleting a worktree, automatically check the "Also delete the branch" option.
-
-
-
- )}
-
- {/* Separator */}
-
-
- {/* Init Script Section */}
-
-
-
-
- Initialization Script
-
-
-
- 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 */}
-
-
-
- Reset
-
-
- {isDeleting ? (
-
- ) : (
-
- )}
- Delete
-
-
- {isSaving ? (
-
- ) : (
-
- )}
- Save
-
-
- >
- )}
- >
- ) : (
-
- 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",