fix: livrable tickets #70 #100 #102 — UX surface scoping

- #70: implémentation suppression modèles locaux téléchargés
- #100: correction scroll OpenCode
- #102: correction fit TUI après switch/layout
- memory note scoping UX
This commit is contained in:
2026-07-26 19:42:06 +02:00
parent 4321d048ac
commit 02603441c1
31 changed files with 1589 additions and 152 deletions

View File

@ -24,14 +24,20 @@ export interface ModelServersViewModel {
servers: LocalModelServerConfig[];
/** Last error message, or `null`. */
error: string | null;
/** Last non-blocking success message, or `null`. */
notice: string | null;
/** Whether a request is in flight. */
busy: boolean;
/** Server id whose managed artifact is currently being deleted, or `null`. */
deletingArtifactId: string | null;
/** Reloads the server list. */
reload: () => Promise<void>;
/** Creates or updates a server; returns the persisted config (or `null` on error). */
save: (config: LocalModelServerConfig) => Promise<LocalModelServerConfig | null>;
/** Deletes a server by id; returns `true` on success. */
remove: (serverId: string) => Promise<boolean>;
/** Deletes only the managed downloaded model artifact; returns `true` on success. */
deleteArtifact: (serverId: string) => Promise<boolean>;
/**
* Asks the backend to build the `llama-server` command line for a draft
* (never reconstructed client-side). Returns `null` when the draft is
@ -62,11 +68,14 @@ export function useModelServers(): ModelServersViewModel {
const { modelServer } = useGateways();
const [servers, setServers] = useState<LocalModelServerConfig[]>([]);
const [error, setError] = useState<string | null>(null);
const [notice, setNotice] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const [deletingArtifactId, setDeletingArtifactId] = useState<string | null>(null);
const reload = useCallback(async () => {
setBusy(true);
setError(null);
setNotice(null);
try {
setServers(await modelServer.listModelServers());
} catch (e) {
@ -84,6 +93,7 @@ export function useModelServers(): ModelServersViewModel {
async (config: LocalModelServerConfig) => {
setBusy(true);
setError(null);
setNotice(null);
try {
const saved = await modelServer.saveModelServer(config);
setServers((prev) => {
@ -110,6 +120,7 @@ export function useModelServers(): ModelServersViewModel {
async (serverId: string) => {
setBusy(true);
setError(null);
setNotice(null);
try {
await modelServer.deleteModelServer(serverId);
setServers((prev) => prev.filter((s) => s.id !== serverId));
@ -132,6 +143,37 @@ export function useModelServers(): ModelServersViewModel {
[modelServer],
);
const deleteArtifact = useCallback(
async (serverId: string) => {
setBusy(true);
setDeletingArtifactId(serverId);
setError(null);
setNotice(null);
try {
await modelServer.deleteModelArtifact(serverId);
setServers(await modelServer.listModelServers());
setNotice("Modèle téléchargé supprimé. Le serveur reste configuré.");
return true;
} catch (e) {
const code = codeOf(e);
if (code === "model_server_in_use") {
setError("Impossible de supprimer ce modèle : téléchargement en cours ou agent actif.");
} else if (code === "invalid") {
setError("Aucun modèle téléchargé géré à supprimer.");
} else if (code === "not_configured") {
setError("Serveur introuvable.");
} else {
setError(describe(e));
}
return false;
} finally {
setDeletingArtifactId(null);
setBusy(false);
}
},
[modelServer],
);
const preview = useCallback(
async (config: LocalModelServerConfig) => {
try {
@ -145,7 +187,22 @@ export function useModelServers(): ModelServersViewModel {
[modelServer],
);
const clearError = useCallback(() => setError(null), []);
const clearError = useCallback(() => {
setError(null);
setNotice(null);
}, []);
return { servers, error, busy, reload, save, remove, preview, clearError };
return {
servers,
error,
notice,
busy,
deletingArtifactId,
reload,
save,
remove,
deleteArtifact,
preview,
clearError,
};
}