feat(cli): merge @fusion/tui into fn dashboard with Ink TUI
Replace the legacy ANSI-based DashboardTUI with an Ink/React rewrite under packages/cli/src/commands/dashboard-tui/, delete the standalone @fusion/tui package, and make `fn` (no args) launch the dashboard. The new TUI keeps the existing 5-panel status mode (system, logs, utilities, stats, settings) but adds an interactive mode (b/a/g) with three views: a kanban board with project picker and per-task detail, an agents list+detail with state management, and a settings editor. Bordered focus-aware panels, solid-background help overlay, static all-blue FUSION splash that adapts to small terminals. DashboardTUI and DashboardLogSink public API are unchanged so dashboard.ts only needed import-path updates plus interactiveData/loadingStatus wiring. Also adds zod to @fusion/dashboard to satisfy a peer dep introduced by pi-coding-agent 0.70. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import type { ProjectItem, TaskItem, InteractiveData } from "../state.js";
|
||||
|
||||
export interface ProjectsState {
|
||||
projects: ProjectItem[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export interface TasksState {
|
||||
tasks: TaskItem[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
export function useProjects(interactiveData: InteractiveData | null): ProjectsState {
|
||||
const [state, setState] = useState<ProjectsState>({ projects: [], loading: false, error: null });
|
||||
|
||||
useEffect(() => {
|
||||
if (!interactiveData) return;
|
||||
setState({ projects: [], loading: true, error: null });
|
||||
interactiveData.listProjects().then((projects) => {
|
||||
setState({ projects, loading: false, error: null });
|
||||
}).catch((err: unknown) => {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setState({ projects: [], loading: false, error: message });
|
||||
});
|
||||
}, [interactiveData]);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
export function useTasks(
|
||||
interactiveData: InteractiveData | null,
|
||||
selectedProject: ProjectItem | null,
|
||||
): TasksState {
|
||||
const [state, setState] = useState<TasksState>({ tasks: [], loading: false, error: null });
|
||||
|
||||
useEffect(() => {
|
||||
if (!interactiveData || !selectedProject) {
|
||||
setState({ tasks: [], loading: false, error: null });
|
||||
return;
|
||||
}
|
||||
setState({ tasks: [], loading: true, error: null });
|
||||
interactiveData.listTasks(selectedProject.path).then((tasks) => {
|
||||
setState({ tasks, loading: false, error: null });
|
||||
}).catch((err: unknown) => {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
setState({ tasks: [], loading: false, error: message });
|
||||
});
|
||||
}, [interactiveData, selectedProject]);
|
||||
|
||||
return state;
|
||||
}
|
||||
Reference in New Issue
Block a user