mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
Merge branch 'v0.14.0rc' of github.com:AutoMaker-Org/automaker into v0.14.0rc
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import type { NavigateOptions } from '@tanstack/react-router';
|
||||
import { ChevronDown, Wrench, Github } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { formatShortcut } from '@/store/app-store';
|
||||
import { formatShortcut, useAppStore } from '@/store/app-store';
|
||||
import type { NavSection } from '../types';
|
||||
import type { Project } from '@/lib/electron';
|
||||
import type { SidebarStyle } from '@automaker/types';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -23,6 +24,7 @@ const sectionIcons: Record<string, React.ComponentType<{ className?: string }>>
|
||||
interface SidebarNavigationProps {
|
||||
currentProject: Project | null;
|
||||
sidebarOpen: boolean;
|
||||
sidebarStyle: SidebarStyle;
|
||||
navSections: NavSection[];
|
||||
isActiveRoute: (id: string) => boolean;
|
||||
navigate: (opts: NavigateOptions) => void;
|
||||
@@ -32,6 +34,7 @@ interface SidebarNavigationProps {
|
||||
export function SidebarNavigation({
|
||||
currentProject,
|
||||
sidebarOpen,
|
||||
sidebarStyle,
|
||||
navSections,
|
||||
isActiveRoute,
|
||||
navigate,
|
||||
@@ -39,21 +42,26 @@ export function SidebarNavigation({
|
||||
}: SidebarNavigationProps) {
|
||||
const navRef = useRef<HTMLElement>(null);
|
||||
|
||||
// Track collapsed state for each collapsible section
|
||||
const [collapsedSections, setCollapsedSections] = useState<Record<string, boolean>>({});
|
||||
// Get collapsed state from store (persisted across restarts)
|
||||
const { collapsedNavSections, setCollapsedNavSections, toggleNavSection } = useAppStore();
|
||||
|
||||
// Initialize collapsed state when sections change (e.g., GitHub section appears)
|
||||
// Only set defaults for sections that don't have a persisted state
|
||||
useEffect(() => {
|
||||
setCollapsedSections((prev) => {
|
||||
const updated = { ...prev };
|
||||
navSections.forEach((section) => {
|
||||
if (section.collapsible && section.label && !(section.label in updated)) {
|
||||
updated[section.label] = section.defaultCollapsed ?? false;
|
||||
}
|
||||
});
|
||||
return updated;
|
||||
let hasNewSections = false;
|
||||
const updated = { ...collapsedNavSections };
|
||||
|
||||
navSections.forEach((section) => {
|
||||
if (section.collapsible && section.label && !(section.label in updated)) {
|
||||
updated[section.label] = section.defaultCollapsed ?? false;
|
||||
hasNewSections = true;
|
||||
}
|
||||
});
|
||||
}, [navSections]);
|
||||
|
||||
if (hasNewSections) {
|
||||
setCollapsedNavSections(updated);
|
||||
}
|
||||
}, [navSections, collapsedNavSections, setCollapsedNavSections]);
|
||||
|
||||
// Check scroll state
|
||||
const checkScrollState = useCallback(() => {
|
||||
@@ -77,14 +85,7 @@ export function SidebarNavigation({
|
||||
nav.removeEventListener('scroll', checkScrollState);
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, [checkScrollState, collapsedSections]);
|
||||
|
||||
const toggleSection = useCallback((label: string) => {
|
||||
setCollapsedSections((prev) => ({
|
||||
...prev,
|
||||
[label]: !prev[label],
|
||||
}));
|
||||
}, []);
|
||||
}, [checkScrollState, collapsedNavSections]);
|
||||
|
||||
// Filter sections: always show non-project sections, only show project sections when project exists
|
||||
const visibleSections = navSections.filter((section) => {
|
||||
@@ -97,10 +98,17 @@ export function SidebarNavigation({
|
||||
});
|
||||
|
||||
return (
|
||||
<nav ref={navRef} className={cn('flex-1 overflow-y-auto scrollbar-hide px-3 pb-2 mt-1')}>
|
||||
<nav
|
||||
ref={navRef}
|
||||
className={cn(
|
||||
'flex-1 overflow-y-auto scrollbar-hide px-3 pb-2',
|
||||
// Add top padding in discord mode since there's no header
|
||||
sidebarStyle === 'discord' ? 'pt-3' : 'mt-1'
|
||||
)}
|
||||
>
|
||||
{/* Navigation sections */}
|
||||
{visibleSections.map((section, sectionIdx) => {
|
||||
const isCollapsed = section.label ? collapsedSections[section.label] : false;
|
||||
const isCollapsed = section.label ? collapsedNavSections[section.label] : false;
|
||||
const isCollapsible = section.collapsible && section.label && sidebarOpen;
|
||||
|
||||
const SectionIcon = section.label ? sectionIcons[section.label] : null;
|
||||
@@ -110,21 +118,37 @@ export function SidebarNavigation({
|
||||
{/* Section Label - clickable if collapsible (expanded sidebar) */}
|
||||
{section.label && sidebarOpen && (
|
||||
<button
|
||||
onClick={() => isCollapsible && toggleSection(section.label!)}
|
||||
onClick={() => isCollapsible && toggleNavSection(section.label!)}
|
||||
className={cn(
|
||||
'flex items-center w-full px-3 mb-1.5',
|
||||
isCollapsible && 'cursor-pointer hover:text-foreground'
|
||||
'group flex items-center w-full px-3 py-1.5 mb-1 rounded-md',
|
||||
'transition-all duration-200 ease-out',
|
||||
isCollapsible
|
||||
? [
|
||||
'cursor-pointer',
|
||||
'hover:bg-accent/50 hover:text-foreground',
|
||||
'border border-transparent hover:border-border/40',
|
||||
]
|
||||
: 'cursor-default'
|
||||
)}
|
||||
disabled={!isCollapsible}
|
||||
>
|
||||
<span className="text-[10px] font-semibold text-muted-foreground/70 uppercase tracking-widest">
|
||||
<span
|
||||
className={cn(
|
||||
'text-[10px] font-semibold uppercase tracking-widest transition-colors duration-200',
|
||||
isCollapsible
|
||||
? 'text-muted-foreground/70 group-hover:text-foreground'
|
||||
: 'text-muted-foreground/70'
|
||||
)}
|
||||
>
|
||||
{section.label}
|
||||
</span>
|
||||
{isCollapsible && (
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
'w-3 h-3 ml-auto text-muted-foreground/50 transition-transform duration-200',
|
||||
isCollapsed && '-rotate-90'
|
||||
'w-3 h-3 ml-auto transition-all duration-200',
|
||||
isCollapsed
|
||||
? '-rotate-90 text-muted-foreground/50 group-hover:text-muted-foreground'
|
||||
: 'text-muted-foreground/50 group-hover:text-muted-foreground'
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -53,6 +53,7 @@ export function Sidebar() {
|
||||
trashedProjects,
|
||||
currentProject,
|
||||
sidebarOpen,
|
||||
sidebarStyle,
|
||||
mobileSidebarHidden,
|
||||
projectHistory,
|
||||
upsertAndSetCurrentProject,
|
||||
@@ -381,17 +382,21 @@ export function Sidebar() {
|
||||
)}
|
||||
|
||||
<div className="flex-1 flex flex-col overflow-hidden">
|
||||
<SidebarHeader
|
||||
sidebarOpen={sidebarOpen}
|
||||
currentProject={currentProject}
|
||||
onNewProject={handleNewProject}
|
||||
onOpenFolder={handleOpenFolder}
|
||||
onProjectContextMenu={handleContextMenu}
|
||||
/>
|
||||
{/* Only show header in unified mode - in discord mode, ProjectSwitcher has the logo */}
|
||||
{sidebarStyle === 'unified' && (
|
||||
<SidebarHeader
|
||||
sidebarOpen={sidebarOpen}
|
||||
currentProject={currentProject}
|
||||
onNewProject={handleNewProject}
|
||||
onOpenFolder={handleOpenFolder}
|
||||
onProjectContextMenu={handleContextMenu}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SidebarNavigation
|
||||
currentProject={currentProject}
|
||||
sidebarOpen={sidebarOpen}
|
||||
sidebarStyle={sidebarStyle}
|
||||
navSections={navSections}
|
||||
isActiveRoute={isActiveRoute}
|
||||
navigate={navigate}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
export const commitTemplate = {
|
||||
id: 'commit',
|
||||
name: 'Commit Changes',
|
||||
colorClass: 'bg-purple-500/20',
|
||||
instructions: `## Commit Changes Step
|
||||
|
||||
# ⚠️ CRITICAL REQUIREMENT: YOU MUST COMMIT ALL CHANGES USING CONVENTIONAL COMMIT FORMAT ⚠️
|
||||
|
||||
**THIS IS NOT OPTIONAL. YOU MUST CREATE AND EXECUTE A GIT COMMIT WITH ALL CHANGES.**
|
||||
|
||||
This step requires you to:
|
||||
1. **REVIEW** all changes made in this feature
|
||||
2. **CREATE** a conventional commit message
|
||||
3. **EXECUTE** the git commit command
|
||||
|
||||
**You cannot complete this step by only reviewing changes. You MUST execute the git commit command.**
|
||||
|
||||
---
|
||||
|
||||
### Phase 1: Review Phase
|
||||
Review all changes made in this feature:
|
||||
|
||||
- Review all modified files using \`git status\` and \`git diff\`
|
||||
- Identify the scope and nature of changes
|
||||
- Determine the appropriate conventional commit type
|
||||
- Identify any breaking changes that need to be documented
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Commit Phase - ⚠️ MANDATORY ACTION REQUIRED ⚠️
|
||||
|
||||
**YOU MUST NOW CREATE AND EXECUTE A GIT COMMIT WITH ALL CHANGES.**
|
||||
|
||||
**This is not optional. You must stage all changes and commit them using conventional commit format.**
|
||||
|
||||
#### Conventional Commit Format
|
||||
|
||||
Follow this format for your commit message:
|
||||
|
||||
\`\`\`
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
\`\`\`
|
||||
|
||||
#### Commit Types (choose the most appropriate):
|
||||
|
||||
- **feat**: A new feature
|
||||
- **fix**: A bug fix
|
||||
- **docs**: Documentation only changes
|
||||
- **style**: Code style changes (formatting, missing semicolons, etc.)
|
||||
- **refactor**: Code refactoring without changing functionality
|
||||
- **perf**: Performance improvements
|
||||
- **test**: Adding or updating tests
|
||||
- **chore**: Changes to build process, dependencies, or tooling
|
||||
- **ci**: Changes to CI configuration
|
||||
- **build**: Changes to build system or dependencies
|
||||
|
||||
#### Scope (optional but recommended):
|
||||
- Component/module name (e.g., \`ui\`, \`server\`, \`auth\`)
|
||||
- Feature area (e.g., \`board\`, \`pipeline\`, \`agent\`)
|
||||
- Package name (e.g., \`@automaker/types\`)
|
||||
|
||||
#### Subject:
|
||||
- Use imperative mood: "add" not "added" or "adds"
|
||||
- First letter lowercase
|
||||
- No period at the end
|
||||
- Maximum 72 characters
|
||||
|
||||
#### Body (optional but recommended for significant changes):
|
||||
- Explain the "what" and "why" of the change
|
||||
- Reference related issues or PRs
|
||||
- Separate from subject with blank line
|
||||
- Wrap at 72 characters
|
||||
|
||||
#### Footer (optional):
|
||||
- Breaking changes: \`BREAKING CHANGE: <description>\`
|
||||
- Issue references: \`Closes #123\`, \`Fixes #456\`
|
||||
|
||||
#### Action Steps (You MUST complete these):
|
||||
|
||||
1. **Stage All Changes** - PREPARE FOR COMMIT:
|
||||
- ✅ Run \`git add .\` or \`git add -A\` to stage all changes
|
||||
- ✅ Verify staged changes with \`git status\`
|
||||
- ✅ Ensure all relevant changes are staged
|
||||
|
||||
2. **Create Commit Message** - FOLLOW CONVENTIONAL COMMIT FORMAT:
|
||||
- ✅ Determine the appropriate commit type based on changes
|
||||
- ✅ Identify the scope (component/module/feature)
|
||||
- ✅ Write a clear, imperative subject line
|
||||
- ✅ Add a body explaining the changes (if significant)
|
||||
- ✅ Include breaking changes in footer if applicable
|
||||
- ✅ Reference related issues if applicable
|
||||
|
||||
3. **Execute Commit** - COMMIT THE CHANGES:
|
||||
- ✅ Run \`git commit -m "<type>(<scope>): <subject>" -m "<body>"\` or use a multi-line commit message
|
||||
- ✅ Verify the commit was created with \`git log -1\`
|
||||
- ✅ **EXECUTE THE ACTUAL GIT COMMIT COMMAND**
|
||||
|
||||
#### Example Commit Messages:
|
||||
|
||||
\`\`\`
|
||||
feat(ui): add pipeline step commit template
|
||||
|
||||
Add a new pipeline step template for committing changes using
|
||||
conventional commit format. This ensures all commits follow
|
||||
a consistent pattern for better changelog generation.
|
||||
|
||||
Closes #123
|
||||
\`\`\`
|
||||
|
||||
\`\`\`
|
||||
fix(server): resolve agent session timeout issue
|
||||
|
||||
The agent session was timing out prematurely due to incorrect
|
||||
WebSocket heartbeat configuration. Updated heartbeat interval
|
||||
to match server expectations.
|
||||
|
||||
Fixes #456
|
||||
\`\`\`
|
||||
|
||||
\`\`\`
|
||||
refactor(pipeline): extract step template logic
|
||||
|
||||
Extract step template loading and validation into separate
|
||||
utility functions to improve code organization and testability.
|
||||
\`\`\`
|
||||
|
||||
---
|
||||
|
||||
### Summary Required
|
||||
After completing BOTH review AND commit phases, provide:
|
||||
- A summary of all changes that were committed
|
||||
- **The exact commit message that was used (this proves you executed the commit)**
|
||||
- The commit hash (if available)
|
||||
- Any notes about the commit (breaking changes, related issues, etc.)
|
||||
|
||||
---
|
||||
|
||||
# ⚠️ FINAL REMINDER ⚠️
|
||||
|
||||
**Reviewing changes without committing is INCOMPLETE and UNACCEPTABLE.**
|
||||
|
||||
**You MUST stage all changes and execute a git commit command.**
|
||||
**You MUST use conventional commit format for the commit message.**
|
||||
**You MUST show evidence of the commit execution in your summary.**
|
||||
**This step is only complete when changes have been committed to git.**`,
|
||||
};
|
||||
@@ -4,6 +4,7 @@ import { uxReviewTemplate } from './ux-review';
|
||||
import { testingTemplate } from './testing';
|
||||
import { documentationTemplate } from './documentation';
|
||||
import { optimizationTemplate } from './optimization';
|
||||
import { commitTemplate } from './commit';
|
||||
|
||||
export interface PipelineStepTemplate {
|
||||
id: string;
|
||||
@@ -19,6 +20,7 @@ export const STEP_TEMPLATES: PipelineStepTemplate[] = [
|
||||
testingTemplate,
|
||||
documentationTemplate,
|
||||
optimizationTemplate,
|
||||
commitTemplate,
|
||||
];
|
||||
|
||||
// Helper to get template color class
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Palette, Moon, Sun, Type, Sparkles } from 'lucide-react';
|
||||
import { Palette, Moon, Sun, Type, Sparkles, PanelLeft, Columns2 } from 'lucide-react';
|
||||
import { darkThemes, lightThemes } from '@/config/theme-options';
|
||||
import {
|
||||
UI_SANS_FONT_OPTIONS,
|
||||
@@ -12,6 +12,7 @@ import { cn } from '@/lib/utils';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { FontSelector } from '@/components/shared';
|
||||
import type { Theme } from '../shared/types';
|
||||
import type { SidebarStyle } from '@automaker/types';
|
||||
|
||||
interface AppearanceSectionProps {
|
||||
effectiveTheme: Theme;
|
||||
@@ -26,6 +27,8 @@ export function AppearanceSection({ effectiveTheme, onThemeChange }: AppearanceS
|
||||
setFontMono,
|
||||
disableSplashScreen,
|
||||
setDisableSplashScreen,
|
||||
sidebarStyle,
|
||||
setSidebarStyle,
|
||||
} = useAppStore();
|
||||
|
||||
// Determine if current theme is light or dark
|
||||
@@ -221,6 +224,94 @@ export function AppearanceSection({ effectiveTheme, onThemeChange }: AppearanceS
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sidebar Style Section */}
|
||||
<div className="space-y-4 pt-6 border-t border-border/50">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<PanelLeft className="w-4 h-4 text-muted-foreground" />
|
||||
<Label className="text-foreground font-medium">Sidebar Layout</Label>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground -mt-2 mb-4">
|
||||
Choose between a modern unified sidebar or classic Discord-style layout with a separate
|
||||
project switcher.
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{/* Unified Sidebar Option */}
|
||||
<button
|
||||
onClick={() => setSidebarStyle('unified')}
|
||||
className={cn(
|
||||
'group flex flex-col items-center gap-3 p-4 rounded-xl',
|
||||
'text-sm font-medium transition-all duration-200 ease-out',
|
||||
sidebarStyle === 'unified'
|
||||
? [
|
||||
'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="sidebar-style-unified"
|
||||
>
|
||||
<PanelLeft
|
||||
className={cn(
|
||||
'w-8 h-8 transition-all duration-200',
|
||||
sidebarStyle === 'unified' ? 'text-brand-500' : 'text-muted-foreground'
|
||||
)}
|
||||
/>
|
||||
<div className="text-center">
|
||||
<div className="font-medium">Unified</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
Single sidebar with project dropdown
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* Discord-style Sidebar Option */}
|
||||
<button
|
||||
onClick={() => setSidebarStyle('discord')}
|
||||
className={cn(
|
||||
'group flex flex-col items-center gap-3 p-4 rounded-xl',
|
||||
'text-sm font-medium transition-all duration-200 ease-out',
|
||||
sidebarStyle === 'discord'
|
||||
? [
|
||||
'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="sidebar-style-discord"
|
||||
>
|
||||
<Columns2
|
||||
className={cn(
|
||||
'w-8 h-8 transition-all duration-200',
|
||||
sidebarStyle === 'discord' ? 'text-brand-500' : 'text-muted-foreground'
|
||||
)}
|
||||
/>
|
||||
<div className="text-center">
|
||||
<div className="font-medium">Classic</div>
|
||||
<div className="text-xs text-muted-foreground mt-1">
|
||||
Separate project switcher + sidebar
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user