fix: make UI look like before
This commit is contained in:
@@ -17,6 +17,15 @@ import {
|
|||||||
} from '@tm/core';
|
} from '@tm/core';
|
||||||
import type { StorageType } from '@tm/core/types';
|
import type { StorageType } from '@tm/core/types';
|
||||||
import * as ui from '../utils/ui.js';
|
import * as ui from '../utils/ui.js';
|
||||||
|
import {
|
||||||
|
displayHeader,
|
||||||
|
displayDashboards,
|
||||||
|
calculateTaskStatistics,
|
||||||
|
calculateSubtaskStatistics,
|
||||||
|
calculateDependencyStatistics,
|
||||||
|
getPriorityBreakdown,
|
||||||
|
type NextTaskInfo
|
||||||
|
} from '../ui/index.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options interface for the list command
|
* Options interface for the list command
|
||||||
@@ -245,19 +254,19 @@ export class ListTasksCommand extends Command {
|
|||||||
* Display in text format with tables
|
* Display in text format with tables
|
||||||
*/
|
*/
|
||||||
private displayText(data: ListTasksResult, withSubtasks?: boolean): void {
|
private displayText(data: ListTasksResult, withSubtasks?: boolean): void {
|
||||||
const { tasks, total, filtered, tag, storageType } = data;
|
const { tasks, tag } = data;
|
||||||
|
|
||||||
// Header
|
// Get file path for display
|
||||||
ui.displayBanner(`Task List${tag ? ` (${tag})` : ''}`);
|
const filePath = this.tmCore ? `.taskmaster/tasks/tasks.json` : undefined;
|
||||||
|
|
||||||
// Statistics
|
// Display header with Task Master banner
|
||||||
console.log(chalk.blue.bold('\n📊 Statistics:\n'));
|
displayHeader({
|
||||||
console.log(` Total tasks: ${chalk.cyan(total)}`);
|
version: '0.26.0', // You may want to get this dynamically
|
||||||
console.log(` Filtered: ${chalk.cyan(filtered)}`);
|
projectName: 'Taskmaster',
|
||||||
if (tag) {
|
tag: tag || 'master',
|
||||||
console.log(` Tag: ${chalk.cyan(tag)}`);
|
filePath: filePath,
|
||||||
}
|
showBanner: true
|
||||||
console.log(` Storage: ${chalk.cyan(storageType)}`);
|
});
|
||||||
|
|
||||||
// No tasks message
|
// No tasks message
|
||||||
if (tasks.length === 0) {
|
if (tasks.length === 0) {
|
||||||
@@ -265,6 +274,26 @@ export class ListTasksCommand extends Command {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate statistics
|
||||||
|
const taskStats = calculateTaskStatistics(tasks);
|
||||||
|
const subtaskStats = calculateSubtaskStatistics(tasks);
|
||||||
|
const depStats = calculateDependencyStatistics(tasks);
|
||||||
|
const priorityBreakdown = getPriorityBreakdown(tasks);
|
||||||
|
|
||||||
|
// Find next task (simplified for now)
|
||||||
|
const nextTask: NextTaskInfo | undefined = tasks
|
||||||
|
.filter(t => t.status === 'pending' && (!t.dependencies || t.dependencies.length === 0))
|
||||||
|
.map(t => ({
|
||||||
|
id: t.id,
|
||||||
|
title: t.title,
|
||||||
|
priority: t.priority,
|
||||||
|
dependencies: t.dependencies,
|
||||||
|
complexity: undefined // Add if available
|
||||||
|
}))[0];
|
||||||
|
|
||||||
|
// Display dashboard boxes
|
||||||
|
displayDashboards(taskStats, subtaskStats, priorityBreakdown, depStats, nextTask);
|
||||||
|
|
||||||
// Task table
|
// Task table
|
||||||
console.log(chalk.blue.bold(`\n📋 Tasks (${tasks.length}):\n`));
|
console.log(chalk.blue.bold(`\n📋 Tasks (${tasks.length}):\n`));
|
||||||
console.log(
|
console.log(
|
||||||
@@ -273,13 +302,6 @@ export class ListTasksCommand extends Command {
|
|||||||
showDependencies: true
|
showDependencies: true
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// Progress bar
|
|
||||||
const completedCount = tasks.filter(
|
|
||||||
(t: Task) => t.status === 'done'
|
|
||||||
).length;
|
|
||||||
console.log(chalk.blue.bold('\n📊 Overall Progress:\n'));
|
|
||||||
console.log(` ${ui.createProgressBar(completedCount, tasks.length)}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
381
apps/cli/src/ui/components/dashboard.component.ts
Normal file
381
apps/cli/src/ui/components/dashboard.component.ts
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Dashboard components for Task Master CLI
|
||||||
|
* Displays project statistics and dependency information
|
||||||
|
*/
|
||||||
|
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import boxen from 'boxen';
|
||||||
|
import type { Task, TaskPriority } from '@tm/core/types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statistics for task collection
|
||||||
|
*/
|
||||||
|
export interface TaskStatistics {
|
||||||
|
total: number;
|
||||||
|
done: number;
|
||||||
|
inProgress: number;
|
||||||
|
pending: number;
|
||||||
|
blocked: number;
|
||||||
|
deferred: number;
|
||||||
|
cancelled: number;
|
||||||
|
review?: number;
|
||||||
|
completionPercentage: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statistics for dependencies
|
||||||
|
*/
|
||||||
|
export interface DependencyStatistics {
|
||||||
|
tasksWithNoDeps: number;
|
||||||
|
tasksReadyToWork: number;
|
||||||
|
tasksBlockedByDeps: number;
|
||||||
|
mostDependedOnTaskId?: number;
|
||||||
|
mostDependedOnCount?: number;
|
||||||
|
avgDependenciesPerTask: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Next task information
|
||||||
|
*/
|
||||||
|
export interface NextTaskInfo {
|
||||||
|
id: string | number;
|
||||||
|
title: string;
|
||||||
|
priority?: TaskPriority;
|
||||||
|
dependencies?: (string | number)[];
|
||||||
|
complexity?: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a progress bar with percentage
|
||||||
|
*/
|
||||||
|
function createProgressBar(percentage: number, width: number = 30): string {
|
||||||
|
const filled = Math.round((percentage / 100) * width);
|
||||||
|
const empty = width - filled;
|
||||||
|
|
||||||
|
const bar = chalk.green('█').repeat(filled) + chalk.gray('░').repeat(empty);
|
||||||
|
return bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate task statistics from a list of tasks
|
||||||
|
*/
|
||||||
|
export function calculateTaskStatistics(tasks: Task[]): TaskStatistics {
|
||||||
|
const stats: TaskStatistics = {
|
||||||
|
total: tasks.length,
|
||||||
|
done: 0,
|
||||||
|
inProgress: 0,
|
||||||
|
pending: 0,
|
||||||
|
blocked: 0,
|
||||||
|
deferred: 0,
|
||||||
|
cancelled: 0,
|
||||||
|
review: 0,
|
||||||
|
completionPercentage: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
tasks.forEach(task => {
|
||||||
|
switch (task.status) {
|
||||||
|
case 'done':
|
||||||
|
stats.done++;
|
||||||
|
break;
|
||||||
|
case 'in-progress':
|
||||||
|
stats.inProgress++;
|
||||||
|
break;
|
||||||
|
case 'pending':
|
||||||
|
stats.pending++;
|
||||||
|
break;
|
||||||
|
case 'blocked':
|
||||||
|
stats.blocked++;
|
||||||
|
break;
|
||||||
|
case 'deferred':
|
||||||
|
stats.deferred++;
|
||||||
|
break;
|
||||||
|
case 'cancelled':
|
||||||
|
stats.cancelled++;
|
||||||
|
break;
|
||||||
|
case 'review':
|
||||||
|
stats.review = (stats.review || 0) + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
stats.completionPercentage = stats.total > 0
|
||||||
|
? Math.round((stats.done / stats.total) * 100)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate subtask statistics from tasks
|
||||||
|
*/
|
||||||
|
export function calculateSubtaskStatistics(tasks: Task[]): TaskStatistics {
|
||||||
|
const stats: TaskStatistics = {
|
||||||
|
total: 0,
|
||||||
|
done: 0,
|
||||||
|
inProgress: 0,
|
||||||
|
pending: 0,
|
||||||
|
blocked: 0,
|
||||||
|
deferred: 0,
|
||||||
|
cancelled: 0,
|
||||||
|
review: 0,
|
||||||
|
completionPercentage: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
tasks.forEach(task => {
|
||||||
|
if (task.subtasks && task.subtasks.length > 0) {
|
||||||
|
task.subtasks.forEach(subtask => {
|
||||||
|
stats.total++;
|
||||||
|
switch (subtask.status) {
|
||||||
|
case 'done':
|
||||||
|
stats.done++;
|
||||||
|
break;
|
||||||
|
case 'in-progress':
|
||||||
|
stats.inProgress++;
|
||||||
|
break;
|
||||||
|
case 'pending':
|
||||||
|
stats.pending++;
|
||||||
|
break;
|
||||||
|
case 'blocked':
|
||||||
|
stats.blocked++;
|
||||||
|
break;
|
||||||
|
case 'deferred':
|
||||||
|
stats.deferred++;
|
||||||
|
break;
|
||||||
|
case 'cancelled':
|
||||||
|
stats.cancelled++;
|
||||||
|
break;
|
||||||
|
case 'review':
|
||||||
|
stats.review = (stats.review || 0) + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
stats.completionPercentage = stats.total > 0
|
||||||
|
? Math.round((stats.done / stats.total) * 100)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate dependency statistics
|
||||||
|
*/
|
||||||
|
export function calculateDependencyStatistics(tasks: Task[]): DependencyStatistics {
|
||||||
|
const completedTaskIds = new Set(
|
||||||
|
tasks.filter(t => t.status === 'done').map(t => t.id)
|
||||||
|
);
|
||||||
|
|
||||||
|
const tasksWithNoDeps = tasks.filter(
|
||||||
|
t => t.status !== 'done' && (!t.dependencies || t.dependencies.length === 0)
|
||||||
|
).length;
|
||||||
|
|
||||||
|
const tasksWithAllDepsSatisfied = tasks.filter(
|
||||||
|
t => t.status !== 'done' &&
|
||||||
|
t.dependencies &&
|
||||||
|
t.dependencies.length > 0 &&
|
||||||
|
t.dependencies.every(depId => completedTaskIds.has(depId))
|
||||||
|
).length;
|
||||||
|
|
||||||
|
const tasksBlockedByDeps = tasks.filter(
|
||||||
|
t => t.status !== 'done' &&
|
||||||
|
t.dependencies &&
|
||||||
|
t.dependencies.length > 0 &&
|
||||||
|
!t.dependencies.every(depId => completedTaskIds.has(depId))
|
||||||
|
).length;
|
||||||
|
|
||||||
|
// Calculate most depended-on task
|
||||||
|
const dependencyCount: Record<string, number> = {};
|
||||||
|
tasks.forEach(task => {
|
||||||
|
if (task.dependencies && task.dependencies.length > 0) {
|
||||||
|
task.dependencies.forEach(depId => {
|
||||||
|
const key = String(depId);
|
||||||
|
dependencyCount[key] = (dependencyCount[key] || 0) + 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mostDependedOnTaskId: number | undefined;
|
||||||
|
let mostDependedOnCount = 0;
|
||||||
|
|
||||||
|
for (const [taskId, count] of Object.entries(dependencyCount)) {
|
||||||
|
if (count > mostDependedOnCount) {
|
||||||
|
mostDependedOnCount = count;
|
||||||
|
mostDependedOnTaskId = parseInt(taskId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate average dependencies
|
||||||
|
const totalDependencies = tasks.reduce(
|
||||||
|
(sum, task) => sum + (task.dependencies ? task.dependencies.length : 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const avgDependenciesPerTask = tasks.length > 0
|
||||||
|
? totalDependencies / tasks.length
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
tasksWithNoDeps,
|
||||||
|
tasksReadyToWork: tasksWithNoDeps + tasksWithAllDepsSatisfied,
|
||||||
|
tasksBlockedByDeps,
|
||||||
|
mostDependedOnTaskId,
|
||||||
|
mostDependedOnCount,
|
||||||
|
avgDependenciesPerTask
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get priority counts
|
||||||
|
*/
|
||||||
|
export function getPriorityBreakdown(tasks: Task[]): Record<TaskPriority, number> {
|
||||||
|
const breakdown: Record<TaskPriority, number> = {
|
||||||
|
critical: 0,
|
||||||
|
high: 0,
|
||||||
|
medium: 0,
|
||||||
|
low: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
tasks.forEach(task => {
|
||||||
|
const priority = task.priority || 'medium';
|
||||||
|
breakdown[priority]++;
|
||||||
|
});
|
||||||
|
|
||||||
|
return breakdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the project dashboard box
|
||||||
|
*/
|
||||||
|
export function displayProjectDashboard(
|
||||||
|
taskStats: TaskStatistics,
|
||||||
|
subtaskStats: TaskStatistics,
|
||||||
|
priorityBreakdown: Record<TaskPriority, number>
|
||||||
|
): string {
|
||||||
|
const taskProgressBar = createProgressBar(taskStats.completionPercentage);
|
||||||
|
const subtaskProgressBar = createProgressBar(subtaskStats.completionPercentage);
|
||||||
|
|
||||||
|
const taskPercentage = `${taskStats.completionPercentage}% ${taskStats.done}%`;
|
||||||
|
const subtaskPercentage = `${subtaskStats.completionPercentage}% ${subtaskStats.done}%`;
|
||||||
|
|
||||||
|
const content =
|
||||||
|
chalk.white.bold('Project Dashboard') + '\n' +
|
||||||
|
`Tasks Progress: ${taskProgressBar} ${chalk.yellow(taskPercentage)}\n` +
|
||||||
|
`Done: ${chalk.green(taskStats.done)} In Progress: ${chalk.blue(taskStats.inProgress)} Pending: ${chalk.yellow(taskStats.pending)} Blocked: ${chalk.red(taskStats.blocked)} Deferred: ${chalk.gray(taskStats.deferred)}\n` +
|
||||||
|
`Cancelled: ${chalk.gray(taskStats.cancelled)}\n\n` +
|
||||||
|
`Subtasks Progress: ${subtaskProgressBar} ${chalk.cyan(subtaskPercentage)}\n` +
|
||||||
|
`Completed: ${chalk.green(`${subtaskStats.done}/${subtaskStats.total}`)} In Progress: ${chalk.blue(subtaskStats.inProgress)} Pending: ${chalk.yellow(subtaskStats.pending)} Blocked: ${chalk.red(subtaskStats.blocked)}\n` +
|
||||||
|
`Deferred: ${chalk.gray(subtaskStats.deferred)} Cancelled: ${chalk.gray(subtaskStats.cancelled)}\n\n` +
|
||||||
|
chalk.cyan.bold('Priority Breakdown:') + '\n' +
|
||||||
|
`${chalk.red('•')} ${chalk.white('High priority:')} ${priorityBreakdown.high}\n` +
|
||||||
|
`${chalk.yellow('•')} ${chalk.white('Medium priority:')} ${priorityBreakdown.medium}\n` +
|
||||||
|
`${chalk.green('•')} ${chalk.white('Low priority:')} ${priorityBreakdown.low}`;
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the dependency dashboard box
|
||||||
|
*/
|
||||||
|
export function displayDependencyDashboard(
|
||||||
|
depStats: DependencyStatistics,
|
||||||
|
nextTask?: NextTaskInfo
|
||||||
|
): string {
|
||||||
|
const content =
|
||||||
|
chalk.white.bold('Dependency Status & Next Task') + '\n' +
|
||||||
|
chalk.cyan.bold('Dependency Metrics:') + '\n' +
|
||||||
|
`${chalk.green('•')} ${chalk.white('Tasks with no dependencies:')} ${depStats.tasksWithNoDeps}\n` +
|
||||||
|
`${chalk.green('•')} ${chalk.white('Tasks ready to work on:')} ${depStats.tasksReadyToWork}\n` +
|
||||||
|
`${chalk.yellow('•')} ${chalk.white('Tasks blocked by dependencies:')} ${depStats.tasksBlockedByDeps}\n` +
|
||||||
|
`${chalk.magenta('•')} ${chalk.white('Most depended-on task:')} ${
|
||||||
|
depStats.mostDependedOnTaskId
|
||||||
|
? chalk.cyan(`#${depStats.mostDependedOnTaskId} (${depStats.mostDependedOnCount} dependents)`)
|
||||||
|
: chalk.gray('None')
|
||||||
|
}\n` +
|
||||||
|
`${chalk.blue('•')} ${chalk.white('Avg dependencies per task:')} ${depStats.avgDependenciesPerTask.toFixed(1)}\n\n` +
|
||||||
|
chalk.cyan.bold('Next Task to Work On:') + '\n' +
|
||||||
|
`ID: ${nextTask ? chalk.cyan(String(nextTask.id)) : chalk.gray('N/A')} - ${
|
||||||
|
nextTask ? chalk.white.bold(nextTask.title) : chalk.yellow('No task available')
|
||||||
|
}\n` +
|
||||||
|
`Priority: ${nextTask?.priority || chalk.gray('N/A')} Dependencies: ${
|
||||||
|
nextTask?.dependencies?.length ? chalk.cyan(nextTask.dependencies.join(', ')) : chalk.gray('None')
|
||||||
|
}\n` +
|
||||||
|
`Complexity: ${nextTask?.complexity || chalk.gray('N/A')}`;
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display dashboard boxes side by side or stacked
|
||||||
|
*/
|
||||||
|
export function displayDashboards(
|
||||||
|
taskStats: TaskStatistics,
|
||||||
|
subtaskStats: TaskStatistics,
|
||||||
|
priorityBreakdown: Record<TaskPriority, number>,
|
||||||
|
depStats: DependencyStatistics,
|
||||||
|
nextTask?: NextTaskInfo
|
||||||
|
): void {
|
||||||
|
const projectDashboardContent = displayProjectDashboard(taskStats, subtaskStats, priorityBreakdown);
|
||||||
|
const dependencyDashboardContent = displayDependencyDashboard(depStats, nextTask);
|
||||||
|
|
||||||
|
// Get terminal width
|
||||||
|
const terminalWidth = process.stdout.columns || 80;
|
||||||
|
const minDashboardWidth = 50;
|
||||||
|
const minDependencyWidth = 50;
|
||||||
|
const totalMinWidth = minDashboardWidth + minDependencyWidth + 4;
|
||||||
|
|
||||||
|
// If terminal is wide enough, show side by side
|
||||||
|
if (terminalWidth >= totalMinWidth) {
|
||||||
|
const halfWidth = Math.floor(terminalWidth / 2);
|
||||||
|
const boxContentWidth = halfWidth - 4;
|
||||||
|
|
||||||
|
const dashboardBox = boxen(projectDashboardContent, {
|
||||||
|
padding: 1,
|
||||||
|
borderColor: 'blue',
|
||||||
|
borderStyle: 'round',
|
||||||
|
width: boxContentWidth,
|
||||||
|
dimBorder: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const dependencyBox = boxen(dependencyDashboardContent, {
|
||||||
|
padding: 1,
|
||||||
|
borderColor: 'magenta',
|
||||||
|
borderStyle: 'round',
|
||||||
|
width: boxContentWidth,
|
||||||
|
dimBorder: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create side-by-side layout
|
||||||
|
const dashboardLines = dashboardBox.split('\n');
|
||||||
|
const dependencyLines = dependencyBox.split('\n');
|
||||||
|
const maxHeight = Math.max(dashboardLines.length, dependencyLines.length);
|
||||||
|
|
||||||
|
const combinedLines = [];
|
||||||
|
for (let i = 0; i < maxHeight; i++) {
|
||||||
|
const dashLine = i < dashboardLines.length ? dashboardLines[i] : '';
|
||||||
|
const depLine = i < dependencyLines.length ? dependencyLines[i] : '';
|
||||||
|
const paddedDashLine = dashLine.padEnd(halfWidth, ' ');
|
||||||
|
combinedLines.push(paddedDashLine + depLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(combinedLines.join('\n'));
|
||||||
|
} else {
|
||||||
|
// Show stacked vertically
|
||||||
|
const dashboardBox = boxen(projectDashboardContent, {
|
||||||
|
padding: 1,
|
||||||
|
borderColor: 'blue',
|
||||||
|
borderStyle: 'round',
|
||||||
|
margin: { top: 0, bottom: 1 }
|
||||||
|
});
|
||||||
|
|
||||||
|
const dependencyBox = boxen(dependencyDashboardContent, {
|
||||||
|
padding: 1,
|
||||||
|
borderColor: 'magenta',
|
||||||
|
borderStyle: 'round',
|
||||||
|
margin: { top: 0, bottom: 1 }
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(dashboardBox);
|
||||||
|
console.log(dependencyBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
101
apps/cli/src/ui/components/header.component.ts
Normal file
101
apps/cli/src/ui/components/header.component.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Task Master header component
|
||||||
|
* Displays the banner, version, project info, and file path
|
||||||
|
*/
|
||||||
|
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import boxen from 'boxen';
|
||||||
|
import figlet from 'figlet';
|
||||||
|
import gradient from 'gradient-string';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header configuration options
|
||||||
|
*/
|
||||||
|
export interface HeaderOptions {
|
||||||
|
title?: string;
|
||||||
|
version?: string;
|
||||||
|
projectName?: string;
|
||||||
|
tag?: string;
|
||||||
|
filePath?: string;
|
||||||
|
showBanner?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the Task Master ASCII art banner
|
||||||
|
*/
|
||||||
|
function createBanner(): string {
|
||||||
|
const bannerText = figlet.textSync('Task Master', {
|
||||||
|
font: 'Standard',
|
||||||
|
horizontalLayout: 'default',
|
||||||
|
verticalLayout: 'default'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a cool gradient effect
|
||||||
|
const coolGradient = gradient(['#0099ff', '#00ffcc']);
|
||||||
|
return coolGradient(bannerText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the Task Master header with project info
|
||||||
|
*/
|
||||||
|
export function displayHeader(options: HeaderOptions = {}): void {
|
||||||
|
const {
|
||||||
|
version = '0.26.0',
|
||||||
|
projectName = 'Taskmaster',
|
||||||
|
tag,
|
||||||
|
filePath,
|
||||||
|
showBanner = true
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
// Display the ASCII banner if requested
|
||||||
|
if (showBanner) {
|
||||||
|
console.log(createBanner());
|
||||||
|
|
||||||
|
// Add creator credit line below the banner
|
||||||
|
console.log(
|
||||||
|
chalk.dim('by ') + chalk.cyan.underline('https://x.com/eyaltoledano')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the version and project info box
|
||||||
|
const infoBoxContent = chalk.white(
|
||||||
|
`${chalk.bold('Version:')} ${version} ${chalk.bold('Project:')} ${projectName}`
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
boxen(infoBoxContent, {
|
||||||
|
padding: { left: 1, right: 1, top: 0, bottom: 0 },
|
||||||
|
margin: { top: 1, bottom: 1 },
|
||||||
|
borderStyle: 'round',
|
||||||
|
borderColor: 'cyan'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Display tag and file path info
|
||||||
|
if (tag || filePath) {
|
||||||
|
let tagInfo = '';
|
||||||
|
|
||||||
|
if (tag && tag !== 'master') {
|
||||||
|
tagInfo = `🏷 tag: ${chalk.cyan(tag)}`;
|
||||||
|
} else {
|
||||||
|
tagInfo = `🏷 tag: ${chalk.cyan('master')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(tagInfo);
|
||||||
|
|
||||||
|
if (filePath) {
|
||||||
|
console.log(
|
||||||
|
`Listing tasks from: ${chalk.dim(filePath)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(); // Empty line for spacing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a simple header without the ASCII art
|
||||||
|
*/
|
||||||
|
export function displaySimpleHeader(options: HeaderOptions = {}): void {
|
||||||
|
displayHeader({ ...options, showBanner: false });
|
||||||
|
}
|
||||||
6
apps/cli/src/ui/components/index.ts
Normal file
6
apps/cli/src/ui/components/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview UI components exports
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './header.component.js';
|
||||||
|
export * from './dashboard.component.js';
|
||||||
9
apps/cli/src/ui/index.ts
Normal file
9
apps/cli/src/ui/index.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Main UI exports
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Export all components
|
||||||
|
export * from './components/index.js';
|
||||||
|
|
||||||
|
// Re-export existing UI utilities
|
||||||
|
export * from '../utils/ui.js';
|
||||||
Reference in New Issue
Block a user