Presets : bouton d'ajout enfin lisible, et suppression branchée

Le bouton d'ajout existait mais était introuvable : l'en-tête des
Réglages n'avait ni flex-wrap ni flex-none, donc les commandes
s'écrasaient les unes sur les autres. « + Preset » se retrouvait
compressé sur deux lignes dans 62 px, et le bouton d'enregistrement
chevauchait le texte de description.

- En-tête : les commandes ne se compriment plus (flex-none,
  whitespace-nowrap) et passent à la ligne si la largeur manque.
- Le bouton devient « + Nouveau preset », explicite, sur une ligne.
- Le preset appliqué reste sélectionné, avec un bouton de suppression :
  deletePreset existait côté API mais n'était appelé nulle part.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SVay7z3y7q2gEe54ByAE6N
This commit is contained in:
Claude committed 2026-08-23 12:39:01 +00:00
1 parent bb4fb1bac7
commit 8305f4e24c
1 file changed
+55 -20
+55 -20
View File
@@ -6,6 +6,7 @@ import {
getBenchScores, getBenchScores,
getHardware, getHardware,
listMcp, listMcp,
deletePreset,
listPresets, listPresets,
pullModel, pullModel,
runBench, runBench,
@@ -19,7 +20,7 @@ import type {
HardwareInfo, HardwareInfo,
McpServer, McpServer,
} from "../api/client"; } from "../api/client";
import { DownloadIcon, RefreshIcon } from "../components/Icon"; import { DownloadIcon, PlusIcon, RefreshIcon, TrashIcon } from "../components/Icon";
const TOOL_DESC: Record<string, string> = { const TOOL_DESC: Record<string, string> = {
read_file: "Lire un fichier du projet", read_file: "Lire un fichier du projet",
@@ -117,8 +118,11 @@ export function SettingsView() {
<div className="scr flex-1 overflow-auto bg-base px-[30px] py-[26px]"> <div className="scr flex-1 overflow-auto bg-base px-[30px] py-[26px]">
<div className="mx-auto max-w-[1000px]"> <div className="mx-auto max-w-[1000px]">
{/* En-tête */} {/* En-tête */}
<div className="mb-[22px] flex items-end justify-between"> {/* flex-wrap + flex-none : sans ça, les commandes s'écrasaient les unes
<div> sur les autres et « + Preset » devenait un bloc illisible sur deux
lignes, chevauchant la description. */}
<div className="mb-[22px] flex flex-wrap items-end justify-between gap-x-6 gap-y-3">
<div className="min-w-[260px] flex-1">
<div className="font-pixel text-[13px] text-ink"> <div className="font-pixel text-[13px] text-ink">
MODÈLE &amp; GÉNÉRATION MODÈLE &amp; GÉNÉRATION
</div> </div>
@@ -127,19 +131,26 @@ export function SettingsView() {
sont sauvegardés pour le modèle sélectionné. sont sauvegardés pour le modèle sélectionné.
</div> </div>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex flex-none items-center gap-2 whitespace-nowrap">
<PresetBar model={selectedModel} onApplied={refreshConfig} /> <PresetBar model={selectedModel} onApplied={refreshConfig} />
<div className="flex items-center gap-1.5 text-[13px] text-ok"> <span
<span className="h-2 w-2 border border-line bg-ok" /> className={`flex flex-none items-center gap-1.5 text-[13px] ${
status?.connected ? "text-ok" : "text-warn"
}`}
>
<span
className={`h-1.5 w-1.5 rounded-full ${
status?.connected ? "bg-ok" : "bg-warn"
}`}
/>
{status?.connected ? "Ollama connecté" : "Ollama déconnecté"} {status?.connected ? "Ollama connecté" : "Ollama déconnecté"}
</div> </span>
<button <button
onClick={save} onClick={save}
disabled={!dirty} disabled={!dirty}
className="flex h-[34px] items-center gap-1.5 border border-line bg-accent px-4 text-[14px] text-white shadow-accent-soft disabled:opacity-40" className="flex h-[34px] flex-none items-center rounded-card border border-line bg-accent px-4 text-[14px] text-white shadow-accent-soft disabled:opacity-40"
style={{ borderRadius: 7 }}
> >
{saved ? "✓ ENREGISTRÉ" : dirty ? "ENREGISTRER" : "À JOUR"} {saved ? "✓ Enregistré" : dirty ? "Enregistrer" : "À jour"}
</button> </button>
</div> </div>
</div> </div>
@@ -932,6 +943,7 @@ function PresetBar({
onApplied: () => void; onApplied: () => void;
}) { }) {
const [presets, setPresets] = useState<string[]>([]); const [presets, setPresets] = useState<string[]>([]);
const [chosen, setChosen] = useState("");
const [busy, setBusy] = useState(false); const [busy, setBusy] = useState(false);
useEffect(() => { useEffect(() => {
@@ -939,6 +951,7 @@ function PresetBar({
}, []); }, []);
const apply = async (name: string) => { const apply = async (name: string) => {
setChosen(name);
if (!name) return; if (!name) return;
setBusy(true); setBusy(true);
try { try {
@@ -955,6 +968,7 @@ function PresetBar({
setBusy(true); setBusy(true);
try { try {
setPresets(await savePreset(name.trim(), model || undefined)); setPresets(await savePreset(name.trim(), model || undefined));
setChosen(name.trim());
} catch (e) { } catch (e) {
window.alert(e instanceof Error ? e.message : "preset refusé"); window.alert(e instanceof Error ? e.message : "preset refusé");
} finally { } finally {
@@ -962,31 +976,52 @@ function PresetBar({
} }
}; };
const remove = async () => {
if (!chosen) return;
if (!window.confirm(`Supprimer le preset « ${chosen} » ?`)) return;
setBusy(true);
try {
setPresets(await deletePreset(chosen));
setChosen("");
} finally {
setBusy(false);
}
};
return ( return (
<div className="flex items-center gap-1.5"> <div className="flex flex-none items-center gap-1.5">
<select <select
value="" value={chosen}
disabled={busy || presets.length === 0} disabled={busy || presets.length === 0}
onChange={(e) => apply(e.target.value)} onChange={(e) => apply(e.target.value)}
className="h-[34px] border border-line bg-card px-2 text-[13px] text-ink disabled:opacity-40" className="h-[34px] flex-none rounded-card border border-line bg-card px-2 text-[13px] text-ink disabled:opacity-40"
title="Appliquer un preset" title="Appliquer un preset enregistré"
> >
<option value=""> <option value="">{presets.length ? "Preset…" : "Aucun preset"}</option>
{presets.length ? "Preset…" : "Aucun preset"}
</option>
{presets.map((p) => ( {presets.map((p) => (
<option key={p} value={p}> <option key={p} value={p}>
{p} {p}
</option> </option>
))} ))}
</select> </select>
{chosen && (
<button
onClick={remove}
disabled={busy}
className="h-[34px] flex-none rounded-card border border-line bg-card px-2 text-[13px] text-warn disabled:opacity-40"
title={`Supprimer le preset « ${chosen} »`}
>
<TrashIcon size={13} />
</button>
)}
<button <button
onClick={create} onClick={create}
disabled={busy} disabled={busy}
className="h-[34px] border border-line bg-card px-2.5 text-[13px] text-muted-2 disabled:opacity-40" className="flex h-[34px] flex-none items-center gap-1 whitespace-nowrap rounded-card border border-line bg-card px-3 text-[13px] text-ink-3 hover:border-accent hover:text-accent disabled:opacity-40"
title="Enregistrer les réglages actuels comme preset" title="Enregistrer les réglages actuels comme nouveau preset"
> >
+ Preset <PlusIcon />
Nouveau preset
</button> </button>
</div> </div>
); );