Merge remote-tracking branch 'upstream/v0.11.0rc' into patchcraft

This commit is contained in:
DhanushSantosh
2026-01-14 00:55:46 +05:30
20 changed files with 341 additions and 209 deletions

View File

@@ -129,10 +129,30 @@ export const TOOL_PRESETS = {
specGeneration: ['Read', 'Glob', 'Grep'] as const, specGeneration: ['Read', 'Glob', 'Grep'] as const,
/** Full tool access for feature implementation */ /** Full tool access for feature implementation */
fullAccess: ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch', 'TodoWrite'] as const, fullAccess: [
'Read',
'Write',
'Edit',
'Glob',
'Grep',
'Bash',
'WebSearch',
'WebFetch',
'TodoWrite',
] as const,
/** Tools for chat/interactive mode */ /** Tools for chat/interactive mode */
chat: ['Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash', 'WebSearch', 'WebFetch', 'TodoWrite'] as const, chat: [
'Read',
'Write',
'Edit',
'Glob',
'Grep',
'Bash',
'WebSearch',
'WebFetch',
'TodoWrite',
] as const,
} as const; } as const;
/** /**

View File

@@ -70,6 +70,8 @@ const eslintConfig = defineConfig([
AbortSignal: 'readonly', AbortSignal: 'readonly',
Audio: 'readonly', Audio: 'readonly',
ScrollBehavior: 'readonly', ScrollBehavior: 'readonly',
URL: 'readonly',
URLSearchParams: 'readonly',
// Timers // Timers
setTimeout: 'readonly', setTimeout: 'readonly',
setInterval: 'readonly', setInterval: 'readonly',

View File

@@ -422,6 +422,31 @@ export function BoardView() {
const selectedWorktreeBranch = const selectedWorktreeBranch =
currentWorktreeBranch || worktrees.find((w) => w.isMain)?.branch || 'main'; currentWorktreeBranch || worktrees.find((w) => w.isMain)?.branch || 'main';
// Helper function to add and select a worktree
const addAndSelectWorktree = useCallback(
(worktreeResult: { path: string; branch: string }) => {
if (!currentProject) return;
const currentWorktrees = getWorktrees(currentProject.path);
const existingWorktree = currentWorktrees.find((w) => w.branch === worktreeResult.branch);
// Only add if it doesn't already exist (to avoid duplicates)
if (!existingWorktree) {
const newWorktreeInfo = {
path: worktreeResult.path,
branch: worktreeResult.branch,
isMain: false,
isCurrent: false,
hasWorktree: true,
};
setWorktrees(currentProject.path, [...currentWorktrees, newWorktreeInfo]);
}
// Select the worktree (whether it existed or was just added)
setCurrentWorktree(currentProject.path, worktreeResult.path, worktreeResult.branch);
},
[currentProject, getWorktrees, setWorktrees, setCurrentWorktree]
);
// Extract all action handlers into a hook // Extract all action handlers into a hook
const { const {
handleAddFeature, handleAddFeature,
@@ -467,43 +492,90 @@ export function BoardView() {
outputFeature, outputFeature,
projectPath: currentProject?.path || null, projectPath: currentProject?.path || null,
onWorktreeCreated: () => setWorktreeRefreshKey((k) => k + 1), onWorktreeCreated: () => setWorktreeRefreshKey((k) => k + 1),
onWorktreeAutoSelect: (newWorktree) => { onWorktreeAutoSelect: addAndSelectWorktree,
if (!currentProject) return;
// Check if worktree already exists in the store (by branch name)
const currentWorktrees = getWorktrees(currentProject.path);
const existingWorktree = currentWorktrees.find((w) => w.branch === newWorktree.branch);
// Only add if it doesn't already exist (to avoid duplicates)
if (!existingWorktree) {
const newWorktreeInfo = {
path: newWorktree.path,
branch: newWorktree.branch,
isMain: false,
isCurrent: false,
hasWorktree: true,
};
setWorktrees(currentProject.path, [...currentWorktrees, newWorktreeInfo]);
}
// Select the worktree (whether it existed or was just added)
setCurrentWorktree(currentProject.path, newWorktree.path, newWorktree.branch);
},
currentWorktreeBranch, currentWorktreeBranch,
}); });
// Handler for bulk updating multiple features // Handler for bulk updating multiple features
const handleBulkUpdate = useCallback( const handleBulkUpdate = useCallback(
async (updates: Partial<Feature>) => { async (updates: Partial<Feature>, workMode: 'current' | 'auto' | 'custom') => {
if (!currentProject || selectedFeatureIds.size === 0) return; if (!currentProject || selectedFeatureIds.size === 0) return;
try { try {
// Determine final branch name based on work mode:
// - 'current': Empty string to clear branch assignment (work on main/current branch)
// - 'auto': Auto-generate branch name based on current branch
// - 'custom': Use the provided branch name
let finalBranchName: string | undefined;
if (workMode === 'current') {
// Empty string clears the branch assignment, moving features to main/current branch
finalBranchName = '';
} else if (workMode === 'auto') {
// Auto-generate a branch name based on current branch and timestamp
const baseBranch =
currentWorktreeBranch || getPrimaryWorktreeBranch(currentProject.path) || 'main';
const timestamp = Date.now();
const randomSuffix = Math.random().toString(36).substring(2, 6);
finalBranchName = `feature/${baseBranch}-${timestamp}-${randomSuffix}`;
} else {
// Custom mode - use provided branch name
finalBranchName = updates.branchName || undefined;
}
// Create worktree for 'auto' or 'custom' modes when we have a branch name
if ((workMode === 'auto' || workMode === 'custom') && finalBranchName) {
try {
const electronApi = getElectronAPI();
if (electronApi?.worktree?.create) {
const result = await electronApi.worktree.create(
currentProject.path,
finalBranchName
);
if (result.success && result.worktree) {
logger.info(
`Worktree for branch "${finalBranchName}" ${
result.worktree?.isNew ? 'created' : 'already exists'
}`
);
// Auto-select the worktree when creating/using it for bulk update
addAndSelectWorktree(result.worktree);
// Refresh worktree list in UI
setWorktreeRefreshKey((k) => k + 1);
} else if (!result.success) {
logger.error(
`Failed to create worktree for branch "${finalBranchName}":`,
result.error
);
toast.error('Failed to create worktree', {
description: result.error || 'An error occurred',
});
return; // Don't proceed with update if worktree creation failed
}
}
} catch (error) {
logger.error('Error creating worktree:', error);
toast.error('Failed to create worktree', {
description: error instanceof Error ? error.message : 'An error occurred',
});
return; // Don't proceed with update if worktree creation failed
}
}
// Use the final branch name in updates
const finalUpdates = {
...updates,
branchName: finalBranchName,
};
const api = getHttpApiClient(); const api = getHttpApiClient();
const featureIds = Array.from(selectedFeatureIds); const featureIds = Array.from(selectedFeatureIds);
const result = await api.features.bulkUpdate(currentProject.path, featureIds, updates); const result = await api.features.bulkUpdate(currentProject.path, featureIds, finalUpdates);
if (result.success) { if (result.success) {
// Update local state // Update local state
featureIds.forEach((featureId) => { featureIds.forEach((featureId) => {
updateFeature(featureId, updates); updateFeature(featureId, finalUpdates);
}); });
toast.success(`Updated ${result.updatedCount} features`); toast.success(`Updated ${result.updatedCount} features`);
exitSelectionMode(); exitSelectionMode();
@@ -517,7 +589,16 @@ export function BoardView() {
toast.error('Failed to update features'); toast.error('Failed to update features');
} }
}, },
[currentProject, selectedFeatureIds, updateFeature, exitSelectionMode] [
currentProject,
selectedFeatureIds,
updateFeature,
exitSelectionMode,
currentWorktreeBranch,
getPrimaryWorktreeBranch,
addAndSelectWorktree,
setWorktreeRefreshKey,
]
); );
// Handler for bulk deleting multiple features // Handler for bulk deleting multiple features
@@ -1325,6 +1406,9 @@ export function BoardView() {
onClose={() => setShowMassEditDialog(false)} onClose={() => setShowMassEditDialog(false)}
selectedFeatures={selectedFeatures} selectedFeatures={selectedFeatures}
onApply={handleBulkUpdate} onApply={handleBulkUpdate}
branchSuggestions={branchSuggestions}
branchCardCounts={branchCardCounts}
currentBranch={currentWorktreeBranch || undefined}
/> />
{/* Board Background Modal */} {/* Board Background Modal */}

View File

@@ -138,7 +138,7 @@ export function BoardHeader({
<div className={controlContainerClass} data-testid="worktrees-toggle-container"> <div className={controlContainerClass} data-testid="worktrees-toggle-container">
<GitBranch className="w-4 h-4 text-muted-foreground" /> <GitBranch className="w-4 h-4 text-muted-foreground" />
<Label htmlFor="worktrees-toggle" className="text-sm font-medium cursor-pointer"> <Label htmlFor="worktrees-toggle" className="text-sm font-medium cursor-pointer">
Worktrees Worktree Bar
</Label> </Label>
<Switch <Switch
id="worktrees-toggle" id="worktrees-toggle"

View File

@@ -123,7 +123,7 @@ interface AddFeatureDialogProps {
selectedNonMainWorktreeBranch?: string; selectedNonMainWorktreeBranch?: string;
/** /**
* When true, forces the dialog to default to 'current' work mode (work on current branch). * When true, forces the dialog to default to 'current' work mode (work on current branch).
* This is used when the "Use selected worktree branch" setting is disabled. * This is used when the "Default to worktree mode" setting is disabled.
*/ */
forceCurrentBranchMode?: boolean; forceCurrentBranchMode?: boolean;
} }

View File

@@ -419,7 +419,7 @@ export function BacklogPlanDialog({
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="py-4">{renderContent()}</div> <div className="py-4 overflow-y-auto">{renderContent()}</div>
<DialogFooter> <DialogFooter>
{mode === 'input' && ( {mode === 'input' && (

View File

@@ -117,7 +117,7 @@ export function CreatePRDialog({
description: `PR already exists for ${result.result.branch}`, description: `PR already exists for ${result.result.branch}`,
action: { action: {
label: 'View PR', label: 'View PR',
onClick: () => window.open(result.result!.prUrl!, '_blank'), onClick: () => window.open(result.result!.prUrl!, '_blank', 'noopener,noreferrer'),
}, },
}); });
} else { } else {
@@ -125,7 +125,7 @@ export function CreatePRDialog({
description: `PR created from ${result.result.branch}`, description: `PR created from ${result.result.branch}`,
action: { action: {
label: 'View PR', label: 'View PR',
onClick: () => window.open(result.result!.prUrl!, '_blank'), onClick: () => window.open(result.result!.prUrl!, '_blank', 'noopener,noreferrer'),
}, },
}); });
} }
@@ -251,7 +251,10 @@ export function CreatePRDialog({
<p className="text-sm text-muted-foreground mt-1">Your PR is ready for review</p> <p className="text-sm text-muted-foreground mt-1">Your PR is ready for review</p>
</div> </div>
<div className="flex gap-2 justify-center"> <div className="flex gap-2 justify-center">
<Button onClick={() => window.open(prUrl, '_blank')} className="gap-2"> <Button
onClick={() => window.open(prUrl, '_blank', 'noopener,noreferrer')}
className="gap-2"
>
<ExternalLink className="w-4 h-4" /> <ExternalLink className="w-4 h-4" />
View Pull Request View Pull Request
</Button> </Button>
@@ -277,7 +280,7 @@ export function CreatePRDialog({
<Button <Button
onClick={() => { onClick={() => {
if (browserUrl) { if (browserUrl) {
window.open(browserUrl, '_blank'); window.open(browserUrl, '_blank', 'noopener,noreferrer');
} }
}} }}
className="gap-2 w-full" className="gap-2 w-full"

View File

@@ -13,7 +13,8 @@ import { Label } from '@/components/ui/label';
import { AlertCircle } from 'lucide-react'; import { AlertCircle } from 'lucide-react';
import { modelSupportsThinking } from '@/lib/utils'; import { modelSupportsThinking } from '@/lib/utils';
import { Feature, ModelAlias, ThinkingLevel, PlanningMode } from '@/store/app-store'; import { Feature, ModelAlias, ThinkingLevel, PlanningMode } from '@/store/app-store';
import { TestingTabContent, PrioritySelect, PlanningModeSelect } from '../shared'; import { TestingTabContent, PrioritySelect, PlanningModeSelect, WorkModeSelector } from '../shared';
import type { WorkMode } from '../shared';
import { PhaseModelSelector } from '@/components/views/settings-view/model-defaults/phase-model-selector'; import { PhaseModelSelector } from '@/components/views/settings-view/model-defaults/phase-model-selector';
import { isCursorModel, isClaudeModel, type PhaseModelEntry } from '@automaker/types'; import { isCursorModel, isClaudeModel, type PhaseModelEntry } from '@automaker/types';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
@@ -23,7 +24,10 @@ interface MassEditDialogProps {
open: boolean; open: boolean;
onClose: () => void; onClose: () => void;
selectedFeatures: Feature[]; selectedFeatures: Feature[];
onApply: (updates: Partial<Feature>) => Promise<void>; onApply: (updates: Partial<Feature>, workMode: WorkMode) => Promise<void>;
branchSuggestions: string[];
branchCardCounts?: Record<string, number>;
currentBranch?: string;
} }
interface ApplyState { interface ApplyState {
@@ -33,6 +37,7 @@ interface ApplyState {
requirePlanApproval: boolean; requirePlanApproval: boolean;
priority: boolean; priority: boolean;
skipTests: boolean; skipTests: boolean;
branchName: boolean;
} }
function getMixedValues(features: Feature[]): Record<string, boolean> { function getMixedValues(features: Feature[]): Record<string, boolean> {
@@ -47,6 +52,7 @@ function getMixedValues(features: Feature[]): Record<string, boolean> {
), ),
priority: !features.every((f) => f.priority === first.priority), priority: !features.every((f) => f.priority === first.priority),
skipTests: !features.every((f) => f.skipTests === first.skipTests), skipTests: !features.every((f) => f.skipTests === first.skipTests),
branchName: !features.every((f) => f.branchName === first.branchName),
}; };
} }
@@ -97,7 +103,15 @@ function FieldWrapper({ label, isMixed, willApply, onApplyChange, children }: Fi
); );
} }
export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: MassEditDialogProps) { export function MassEditDialog({
open,
onClose,
selectedFeatures,
onApply,
branchSuggestions,
branchCardCounts,
currentBranch,
}: MassEditDialogProps) {
const [isApplying, setIsApplying] = useState(false); const [isApplying, setIsApplying] = useState(false);
// Track which fields to apply // Track which fields to apply
@@ -108,6 +122,7 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
requirePlanApproval: false, requirePlanApproval: false,
priority: false, priority: false,
skipTests: false, skipTests: false,
branchName: false,
}); });
// Field values // Field values
@@ -118,6 +133,18 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
const [priority, setPriority] = useState(2); const [priority, setPriority] = useState(2);
const [skipTests, setSkipTests] = useState(false); const [skipTests, setSkipTests] = useState(false);
// Work mode and branch name state
const [workMode, setWorkMode] = useState<WorkMode>(() => {
// Derive initial work mode from first selected feature's branchName
if (selectedFeatures.length > 0 && selectedFeatures[0].branchName) {
return 'custom';
}
return 'current';
});
const [branchName, setBranchName] = useState(() => {
return getInitialValue(selectedFeatures, 'branchName', '') as string;
});
// Calculate mixed values // Calculate mixed values
const mixedValues = useMemo(() => getMixedValues(selectedFeatures), [selectedFeatures]); const mixedValues = useMemo(() => getMixedValues(selectedFeatures), [selectedFeatures]);
@@ -131,6 +158,7 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
requirePlanApproval: false, requirePlanApproval: false,
priority: false, priority: false,
skipTests: false, skipTests: false,
branchName: false,
}); });
setModel(getInitialValue(selectedFeatures, 'model', 'sonnet') as ModelAlias); setModel(getInitialValue(selectedFeatures, 'model', 'sonnet') as ModelAlias);
setThinkingLevel(getInitialValue(selectedFeatures, 'thinkingLevel', 'none') as ThinkingLevel); setThinkingLevel(getInitialValue(selectedFeatures, 'thinkingLevel', 'none') as ThinkingLevel);
@@ -138,6 +166,10 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
setRequirePlanApproval(getInitialValue(selectedFeatures, 'requirePlanApproval', false)); setRequirePlanApproval(getInitialValue(selectedFeatures, 'requirePlanApproval', false));
setPriority(getInitialValue(selectedFeatures, 'priority', 2)); setPriority(getInitialValue(selectedFeatures, 'priority', 2));
setSkipTests(getInitialValue(selectedFeatures, 'skipTests', false)); setSkipTests(getInitialValue(selectedFeatures, 'skipTests', false));
// Reset work mode and branch name
const initialBranchName = getInitialValue(selectedFeatures, 'branchName', '') as string;
setBranchName(initialBranchName);
setWorkMode(initialBranchName ? 'custom' : 'current');
} }
}, [open, selectedFeatures]); }, [open, selectedFeatures]);
@@ -150,6 +182,12 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
if (applyState.requirePlanApproval) updates.requirePlanApproval = requirePlanApproval; if (applyState.requirePlanApproval) updates.requirePlanApproval = requirePlanApproval;
if (applyState.priority) updates.priority = priority; if (applyState.priority) updates.priority = priority;
if (applyState.skipTests) updates.skipTests = skipTests; if (applyState.skipTests) updates.skipTests = skipTests;
if (applyState.branchName) {
// For 'current' mode, use empty string (work on current branch)
// For 'auto' mode, use empty string (will be auto-generated)
// For 'custom' mode, use the specified branch name
updates.branchName = workMode === 'custom' ? branchName : '';
}
if (Object.keys(updates).length === 0) { if (Object.keys(updates).length === 0) {
onClose(); onClose();
@@ -158,7 +196,7 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
setIsApplying(true); setIsApplying(true);
try { try {
await onApply(updates); await onApply(updates, workMode);
onClose(); onClose();
} finally { } finally {
setIsApplying(false); setIsApplying(false);
@@ -293,6 +331,25 @@ export function MassEditDialog({ open, onClose, selectedFeatures, onApply }: Mas
testIdPrefix="mass-edit" testIdPrefix="mass-edit"
/> />
</FieldWrapper> </FieldWrapper>
{/* Branch / Work Mode */}
<FieldWrapper
label="Branch / Work Mode"
isMixed={mixedValues.branchName}
willApply={applyState.branchName}
onApplyChange={(apply) => setApplyState((prev) => ({ ...prev, branchName: apply }))}
>
<WorkModeSelector
workMode={workMode}
onWorkModeChange={setWorkMode}
branchName={branchName}
onBranchNameChange={setBranchName}
branchSuggestions={branchSuggestions}
branchCardCounts={branchCardCounts}
currentBranch={currentBranch}
testIdPrefix="mass-edit-work-mode"
/>
</FieldWrapper>
</div> </div>
<DialogFooter> <DialogFooter>

View File

@@ -45,7 +45,7 @@ export function PlanSettingsDialog({
className="text-sm font-medium cursor-pointer flex items-center gap-2" className="text-sm font-medium cursor-pointer flex items-center gap-2"
> >
<GitBranch className="w-4 h-4 text-brand-500" /> <GitBranch className="w-4 h-4 text-brand-500" />
Use selected worktree branch Default to worktree mode
</Label> </Label>
<Switch <Switch
id="plan-worktree-branch-toggle" id="plan-worktree-branch-toggle"
@@ -55,8 +55,8 @@ export function PlanSettingsDialog({
/> />
</div> </div>
<p className="text-xs text-muted-foreground leading-relaxed"> <p className="text-xs text-muted-foreground leading-relaxed">
When enabled, features created via the Plan dialog will be assigned to the currently Planned features will automatically use isolated worktrees, keeping changes separate
selected worktree branch. When disabled, features will be added to the main branch. from your main branch until you're ready to merge.
</p> </p>
</div> </div>
</div> </div>

View File

@@ -45,7 +45,7 @@ export function WorktreeSettingsDialog({
className="text-sm font-medium cursor-pointer flex items-center gap-2" className="text-sm font-medium cursor-pointer flex items-center gap-2"
> >
<GitBranch className="w-4 h-4 text-brand-500" /> <GitBranch className="w-4 h-4 text-brand-500" />
Use selected worktree branch Default to worktree mode
</Label> </Label>
<Switch <Switch
id="worktree-branch-toggle" id="worktree-branch-toggle"
@@ -55,8 +55,8 @@ export function WorktreeSettingsDialog({
/> />
</div> </div>
<p className="text-xs text-muted-foreground leading-relaxed"> <p className="text-xs text-muted-foreground leading-relaxed">
When enabled, the Add Feature dialog will default to custom branch mode with the New features will automatically use isolated worktrees, keeping changes separate
currently selected worktree branch pre-filled. from your main branch until you're ready to merge.
</p> </p>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,7 @@ import { Badge } from '@/components/ui/badge';
import { Brain, AlertTriangle } from 'lucide-react'; import { Brain, AlertTriangle } from 'lucide-react';
import { AnthropicIcon, CursorIcon, OpenAIIcon } from '@/components/ui/provider-icon'; import { AnthropicIcon, CursorIcon, OpenAIIcon } from '@/components/ui/provider-icon';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import type { ModelAlias } from '@/store/app-store';
import { useAppStore } from '@/store/app-store'; import { useAppStore } from '@/store/app-store';
import { useSetupStore } from '@/store/setup-store'; import { useSetupStore } from '@/store/setup-store';
import { getModelProvider, PROVIDER_PREFIXES, stripProviderPrefix } from '@automaker/types'; import { getModelProvider, PROVIDER_PREFIXES, stripProviderPrefix } from '@automaker/types';
@@ -18,10 +19,6 @@ interface ModelSelectorProps {
testIdPrefix?: string; testIdPrefix?: string;
} }
const CODEX_EMPTY_AVAILABLE_MESSAGE = 'No Codex models available';
const CODEX_EMPTY_ENABLED_MESSAGE =
'No Codex models enabled. Enable models in Settings → AI Providers.';
export function ModelSelector({ export function ModelSelector({
selectedModel, selectedModel,
onModelSelect, onModelSelect,
@@ -30,8 +27,6 @@ export function ModelSelector({
const { const {
enabledCursorModels, enabledCursorModels,
cursorDefaultModel, cursorDefaultModel,
enabledCodexModels,
codexDefaultModel,
codexModels, codexModels,
codexModelsLoading, codexModelsLoading,
codexModelsError, codexModelsError,
@@ -54,10 +49,8 @@ export function ModelSelector({
} }
}, [isCodexAvailable, codexModels.length, codexModelsLoading, fetchCodexModels]); }, [isCodexAvailable, codexModels.length, codexModelsLoading, fetchCodexModels]);
const enabledCodexModelIds = new Set(enabledCodexModels);
// Transform codex models from store to ModelOption format // Transform codex models from store to ModelOption format
const codexModelOptions: ModelOption[] = codexModels.map((model) => { const dynamicCodexModels: ModelOption[] = codexModels.map((model) => {
// Infer badge based on tier // Infer badge based on tier
let badge: string | undefined; let badge: string | undefined;
if (model.tier === 'premium') badge = 'Premium'; if (model.tier === 'premium') badge = 'Premium';
@@ -74,10 +67,6 @@ export function ModelSelector({
}; };
}); });
const enabledCodexModelOptions = codexModelOptions.filter((model) =>
enabledCodexModelIds.has(model.id)
);
// Filter Cursor models based on enabled models from global settings // Filter Cursor models based on enabled models from global settings
const filteredCursorModels = CURSOR_MODELS.filter((model) => { const filteredCursorModels = CURSOR_MODELS.filter((model) => {
// Extract the cursor model ID from the prefixed ID (e.g., "cursor-auto" -> "auto") // Extract the cursor model ID from the prefixed ID (e.g., "cursor-auto" -> "auto")
@@ -85,36 +74,21 @@ export function ModelSelector({
return enabledCursorModels.includes(cursorModelId as any); return enabledCursorModels.includes(cursorModelId as any);
}); });
const hasEnabledCodexModels = enabledCodexModelOptions.length > 0;
const codexDefaultSelection =
codexModelOptions.find((model) => model.id === codexDefaultModel)?.id ||
enabledCodexModelOptions[0]?.id ||
codexModelOptions[0]?.id;
const handleProviderChange = (provider: ModelProvider) => { const handleProviderChange = (provider: ModelProvider) => {
if (provider === 'cursor' && selectedProvider !== 'cursor') { if (provider === 'cursor' && selectedProvider !== 'cursor') {
// Switch to Cursor's default model (from global settings) // Switch to Cursor's default model (from global settings)
onModelSelect(`${PROVIDER_PREFIXES.cursor}${cursorDefaultModel}`); onModelSelect(`${PROVIDER_PREFIXES.cursor}${cursorDefaultModel}`);
} else if (provider === 'codex' && selectedProvider !== 'codex') { } else if (provider === 'codex' && selectedProvider !== 'codex') {
// Switch to Codex's default model (from global settings) // Switch to Codex's default model (use isDefault flag from dynamic models)
if (codexDefaultSelection) { const defaultModel = codexModels.find((m) => m.isDefault);
onModelSelect(codexDefaultSelection); const defaultModelId = defaultModel?.id || codexModels[0]?.id || 'codex-gpt-5.2-codex';
} onModelSelect(defaultModelId);
} else if (provider === 'claude' && selectedProvider !== 'claude') { } else if (provider === 'claude' && selectedProvider !== 'claude') {
// Switch to Claude's default model // Switch to Claude's default model
onModelSelect('sonnet'); onModelSelect('sonnet');
} }
}; };
const showCodexAvailableEmpty =
!codexModelsLoading && !codexModelsError && codexModelOptions.length === 0;
const showCodexEnabledEmpty =
!codexModelsLoading &&
!codexModelsError &&
codexModelOptions.length > 0 &&
!hasEnabledCodexModels;
const showCodexList = !codexModelsLoading && !codexModelsError && hasEnabledCodexModels;
return ( return (
<div className="space-y-4"> <div className="space-y-4">
{/* Provider Selection */} {/* Provider Selection */}
@@ -298,7 +272,7 @@ export function ModelSelector({
</div> </div>
{/* Loading state */} {/* Loading state */}
{codexModelsLoading && codexModelOptions.length === 0 && ( {codexModelsLoading && dynamicCodexModels.length === 0 && (
<div className="flex items-center justify-center gap-2 p-6 text-sm text-muted-foreground"> <div className="flex items-center justify-center gap-2 p-6 text-sm text-muted-foreground">
<RefreshCw className="w-4 h-4 animate-spin" /> <RefreshCw className="w-4 h-4 animate-spin" />
Loading models... Loading models...
@@ -323,21 +297,15 @@ export function ModelSelector({
)} )}
{/* Model list */} {/* Model list */}
{showCodexAvailableEmpty && ( {!codexModelsLoading && !codexModelsError && dynamicCodexModels.length === 0 && (
<div className="text-sm text-muted-foreground p-3 border border-dashed rounded-md text-center"> <div className="text-sm text-muted-foreground p-3 border border-dashed rounded-md text-center">
{CODEX_EMPTY_AVAILABLE_MESSAGE} No Codex models available
</div> </div>
)} )}
{showCodexEnabledEmpty && ( {!codexModelsLoading && dynamicCodexModels.length > 0 && (
<div className="text-sm text-muted-foreground p-3 border border-dashed rounded-md text-center">
{CODEX_EMPTY_ENABLED_MESSAGE}
</div>
)}
{showCodexList && (
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
{enabledCodexModelOptions.map((option) => { {dynamicCodexModels.map((option) => {
const isSelected = selectedModel === option.id; const isSelected = selectedModel === option.id;
return ( return (
<button <button

View File

@@ -143,8 +143,12 @@ export function WorktreeActionsDropdown({
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" /> <span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
Dev Server Running (:{devServerInfo?.port}) Dev Server Running (:{devServerInfo?.port})
</DropdownMenuLabel> </DropdownMenuLabel>
<DropdownMenuItem onClick={() => onOpenDevServerUrl(worktree)} className="text-xs"> <DropdownMenuItem
<Globe className="w-3.5 h-3.5 mr-2" /> onClick={() => onOpenDevServerUrl(worktree)}
className="text-xs"
aria-label={`Open dev server on port ${devServerInfo?.port} in browser`}
>
<Globe className="w-3.5 h-3.5 mr-2" aria-hidden="true" />
Open in Browser Open in Browser
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
@@ -320,7 +324,7 @@ export function WorktreeActionsDropdown({
<> <>
<DropdownMenuItem <DropdownMenuItem
onClick={() => { onClick={() => {
window.open(worktree.pr!.url, '_blank'); window.open(worktree.pr!.url, '_blank', 'noopener,noreferrer');
}} }}
className="text-xs" className="text-xs"
> >

View File

@@ -298,20 +298,29 @@ export function WorktreeTab({
)} )}
{isDevServerRunning && ( {isDevServerRunning && (
<Button <TooltipProvider>
variant={isSelected ? 'default' : 'outline'} <Tooltip>
size="sm" <TooltipTrigger asChild>
className={cn( <Button
'h-7 w-7 p-0 rounded-none border-r-0', variant={isSelected ? 'default' : 'outline'}
isSelected && 'bg-primary text-primary-foreground', size="sm"
!isSelected && 'bg-secondary/50 hover:bg-secondary', className={cn(
'text-green-500' 'h-7 w-7 p-0 rounded-none border-r-0',
)} isSelected && 'bg-primary text-primary-foreground',
onClick={() => onOpenDevServerUrl(worktree)} !isSelected && 'bg-secondary/50 hover:bg-secondary',
title={`Open dev server (port ${devServerInfo?.port})`} 'text-green-500'
> )}
<Globe className="w-3 h-3" /> onClick={() => onOpenDevServerUrl(worktree)}
</Button> aria-label={`Open dev server on port ${devServerInfo?.port} in browser`}
>
<Globe className="w-3 h-3" aria-hidden="true" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Open dev server (:{devServerInfo?.port})</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)} )}
<WorktreeActionsDropdown <WorktreeActionsDropdown

View File

@@ -118,8 +118,37 @@ export function useDevServers({ projectPath }: UseDevServersOptions) {
const handleOpenDevServerUrl = useCallback( const handleOpenDevServerUrl = useCallback(
(worktree: WorktreeInfo) => { (worktree: WorktreeInfo) => {
const serverInfo = runningDevServers.get(getWorktreeKey(worktree)); const serverInfo = runningDevServers.get(getWorktreeKey(worktree));
if (serverInfo) { if (!serverInfo) {
window.open(serverInfo.url, '_blank'); logger.warn('No dev server info found for worktree:', getWorktreeKey(worktree));
toast.error('Dev server not found', {
description: 'The dev server may have stopped. Try starting it again.',
});
return;
}
try {
// Rewrite URL hostname to match the current browser's hostname.
// This ensures dev server URLs work when accessing Automaker from
// remote machines (e.g., 192.168.x.x or hostname.local instead of localhost).
const devServerUrl = new URL(serverInfo.url);
// Security: Only allow http/https protocols to prevent potential attacks
// via data:, javascript:, file:, or other dangerous URL schemes
if (devServerUrl.protocol !== 'http:' && devServerUrl.protocol !== 'https:') {
logger.error('Invalid dev server URL protocol:', devServerUrl.protocol);
toast.error('Invalid dev server URL', {
description: 'The server returned an unsupported URL protocol.',
});
return;
}
devServerUrl.hostname = window.location.hostname;
window.open(devServerUrl.toString(), '_blank', 'noopener,noreferrer');
} catch (error) {
logger.error('Failed to parse dev server URL:', error);
toast.error('Failed to open dev server', {
description: 'The server URL could not be processed. Please try again.',
});
} }
}, },
[runningDevServers, getWorktreeKey] [runningDevServers, getWorktreeKey]

View File

@@ -48,7 +48,7 @@ export function AddEditServerDialog({
Configure an MCP server to extend agent capabilities with custom tools. Configure an MCP server to extend agent capabilities with custom tools.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4 overflow-y-auto">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="server-name">Name</Label> <Label htmlFor="server-name">Name</Label>
<Input <Input

View File

@@ -159,9 +159,6 @@ export function PhaseModelSelector({
const expandedCodexTriggerRef = useRef<HTMLDivElement>(null); const expandedCodexTriggerRef = useRef<HTMLDivElement>(null);
const { const {
enabledCursorModels, enabledCursorModels,
enabledCodexModels,
enabledOpencodeModels,
enabledDynamicModelIds,
favoriteModels, favoriteModels,
toggleFavoriteModel, toggleFavoriteModel,
codexModels, codexModels,
@@ -264,14 +261,6 @@ export function PhaseModelSelector({
})); }));
}, [codexModels]); }, [codexModels]);
const availableCodexModels = useMemo(
() =>
transformedCodexModels.filter((model) =>
enabledCodexModels.includes(model.id as CodexModelId)
),
[transformedCodexModels, enabledCodexModels]
);
// Filter Cursor models to only show enabled ones // Filter Cursor models to only show enabled ones
const availableCursorModels = CURSOR_MODELS.filter((model) => { const availableCursorModels = CURSOR_MODELS.filter((model) => {
const cursorId = stripProviderPrefix(model.id) as CursorModelId; const cursorId = stripProviderPrefix(model.id) as CursorModelId;
@@ -377,20 +366,16 @@ export function PhaseModelSelector({
// Combine static and dynamic OpenCode models // Combine static and dynamic OpenCode models
const allOpencodeModels: ModelOption[] = useMemo(() => { const allOpencodeModels: ModelOption[] = useMemo(() => {
// Start with static models // Start with static models
const staticModels = OPENCODE_MODELS.filter((model) => const staticModels = [...OPENCODE_MODELS];
enabledOpencodeModels.includes(model.id)
);
// Add dynamic models (convert ModelDefinition to ModelOption) // Add dynamic models (convert ModelDefinition to ModelOption)
const dynamicModelOptions: ModelOption[] = dynamicOpencodeModels const dynamicModelOptions: ModelOption[] = dynamicOpencodeModels.map((model) => ({
.filter((model) => enabledDynamicModelIds.includes(model.id)) id: model.id,
.map((model) => ({ label: model.name,
id: model.id, description: model.description,
label: model.name, badge: model.tier === 'premium' ? 'Premium' : model.tier === 'basic' ? 'Free' : undefined,
description: model.description, provider: 'opencode' as const,
badge: model.tier === 'premium' ? 'Premium' : model.tier === 'basic' ? 'Free' : undefined, }));
provider: 'opencode' as const,
}));
// Merge, avoiding duplicates (static models take precedence for same ID) // Merge, avoiding duplicates (static models take precedence for same ID)
// In practice, static and dynamic IDs don't overlap // In practice, static and dynamic IDs don't overlap
@@ -398,14 +383,14 @@ export function PhaseModelSelector({
const uniqueDynamic = dynamicModelOptions.filter((m) => !staticIds.has(m.id)); const uniqueDynamic = dynamicModelOptions.filter((m) => !staticIds.has(m.id));
return [...staticModels, ...uniqueDynamic]; return [...staticModels, ...uniqueDynamic];
}, [dynamicOpencodeModels, enabledOpencodeModels, enabledDynamicModelIds]); }, [dynamicOpencodeModels]);
// Group models // Group models
const { favorites, claude, cursor, codex, opencode } = useMemo(() => { const { favorites, claude, cursor, codex, opencode } = useMemo(() => {
const favs: typeof CLAUDE_MODELS = []; const favs: typeof CLAUDE_MODELS = [];
const cModels: typeof CLAUDE_MODELS = []; const cModels: typeof CLAUDE_MODELS = [];
const curModels: typeof CURSOR_MODELS = []; const curModels: typeof CURSOR_MODELS = [];
const codModels: typeof availableCodexModels = []; const codModels: typeof transformedCodexModels = [];
const ocModels: ModelOption[] = []; const ocModels: ModelOption[] = [];
// Process Claude Models // Process Claude Models
@@ -427,7 +412,7 @@ export function PhaseModelSelector({
}); });
// Process Codex Models // Process Codex Models
availableCodexModels.forEach((model) => { transformedCodexModels.forEach((model) => {
if (favoriteModels.includes(model.id)) { if (favoriteModels.includes(model.id)) {
favs.push(model); favs.push(model);
} else { } else {
@@ -451,7 +436,7 @@ export function PhaseModelSelector({
codex: codModels, codex: codModels,
opencode: ocModels, opencode: ocModels,
}; };
}, [favoriteModels, availableCursorModels, availableCodexModels, allOpencodeModels]); }, [favoriteModels, availableCursorModels, transformedCodexModels, allOpencodeModels]);
// Group OpenCode models by model type for better organization // Group OpenCode models by model type for better organization
const opencodeSections = useMemo(() => { const opencodeSections = useMemo(() => {
@@ -468,11 +453,8 @@ export function PhaseModelSelector({
free: {}, free: {},
dynamic: {}, dynamic: {},
}; };
const enabledDynamicProviders = dynamicOpencodeModels.filter((model) =>
enabledDynamicModelIds.includes(model.id)
);
const dynamicProviderById = new Map( const dynamicProviderById = new Map(
enabledDynamicProviders.map((model) => [model.id, model.provider]) dynamicOpencodeModels.map((model) => [model.id, model.provider])
); );
const resolveProviderKey = (modelId: string): string => { const resolveProviderKey = (modelId: string): string => {
@@ -542,10 +524,10 @@ export function PhaseModelSelector({
}).filter(Boolean) as OpencodeSection[]; }).filter(Boolean) as OpencodeSection[];
return builtSections; return builtSections;
}, [opencode, dynamicOpencodeModels, enabledDynamicModelIds]); }, [opencode, dynamicOpencodeModels]);
// Render Codex model item with secondary popover for reasoning effort (only for models that support it) // Render Codex model item with secondary popover for reasoning effort (only for models that support it)
const renderCodexModelItem = (model: (typeof availableCodexModels)[0]) => { const renderCodexModelItem = (model: (typeof transformedCodexModels)[0]) => {
const isSelected = selectedModel === model.id; const isSelected = selectedModel === model.id;
const isFavorite = favoriteModels.includes(model.id); const isFavorite = favoriteModels.includes(model.id);
const hasReasoning = codexModelHasThinking(model.id as CodexModelId); const hasReasoning = codexModelHasThinking(model.id as CodexModelId);
@@ -726,7 +708,7 @@ export function PhaseModelSelector({
}; };
// Render OpenCode model item (simple selector, no thinking/reasoning options) // Render OpenCode model item (simple selector, no thinking/reasoning options)
const renderOpencodeModelItem = (model: ModelOption) => { const renderOpencodeModelItem = (model: (typeof OPENCODE_MODELS)[0]) => {
const isSelected = selectedModel === model.id; const isSelected = selectedModel === model.id;
const isFavorite = favoriteModels.includes(model.id); const isFavorite = favoriteModels.includes(model.id);
@@ -1172,7 +1154,7 @@ export function PhaseModelSelector({
} }
// Codex model // Codex model
if (model.provider === 'codex') { if (model.provider === 'codex') {
return renderCodexModelItem(model as (typeof availableCodexModels)[0]); return renderCodexModelItem(model as (typeof transformedCodexModels)[0]);
} }
// OpenCode model // OpenCode model
if (model.provider === 'opencode') { if (model.provider === 'opencode') {

View File

@@ -50,7 +50,7 @@ export function CreateSpecDialog({
<DialogDescription className="text-muted-foreground">{description}</DialogDescription> <DialogDescription className="text-muted-foreground">{description}</DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4 overflow-y-auto">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Project Overview</label> <label className="text-sm font-medium">Project Overview</label>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">

View File

@@ -51,7 +51,7 @@ export function RegenerateSpecDialog({
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4 overflow-y-auto">
<div className="space-y-2"> <div className="space-y-2">
<label className="text-sm font-medium">Describe your project</label> <label className="text-sm font-medium">Describe your project</label>
<p className="text-xs text-muted-foreground"> <p className="text-xs text-muted-foreground">

View File

@@ -136,12 +136,11 @@ volumes:
## Troubleshooting ## Troubleshooting
| Problem | Solution | | Problem | Solution |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Container won't start | Check `.env` has `ANTHROPIC_API_KEY` set. Run `docker-compose logs` for errors. | | Container won't start | Check `.env` has `ANTHROPIC_API_KEY` set. Run `docker-compose logs` for errors. |
| Can't access web UI | Verify container is running with `docker ps \| grep automaker` | | Can't access web UI | Verify container is running with `docker ps \| grep automaker` |
| Need a fresh start | Run `docker-compose down && docker volume rm automaker-data && docker-compose up -d --build` | | Need a fresh start | Run `docker-compose down && docker volume rm automaker-data && docker-compose up -d --build` |
| Cursor auth fails | Re-extract token with `./scripts/get-cursor-token.sh` - tokens expire periodically. Make sure you've run `cursor-agent login` on your host first. | | Cursor auth fails | Re-extract token with `./scripts/get-cursor-token.sh` - tokens expire periodically. Make sure you've run `cursor-agent login` on your host first. |
| OpenCode not detected | Mount `~/.local/share/opencode` to `/home/automaker/.local/share/opencode`. Make sure you've run `opencode auth login` on your host first. | | OpenCode not detected | Mount `~/.local/share/opencode` to `/home/automaker/.local/share/opencode`. Make sure you've run `opencode auth login` on your host first. |
| File permission errors | Rebuild with `UID=$(id -u) GID=$(id -g) docker-compose build` to match container user to your host user. See [Fixing File Permission Issues](#fixing-file-permission-issues). | | File permission errors | Rebuild with `UID=$(id -u) GID=$(id -g) docker-compose build` to match container user to your host user. See [Fixing File Permission Issues](#fixing-file-permission-issues). |

91
package-lock.json generated
View File

@@ -679,6 +679,7 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.27.1", "@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5", "@babel/generator": "^7.28.5",
@@ -1271,6 +1272,7 @@
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.4.tgz", "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.39.4.tgz",
"integrity": "sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==", "integrity": "sha512-xMF6OfEAUVY5Waega4juo1QGACfNkNF+aJLqpd8oUJz96ms2zbfQ9Gh35/tI3y8akEV31FruKfj7hBnIU/nkqA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@codemirror/state": "^6.5.0", "@codemirror/state": "^6.5.0",
"crelt": "^1.0.6", "crelt": "^1.0.6",
@@ -1313,6 +1315,7 @@
"resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz",
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/accessibility": "^3.1.1",
"@dnd-kit/utilities": "^3.2.2", "@dnd-kit/utilities": "^3.2.2",
@@ -2133,7 +2136,6 @@
"dev": true, "dev": true,
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"optional": true, "optional": true,
"peer": true,
"dependencies": { "dependencies": {
"cross-dirname": "^0.1.0", "cross-dirname": "^0.1.0",
"debug": "^4.3.4", "debug": "^4.3.4",
@@ -2155,7 +2157,6 @@
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true,
"dependencies": { "dependencies": {
"graceful-fs": "^4.2.0", "graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1", "jsonfile": "^6.0.1",
@@ -2172,7 +2173,6 @@
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true,
"dependencies": { "dependencies": {
"universalify": "^2.0.0" "universalify": "^2.0.0"
}, },
@@ -2187,7 +2187,6 @@
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true,
"engines": { "engines": {
"node": ">= 10.0.0" "node": ">= 10.0.0"
} }
@@ -2955,7 +2954,6 @@
"integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true,
"engines": { "engines": {
"node": ">=18" "node": ">=18"
} }
@@ -3080,7 +3078,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -3097,7 +3094,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -3114,7 +3110,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -3223,7 +3218,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -3246,7 +3240,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -3269,7 +3262,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -3355,7 +3347,6 @@
], ],
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true, "optional": true,
"peer": true,
"dependencies": { "dependencies": {
"@emnapi/runtime": "^1.7.0" "@emnapi/runtime": "^1.7.0"
}, },
@@ -3378,7 +3369,6 @@
"os": [ "os": [
"win32" "win32"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -3398,7 +3388,6 @@
"os": [ "os": [
"win32" "win32"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -3798,8 +3787,7 @@
"version": "16.0.10", "version": "16.0.10",
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz", "resolved": "https://registry.npmjs.org/@next/env/-/env-16.0.10.tgz",
"integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==", "integrity": "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/@next/swc-darwin-arm64": { "node_modules/@next/swc-darwin-arm64": {
"version": "16.0.10", "version": "16.0.10",
@@ -3813,7 +3801,6 @@
"os": [ "os": [
"darwin" "darwin"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
@@ -3830,7 +3817,6 @@
"os": [ "os": [
"darwin" "darwin"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
@@ -3847,7 +3833,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
@@ -3864,7 +3849,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
@@ -3881,7 +3865,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
@@ -3898,7 +3881,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
@@ -3915,7 +3897,6 @@
"os": [ "os": [
"win32" "win32"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
@@ -3932,7 +3913,6 @@
"os": [ "os": [
"win32" "win32"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 10" "node": ">= 10"
} }
@@ -4032,6 +4012,7 @@
"integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"playwright": "1.57.0" "playwright": "1.57.0"
}, },
@@ -5472,7 +5453,6 @@
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"tslib": "^2.8.0" "tslib": "^2.8.0"
} }
@@ -5806,6 +5786,7 @@
"resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.141.6.tgz", "resolved": "https://registry.npmjs.org/@tanstack/react-router/-/react-router-1.141.6.tgz",
"integrity": "sha512-qWFxi2D6eGc1L03RzUuhyEOplZ7Q6q62YOl7Of9Y0q4YjwQwxRm4zxwDVtvUIoy4RLVCpqp5UoE+Nxv2PY9trg==", "integrity": "sha512-qWFxi2D6eGc1L03RzUuhyEOplZ7Q6q62YOl7Of9Y0q4YjwQwxRm4zxwDVtvUIoy4RLVCpqp5UoE+Nxv2PY9trg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@tanstack/history": "1.141.0", "@tanstack/history": "1.141.0",
"@tanstack/react-store": "^0.8.0", "@tanstack/react-store": "^0.8.0",
@@ -6232,6 +6213,7 @@
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/body-parser": "*", "@types/body-parser": "*",
"@types/express-serve-static-core": "^5.0.0", "@types/express-serve-static-core": "^5.0.0",
@@ -6374,6 +6356,7 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
"integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"csstype": "^3.2.2" "csstype": "^3.2.2"
} }
@@ -6384,6 +6367,7 @@
"integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"peer": true,
"peerDependencies": { "peerDependencies": {
"@types/react": "^19.2.0" "@types/react": "^19.2.0"
} }
@@ -6489,6 +6473,7 @@
"integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/scope-manager": "8.50.0",
"@typescript-eslint/types": "8.50.0", "@typescript-eslint/types": "8.50.0",
@@ -6982,7 +6967,8 @@
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/@xyflow/react": { "node_modules/@xyflow/react": {
"version": "12.10.0", "version": "12.10.0",
@@ -7080,6 +7066,7 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -7140,6 +7127,7 @@
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0", "fast-json-stable-stringify": "^2.0.0",
@@ -7738,6 +7726,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.9.0", "baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759", "caniuse-lite": "^1.0.30001759",
@@ -8269,8 +8258,7 @@
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/cliui": { "node_modules/cliui": {
"version": "8.0.1", "version": "8.0.1",
@@ -8575,8 +8563,7 @@
"integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true
"peer": true
}, },
"node_modules/cross-env": { "node_modules/cross-env": {
"version": "10.1.0", "version": "10.1.0",
@@ -8673,6 +8660,7 @@
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC", "license": "ISC",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
@@ -8974,6 +8962,7 @@
"integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==", "integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"app-builder-lib": "26.0.12", "app-builder-lib": "26.0.12",
"builder-util": "26.0.11", "builder-util": "26.0.11",
@@ -9300,7 +9289,6 @@
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@electron/asar": "^3.2.1", "@electron/asar": "^3.2.1",
"debug": "^4.1.1", "debug": "^4.1.1",
@@ -9321,7 +9309,6 @@
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0", "jsonfile": "^4.0.0",
@@ -9572,6 +9559,7 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@@ -9886,6 +9874,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"accepts": "^2.0.0", "accepts": "^2.0.0",
"body-parser": "^2.2.1", "body-parser": "^2.2.1",
@@ -11553,7 +11542,6 @@
"os": [ "os": [
"android" "android"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 12.0.0" "node": ">= 12.0.0"
}, },
@@ -11619,7 +11607,6 @@
"os": [ "os": [
"freebsd" "freebsd"
], ],
"peer": true,
"engines": { "engines": {
"node": ">= 12.0.0" "node": ">= 12.0.0"
}, },
@@ -14061,7 +14048,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.6", "nanoid": "^3.3.6",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
@@ -14078,7 +14064,6 @@
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true,
"dependencies": { "dependencies": {
"commander": "^9.4.0" "commander": "^9.4.0"
}, },
@@ -14096,7 +14081,6 @@
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"peer": true,
"engines": { "engines": {
"node": "^12.20.0 || >=14" "node": "^12.20.0 || >=14"
} }
@@ -14285,6 +14269,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
"integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@@ -14294,6 +14279,7 @@
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz",
"integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"scheduler": "^0.27.0" "scheduler": "^0.27.0"
}, },
@@ -14652,7 +14638,6 @@
"deprecated": "Rimraf versions prior to v4 are no longer supported", "deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"peer": true,
"dependencies": { "dependencies": {
"glob": "^7.1.3" "glob": "^7.1.3"
}, },
@@ -14841,6 +14826,7 @@
"resolved": "https://registry.npmjs.org/seroval/-/seroval-1.4.0.tgz", "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.4.0.tgz",
"integrity": "sha512-BdrNXdzlofomLTiRnwJTSEAaGKyHHZkbMXIywOh7zlzp4uZnXErEwl9XZ+N1hJSNpeTtNxWvVwN0wUzAIQ4Hpg==", "integrity": "sha512-BdrNXdzlofomLTiRnwJTSEAaGKyHHZkbMXIywOh7zlzp4uZnXErEwl9XZ+N1hJSNpeTtNxWvVwN0wUzAIQ4Hpg==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=10" "node": ">=10"
} }
@@ -14889,7 +14875,6 @@
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"optional": true, "optional": true,
"peer": true,
"dependencies": { "dependencies": {
"@img/colour": "^1.0.0", "@img/colour": "^1.0.0",
"detect-libc": "^2.1.2", "detect-libc": "^2.1.2",
@@ -14940,7 +14925,6 @@
"os": [ "os": [
"darwin" "darwin"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -14963,7 +14947,6 @@
"os": [ "os": [
"darwin" "darwin"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -14986,7 +14969,6 @@
"os": [ "os": [
"darwin" "darwin"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -15003,7 +14985,6 @@
"os": [ "os": [
"darwin" "darwin"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -15020,7 +15001,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -15037,7 +15017,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -15054,7 +15033,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -15071,7 +15049,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -15088,7 +15065,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"funding": { "funding": {
"url": "https://opencollective.com/libvips" "url": "https://opencollective.com/libvips"
} }
@@ -15105,7 +15081,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -15128,7 +15103,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -15151,7 +15125,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -15174,7 +15147,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -15197,7 +15169,6 @@
"os": [ "os": [
"linux" "linux"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -15220,7 +15191,6 @@
"os": [ "os": [
"win32" "win32"
], ],
"peer": true,
"engines": { "engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0" "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
}, },
@@ -15689,7 +15659,6 @@
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
"integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"client-only": "0.0.1" "client-only": "0.0.1"
}, },
@@ -15859,7 +15828,6 @@
"integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"rimraf": "~2.6.2" "rimraf": "~2.6.2"
@@ -15923,7 +15891,6 @@
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"minimist": "^1.2.6" "minimist": "^1.2.6"
}, },
@@ -16021,6 +15988,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -16225,6 +16193,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
@@ -16596,6 +16565,7 @@
"integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.27.0", "esbuild": "^0.27.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",
@@ -16685,7 +16655,8 @@
"resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz", "resolved": "https://registry.npmjs.org/vite-plugin-electron-renderer/-/vite-plugin-electron-renderer-0.14.6.tgz",
"integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==", "integrity": "sha512-oqkWFa7kQIkvHXG7+Mnl1RTroA4sP0yesKatmAy0gjZC4VwUqlvF9IvOpHd1fpLWsqYX/eZlVxlhULNtaQ78Jw==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT",
"peer": true
}, },
"node_modules/vite/node_modules/fdir": { "node_modules/vite/node_modules/fdir": {
"version": "6.5.0", "version": "6.5.0",
@@ -16711,6 +16682,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -16753,6 +16725,7 @@
"integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==", "integrity": "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vitest/expect": "4.0.16", "@vitest/expect": "4.0.16",
"@vitest/mocker": "4.0.16", "@vitest/mocker": "4.0.16",
@@ -17010,6 +16983,7 @@
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"peer": true,
"bin": { "bin": {
"yaml": "bin.mjs" "yaml": "bin.mjs"
}, },
@@ -17078,6 +17052,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz", "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
"integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
"license": "MIT", "license": "MIT",
"peer": true,
"funding": { "funding": {
"url": "https://github.com/sponsors/colinhacks" "url": "https://github.com/sponsors/colinhacks"
} }