From 0c74ef25d6078ecba55f7326cd3bc16f9d675269 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 8 Jan 2026 20:13:21 +0000 Subject: [PATCH] refactor: normalize skills UI status keys --- ui/src/ui/app-render.ts | 5 +++-- ui/src/ui/controllers/skills.ts | 38 +++++++++++++++++++++++---------- ui/src/ui/views/skills.ts | 10 ++++----- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/ui/src/ui/app-render.ts b/ui/src/ui/app-render.ts index 473d41b09..417f267b3 100644 --- a/ui/src/ui/app-render.ts +++ b/ui/src/ui/app-render.ts @@ -396,11 +396,12 @@ export function renderApp(state: AppViewState) { messages: state.skillMessages, busyKey: state.skillsBusyKey, onFilterChange: (next) => (state.skillsFilter = next), - onRefresh: () => loadSkills(state), + onRefresh: () => loadSkills(state, { clearMessages: true }), onToggle: (key, enabled) => updateSkillEnabled(state, key, enabled), onEdit: (key, value) => updateSkillEdit(state, key, value), onSaveKey: (key) => saveSkillApiKey(state, key), - onInstall: (name, installId) => installSkill(state, name, installId), + onInstall: (skillKey, name, installId) => + installSkill(state, skillKey, name, installId), }) : nothing} diff --git a/ui/src/ui/controllers/skills.ts b/ui/src/ui/controllers/skills.ts index 688c54fb4..6e26a98ae 100644 --- a/ui/src/ui/controllers/skills.ts +++ b/ui/src/ui/controllers/skills.ts @@ -19,6 +19,10 @@ export type SkillMessage = { export type SkillMessageMap = Record; +type LoadSkillsOptions = { + clearMessages?: boolean; +}; + function setSkillMessage(state: SkillsState, key: string, message?: SkillMessage) { if (!key.trim()) return; const next = { ...state.skillMessages }; @@ -27,7 +31,15 @@ function setSkillMessage(state: SkillsState, key: string, message?: SkillMessage state.skillMessages = next; } -export async function loadSkills(state: SkillsState) { +function getErrorMessage(err: unknown) { + if (err instanceof Error) return err.message; + return String(err); +} + +export async function loadSkills(state: SkillsState, options?: LoadSkillsOptions) { + if (options?.clearMessages && Object.keys(state.skillMessages).length > 0) { + state.skillMessages = {}; + } if (!state.client || !state.connected) return; if (state.skillsLoading) return; state.skillsLoading = true; @@ -38,7 +50,7 @@ export async function loadSkills(state: SkillsState) { | undefined; if (res) state.skillsReport = res; } catch (err) { - state.skillsError = String(err); + state.skillsError = getErrorMessage(err); } finally { state.skillsLoading = false; } @@ -68,10 +80,11 @@ export async function updateSkillEnabled( message: enabled ? "Skill enabled" : "Skill disabled", }); } catch (err) { - state.skillsError = String(err); + const message = getErrorMessage(err); + state.skillsError = message; setSkillMessage(state, skillKey, { kind: "error", - message: String(err), + message, }); } finally { state.skillsBusyKey = null; @@ -91,10 +104,11 @@ export async function saveSkillApiKey(state: SkillsState, skillKey: string) { message: "API key saved", }); } catch (err) { - state.skillsError = String(err); + const message = getErrorMessage(err); + state.skillsError = message; setSkillMessage(state, skillKey, { kind: "error", - message: String(err), + message, }); } finally { state.skillsBusyKey = null; @@ -103,11 +117,12 @@ export async function saveSkillApiKey(state: SkillsState, skillKey: string) { export async function installSkill( state: SkillsState, + skillKey: string, name: string, installId: string, ) { if (!state.client || !state.connected) return; - state.skillsBusyKey = name; + state.skillsBusyKey = skillKey; state.skillsError = null; try { const result = (await state.client.request("skills.install", { @@ -116,15 +131,16 @@ export async function installSkill( timeoutMs: 120000, })) as { ok?: boolean; message?: string }; await loadSkills(state); - setSkillMessage(state, name, { + setSkillMessage(state, skillKey, { kind: "success", message: result?.message ?? "Installed", }); } catch (err) { - state.skillsError = String(err); - setSkillMessage(state, name, { + const message = getErrorMessage(err); + state.skillsError = message; + setSkillMessage(state, skillKey, { kind: "error", - message: String(err), + message, }); } finally { state.skillsBusyKey = null; diff --git a/ui/src/ui/views/skills.ts b/ui/src/ui/views/skills.ts index f10aae6dc..cfc024cb1 100644 --- a/ui/src/ui/views/skills.ts +++ b/ui/src/ui/views/skills.ts @@ -17,7 +17,7 @@ export type SkillsProps = { onToggle: (skillKey: string, enabled: boolean) => void; onEdit: (skillKey: string, value: string) => void; onSaveKey: (skillKey: string) => void; - onInstall: (name: string, installId: string) => void; + onInstall: (skillKey: string, name: string, installId: string) => void; }; export function renderSkills(props: SkillsProps) { @@ -73,10 +73,9 @@ export function renderSkills(props: SkillsProps) { } function renderSkill(skill: SkillStatusEntry, props: SkillsProps) { - const busy = props.busyKey === skill.skillKey || props.busyKey === skill.name; + const busy = props.busyKey === skill.skillKey; const apiKey = props.edits[skill.skillKey] ?? ""; - const message = - props.messages[skill.skillKey] ?? props.messages[skill.name] ?? null; + const message = props.messages[skill.skillKey] ?? null; const canInstall = skill.install.length > 0 && skill.missing.bins.length > 0; const missing = [ @@ -130,7 +129,8 @@ function renderSkill(skill: SkillStatusEntry, props: SkillsProps) { ? html``