Skip to content

Commit ed9bd89

Browse files
committed
feat: Add repository button + move settings to left sidebar (#289)
1 parent 7a973cb commit ed9bd89

File tree

9 files changed

+201
-105
lines changed

9 files changed

+201
-105
lines changed

apps/array/src/main/services/dock-badge/service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class DockBadgeService {
1919

2020
this.hasBadge = true;
2121
if (process.platform === "darwin" || process.platform === "linux") {
22-
app.setBadgeCount(1);
22+
app.dock.setBadge("•");
2323
}
2424
log.info("Dock badge shown");
2525
}
@@ -29,7 +29,7 @@ export class DockBadgeService {
2929

3030
this.hasBadge = false;
3131
if (process.platform === "darwin" || process.platform === "linux") {
32-
app.setBadgeCount(0);
32+
app.dock.setBadge("");
3333
}
3434
log.info("Dock badge cleared");
3535
}

apps/array/src/main/services/folders/schemas.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const registeredFolderSchema = z.object({
1111
export const getFoldersOutput = z.array(registeredFolderSchema);
1212

1313
export const addFolderInput = z.object({
14-
folderPath: z.string(),
14+
folderPath: z.string().min(2, "Folder path must be a valid directory path"),
1515
});
1616

1717
export const addFolderOutput = registeredFolderSchema;

apps/array/src/main/services/folders/service.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,20 @@ const log = logger.scope("folders-service");
2121
@injectable()
2222
export class FoldersService {
2323
async getFolders(): Promise<RegisteredFolder[]> {
24-
return foldersStore.get("folders", []);
24+
const folders = foldersStore.get("folders", []);
25+
// Filter out any folders with empty names (from invalid paths like "/")
26+
return folders.filter((f) => f.name && f.path);
2527
}
2628

2729
async addFolder(folderPath: string): Promise<RegisteredFolder> {
30+
// Validate the path before proceeding
31+
const folderName = path.basename(folderPath);
32+
if (!folderPath || !folderName) {
33+
throw new Error(
34+
`Invalid folder path: "${folderPath}" - path must have a valid directory name`,
35+
);
36+
}
37+
2838
const isRepo = await isGitRepository(folderPath);
2939

3040
if (!isRepo) {
@@ -65,7 +75,7 @@ export class FoldersService {
6575
const newFolder: RegisteredFolder = {
6676
id: generateId("folder", 7),
6777
path: folderPath,
68-
name: path.basename(folderPath),
78+
name: folderName,
6979
lastAccessed: new Date().toISOString(),
7080
createdAt: new Date().toISOString(),
7181
};

apps/array/src/main/services/workspace/schemas.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ export const scriptExecutionResultSchema = z.object({
5151
// Input schemas
5252
export const createWorkspaceInput = z.object({
5353
taskId: z.string(),
54-
mainRepoPath: z.string(),
54+
mainRepoPath: z
55+
.string()
56+
.min(2, "Repository path must be a valid directory path"),
5557
folderId: z.string(),
56-
folderPath: z.string(),
58+
folderPath: z.string().min(2, "Folder path must be a valid directory path"),
5759
mode: workspaceModeSchema,
5860
branch: z.string().optional(),
5961
});

apps/array/src/renderer/components/StatusBar.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { StatusBarMenu } from "@components/StatusBarMenu";
2-
import { GearIcon } from "@radix-ui/react-icons";
3-
import { Badge, Box, Code, Flex, IconButton, Kbd } from "@radix-ui/themes";
4-
import { useNavigationStore } from "@stores/navigationStore";
2+
import { Badge, Box, Code, Flex, Kbd } from "@radix-ui/themes";
53
import { useStatusBarStore } from "@stores/statusBarStore";
64

75
import { IS_DEV } from "@/constants/environment";
@@ -12,7 +10,6 @@ interface StatusBarProps {
1210

1311
export function StatusBar({ showKeyHints = true }: StatusBarProps) {
1412
const { statusText, keyHints } = useStatusBarStore();
15-
const { toggleSettings } = useNavigationStore();
1613

1714
return (
1815
<Box className="flex flex-row items-center justify-between border-gray-6 border-t bg-gray-2 px-4 py-2">
@@ -46,15 +43,6 @@ export function StatusBar({ showKeyHints = true }: StatusBarProps) {
4643
)}
4744

4845
<Flex align="center" gap="2">
49-
<IconButton
50-
size="1"
51-
variant="ghost"
52-
color="gray"
53-
onClick={toggleSettings}
54-
title="Settings"
55-
>
56-
<GearIcon />
57-
</IconButton>
5846
{IS_DEV && (
5947
<Badge size="1">
6048
<Code size="1" variant="ghost">

apps/array/src/renderer/features/code-editor/components/DiffEditorPanel.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import { PanelMessage } from "@components/ui/PanelMessage";
22
import { CodeMirrorDiffEditor } from "@features/code-editor/components/CodeMirrorDiffEditor";
33
import { CodeMirrorEditor } from "@features/code-editor/components/CodeMirrorEditor";
44
import { getRelativePath } from "@features/code-editor/utils/pathUtils";
5+
import { usePanelLayoutStore } from "@features/panels/store/panelLayoutStore";
56
import { useTaskData } from "@features/task-detail/hooks/useTaskData";
67
import { Box } from "@radix-ui/themes";
78
import { trpcVanilla } from "@renderer/trpc/client";
89
import type { Task } from "@shared/types";
910
import { useQuery, useQueryClient } from "@tanstack/react-query";
10-
import { useCallback } from "react";
11+
import { useCallback, useEffect } from "react";
1112
import {
1213
selectWorktreePath,
1314
useWorkspaceStore,
@@ -29,6 +30,9 @@ export function DiffEditorPanel({
2930
const repoPath = worktreePath ?? taskData.repoPath;
3031
const filePath = getRelativePath(absolutePath, repoPath);
3132
const queryClient = useQueryClient();
33+
const closeDiffTabsForFile = usePanelLayoutStore(
34+
(s) => s.closeDiffTabsForFile,
35+
);
3236

3337
const { data: changedFiles = [] } = useQuery({
3438
queryKey: ["changed-files-head", repoPath],
@@ -41,6 +45,7 @@ export function DiffEditorPanel({
4145
});
4246

4347
const fileInfo = changedFiles.find((f) => f.path === filePath);
48+
const isFileStillChanged = !!fileInfo;
4449
const status = fileInfo?.status ?? "modified";
4550
const originalPath = fileInfo?.originalPath ?? filePath;
4651
const isDeleted = status === "deleted";
@@ -90,16 +95,33 @@ export function DiffEditorPanel({
9095
[repoPath, filePath, queryClient],
9196
);
9297

98+
const isLoading =
99+
(!isDeleted && loadingModified) || (!isNew && loadingOriginal);
100+
101+
const hasNoChanges =
102+
!!repoPath &&
103+
!isLoading &&
104+
(!isFileStillChanged ||
105+
(!isDeleted && !isNew && originalContent === modifiedContent));
106+
107+
useEffect(() => {
108+
if (hasNoChanges) {
109+
closeDiffTabsForFile(taskId, filePath);
110+
}
111+
}, [hasNoChanges, closeDiffTabsForFile, taskId, filePath]);
112+
93113
if (!repoPath) {
94114
return <PanelMessage>No repository path available</PanelMessage>;
95115
}
96116

97-
const isLoading =
98-
(!isDeleted && loadingModified) || (!isNew && loadingOriginal);
99117
if (isLoading) {
100118
return <PanelMessage>Loading diff...</PanelMessage>;
101119
}
102120

121+
if (hasNoChanges) {
122+
return null;
123+
}
124+
103125
const showDiff = !isDeleted && !isNew;
104126
const content = isDeleted ? originalContent : modifiedContent;
105127

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Plus } from "@phosphor-icons/react";
2+
import { GearIcon } from "@radix-ui/react-icons";
3+
import { Box, Button, Flex, IconButton } from "@radix-ui/themes";
4+
import { useRegisteredFoldersStore } from "@renderer/stores/registeredFoldersStore";
5+
import { trpcVanilla } from "@renderer/trpc";
6+
import { useNavigationStore } from "@stores/navigationStore";
7+
import { useCallback } from "react";
8+
9+
export function SidebarFooter() {
10+
const addFolder = useRegisteredFoldersStore((state) => state.addFolder);
11+
const { toggleSettings } = useNavigationStore();
12+
13+
const handleAddRepository = useCallback(async () => {
14+
const selectedPath = await trpcVanilla.os.selectDirectory.query();
15+
if (selectedPath) {
16+
await addFolder(selectedPath);
17+
}
18+
}, [addFolder]);
19+
20+
return (
21+
<Box
22+
style={{
23+
position: "absolute",
24+
bottom: 0,
25+
left: 0,
26+
right: 0,
27+
borderTop: "1px solid var(--gray-6)",
28+
background: "var(--color-background)",
29+
padding: "12px",
30+
}}
31+
>
32+
<Flex align="center" gap="2" justify="between">
33+
<Button
34+
size="1"
35+
variant="ghost"
36+
color="gray"
37+
onClick={handleAddRepository}
38+
>
39+
<Plus size={14} weight="bold" />
40+
Add repository
41+
</Button>
42+
43+
<IconButton
44+
size="1"
45+
variant="ghost"
46+
color="gray"
47+
onClick={toggleSettings}
48+
title="Settings"
49+
>
50+
<GearIcon />
51+
</IconButton>
52+
</Flex>
53+
</Box>
54+
);
55+
}

0 commit comments

Comments
 (0)