Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 36 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,44 @@ jobs:
name: Check Not Allowed File Changes
runs-on: ubuntu-latest
steps:
# Note: dorny/paths-filter fetches file info via GitHub API, no checkout needed
# Avoid GitHub API rate-limits by diffing locally.
# We checkout both base and head without running any PR code.
- name: Checkout base (target) commit
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha }}
path: base
fetch-depth: 1

- name: Checkout head (PR) commit
uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.event.pull_request.head.sha }}
path: head
fetch-depth: 1
persist-credentials: false

- name: Check Not Allowed File Changes
uses: dorny/paths-filter@v4
id: filter_not_allowed
with:
list-files: json
filters: |
change:
- 'package-lock.json'
- 'yarn.lock'
- 'pnpm-lock.yaml'
shell: bash
run: |
set -euo pipefail

# List changed files between base and head directories.
CHANGED_FILES="$(git diff --name-only --no-index --relative base head || true)"

echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
echo "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"

# Reject lockfile changes.
FORBIDDEN_REGEX='^(package-lock\.json|yarn\.lock|pnpm-lock\.yaml)$'
if echo "$CHANGED_FILES" | grep -Eq "$FORBIDDEN_REGEX"; then
echo "change=true" >> "$GITHUB_OUTPUT"
else
echo "change=false" >> "$GITHUB_OUTPUT"
fi

# ref: https://github.com/github/docs/blob/main/.github/workflows/triage-unallowed-contributions.yml
- name: Comment About Changes We Can't Accept
Expand Down
169 changes: 116 additions & 53 deletions src/components/Folder/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { TooltipSimple } from '@/components/ui/tooltip';
import {
ChevronDown,
ChevronRight,
Expand Down Expand Up @@ -130,6 +131,15 @@ interface FileInfo {
isRemote?: boolean;
}

function filterFolderOnlyTree(node: FileTreeNode): FileTreeNode {
if (!node.children || node.children.length === 0)
return { ...node, children: [] };
const children = node.children
.filter((c) => c.isFolder)
.map((c) => filterFolderOnlyTree(c));
return { ...node, children };
}

// FileTree component to render nested file structure
interface FileTreeProps {
node: FileTreeNode;
Expand Down Expand Up @@ -167,51 +177,53 @@ export const FileTree: React.FC<FileTreeProps> = ({

return (
<div key={child.path}>
<button
onClick={() => {
if (child.isFolder) {
onToggleFolder(child.path);
} else {
onSelectFile(fileInfo);
}
}}
className={`text-primary flex w-full items-center justify-start gap-2 rounded-xl bg-fill-fill-transparent p-2 text-left text-sm backdrop-blur-lg transition-colors hover:bg-fill-fill-transparent-active ${
selectedFile?.path === child.path
? 'bg-fill-fill-transparent-active'
: ''
}`}
>
{child.isFolder ? (
<span className="flex h-4 w-4 flex-shrink-0 items-center justify-center">
{isExpanded ? (
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-4 w-4" />
)}
</span>
) : (
<span
className="flex h-4 w-4 flex-shrink-0 items-center justify-center"
aria-hidden
/>
)}

{child.isFolder ? (
<FolderIcon className="h-5 w-5 flex-shrink-0 text-yellow-600" />
) : child.icon ? (
<child.icon className="h-5 w-5 flex-shrink-0" />
) : (
<FileText className="h-5 w-5 flex-shrink-0" />
)}

<span
className={`truncate text-[13px] leading-5 ${
child.isFolder ? 'font-semibold' : 'font-medium'
<TooltipSimple content={child.name}>
<button
onClick={() => {
if (child.isFolder) {
onToggleFolder(child.path);
} else {
onSelectFile(fileInfo);
}
}}
className={`text-primary flex w-full items-center justify-start gap-2 rounded-xl bg-fill-fill-transparent p-2 text-left text-sm backdrop-blur-lg transition-colors hover:bg-fill-fill-transparent-active ${
selectedFile?.path === child.path
? 'bg-fill-fill-transparent-active'
: ''
}`}
>
{child.name}
</span>
</button>
{child.isFolder ? (
<span className="flex h-4 w-4 flex-shrink-0 items-center justify-center">
{isExpanded ? (
<ChevronDown className="h-4 w-4" />
) : (
<ChevronRight className="h-4 w-4" />
)}
</span>
) : (
<span
className="flex h-4 w-4 flex-shrink-0 items-center justify-center"
aria-hidden
/>
)}

{child.isFolder ? (
<FolderIcon className="h-5 w-5 flex-shrink-0 text-yellow-600" />
) : child.icon ? (
<child.icon className="h-5 w-5 flex-shrink-0" />
) : (
<FileText className="h-5 w-5 flex-shrink-0" />
)}

<span
className={`truncate text-[13px] leading-5 ${
child.isFolder ? 'font-semibold' : 'font-medium'
}`}
>
{child.name}
</span>
</button>
</TooltipSimple>

{child.isFolder && isExpanded && child.children && (
<FileTree
Expand All @@ -234,14 +246,14 @@ export const FileTree: React.FC<FileTreeProps> = ({
function downloadByBrowser(url: string) {
window.ipcRenderer
.invoke('download-file', url)
.then((result) => {
.then((result: any) => {
if (result.success) {
console.log('download-file success:', result.path);
} else {
console.error('download-file error:', result.error);
}
})
.catch((error) => {
.catch((error: any) => {
console.error('download-file error:', error);
});
}
Expand Down Expand Up @@ -277,6 +289,23 @@ export default function Folder({ data: _data }: { data?: Agent }) {
]);
const hasFetchedRemote = useRef(false);

const folderOnlyTree = useMemo(
() => filterFolderOnlyTree(fileTree),
[fileTree]
);
const filesOnly = useMemo(() => {
const files = fileGroups?.[0]?.files || [];
return files
.filter((f) => !f.isFolder)
.sort((a, b) => {
const aKey =
`${a.relativePath ? `${a.relativePath}/` : ''}${a.name}`.toLowerCase();
const bKey =
`${b.relativePath ? `${b.relativePath}/` : ''}${b.name}`.toLowerCase();
return aKey.localeCompare(bKey);
});
}, [fileGroups]);

const selectedFileChange = (file: FileInfo, isShowSourceCode?: boolean) => {
if (file.type === 'zip') {
// if file is remote, don't call reveal-in-folder
Expand Down Expand Up @@ -304,7 +333,7 @@ export default function Folder({ data: _data }: { data?: Agent }) {
chatStore.setSelectedFile(chatStore.activeTaskId as string, file);
setLoading(false);
})
.catch((error) => {
.catch((error: any) => {
console.error('read-file-dataurl error:', error);
setLoading(false);
});
Expand All @@ -322,12 +351,12 @@ export default function Folder({ data: _data }: { data?: Agent }) {
// all other files call open-file interface, the backend handles download and parsing
window.ipcRenderer
.invoke('open-file', file.type, file.path, isShowSourceCode)
.then((res) => {
.then((res: any) => {
setSelectedFile({ ...file, content: res });
chatStore.setSelectedFile(chatStore.activeTaskId as string, file);
setLoading(false);
})
.catch((error) => {
.catch((error: any) => {
console.error('open-file error:', error);
setLoading(false);
});
Expand Down Expand Up @@ -623,19 +652,53 @@ export default function Folder({ data: _data }: { data?: Agent }) {
<div className="p-2">
<div className="mb-2">
<div className="text-primary px-2 py-1 text-[10px] font-bold leading-4">
{t('chat.files')}
{t('chat.folders')}
</div>
<FileTree
node={fileTree}
node={folderOnlyTree}
selectedFile={selectedFile}
expandedFolders={expandedFolders}
onToggleFolder={toggleFolder}
onSelectFile={(file) =>
selectedFileChange(file, isShowSourceCode)
}
onSelectFile={() => {}}
isShowSourceCode={isShowSourceCode}
/>
</div>

<div className="mb-2 mt-3">
<div className="text-primary px-2 py-1 text-[10px] font-bold leading-4">
{t('chat.files')}
</div>
<div className="space-y-1">
{filesOnly.map((file) => {
const displayName = file.relativePath
? `${file.relativePath}/${file.name}`
: file.name;
return (
<TooltipSimple key={file.path} content={displayName}>
<button
onClick={() =>
selectedFileChange(file, isShowSourceCode)
}
className={`text-primary flex w-full items-center justify-start gap-2 rounded-xl bg-fill-fill-transparent p-2 text-left text-sm backdrop-blur-lg transition-colors hover:bg-fill-fill-transparent-active ${
selectedFile?.path === file.path
? 'bg-fill-fill-transparent-active'
: ''
}`}
>
{file.icon ? (
<file.icon className="h-5 w-5 flex-shrink-0" />
) : (
<FileText className="h-5 w-5 flex-shrink-0" />
)}
<span className="truncate text-[13px] font-medium leading-5">
{displayName}
</span>
</button>
</TooltipSimple>
);
})}
</div>
</div>
</div>
) : (
// Display simplified file icons when collapsed
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/ar/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "افتح",
"close": "اغلق",
"search": "بحث",
"folders": "مجلدات",
"files": "ملفات",
"loading": "تحميل",
"select-a-file-to-view-its-contents": "حدد ملفًا لعرض محتوياته",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/de/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "Öffnen",
"close": "Schließen",
"search": "Suchen",
"folders": "Ordner",
"files": "Dateien",
"loading": "Wird geladen",
"select-a-file-to-view-its-contents": "Wählen Sie eine Datei aus, um ihren Inhalt anzuzeigen",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/en-us/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "Open",
"close": "Close",
"search": "Search",
"folders": "Folders",
"files": "Files",
"loading": "Loading",
"select-a-file-to-view-its-contents": "Select a file to view its contents",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/es/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "Abrir",
"close": "Cerrar",
"search": "Buscar",
"folders": "Carpetas",
"files": "Archivos",
"loading": "Cargando",
"select-a-file-to-view-its-contents": "Selecciona un archivo para ver su contenido",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/fr/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "Ouvrir",
"close": "Fermer",
"search": "Rechercher",
"folders": "Dossiers",
"files": "Fichiers",
"loading": "Chargement",
"select-a-file-to-view-its-contents": "Sélectionnez un fichier pour afficher son contenu",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/it/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "Apri",
"close": "Chiudi",
"search": "Cerca",
"folders": "Cartelle",
"files": "File",
"loading": "Caricamento",
"select-a-file-to-view-its-contents": "Seleziona un file per visualizzarne il contenuto",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/ja/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "開く",
"close": "閉じる",
"search": "検索",
"folders": "フォルダ",
"files": "ファイル",
"loading": "読み込み中",
"select-a-file-to-view-its-contents": "ファイルを選択して内容を表示",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/ko/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "열기",
"close": "닫기",
"search": "검색",
"folders": "폴더",
"files": "파일",
"loading": "로딩 중",
"select-a-file-to-view-its-contents": "내용을 보려면 파일을 선택하세요",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/ru/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "Открыть",
"close": "Закрыть",
"search": "Поиск",
"folders": "Папки",
"files": "Файлы",
"loading": "Загрузка",
"select-a-file-to-view-its-contents": "Выберите файл, чтобы просмотреть его содержимое",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/zh-Hans/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "打开",
"close": "关闭",
"search": "搜索",
"folders": "文件夹",
"files": "文件",
"loading": "加载中",
"select-a-file-to-view-its-contents": "选择一个文件以查看其内容",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/locales/zh-Hant/chat.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"open": "打開",
"close": "關閉",
"search": "搜索",
"folders": "資料夾",
"files": "文件",
"loading": "加載中",
"select-a-file-to-view-its-contents": "選擇一個文件以查看其內容",
Expand Down