diff options
| -rw-r--r-- | main.html | 2 | ||||
| -rw-r--r-- | modules/ext/kanban.js | 190 |
2 files changed, 191 insertions, 1 deletions
@@ -1305,7 +1305,7 @@ var mods = [ ['Foundation (always loaded)', ['virtual-fs.js','module-manager.js','ollama-manager.js','terminal.js'], true], ['Built-in modules', ['module-system.js','module-fs.js','module-ollama.js','module-my.js','module-searxng.js','module-servermon.js','module-ollamamodels.js','module-config.js','module-simplegui.js','module-io.js','module-dynamicprompts.js','module-aiassist.js','module-aiasm.js','module-smartctx.js','module-duckduckgo.js','module-fetcher.js','module-chatstudio.js'], false], - ['Extensions', ['calc.js','voice.js','cron.js','theme.js','wpm.js','mindmap.js','webdav.js','skill-pack-core.js','editor.js','diff.js','alias.js','finder.js','filemgr.js','cryptogzip.js','tutorial.js','shell.js','busybox.js','floating.js','console.js'], false] + ['Extensions', ['calc.js','voice.js','cron.js','theme.js','wpm.js','mindmap.js','webdav.js','skill-pack-core.js','editor.js','diff.js','alias.js','finder.js','filemgr.js','cryptogzip.js','tutorial.js','shell.js','busybox.js','floating.js','console.js','kanban.js'], false] ]; var isSel = function(name) { return true; }; // default: all selected mods.forEach(function(section) { diff --git a/modules/ext/kanban.js b/modules/ext/kanban.js new file mode 100644 index 0000000..63d7f09 --- /dev/null +++ b/modules/ext/kanban.js @@ -0,0 +1,190 @@ +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 <command> [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 <task> Add task to TODO column', 'info'); + this.terminal.print(' kanban move <id> <column> Move task to column (todo/doing/done)', 'info'); + this.terminal.print(' kanban list Show all columns with tasks', 'info'); + this.terminal.print(' kanban rm <id> Remove task', 'info'); + this.terminal.print(' kanban clear Clear all tasks', 'info'); + this.terminal.print(' kanban priority <id> <level> 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 <task>', '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 <id> <column>', '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 <id>', '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 <id> <low|medium|high>', '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'); +} |