From 2f1c026b4f3365832b7d9e6abe5343aba777d6e7 Mon Sep 17 00:00:00 2001 From: zerob13 Date: Fri, 5 Dec 2025 11:04:40 +0800 Subject: [PATCH 1/5] fix: keep last file list --- src/renderer/src/stores/acpWorkspace.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/stores/acpWorkspace.ts b/src/renderer/src/stores/acpWorkspace.ts index 610bf40fd..67d6fab19 100644 --- a/src/renderer/src/stores/acpWorkspace.ts +++ b/src/renderer/src/stores/acpWorkspace.ts @@ -19,6 +19,7 @@ export const useAcpWorkspaceStore = defineStore('acpWorkspace', () => { const fileTree = ref([]) const terminalSnippets = ref([]) const lastSyncedConversationId = ref(null) + const lastSuccessfulWorkdir = ref(null) // Debounce timer for file refresh let fileRefreshDebounceTimer: ReturnType | null = null @@ -71,11 +72,14 @@ export const useAcpWorkspaceStore = defineStore('acpWorkspace', () => { // Guard against race condition: only update if still on the same conversation if (chatStore.getActiveThreadId() === conversationIdBefore) { fileTree.value = result + lastSuccessfulWorkdir.value = workdir } } catch (error) { console.error('[AcpWorkspace] Failed to load file tree:', error) if (chatStore.getActiveThreadId() === conversationIdBefore) { - fileTree.value = [] + if (lastSuccessfulWorkdir.value !== workdir) { + fileTree.value = [] + } } } finally { if (chatStore.getActiveThreadId() === conversationIdBefore) { @@ -156,6 +160,7 @@ export const useAcpWorkspaceStore = defineStore('acpWorkspace', () => { fileTree.value = [] terminalSnippets.value = [] lastSyncedConversationId.value = null + lastSuccessfulWorkdir.value = null } // === Event Listeners === @@ -211,7 +216,11 @@ export const useAcpWorkspaceStore = defineStore('acpWorkspace', () => { // Watch for workdir changes watch( currentWorkdir, - (workdir) => { + (workdir, previousWorkdir) => { + if (workdir !== previousWorkdir) { + lastSuccessfulWorkdir.value = null + } + if (isAcpMode.value && workdir) { refreshFileTree() } From df9e5d795727325142fcaff126f785d3275f005e Mon Sep 17 00:00:00 2001 From: zerob13 Date: Fri, 5 Dec 2025 15:35:39 +0800 Subject: [PATCH 2/5] feat(acp-workspace): add file context actions --- .../presenter/acpWorkspacePresenter/index.ts | 41 +++++++ src/renderer/src/components/ChatView.vue | 28 ++++- .../acp-workspace/AcpWorkspaceFileNode.vue | 108 ++++++++++++++---- .../acp-workspace/AcpWorkspaceFiles.vue | 8 ++ .../acp-workspace/AcpWorkspaceView.vue | 5 +- src/renderer/src/i18n/da-DK/chat.json | 7 +- src/renderer/src/i18n/en-US/chat.json | 7 +- src/renderer/src/i18n/fa-IR/chat.json | 7 +- src/renderer/src/i18n/fr-FR/chat.json | 7 +- src/renderer/src/i18n/ja-JP/chat.json | 7 +- src/renderer/src/i18n/ko-KR/chat.json | 7 +- src/renderer/src/i18n/pt-BR/chat.json | 7 +- src/renderer/src/i18n/ru-RU/chat.json | 7 +- src/renderer/src/i18n/zh-CN/chat.json | 7 +- src/renderer/src/i18n/zh-HK/chat.json | 7 +- src/renderer/src/i18n/zh-TW/chat.json | 7 +- .../types/presenters/acp-workspace.d.ts | 12 ++ 17 files changed, 246 insertions(+), 33 deletions(-) diff --git a/src/main/presenter/acpWorkspacePresenter/index.ts b/src/main/presenter/acpWorkspacePresenter/index.ts index 5256d7d95..20ce6ed99 100644 --- a/src/main/presenter/acpWorkspacePresenter/index.ts +++ b/src/main/presenter/acpWorkspacePresenter/index.ts @@ -1,4 +1,5 @@ import path from 'path' +import { shell } from 'electron' import { eventBus, SendTarget } from '@/eventbus' import { ACP_WORKSPACE_EVENTS } from '@/events' import { readDirectoryShallow } from './directoryReader' @@ -73,6 +74,46 @@ export class AcpWorkspacePresenter implements IAcpWorkspacePresenter { return readDirectoryShallow(dirPath) } + /** + * Reveal a file or directory in the system file manager + */ + async revealFileInFolder(filePath: string): Promise { + // Security check: only allow revealing within registered workdirs + if (!this.isPathAllowed(filePath)) { + console.warn(`[AcpWorkspace] Blocked reveal attempt for unauthorized path: ${filePath}`) + return + } + + const normalizedPath = path.resolve(filePath) + + try { + shell.showItemInFolder(normalizedPath) + } catch (error) { + console.error(`[AcpWorkspace] Failed to reveal path: ${normalizedPath}`, error) + } + } + + /** + * Open a file or directory with the system default application + */ + async openFile(filePath: string): Promise { + if (!this.isPathAllowed(filePath)) { + console.warn(`[AcpWorkspace] Blocked open attempt for unauthorized path: ${filePath}`) + return + } + + const normalizedPath = path.resolve(filePath) + + try { + const errorMessage = await shell.openPath(normalizedPath) + if (errorMessage) { + console.error(`[AcpWorkspace] Failed to open path: ${normalizedPath}`, errorMessage) + } + } catch (error) { + console.error(`[AcpWorkspace] Failed to open path: ${normalizedPath}`, error) + } + } + /** * Get plan entries */ diff --git a/src/renderer/src/components/ChatView.vue b/src/renderer/src/components/ChatView.vue index f7fccb6b2..8a1dd2759 100644 --- a/src/renderer/src/components/ChatView.vue +++ b/src/renderer/src/components/ChatView.vue @@ -12,7 +12,11 @@ - + @@ -48,6 +52,7 @@ diff --git a/src/renderer/src/components/acp-workspace/AcpWorkspaceFiles.vue b/src/renderer/src/components/acp-workspace/AcpWorkspaceFiles.vue index 37b3a0f34..d71f49dc8 100644 --- a/src/renderer/src/components/acp-workspace/AcpWorkspaceFiles.vue +++ b/src/renderer/src/components/acp-workspace/AcpWorkspaceFiles.vue @@ -32,6 +32,7 @@ :node="node" :depth="0" @toggle="handleToggle" + @append-path="handleAppendPath" />
@@ -53,6 +54,9 @@ import type { AcpFileNode } from '@shared/presenter' const { t } = useI18n() const store = useAcpWorkspaceStore() const showFiles = ref(true) +const emit = defineEmits<{ + 'append-path': [filePath: string] +}>() const countFiles = (nodes: AcpFileNode[]): number => { let count = 0 @@ -70,6 +74,10 @@ const fileCount = computed(() => countFiles(store.fileTree)) const handleToggle = async (node: AcpFileNode) => { await store.toggleFileNode(node) } + +const handleAppendPath = (filePath: string) => { + emit('append-path', filePath) +}