terminal-cli-complex/src/widgets/RoadmapPanel.ts

152 lines
3.6 KiB
TypeScript

/**
* RoadmapPanel Widget - Displays phase roadmap with completion status
*
* Features:
* - Phase tracking with visual indicators
* - Current phase highlighting
* - Completed phase markers
* - Auto-updating current phase
*/
import * as blessed from 'blessed'
// =============================================================================
// Types
// =============================================================================
export interface PhaseInfo {
index: number
name: string
completed: boolean
}
export interface RoadmapPanelOptions {
/** Label for the panel */
label?: string
/** Box options */
boxOptions?: blessed.Widgets.BoxOptions
}
// =============================================================================
// RoadmapPanel Widget
// =============================================================================
export class RoadmapPanel {
private box: blessed.Widgets.BoxElement
private phases: PhaseInfo[] = []
private currentPhase = 0
constructor(parent: blessed.Widgets.Screen | blessed.Widgets.Node, options: RoadmapPanelOptions = {}) {
this.box = blessed.box({
parent,
label: ` ${options.label ?? 'Roadmap'} `,
border: { type: 'line' },
style: {
border: { fg: 'cyan' },
label: { fg: 'white', bold: true } as unknown as string, // blessed supports object form
},
tags: true,
...options.boxOptions,
})
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
* Set or update a phase
*/
setPhase(index: number, name: string, completed = false): void {
const existing = this.phases.find(p => p.index === index)
if (existing) {
existing.name = name
existing.completed = completed
} else {
this.phases.push({ index, name, completed })
this.phases.sort((a, b) => a.index - b.index)
}
this.render()
}
/**
* Set the current phase (also marks previous phases as completed)
*/
setCurrentPhase(index: number, name?: string): void {
this.currentPhase = index
// Mark previous phases as completed
for (const phase of this.phases) {
if (phase.index < index) {
phase.completed = true
}
}
// Update or add current phase
if (name) {
this.setPhase(index, name, false)
}
this.render()
}
/**
* Mark a phase as completed
*/
completePhase(index: number): void {
const phase = this.phases.find(p => p.index === index)
if (phase) {
phase.completed = true
this.render()
}
}
/**
* Clear all phases
*/
clear(): void {
this.phases = []
this.currentPhase = 0
this.box.setContent('')
this.box.screen.render()
}
/**
* Render the widget
*/
render(): void {
const lines: string[] = []
for (const phase of this.phases) {
const isCurrent = phase.index === this.currentPhase
let symbol: string
let color: string
if (phase.completed) {
symbol = '✓'
color = 'green'
} else if (isCurrent) {
symbol = '●'
color = 'yellow'
} else {
symbol = '○'
color = 'gray'
}
const nameStyle = isCurrent ? '{bold}' : ''
const nameStyleEnd = isCurrent ? '{/bold}' : ''
lines.push(`{${color}-fg}${symbol}{/} ${nameStyle}${phase.name}${nameStyleEnd}`)
}
this.box.setContent(lines.join('\n'))
this.box.screen.render()
}
/**
* Get the underlying blessed element
*/
get element(): blessed.Widgets.BoxElement {
return this.box
}
}