c4dslbuilder
Version:
A CLI tool designed to compile a folder structure of markdowns and mermaid files into a site, pdf, single file markdown or a collection of markdowns with links - inspired by c4builder
132 lines (131 loc) • 4.21 kB
JavaScript
import chalk from 'chalk';
export class WatchModeUI {
logger;
state;
logHistory = [];
stdin;
maxLogLines = 20;
isActive = false;
constructor(port, logger) {
this.logger = logger;
this.state = {
status: 'watching',
rebuildCount: 0,
port,
lastActivity: new Date().toLocaleTimeString(),
};
this.stdin = process.stdin;
}
start() {
this.isActive = true;
this.setupInputHandling();
this.render();
}
stop() {
this.isActive = false;
if (this.stdin.isTTY) {
this.stdin.setRawMode(false);
this.stdin.pause();
}
console.clear();
}
setupInputHandling() {
if (!this.stdin.isTTY) {
return;
}
this.stdin.setRawMode(true);
this.stdin.resume();
this.stdin.setEncoding('utf8');
this.stdin.on('data', (key) => {
if (key === 'q' || key === '\u0003') {
// q or Ctrl+C
this.log(chalk.yellow('\nStopping watch mode...'));
this.stop();
process.exit(0);
}
else if (key === 'r') {
const result = this.onRebuildRequest?.();
if (result instanceof Promise) {
result.catch((err) => this.logger.error('Rebuild failed', err));
}
}
});
}
onRebuildRequest;
log(message) {
const timestamp = new Date().toLocaleTimeString();
this.logHistory.push(`[${timestamp}] ${message}`);
if (this.logHistory.length > this.maxLogLines) {
this.logHistory.shift();
}
if (this.isActive) {
this.render();
}
else {
console.log(message);
}
}
updateStatus(status) {
this.state.status = status;
this.state.lastActivity = new Date().toLocaleTimeString();
if (status === 'building') {
this.state.rebuildCount++;
}
if (this.isActive) {
this.render();
}
}
render() {
// Clear screen and move to top
console.clear();
// Header
console.log(chalk.bold.cyan('═'.repeat(60)));
console.log(chalk.bold.cyan(' C4 DSL Builder - Watch Mode'));
console.log(chalk.bold.cyan('═'.repeat(60)));
console.log();
// Log history section
console.log(chalk.bold('Recent Activity:'));
console.log(chalk.gray('─'.repeat(60)));
if (this.logHistory.length === 0) {
console.log(chalk.gray(' (No activity yet)'));
}
else {
this.logHistory.forEach((line) => {
console.log(` ${line}`);
});
}
// Fill remaining log space
const emptyLines = this.maxLogLines - this.logHistory.length;
for (let i = 0; i < emptyLines; i++) {
console.log();
}
console.log(chalk.gray('─'.repeat(60)));
console.log();
// Status section
console.log(chalk.bold('Status:'));
let statusColor;
if (this.state.status === 'watching') {
statusColor = chalk.green;
}
else if (this.state.status === 'building') {
statusColor = chalk.yellow;
}
else {
statusColor = chalk.red;
}
const serverUrl = `http://localhost:${this.state.port}`;
console.log(` Status: ${statusColor(this.state.status.toUpperCase())}`);
console.log(` Rebuilds: ${chalk.cyan(this.state.rebuildCount.toString())}`);
console.log(` Last update: ${chalk.gray(this.state.lastActivity)}`);
console.log(` Server: ${chalk.cyan(serverUrl)}`);
console.log();
// Help section
console.log(chalk.bold.cyan('─'.repeat(60)));
console.log(chalk.gray('Press ') +
chalk.bold.white('r') +
chalk.gray(' to force refresh, ') +
chalk.bold.white('q') +
chalk.gray(' to stop'));
console.log(chalk.bold.cyan('─'.repeat(60)));
}
}