Skip to content

Commit 832b8ce

Browse files
authored
fix: Various bug fixes (listed) (#544)
- Remove folders that no longer exist on disk - Add scroll bar to session view - Fix file changes detection bug report - Fix Dangerous Auto-Accept on Click (From Array Feedback PDF) - Fix cursor glow not respecting the cursor glow setting
1 parent 844e1f4 commit 832b8ce

10 files changed

Lines changed: 58 additions & 40 deletions

File tree

apps/twig/src/renderer/components/TorchGlow.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useSettingsStore } from "@features/settings/stores/settingsStore";
12
import { useThemeStore } from "@stores/themeStore";
23
import { useEffect, useState } from "react";
34

@@ -7,6 +8,7 @@ interface TorchGlowProps {
78

89
export function TorchGlow({ containerRef }: TorchGlowProps) {
910
const isDarkMode = useThemeStore((state) => state.isDarkMode);
11+
const cursorGlow = useSettingsStore((state) => state.cursorGlow);
1012
const [mousePos, setMousePos] = useState<{ x: number; y: number } | null>(
1113
null,
1214
);
@@ -41,8 +43,8 @@ export function TorchGlow({ containerRef }: TorchGlowProps) {
4143
};
4244
}, [containerRef]);
4345

44-
// Only show in dark mode when hovering
45-
if (!isDarkMode || !isHovering || !mousePos) return null;
46+
// Only show in dark mode when hovering and cursor glow is enabled
47+
if (!isDarkMode || !cursorGlow || !isHovering || !mousePos) return null;
4648

4749
return (
4850
<>

apps/twig/src/renderer/features/panels/hooks/usePanelLayoutHooks.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { FileIcon } from "@components/ui/FileIcon";
22
import { TabContentRenderer } from "@features/task-detail/components/TabContentRenderer";
3-
import { useTaskExecutionStore } from "@features/task-detail/stores/taskExecutionStore";
3+
import {
4+
selectTaskRepoPath,
5+
useTaskExecutionStore,
6+
} from "@features/task-detail/stores/taskExecutionStore";
47
import { ChatCenteredText, Terminal } from "@phosphor-icons/react";
58
import type { Task } from "@shared/types";
69
import { useCallback, useEffect, useMemo, useRef } from "react";
@@ -82,9 +85,7 @@ export function useTabInjection(
8285
closeTab: (taskId: string, panelId: string, tabId: string) => void,
8386
): Tab[] {
8487
const worktreePath = useWorkspaceStore(selectWorktreePath(taskId));
85-
const storedRepoPath = useTaskExecutionStore(
86-
(state) => state.taskStates[taskId]?.repoPath ?? null,
87-
);
88+
const storedRepoPath = useTaskExecutionStore(selectTaskRepoPath(taskId));
8889
const repoPath = worktreePath || storedRepoPath || "";
8990

9091
return useMemo(

apps/twig/src/renderer/features/right-sidebar/stores/fileTreeStore.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,8 @@ export const useFileTreeStore = create<FileTreeStore>()((set) => ({
3838
},
3939
})),
4040
}));
41+
42+
// Selector factory for checking if a path is expanded
43+
export const selectIsPathExpanded =
44+
(taskId: string, path: string) => (state: FileTreeStore) =>
45+
state.expandedPaths[taskId]?.has(path) ?? false;

apps/twig/src/renderer/features/sessions/components/ConversationView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export function ConversationView({
121121
<div className="relative flex-1">
122122
<div
123123
ref={scrollRef}
124-
className="scrollbar-hide absolute inset-0 overflow-auto bg-white p-2 pb-16 dark:bg-gray-1"
124+
className="absolute inset-0 overflow-auto bg-white p-2 pb-16 dark:bg-gray-1"
125125
>
126126
<div className="flex flex-col gap-3">
127127
{items.map((item) =>

apps/twig/src/renderer/features/sessions/components/InlinePermissionSelector.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,6 @@ export function InlinePermissionSelector({
202202
[customInput, options, onSelect],
203203
);
204204

205-
const handleOptionClick = (index: number) => {
206-
if (disabled) return;
207-
const opt = allOptions[index];
208-
if (isCustomOption(opt.optionId)) {
209-
setSelectedIndex(index);
210-
setIsCustomInputMode(true);
211-
} else {
212-
onSelect(opt.optionId);
213-
}
214-
};
215-
216205
return (
217206
<Box
218207
ref={containerRef}
@@ -258,10 +247,9 @@ export function InlinePermissionSelector({
258247
key={option.optionId}
259248
align="center"
260249
gap="1"
261-
className={`cursor-pointer py-0.5 ${
250+
className={`py-0.5 ${
262251
isSelected ? "text-gray-12" : "text-gray-10"
263252
}`}
264-
onClick={() => handleOptionClick(index)}
265253
>
266254
<span
267255
className={`inline-block w-3 text-xs ${

apps/twig/src/renderer/features/task-detail/components/FileTreePanel.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { FileIcon } from "@components/ui/FileIcon";
22
import { PanelMessage } from "@components/ui/PanelMessage";
33
import { isFileTabActiveInTree, usePanelLayoutStore } from "@features/panels";
4-
import { useFileTreeStore } from "@features/right-sidebar/stores/fileTreeStore";
4+
import {
5+
selectIsPathExpanded,
6+
useFileTreeStore,
7+
} from "@features/right-sidebar/stores/fileTreeStore";
58
import { useTaskData } from "@features/task-detail/hooks/useTaskData";
69
import { CaretRight, FolderIcon, FolderOpenIcon } from "@phosphor-icons/react";
710
import { Box, Flex, Text } from "@radix-ui/themes";
@@ -40,9 +43,7 @@ function LazyTreeItem({
4043
repoPath,
4144
isFileActive,
4245
}: LazyTreeItemProps) {
43-
const isExpanded = useFileTreeStore(
44-
(state) => state.expandedPaths[taskId]?.has(entry.path) ?? false,
45-
);
46+
const isExpanded = useFileTreeStore(selectIsPathExpanded(taskId, entry.path));
4647
const togglePath = useFileTreeStore((state) => state.togglePath);
4748
const collapseAll = useFileTreeStore((state) => state.collapseAll);
4849
const openFile = usePanelLayoutStore((state) => state.openFile);

apps/twig/src/renderer/features/task-detail/components/TaskDetail.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import { useStatusBar } from "@hooks/useStatusBar";
1111
import { Box, Code, Flex, Text } from "@radix-ui/themes";
1212
import type { Task } from "@shared/types";
1313
import { useMemo } from "react";
14-
import { useWorkspaceStore } from "@/renderer/features/workspace/stores/workspaceStore";
14+
import {
15+
selectWorkspace,
16+
selectWorktreePath,
17+
useWorkspaceStore,
18+
} from "@/renderer/features/workspace/stores/workspaceStore";
1519

1620
interface TaskDetailProps {
1721
task: Task;
@@ -23,9 +27,7 @@ export function TaskDetail({ task: initialTask }: TaskDetailProps) {
2327
initialTask,
2428
});
2529

26-
const worktreePath = useWorkspaceStore(
27-
(state) => state.workspaces[initialTask.id]?.worktreePath,
28-
);
30+
const worktreePath = useWorkspaceStore(selectWorktreePath(initialTask.id));
2931
const effectiveRepoPath = worktreePath ?? taskData.repoPath;
3032

3133
useFileWatcher(effectiveRepoPath, taskData.task.id);
@@ -55,10 +57,7 @@ export function TaskDetail({ task: initialTask }: TaskDetailProps) {
5557

5658
useWorkspaceEvents(taskId);
5759
const task = taskData.task;
58-
const workspace = useWorkspaceStore(
59-
(state) => state.workspaces[taskId] ?? null,
60-
);
61-
60+
const workspace = useWorkspaceStore(selectWorkspace(taskId));
6261
const branchName = workspace?.branchName;
6362

6463
const headerContent = useMemo(

apps/twig/src/renderer/features/task-detail/hooks/useTaskData.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { useTaskExecutionStore } from "@features/task-detail/stores/taskExecutionStore";
1+
import {
2+
selectTaskRepoExists,
3+
selectTaskRepoPath,
4+
useTaskExecutionStore,
5+
} from "@features/task-detail/stores/taskExecutionStore";
26
import { useTasks } from "@features/tasks/hooks/useTasks";
37
import type { Task } from "@shared/types";
48
import { cloneStore } from "@stores/cloneStore";
@@ -27,12 +31,8 @@ export function useTaskData({ taskId, initialTask }: UseTaskDataParams) {
2731
}, [initializeRepoPath, taskId, task]);
2832

2933
// Subscribe to specific fields reactively to avoid unnecessary rerenders
30-
const repoPath = useTaskExecutionStore(
31-
(state) => state.taskStates[taskId]?.repoPath ?? null,
32-
);
33-
const repoExists = useTaskExecutionStore(
34-
(state) => state.taskStates[taskId]?.repoExists ?? null,
35-
);
34+
const repoPath = useTaskExecutionStore(selectTaskRepoPath(taskId));
35+
const repoExists = useTaskExecutionStore(selectTaskRepoExists(taskId));
3636

3737
const repository = getTaskRepository(task);
3838

apps/twig/src/renderer/features/task-detail/stores/taskExecutionStore.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,12 @@ export const useTaskExecutionStore = create<TaskExecutionStore>()(
147147
},
148148
),
149149
);
150+
151+
// Selector factories for parameterized access (taskId-based)
152+
export const selectTaskRepoPath =
153+
(taskId: string) => (state: TaskExecutionStore) =>
154+
state.taskStates[taskId]?.repoPath ?? null;
155+
156+
export const selectTaskRepoExists =
157+
(taskId: string) => (state: TaskExecutionStore) =>
158+
state.taskStates[taskId]?.repoExists ?? null;

apps/twig/src/renderer/stores/registeredFoldersStore.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,23 @@ export const useRegisteredFoldersStore = create<RegisteredFoldersState>()(
4343
(async () => {
4444
try {
4545
const loadedFolders = await loadFolders();
46+
47+
// Remove folders that no longer exist on disk
48+
const deletedFolders = loadedFolders.filter((f) => f.exists === false);
49+
for (const folder of deletedFolders) {
50+
trpcVanilla.folders.removeFolder
51+
.mutate({ folderId: folder.id })
52+
.catch((err) =>
53+
log.error(`Failed to remove deleted folder ${folder.path}:`, err),
54+
);
55+
}
56+
const existingFolders = loadedFolders.filter((f) => f.exists !== false);
57+
4658
// Merge with existing state to preserve folders added during load
4759
set((state) => {
4860
// Dedupe by id, local state wins for freshness
4961
const byId = new Map<string, RegisteredFolder>();
50-
for (const f of loadedFolders) byId.set(f.id, f);
62+
for (const f of existingFolders) byId.set(f.id, f);
5163
for (const f of state.folders) byId.set(f.id, f);
5264
return { folders: Array.from(byId.values()), isLoaded: true };
5365
});
@@ -140,6 +152,7 @@ export const useRegisteredFoldersStore = create<RegisteredFoldersState>()(
140152

141153
getRecentFolders: (limit = 5) => {
142154
return [...get().folders]
155+
.filter((f) => f.exists !== false)
143156
.sort(
144157
(a, b) =>
145158
new Date(b.lastAccessed).getTime() -

0 commit comments

Comments
 (0)