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
129 changes: 67 additions & 62 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@milkdown/plugin-math": "^7.5.9",
"@milkdown/react": "^7.22.1",
"@monaco-editor/react": "^4.6.0",
"@tauri-apps/api": "2.10.1",
"@tauri-apps/api": "~2.12.0",
"@tauri-apps/plugin-dialog": "2.7.0",
"@tauri-apps/plugin-opener": "^2.7.2",
"monaco-editor": "^0.48.0",
Expand All @@ -31,7 +31,7 @@
"react-dom": "^19.3.0"
},
"devDependencies": {
"@tauri-apps/cli": "2.10.1",
"@tauri-apps/cli": "~2.12.0",
"@types/node": "20.19.39",
"@types/react": "^19.3.0",
"@types/react-dom": "^19.3.0",
Expand Down
99 changes: 84 additions & 15 deletions frontend/src/components/ProjectExplorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,89 @@ export default function ProjectExplorer({ root, onOpenFile, onOpenProject, onClo
const name = root?.split(/[\\/]/).filter(Boolean).pop() || "Project";
const [collapsed, setCollapsed] = useState(false);

if (collapsed) {
return <aside className="flex h-full w-10 shrink-0 flex-col border-r border-gray-200 bg-gray-50 dark:border-gray-700 dark:bg-[#252526]">
<button onClick={() => setCollapsed(false)} title="Show Explorer" aria-label="Show Explorer" className="px-2 py-2 text-lg text-gray-500 hover:bg-gray-100 hover:text-blue-500 dark:text-gray-300 dark:hover:bg-[#333]">›</button>
</aside>;
}
if (collapsed) {
return (
<aside className="flex h-full w-12 shrink-0 flex-col items-center border-r border-gray-200 bg-gray-50 py-2 dark:border-[#333] dark:bg-[#333333]">
<button
onClick={() => setCollapsed(false)}
title="Explorer"
aria-label="Explorer"
className="flex h-12 w-12 items-center justify-center text-gray-500 hover:text-black dark:text-gray-400 dark:hover:text-white"
>
<svg
fill="#000000"
viewBox="-3 0 30 30"
xmlns="http://www.w3.org/2000/svg">
<path d="M21,8.94a1.31,1.31,0,0,0-.06-.27l0-.09a1.07,1.07,0,0,0-.19-.28h0l-6-6h0a1.07,1.07,0,0,0-.28-.19.32.32,0,0,0-.09,0A.88.88,0,0,0,14.05,2H10A3,3,0,0,0,7,5V6H6A3,3,0,0,0,3,9V19a3,3,0,0,0,3,3h8a3,3,0,0,0,3-3V18h1a3,3,0,0,0,3-3V9S21,9,21,8.94ZM15,5.41,17.59,8H16a1,1,0,0,1-1-1ZM15,19a1,1,0,0,1-1,1H6a1,1,0,0,1-1-1V9A1,1,0,0,1,6,8H7v7a3,3,0,0,0,3,3h5Zm4-4a1,1,0,0,1-1,1H10a1,1,0,0,1-1-1V5a1,1,0,0,1,1-1h3V7a3,3,0,0,0,3,3h3Z"/>
</svg>
</button>
</aside>
);
}

return <aside className="flex h-full w-56 shrink-0 flex-col border-r border-gray-200 bg-gray-50 text-sm text-gray-800 dark:border-gray-700 dark:bg-[#252526] dark:text-gray-200">
<div className="flex items-center justify-between border-b border-gray-200 px-2 py-2 dark:border-gray-700">
<span className="truncate text-xs font-semibold uppercase tracking-wide">Explorer</span>
<div className="flex items-center gap-1"><button onClick={onOpenProject} title="Open project" className="px-1 hover:text-blue-500">+</button><button onClick={() => setCollapsed(true)} title="Hide Explorer" aria-label="Hide Explorer" className="px-1 text-gray-500 hover:text-blue-500 dark:text-gray-400">‹</button></div>
</div>
{!root ? <div className="p-3 text-xs text-gray-500 dark:text-gray-400"><p>No project folder is open.</p><button onClick={onOpenProject} className="mt-2 text-blue-600 hover:underline dark:text-blue-400">Open Project Folder</button></div> : <>
<div className="flex items-center justify-between border-b border-gray-200 px-2 py-2 text-xs font-medium dark:border-gray-700"><span className="truncate" title={root}>{name}</span><button onClick={onCloseProject} title="Close project" className="text-gray-500 hover:text-red-500 dark:text-gray-400">×</button></div>
<div className="min-h-0 flex-1 overflow-auto"><Tree key={`${root}-${refreshToken}`} path={root} depth={0} onOpenFile={onOpenFile} /></div>
</>}
</aside>;
return (
<div className="flex h-full shrink-0">
{/* BARRA IZQUIERDA: Siempre visible */}
<div className="flex w-12 shrink-0 flex-col items-center border-r border-gray-200 bg-gray-50 py-2 dark:border-[#333] dark:bg-[#333333]">
<button
onClick={() => setCollapsed(!collapsed)}
title="Explorer"
aria-label="Explorer"
className={`flex h-12 w-12 items-center justify-center border-l-2 transition-colors ${
!collapsed
? "border-blue-500 text-black dark:text-white" // Activo (como VS Code)
: "border-transparent text-gray-500 hover:text-black dark:text-gray-400 dark:hover:text-white" // Inactivo
}`}
>
{/* SVG EXACTO: Documento doble con esquina doblada */}
<svg
fill="#000000"
viewBox="-3 0 30 30"
xmlns="http://www.w3.org/2000/svg">
<path d="M21,8.94a1.31,1.31,0,0,0-.06-.27l0-.09a1.07,1.07,0,0,0-.19-.28h0l-6-6h0a1.07,1.07,0,0,0-.28-.19.32.32,0,0,0-.09,0A.88.88,0,0,0,14.05,2H10A3,3,0,0,0,7,5V6H6A3,3,0,0,0,3,9V19a3,3,0,0,0,3,3h8a3,3,0,0,0,3-3V18h1a3,3,0,0,0,3-3V9S21,9,21,8.94ZM15,5.41,17.59,8H16a1,1,0,0,1-1-1ZM15,19a1,1,0,0,1-1,1H6a1,1,0,0,1-1-1V9A1,1,0,0,1,6,8H7v7a3,3,0,0,0,3,3h5Zm4-4a1,1,0,0,1-1,1H10a1,1,0,0,1-1-1V5a1,1,0,0,1,1-1h3V7a3,3,0,0,0,3,3h3Z"/>
</svg>
</button>
</div>

{/* PANEL DERECHO: Solo se muestra si NO está colapsado */}
{!collapsed && (
<aside className="flex h-full w-56 shrink-0 flex-col border-r border-gray-200 bg-gray-50 text-sm text-gray-800 dark:border-gray-700 dark:bg-[#252526] dark:text-gray-200">

{/* Cabecera: EXPLORER y botones de acción */}
<div className="flex items-center justify-between border-b border-gray-200 px-3 py-2 dark:border-gray-700">
<span className="truncate text-xs font-semibold uppercase tracking-wide text-gray-600 dark:text-gray-400">
Explorer
</span>
<div className="flex items-center gap-1">
<button onClick={onOpenProject} title="Open project" className="rounded p-1 text-gray-500 hover:bg-gray-200 dark:text-gray-400 dark:hover:bg-[#3a3d41]">
+
</button>
</div>
</div>

{/* Contenido: Árbol de archivos o mensaje vacío */}
{!root ? (
<div className="p-3 text-xs text-gray-500 dark:text-gray-400">
<p>No project folder is open.</p>
<button onClick={onOpenProject} className="mt-2 text-blue-600 hover:underline dark:text-blue-400">
Open Project Folder
</button>
</div>
) : (
<>
<div className="flex items-center justify-between border-b border-gray-200 px-2 py-2 text-xs font-medium dark:border-gray-700">
<span className="truncate" title={root}>{name}</span>
<button onClick={onCloseProject} title="Close project" className="text-gray-500 hover:text-red-500 dark:text-gray-400">
×
</button>
</div>
<div className="min-h-0 flex-1 overflow-auto">
<Tree key={`${root}-${refreshToken}`} path={root} depth={0} onOpenFile={onOpenFile} />
</div>
</>
)}
</aside>
)}
</div>
);
}
Loading