Merge branch 'v0.14.0rc' of github.com:AutoMaker-Org/automaker into v0.14.0rc

This commit is contained in:
webdevcody
2026-01-23 12:57:46 -05:00
12 changed files with 782 additions and 37 deletions

View File

@@ -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'
)}
/>
)}

View File

@@ -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}

View File

@@ -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.**`,
};

View File

@@ -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

View File

@@ -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>
);