() }: Props = $props();
const statusCode = $derived(section === 'staged' ? file.indexStatus : file.workingStatus);
const statusLabel = $derived(getGitStatusLabel(statusCode));
@@ -31,6 +32,14 @@
});
const fileIcon = $derived(getFileIcon(fileName) as IconName);
+ // AI changes indicator — file.path is relative; build absolute to match aiChangesSet
+ const hasAiChange = $derived(() => {
+ const base = projectState.currentProject?.path;
+ if (!base) return false;
+ const sep = base.includes('\\') ? '\\' : '/';
+ return aiChangesSet.has(`${base}${sep}${file.path}`);
+ });
+
function openInFilesPanel(e: MouseEvent) {
e.stopPropagation();
const basePath = projectState.currentProject?.path;
@@ -65,8 +74,16 @@
{/if}
-
- {statusLabel}
+
+
+ {#if hasAiChange()}
+
+ {/if}
+ {statusLabel}
+
diff --git a/frontend/components/workspace/panels/FilesPanel.svelte b/frontend/components/workspace/panels/FilesPanel.svelte
index 8527de7a..0f269530 100644
--- a/frontend/components/workspace/panels/FilesPanel.svelte
+++ b/frontend/components/workspace/panels/FilesPanel.svelte
@@ -80,6 +80,7 @@
import { getGitStatusLabel, getGitStatusColor } from '$frontend/utils/git-status';
import type { IconName } from '$shared/types/ui/icons';
import { fileState, clearRevealRequest, collapseAllTrigger } from '$frontend/stores/core/files.svelte';
+ import { onAiFilesChange } from '$frontend/utils/ai-changes';
import {
gitStatusState,
initGitStatus,
@@ -114,6 +115,9 @@
let error = $state('');
let expandedFolders = $state(new Set());
+ // AI changes set for explorer dot indicators
+ let aiChangesSet = $state(new Set());
+
// Watch global collapse-all signal
$effect(() => {
collapseAllTrigger.count; // track changes
@@ -495,6 +499,9 @@
function openFileInTab(file: FileNode, target?: { line: number; column?: number; length?: number }) {
if (file.type === 'directory') return;
+ // Reset active AI reveal filter on normal file tree/tab navigation
+ fileViewerRef?.resetRevealFilter?.();
+
// Snapshot current active tab's editor scroll before switching
snapshotActiveTabScroll();
@@ -564,6 +571,9 @@
}
function selectTab(path: string) {
+ // Reset active AI reveal filter on normal file tree/tab navigation
+ fileViewerRef?.resetRevealFilter?.();
+
// Snapshot current active tab's editor scroll before switching away
snapshotActiveTabScroll();
@@ -2271,6 +2281,11 @@
initGitStatus();
initIgnoredPaths();
+ // Subscribe to AI changes for explorer dot indicators
+ const unsubAiFiles = onAiFilesChange((paths) => {
+ aiChangesSet = new Set(paths);
+ });
+
// Safety-net reconcile when the user returns to the app/tab. File-watch
// push events can be missed while the window is hidden (OS throttling,
// sleep/wake, dropped events); reconciling on focus re-establishes truth
@@ -2291,6 +2306,7 @@
}
return () => {
+ unsubAiFiles();
if (typeof window !== 'undefined') {
window.removeEventListener('focus', scheduleReconcile);
document.removeEventListener('visibilitychange', handleVisibilityChange);
@@ -2487,6 +2503,7 @@
{isRootDropTarget}
{busyPaths}
{isRootBusy}
+ {aiChangesSet}
/>
diff --git a/frontend/components/workspace/panels/GitPanel.svelte b/frontend/components/workspace/panels/GitPanel.svelte
index e877a9ca..9f605c80 100644
--- a/frontend/components/workspace/panels/GitPanel.svelte
+++ b/frontend/components/workspace/panels/GitPanel.svelte
@@ -1,5 +1,6 @@