Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/pages/audit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ export default function AuditPage() {
>
<RefreshCw
size={12}
className={loading ? "animate-spin" : ""}
className={loading ? "origin-center animate-spin" : ""}
aria-hidden="true"
/>
{loading ? "Auditing..." : "Run Audit"}
Expand Down
8 changes: 4 additions & 4 deletions src/pages/extensions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ export default function ExtensionsPage() {
const groups = allGrouped();
const match = groupKeyParam
? groups.find((g) => g.groupKey === groupKeyParam)
: groups.find(
(g) => g.name.toLowerCase() === nameParam!.toLowerCase(),
);
: nameParam
? groups.find((g) => g.name.toLowerCase() === nameParam.toLowerCase())
: undefined;
if (match) {
setSelectedId(match.groupKey);
setScrollToId(match.groupKey);
Expand Down Expand Up @@ -196,7 +196,7 @@ export default function ExtensionsPage() {
>
<RefreshCw
size={12}
className={checkingUpdates ? "animate-spin" : ""}
className={checkingUpdates ? "origin-center animate-spin" : ""}
/>
{checkingUpdates ? "Checking..." : "Check Updates"}
</button>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ function QuickAction({
className={
loading
? Icon === RefreshCw
? "animate-spin"
? "origin-center animate-spin"
: "animate-scanning"
: ""
}
Expand Down
18 changes: 8 additions & 10 deletions src/pages/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,10 @@ function UpdateSection() {
disabled={checking}
className="flex items-center gap-1.5 rounded-lg border border-border px-2.5 py-1 text-xs text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-50 transition-colors"
>
{checking ? (
<Loader2 size={12} className="animate-spin" />
) : (
<RefreshCw size={12} />
)}
<RefreshCw
size={12}
className={checking ? "origin-center animate-spin" : ""}
/>
{checking ? "Checking..." : "Check for Updates"}
</button>
)}
Expand Down Expand Up @@ -135,11 +134,10 @@ function WebUpdateSection() {
disabled={checking}
className="flex items-center gap-1.5 rounded-lg border border-border px-2.5 py-1 text-xs text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-50 transition-colors"
>
{checking ? (
<Loader2 size={12} className="animate-spin" />
) : (
<RefreshCw size={12} />
)}
<RefreshCw
size={12}
className={checking ? "origin-center animate-spin" : ""}
/>
{checking ? "Checking..." : "Check for Updates"}
</button>
)}
Expand Down
12 changes: 10 additions & 2 deletions src/stores/audit-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type { AuditResult, TrustTier } from "@/lib/types";
import { useExtensionStore } from "@/stores/extension-store";
import { toast } from "@/stores/toast-store";

const MIN_AUDIT_LOADING_VISIBLE_MS = 600;

interface AuditState {
results: AuditResult[];
loading: boolean;
Expand Down Expand Up @@ -31,18 +33,24 @@ export const useAuditStore = create<AuditState>((set) => ({
}
},
async runAudit() {
const startedAt = Date.now();
set({ loading: true });
// Yield to let the browser paint loading state before Tauri IPC call
await new Promise((r) => setTimeout(r, 50));
try {
const results = await api.runAudit();
set({ results, loading: false });
set({ results });
// Refresh extensions so trust_score updates in the Extensions page
useExtensionStore.getState().fetch();
toast.success("Audit complete");
} catch {
set({ loading: false });
toast.error("Audit failed");
} finally {
const remaining = MIN_AUDIT_LOADING_VISIBLE_MS - (Date.now() - startedAt);
if (remaining > 0) {
await new Promise((resolve) => setTimeout(resolve, remaining));
}
set({ loading: false });
}
},
}));
7 changes: 7 additions & 0 deletions src/stores/extension-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { toast } from "./toast-store";

export { buildGroups } from "./extension-helpers";

const MIN_CHECK_UPDATES_VISIBLE_MS = 600;

interface PendingDelete {
ids: Set<string>;
extensions: Extension[];
Expand Down Expand Up @@ -338,6 +340,7 @@ export const useExtensionStore = create<ExtensionState>((set, get) => ({
},

async checkUpdates() {
const startedAt = Date.now();
set({ checkingUpdates: true });
try {
const result = await api.checkUpdates();
Expand All @@ -347,6 +350,10 @@ export const useExtensionStore = create<ExtensionState>((set, get) => ({
}
set({ updateStatuses: map, newRepoSkills: result.new_skills });
} finally {
const remaining = MIN_CHECK_UPDATES_VISIBLE_MS - (Date.now() - startedAt);
if (remaining > 0) {
await new Promise((resolve) => setTimeout(resolve, remaining));
}
set({ checkingUpdates: false });
}
},
Expand Down
7 changes: 7 additions & 0 deletions src/stores/update-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export function cleanChangelog(body: string): string {
* Shared between desktop and web update flows so a dismissal in either mode silences both. */
export const DISMISS_KEY_PREFIX = "hk-update-dismissed-v";

const MIN_UPDATE_CHECK_VISIBLE_MS = 600;

interface UpdateState {
/** Available update version, null if none or not checked yet */
available: { version: string; body: string } | null;
Expand Down Expand Up @@ -66,6 +68,7 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({

async checkForUpdate() {
if (get().checking) return;
const startedAt = Date.now();
set({ checking: true });
try {
const update = await check();
Expand All @@ -84,6 +87,10 @@ export const useUpdateStore = create<UpdateState>((set, get) => ({
} catch {
// Silent failure — update check is non-critical
} finally {
const remaining = MIN_UPDATE_CHECK_VISIBLE_MS - (Date.now() - startedAt);
if (remaining > 0) {
await new Promise((resolve) => setTimeout(resolve, remaining));
}
set({ checking: false });
}
},
Expand Down
6 changes: 6 additions & 0 deletions src/stores/web-update-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const RELEASES_URL =
"https://api.github.com/repos/RealZST/HarnessKit/releases/latest";
const CACHE_KEY = "hk-web-update-cache";
const CACHE_TTL_MS = 60 * 60 * 1000;
const MIN_UPDATE_CHECK_VISIBLE_MS = 600;

interface CachedRelease {
tag: string;
Expand Down Expand Up @@ -100,6 +101,7 @@ export const useWebUpdateStore = create<WebUpdateState>((set, get) => ({

async checkForUpdate(force = false) {
if (get().checking) return;
const startedAt = Date.now();
set({ checking: true });
try {
const release = await fetchLatestRelease(force);
Expand All @@ -113,6 +115,10 @@ export const useWebUpdateStore = create<WebUpdateState>((set, get) => ({
dismissed,
});
} finally {
const remaining = MIN_UPDATE_CHECK_VISIBLE_MS - (Date.now() - startedAt);
if (remaining > 0) {
await new Promise((resolve) => setTimeout(resolve, remaining));
}
set({ checking: false });
}
},
Expand Down
Loading