diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9cdaf9f..e464d53 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -4,7 +4,7 @@ import { TopBar } from "./components/TopBar"; import { ChatPanel } from "./panels/ChatPanel"; import { PreviewPanel } from "./panels/PreviewPanel"; import { SettingsView } from "./panels/SettingsView"; -import { FilesView, HistoryView, ToolsView } from "./panels/ActivityViews"; +import { HistoryView, ToolsView } from "./panels/ActivityViews"; import { useStore } from "./store/useStore"; export default function App() { @@ -65,12 +65,6 @@ export default function App() { )} {view === "history" && setView("chat")} />} - {view === "files" && ( - <> - - - - )} {view === "tools" && setView("settings")} />} {view === "settings" && } diff --git a/frontend/src/components/FileTree.tsx b/frontend/src/components/FileTree.tsx new file mode 100644 index 0000000..755e5b7 --- /dev/null +++ b/frontend/src/components/FileTree.tsx @@ -0,0 +1,79 @@ +import { useStore } from "../store/useStore"; +import { downloadFile, type FileNode } from "../api/client"; +import { DownloadIcon, TrashIcon } from "./Icon"; + +/** Arborescence du workspace : ouvrir dans l'aperçu, télécharger, supprimer. */ +export function FileTree({ + nodes, + depth = 0, +}: { + nodes: FileNode[]; + depth?: number; +}) { + const { openPreview, previewPath, removeFile, currentProject } = useStore(); + + const confirmDelete = (n: FileNode) => { + const msg = + n.type === "dir" + ? `Supprimer le dossier ${n.path} et tout son contenu ?` + : `Supprimer ${n.path} ?`; + if (window.confirm(msg)) void removeFile(n.path); + }; + + return ( + <> + {nodes.map((n) => { + const active = n.type === "file" && n.path === previewPath; + return ( +
+
n.type === "file" && openPreview(n.path)} + className={`group mb-[2px] flex items-center gap-2 rounded-card px-2 py-[5px] text-[13.5px] ${ + n.type === "file" + ? active + ? "cursor-pointer border border-accent-line bg-accent-ghost text-ink" + : "cursor-pointer border border-transparent text-ink-3 hover:bg-accent-ghost" + : "text-muted-2" + }`} + style={{ paddingLeft: 8 + depth * 14 }} + > + {n.type === "dir" ? ( + ▾ + ) : ( + + )} + {n.name} + {n.type === "file" && ( + + )} + +
+ {n.children && } +
+ ); + })} + + ); +} diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 9a6e4ed..0c48852 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -2,21 +2,25 @@ import { useStore } from "../store/useStore"; import { ChatIcon, ClockIcon, - FilesIcon, LokiMark, NodesIcon, + PlusIcon, SettingsIcon, } from "./Icon"; -export type View = "chat" | "history" | "files" | "tools" | "settings"; +export type View = "chat" | "history" | "tools" | "settings"; +// Les fichiers ne sont plus une vue : ils vivent dans l'onglet « Fichiers » +// du panneau droit, à côté de Code. const NAV: { id: View; label: string; icon: React.ReactNode }[] = [ - { id: "chat", label: "Demander", icon: }, + { id: "chat", label: "Discussion", icon: }, { id: "history", label: "Historique", icon: }, - { id: "files", label: "Fichiers", icon: }, { id: "tools", label: "Outils", icon: }, ]; +/** Nombre de discussions récentes listées sous « Discussion ». */ +const RECENT = 5; + /** Barre latérale (264 px) : marque, navigation, état Ollama, compte. */ export function Sidebar({ active, @@ -25,8 +29,28 @@ export function Sidebar({ active: View; onChange: (v: View) => void; }) { - const { status, selectedModel, systemStats } = useStore(); + const { + status, + selectedModel, + systemStats, + sessions, + currentSessionId, + streamingSessionId, + newSession, + openSession, + } = useStore(); const gpu = systemStats?.gpu ?? null; + const recent = sessions.slice(0, RECENT); + + const startChat = () => { + void newSession(); + onChange("chat"); + }; + + const pick = (id: string) => { + void openSession(id); + onChange("chat"); + }; return (