feat(dashboard,cli): update-check frequency, GitHub star button, more resizable modals
Update-check frequency (end-to-end) - Add `updateCheckFrequency: "manual" | "on-startup" | "daily" | "weekly"` to GlobalSettings (default: "daily"). - Backend `performUpdateCheck` honors the frequency: TTL = day/week, `manual` returns cache or empty without hitting npm, `on-startup` refreshes once per process lifetime then serves cache. `/refresh` route forces network regardless. - Settings → Updates surfaces a working `<select>` for the cadence, disabled when auto-checks are off entirely. - Tests: 4 new cases covering ttlForFrequency mapping, weekly window, manual semantics, on-startup once-per-process behavior. TUI update notice - Read cached update result synchronously on TUI startup. When an update is available, render a yellow notice line on the splash and a colored ● next to the version in the status bar. Settings modal header polish - Add "Star on GitHub" pill (icon + Star + cached star count from the GitHub API, 1h localStorage TTL) and "Help" button (opens project Discussions). Both link to the Runfusion/Fusion repo. - Star button auto-hides after the user clicks it (intent = star), tracked in localStorage `fusion:github-star-clicked`. - Settings → General gets a "Show Star on GitHub button" checkbox so users can hide it preemptively. New global setting `showGitHubStarButton: boolean` (default true) gates rendering. More resizable modals - Task Detail modal: 85vh default, resize: both, persisted via useModalResizePersist (key `fusion:task-detail-modal-size`). - Quick Chat FAB: full 8-direction resize (4 corners + 4 edges) via pointer-event handlers; persisted to `fusion:quick-chat-size-<projectId>`. Each handle has the right cursor + role="separator" for accessibility. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import os from "node:os";
|
||||
import v8 from "node:v8";
|
||||
import { execSync } from "node:child_process";
|
||||
import { appendFileSync } from "node:fs";
|
||||
import { getCachedUpdateStatus } from "../../update-cache.js";
|
||||
|
||||
const TUI_DEBUG_LOG = process.env.FUSION_TUI_DEBUG_LOG;
|
||||
function tuiDebug(tag: string, data: Record<string, unknown>): void {
|
||||
@@ -25,6 +26,7 @@ import type {
|
||||
DashboardState,
|
||||
InteractiveData,
|
||||
InteractiveView,
|
||||
UpdateStatus,
|
||||
} from "./state.js";
|
||||
import { SECTION_ORDER } from "./state.js";
|
||||
|
||||
@@ -74,6 +76,7 @@ export class DashboardTUI {
|
||||
interactiveData: InteractiveData | null = null;
|
||||
interactiveView: InteractiveView = "board";
|
||||
interactiveInputLocked = false;
|
||||
updateStatus: UpdateStatus | null = null;
|
||||
|
||||
// Subscribers registered by the Ink App component.
|
||||
private subscribers: Set<() => void> = new Set();
|
||||
@@ -112,6 +115,17 @@ export class DashboardTUI {
|
||||
|
||||
constructor() {
|
||||
this.logBuffer = new LogRingBuffer();
|
||||
// Read the update-check cache synchronously at construction time so the
|
||||
// splash screen and status bar can render the notice immediately, without
|
||||
// any network access.
|
||||
const cached = getCachedUpdateStatus();
|
||||
if (cached) {
|
||||
this.updateStatus = {
|
||||
updateAvailable: cached.updateAvailable,
|
||||
currentVersion: cached.currentVersion,
|
||||
latestVersion: cached.latestVersion,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ── Subscription API (for Ink App) ────────────────────────────────────────
|
||||
@@ -144,6 +158,7 @@ export class DashboardTUI {
|
||||
interactiveInputLocked: this.interactiveInputLocked,
|
||||
autoKillVitestOnPressure: this.autoKillVitestOnPressure,
|
||||
vitestKillThreshold: this.vitestKillThreshold,
|
||||
updateStatus: this.updateStatus,
|
||||
};
|
||||
return this.cachedSnapshot;
|
||||
}
|
||||
@@ -353,6 +368,11 @@ export class DashboardTUI {
|
||||
this.notify();
|
||||
}
|
||||
|
||||
setUpdateStatus(status: UpdateStatus | null): void {
|
||||
this.updateStatus = status;
|
||||
this.notify();
|
||||
}
|
||||
|
||||
addLog(entry: Omit<LogEntry, "timestamp">): void {
|
||||
// If the cursor was sitting on the most recent entry (or there were no
|
||||
// entries yet), keep it pinned to the new tail so live logs follow the
|
||||
|
||||
Reference in New Issue
Block a user