class KanbanModule { constructor() { this.commands = {}; this.terminal = null; this.storageKey = 'webx11_kanban_tasks'; this.columns = ['todo', 'doing', 'done']; this.priorities = ['low', 'medium', 'high']; } getHelpInfo() { return { 'kanban': 'kanban [args] — Manage tasks on a kanban board (todo, doing, done columns)' }; } registerCommands() { this.commands['kanban'] = (args) => this.handleKanban(args); } loadTasks() { try { const data = localStorage.getItem(this.storageKey); return data ? JSON.parse(data) : []; } catch (e) { return []; } } saveTasks(tasks) { try { localStorage.setItem(this.storageKey, JSON.stringify(tasks)); } catch (e) { if (this.terminal) { this.terminal.print('Failed to save kanban tasks', 'error'); } } } getNextId(tasks) { if (!tasks.length) return 1; return Math.max(...tasks.map(t => t.id)) + 1; } printUsage() { this.terminal.print('Usage:', 'info'); this.terminal.print(' kanban add Add task to TODO column', 'info'); this.terminal.print(' kanban move Move task to column (todo/doing/done)', 'info'); this.terminal.print(' kanban list Show all columns with tasks', 'info'); this.terminal.print(' kanban rm Remove task', 'info'); this.terminal.print(' kanban clear Clear all tasks', 'info'); this.terminal.print(' kanban priority Set priority (low/medium/high)', 'info'); } handleKanban(args) { if (!args[0]) { return this.printUsage(); } const sub = args[0].toLowerCase(); switch (sub) { case 'add': return this.addTask(args.slice(1)); case 'move': return this.moveTask(args[1], args[2]); case 'list': return this.listTasks(); case 'rm': return this.removeTask(args[1]); case 'clear': return this.clearTasks(); case 'priority': return this.setPriority(args[1], args[2]); default: this.terminal.print(`Unknown kanban command: ${sub}`, 'error'); return this.printUsage(); } } addTask(titleArgs) { const title = titleArgs.join(' ').trim(); if (!title) { return this.terminal.print('Usage: kanban add ', 'warning'); } const tasks = this.loadTasks(); const task = { id: this.getNextId(tasks), title: title, column: 'todo', priority: 'medium', created: new Date().toISOString() }; tasks.push(task); this.saveTasks(tasks); this.terminal.print(`Added task #${task.id}: ${title}`, 'success'); } moveTask(idStr, column) { const id = parseInt(idStr, 10); if (isNaN(id) || !column) { return this.terminal.print('Usage: kanban move ', 'warning'); } const col = column.toLowerCase(); if (!this.columns.includes(col)) { return this.terminal.print(`Unknown column: ${column}. Use todo/doing/done.`, 'error'); } const tasks = this.loadTasks(); const task = tasks.find(t => t.id === id); if (!task) { return this.terminal.print(`Task #${id} not found`, 'error'); } task.column = col; this.saveTasks(tasks); this.terminal.print(`Moved task #${id} to ${col}`, 'success'); } listTasks() { const tasks = this.loadTasks(); if (!tasks.length) { return this.terminal.print('No tasks on the kanban board.', 'info'); } this.terminal.print('Kanban board:', 'info'); for (const column of this.columns) { const columnTasks = tasks.filter(t => t.column === column); this.terminal.print(`\n[${column.toUpperCase()}]`, 'info'); if (!columnTasks.length) { this.terminal.print(' (empty)', 'info'); continue; } for (const task of columnTasks) { this.terminal.print(` #${task.id} ${task.title} [${task.priority}]`, 'info'); } } } removeTask(idStr) { const id = parseInt(idStr, 10); if (isNaN(id)) { return this.terminal.print('Usage: kanban rm ', 'warning'); } const tasks = this.loadTasks(); const index = tasks.findIndex(t => t.id === id); if (index === -1) { return this.terminal.print(`Task #${id} not found`, 'error'); } const removed = tasks.splice(index, 1)[0]; this.saveTasks(tasks); this.terminal.print(`Removed task #${id}: ${removed.title}`, 'success'); } clearTasks() { this.saveTasks([]); this.terminal.print('All kanban tasks cleared', 'success'); } setPriority(idStr, priority) { const id = parseInt(idStr, 10); const level = (priority || '').toLowerCase(); if (isNaN(id) || !this.priorities.includes(level)) { return this.terminal.print('Usage: kanban priority ', 'warning'); } const tasks = this.loadTasks(); const task = tasks.find(t => t.id === id); if (!task) { return this.terminal.print(`Task #${id} not found`, 'error'); } task.priority = level; this.saveTasks(tasks); this.terminal.print(`Set task #${id} priority to ${level}`, 'success'); } } if (typeof window.webx11 !== 'undefined' && window.webx11) { webx11.moduleManager.registerModule(new KanbanModule(), 'https://webx11.duckdns.org/spaces/newos4/modules/ext/kanban.js'); }