From 724f30c02bea33a693b5e2a9ef2891b89f66c2fd Mon Sep 17 00:00:00 2001 From: Mitchell-Johnson Date: Thu, 17 Sep 2026 18:04:45 -0700 Subject: [PATCH] Feedback sweep: spinners on every async button, toasts for action outcomes, top activity bar, loading states on every page, terminal connecting overlay, dashboard save indicator, task pane pinned above the package list with auto-scroll and completion toast Co-Authored-By: Claude Fable 5.1 --- web/src/Cron.tsx | 32 +++++++++--------- web/src/Dashboard.tsx | 17 ++++++++-- web/src/Login.tsx | 8 ++--- web/src/Power.tsx | 21 ++++++------ web/src/Rail.tsx | 7 ++-- web/src/Services.tsx | 29 ++++++++++------- web/src/Shell.tsx | 33 ++++++++++++------- web/src/TaskOutput.tsx | 20 +++++++----- web/src/TerminalPane.tsx | 18 ++++++++--- web/src/Updates.tsx | 51 ++++++++++++++++++----------- web/src/api.ts | 26 +++++++++++---- web/src/styles.css | 22 +++++++++++++ web/src/ui.tsx | 70 ++++++++++++++++++++++++++++++++++++++++ 13 files changed, 261 insertions(+), 93 deletions(-) create mode 100644 web/src/ui.tsx diff --git a/web/src/Cron.tsx b/web/src/Cron.tsx index 17dfb4d..23ffb99 100644 --- a/web/src/Cron.tsx +++ b/web/src/Cron.tsx @@ -1,6 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { api, CronJob, CronOverview, CronRun, CronTimer, LogEntry } from './api' import { Logs } from './Services' +import { Btn, Loading, Spinner, toast } from './ui' const fmtTime = (ms: number) => { const d = new Date(ms); return d.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: '2-digit' }) + ' ' + d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) } function rel(ms: number): string { @@ -39,7 +40,7 @@ function SourceEditor({ name, onClose, onSaved }: { name: string; onClose: () => async function save() { if (text === null) return setBusy(true); setErr('') - try { await api.cronWriteSource(name, text); onSaved(); onClose() } + try { await api.cronWriteSource(name, text); toast(`${srcLabel(name)} saved`); onSaved(); onClose() } catch (e) { setErr(e instanceof Error ? e.message : 'save failed') } finally { setBusy(false) } } @@ -48,10 +49,10 @@ function SourceEditor({ name, onClose, onSaved }: { name: string; onClose: () =>

{srcLabel(name)}

{!editable &&
Read-only: writing this file needs passwordless sudo on the host.
} - {text === null ?
…
: