feat: allow control over displaying hidden/dotfiles and .gitignore matches (#179)
* feat: add chat setting to toggle hidden files (dotfiles) * feat: add toggle to show/hide gitignored files in file browser * refactor: don't reuse visibility setting for dotfiles toggle * fix: honor hidden/gitignored toggles across runtimes --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
6544b12e78
commit
2de0d9d3dd
@@ -322,6 +322,8 @@ export interface FileSearchQuery {
|
||||
directory: string;
|
||||
query: string;
|
||||
maxResults?: number;
|
||||
includeHidden?: boolean;
|
||||
respectGitignore?: boolean;
|
||||
}
|
||||
|
||||
export interface FileSearchResult {
|
||||
@@ -576,4 +578,3 @@ export interface SkillsInstallResponse {
|
||||
skipped?: Array<{ skillName: string; reason: string }>;
|
||||
error?: SkillsInstallError;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import { getSafeStorage } from '@/stores/utils/safeStorage';
|
||||
|
||||
const SHOW_HIDDEN_STORAGE_KEY = 'directoryTreeShowHidden';
|
||||
const SHOW_HIDDEN_EVENT = 'directory-show-hidden-change';
|
||||
|
||||
const readStoredShowHidden = (): boolean => {
|
||||
if (typeof window === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const stored = getSafeStorage().getItem(SHOW_HIDDEN_STORAGE_KEY);
|
||||
return stored === 'true';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const notifyDirectoryShowHiddenChanged = () => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
window.dispatchEvent(new Event(SHOW_HIDDEN_EVENT));
|
||||
};
|
||||
|
||||
export const setDirectoryShowHidden = (value: boolean) => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getSafeStorage().setItem(SHOW_HIDDEN_STORAGE_KEY, value ? 'true' : 'false');
|
||||
notifyDirectoryShowHiddenChanged();
|
||||
} catch {
|
||||
// ignore storage errors
|
||||
}
|
||||
};
|
||||
|
||||
export const useDirectoryShowHidden = (): boolean => {
|
||||
const [showHidden, setShowHidden] = React.useState(readStoredShowHidden);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const handleChange = () => {
|
||||
setShowHidden(readStoredShowHidden());
|
||||
};
|
||||
|
||||
window.addEventListener('storage', handleChange);
|
||||
window.addEventListener(SHOW_HIDDEN_EVENT, handleChange);
|
||||
return () => {
|
||||
window.removeEventListener('storage', handleChange);
|
||||
window.removeEventListener(SHOW_HIDDEN_EVENT, handleChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return showHidden;
|
||||
};
|
||||
|
||||
export const DIRECTORY_SHOW_HIDDEN_STORAGE_KEY = SHOW_HIDDEN_STORAGE_KEY;
|
||||
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
|
||||
import { getSafeStorage } from '@/stores/utils/safeStorage';
|
||||
|
||||
const SHOW_GITIGNORED_STORAGE_KEY = 'filesViewShowGitignored';
|
||||
const SHOW_GITIGNORED_EVENT = 'files-view-show-gitignored-change';
|
||||
|
||||
const readStoredShowGitignored = (): boolean => {
|
||||
if (typeof window === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const stored = getSafeStorage().getItem(SHOW_GITIGNORED_STORAGE_KEY);
|
||||
return stored === 'true';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const notifyFilesViewShowGitignoredChanged = () => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
window.dispatchEvent(new Event(SHOW_GITIGNORED_EVENT));
|
||||
};
|
||||
|
||||
export const setFilesViewShowGitignored = (value: boolean) => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
getSafeStorage().setItem(SHOW_GITIGNORED_STORAGE_KEY, value ? 'true' : 'false');
|
||||
notifyFilesViewShowGitignoredChanged();
|
||||
} catch {
|
||||
// ignore storage errors
|
||||
}
|
||||
};
|
||||
|
||||
export const useFilesViewShowGitignored = (): boolean => {
|
||||
const [showGitignored, setShowGitignored] = React.useState(readStoredShowGitignored);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
const handleChange = () => {
|
||||
setShowGitignored(readStoredShowGitignored());
|
||||
};
|
||||
|
||||
window.addEventListener('storage', handleChange);
|
||||
window.addEventListener(SHOW_GITIGNORED_EVENT, handleChange);
|
||||
return () => {
|
||||
window.removeEventListener('storage', handleChange);
|
||||
window.removeEventListener(SHOW_GITIGNORED_EVENT, handleChange);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return showGitignored;
|
||||
};
|
||||
|
||||
export const FILES_VIEW_SHOW_GITIGNORED_STORAGE_KEY = SHOW_GITIGNORED_STORAGE_KEY;
|
||||
@@ -1708,7 +1708,7 @@ class OpencodeService {
|
||||
const desktopFiles = getDesktopFilesApi();
|
||||
if (desktopFiles) {
|
||||
try {
|
||||
const result = await desktopFiles.listDirectory(directoryPath || '');
|
||||
const result = await desktopFiles.listDirectory(directoryPath || '', options);
|
||||
if (!result || !Array.isArray(result.entries)) {
|
||||
return [];
|
||||
}
|
||||
@@ -1753,7 +1753,15 @@ class OpencodeService {
|
||||
}
|
||||
}
|
||||
|
||||
async searchFiles(query: string, options?: { directory?: string | null; limit?: number }): Promise<ProjectFileSearchHit[]> {
|
||||
async searchFiles(
|
||||
query: string,
|
||||
options?: {
|
||||
directory?: string | null;
|
||||
limit?: number;
|
||||
includeHidden?: boolean;
|
||||
respectGitignore?: boolean;
|
||||
}
|
||||
): Promise<ProjectFileSearchHit[]> {
|
||||
const desktopFiles = getDesktopFilesApi();
|
||||
const directory = typeof options?.directory === 'string' && options.directory.trim().length > 0
|
||||
? options.directory.trim()
|
||||
@@ -1766,6 +1774,8 @@ class OpencodeService {
|
||||
directory: directory || '',
|
||||
query,
|
||||
maxResults: options?.limit,
|
||||
includeHidden: options?.includeHidden,
|
||||
respectGitignore: options?.respectGitignore,
|
||||
});
|
||||
|
||||
if (!Array.isArray(results)) {
|
||||
@@ -1809,6 +1819,12 @@ class OpencodeService {
|
||||
if (typeof options?.limit === 'number' && Number.isFinite(options.limit)) {
|
||||
params.set('limit', String(options.limit));
|
||||
}
|
||||
if (options?.includeHidden) {
|
||||
params.set('includeHidden', 'true');
|
||||
}
|
||||
if (options?.respectGitignore === false) {
|
||||
params.set('respectGitignore', 'false');
|
||||
}
|
||||
|
||||
const searchUrl = `${this.baseUrl}/fs/search${params.toString() ? `?${params.toString()}` : ''}`;
|
||||
const response = await fetch(searchUrl, {
|
||||
|
||||
Reference in New Issue
Block a user