mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-02 20:43:36 +00:00
feat(backup): add backup.json for feature tracking and status updates
- Introduced a new `backup.json` file to track feature statuses, descriptions, and summaries for better project management. - Updated `.automaker/feature_list.json` to reflect verified statuses for several features, ensuring accurate representation of progress. - Enhanced `memory.md` with details on drag-and-drop functionality for features in `waiting_approval` status. - Improved auto mode service to allow running tasks to complete when auto mode is stopped, enhancing user experience.
This commit is contained in:
@@ -68,6 +68,7 @@ export function SettingsView() {
|
||||
setCurrentView,
|
||||
theme,
|
||||
setTheme,
|
||||
setProjectTheme,
|
||||
kanbanCardDetailLevel,
|
||||
setKanbanCardDetailLevel,
|
||||
defaultSkipTests,
|
||||
@@ -79,6 +80,18 @@ export function SettingsView() {
|
||||
currentProject,
|
||||
moveProjectToTrash,
|
||||
} = useAppStore();
|
||||
|
||||
// Compute the effective theme for the current project
|
||||
const effectiveTheme = currentProject?.theme || theme;
|
||||
|
||||
// Handler to set theme - saves to project if one is selected, otherwise to global
|
||||
const handleSetTheme = (newTheme: typeof theme) => {
|
||||
if (currentProject) {
|
||||
setProjectTheme(currentProject.id, newTheme);
|
||||
} else {
|
||||
setTheme(newTheme);
|
||||
}
|
||||
};
|
||||
const [anthropicKey, setAnthropicKey] = useState(apiKeys.anthropic);
|
||||
const [googleKey, setGoogleKey] = useState(apiKeys.google);
|
||||
const [openaiKey, setOpenaiKey] = useState(apiKeys.openai);
|
||||
@@ -171,13 +184,28 @@ export function SettingsView() {
|
||||
if (!container) return;
|
||||
|
||||
const handleScroll = () => {
|
||||
const sections = NAV_ITEMS.map((item) => ({
|
||||
id: item.id,
|
||||
element: document.getElementById(item.id),
|
||||
})).filter((s) => s.element);
|
||||
const sections = NAV_ITEMS.filter(
|
||||
(item) => item.id !== "danger" || currentProject
|
||||
)
|
||||
.map((item) => ({
|
||||
id: item.id,
|
||||
element: document.getElementById(item.id),
|
||||
}))
|
||||
.filter((s) => s.element);
|
||||
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const scrollTop = container.scrollTop;
|
||||
const scrollHeight = container.scrollHeight;
|
||||
const clientHeight = container.clientHeight;
|
||||
|
||||
// Check if scrolled to bottom (within a small threshold)
|
||||
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 50;
|
||||
|
||||
if (isAtBottom && sections.length > 0) {
|
||||
// If at bottom, highlight the last visible section
|
||||
setActiveSection(sections[sections.length - 1].id);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = sections.length - 1; i >= 0; i--) {
|
||||
const section = sections[i];
|
||||
@@ -194,7 +222,7 @@ export function SettingsView() {
|
||||
|
||||
container.addEventListener("scroll", handleScroll);
|
||||
return () => container.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
}, [currentProject]);
|
||||
|
||||
const scrollToSection = useCallback((sectionId: string) => {
|
||||
const element = document.getElementById(sectionId);
|
||||
@@ -407,7 +435,7 @@ export function SettingsView() {
|
||||
|
||||
{/* Scrollable Content */}
|
||||
<div ref={scrollContainerRef} className="flex-1 overflow-y-auto p-8">
|
||||
<div className="max-w-4xl mx-auto space-y-6">
|
||||
<div className="max-w-4xl mx-auto space-y-6 pb-96">
|
||||
{/* API Keys Section */}
|
||||
<div
|
||||
id="api-keys"
|
||||
@@ -1012,13 +1040,20 @@ export function SettingsView() {
|
||||
</div>
|
||||
<div className="p-6 space-y-4">
|
||||
<div className="space-y-3">
|
||||
<Label className="text-foreground">Theme</Label>
|
||||
<Label className="text-foreground">
|
||||
Theme{" "}
|
||||
{currentProject
|
||||
? `(for ${currentProject.name})`
|
||||
: "(Global)"}
|
||||
</Label>
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
<Button
|
||||
variant={theme === "dark" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("dark")}
|
||||
variant={
|
||||
effectiveTheme === "dark" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("dark")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "dark"
|
||||
effectiveTheme === "dark"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1028,10 +1063,12 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Dark</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "light" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("light")}
|
||||
variant={
|
||||
effectiveTheme === "light" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("light")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "light"
|
||||
effectiveTheme === "light"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1041,10 +1078,12 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Light</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "retro" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("retro")}
|
||||
variant={
|
||||
effectiveTheme === "retro" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("retro")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "retro"
|
||||
effectiveTheme === "retro"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1054,10 +1093,12 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Retro</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "dracula" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("dracula")}
|
||||
variant={
|
||||
effectiveTheme === "dracula" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("dracula")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "dracula"
|
||||
effectiveTheme === "dracula"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1067,10 +1108,12 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Dracula</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "nord" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("nord")}
|
||||
variant={
|
||||
effectiveTheme === "nord" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("nord")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "nord"
|
||||
effectiveTheme === "nord"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1080,10 +1123,12 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Nord</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "monokai" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("monokai")}
|
||||
variant={
|
||||
effectiveTheme === "monokai" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("monokai")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "monokai"
|
||||
effectiveTheme === "monokai"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1093,10 +1138,14 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Monokai</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "tokyonight" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("tokyonight")}
|
||||
variant={
|
||||
effectiveTheme === "tokyonight"
|
||||
? "secondary"
|
||||
: "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("tokyonight")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "tokyonight"
|
||||
effectiveTheme === "tokyonight"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1106,10 +1155,12 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Tokyo Night</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "solarized" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("solarized")}
|
||||
variant={
|
||||
effectiveTheme === "solarized" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("solarized")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "solarized"
|
||||
effectiveTheme === "solarized"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1119,10 +1170,12 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Solarized</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "gruvbox" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("gruvbox")}
|
||||
variant={
|
||||
effectiveTheme === "gruvbox" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("gruvbox")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "gruvbox"
|
||||
effectiveTheme === "gruvbox"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1132,10 +1185,14 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Gruvbox</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "catppuccin" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("catppuccin")}
|
||||
variant={
|
||||
effectiveTheme === "catppuccin"
|
||||
? "secondary"
|
||||
: "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("catppuccin")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "catppuccin"
|
||||
effectiveTheme === "catppuccin"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1145,10 +1202,12 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">Catppuccin</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "onedark" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("onedark")}
|
||||
variant={
|
||||
effectiveTheme === "onedark" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("onedark")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "onedark"
|
||||
effectiveTheme === "onedark"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1158,10 +1217,12 @@ export function SettingsView() {
|
||||
<span className="font-medium text-sm">One Dark</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={theme === "synthwave" ? "secondary" : "outline"}
|
||||
onClick={() => setTheme("synthwave")}
|
||||
variant={
|
||||
effectiveTheme === "synthwave" ? "secondary" : "outline"
|
||||
}
|
||||
onClick={() => handleSetTheme("synthwave")}
|
||||
className={`flex items-center justify-center gap-2 px-3 py-3 h-auto ${
|
||||
theme === "synthwave"
|
||||
effectiveTheme === "synthwave"
|
||||
? "border-brand-500 ring-1 ring-brand-500/50"
|
||||
: ""
|
||||
}`}
|
||||
@@ -1307,10 +1368,11 @@ export function SettingsView() {
|
||||
Show profiles only by default
|
||||
</Label>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
When enabled, the Add Feature dialog will show only AI profiles
|
||||
and hide advanced model tweaking options (Claude SDK, thinking levels,
|
||||
and OpenAI Codex CLI). This creates a cleaner, less overwhelming UI.
|
||||
You can always disable this to access advanced settings.
|
||||
When enabled, the Add Feature dialog will show only AI
|
||||
profiles and hide advanced model tweaking options
|
||||
(Claude SDK, thinking levels, and OpenAI Codex CLI).
|
||||
This creates a cleaner, less overwhelming UI. You can
|
||||
always disable this to access advanced settings.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user