@qodalis/cli-todo 0.0.6 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +16 -24
- package/public-api.d.mts +48 -0
- package/public-api.d.ts +48 -2
- package/public-api.js +521 -0
- package/public-api.js.map +1 -0
- package/public-api.mjs +512 -0
- package/public-api.mjs.map +1 -0
- package/umd/index.global.js +3308 -0
- package/README.md +0 -34
- package/esm2022/lib/cli-todo.module.mjs +0 -19
- package/esm2022/lib/processors/cli-todo-command-processor.mjs +0 -181
- package/esm2022/lib/version.mjs +0 -3
- package/esm2022/public-api.mjs +0 -6
- package/esm2022/qodalis-cli-todo.mjs +0 -5
- package/fesm2022/qodalis-cli-todo.mjs +0 -210
- package/fesm2022/qodalis-cli-todo.mjs.map +0 -1
- package/index.d.ts +0 -5
- package/lib/cli-todo.module.d.ts +0 -6
- package/lib/processors/cli-todo-command-processor.d.ts +0 -23
- package/lib/version.d.ts +0 -1
- package/umd/index.js +0 -220
package/package.json
CHANGED
|
@@ -1,44 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qodalis/cli-todo",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0-beta.2",
|
|
4
|
+
"description": "@qodalis/cli extension for managing TODO tasks with add, list, complete, and remove commands.",
|
|
5
5
|
"author": "Nicolae Lupei, Qodalis Solutions",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/qodalis-solutions/
|
|
9
|
+
"url": "https://github.com/qodalis-solutions/web-cli"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://qodalis.com",
|
|
12
12
|
"keywords": [
|
|
13
|
-
"angular",
|
|
14
13
|
"cli",
|
|
15
14
|
"qodalis",
|
|
16
15
|
"terminal",
|
|
17
|
-
"todo"
|
|
16
|
+
"todo",
|
|
17
|
+
"tasks",
|
|
18
|
+
"task-management"
|
|
18
19
|
],
|
|
19
|
-
"umd": "./umd/index.js",
|
|
20
|
-
"unpkg": "./umd/index.js",
|
|
21
|
-
"peerDependencies": {
|
|
22
|
-
"@angular/common": "^16.2.0",
|
|
23
|
-
"@angular/core": "^16.2.0"
|
|
24
|
-
},
|
|
20
|
+
"umd": "./umd/index.global.js",
|
|
21
|
+
"unpkg": "./umd/index.global.js",
|
|
25
22
|
"dependencies": {
|
|
26
|
-
"
|
|
27
|
-
"@qodalis/cli-core": "^0.0.16",
|
|
28
|
-
"@qodalis/angular-cli": "^1.0.37"
|
|
23
|
+
"@qodalis/cli-core": "2.0.0-beta.2"
|
|
29
24
|
},
|
|
30
25
|
"sideEffects": false,
|
|
31
|
-
"
|
|
32
|
-
"
|
|
26
|
+
"main": "./public-api.js",
|
|
27
|
+
"module": "./public-api.mjs",
|
|
28
|
+
"types": "./public-api.d.ts",
|
|
33
29
|
"exports": {
|
|
34
|
-
"./package.json": {
|
|
35
|
-
"default": "./package.json"
|
|
36
|
-
},
|
|
37
30
|
".": {
|
|
38
|
-
"types": "./
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"default": "./fesm2022/qodalis-cli-todo.mjs"
|
|
31
|
+
"types": "./public-api.d.ts",
|
|
32
|
+
"import": "./public-api.mjs",
|
|
33
|
+
"require": "./public-api.js"
|
|
42
34
|
}
|
|
43
35
|
}
|
|
44
|
-
}
|
|
36
|
+
}
|
package/public-api.d.mts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as _qodalis_cli_core from '@qodalis/cli-core';
|
|
2
|
+
import { ICliCommandProcessor, CliProcessorMetadata, CliStateConfiguration, CliProcessCommand, ICliExecutionContext, ICliCompletionProvider, ICliCompletionContext, ICliModule } from '@qodalis/cli-core';
|
|
3
|
+
|
|
4
|
+
type TodoItem = {
|
|
5
|
+
id: number;
|
|
6
|
+
text: string;
|
|
7
|
+
completed: boolean;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
completedAt?: string;
|
|
10
|
+
dueDate?: string;
|
|
11
|
+
};
|
|
12
|
+
declare function parseDueDate(input: string): string | null;
|
|
13
|
+
declare function formatRelativeDate(isoDate: string): string;
|
|
14
|
+
declare function isOverdue(todo: TodoItem): boolean;
|
|
15
|
+
declare function migrateTodoItem(item: any): TodoItem;
|
|
16
|
+
declare function lineThroughText(text: string): string;
|
|
17
|
+
|
|
18
|
+
declare class CliTodoCommandProcessor implements ICliCommandProcessor {
|
|
19
|
+
command: string;
|
|
20
|
+
description: string;
|
|
21
|
+
author: _qodalis_cli_core.ICliCommandAuthor;
|
|
22
|
+
version: string;
|
|
23
|
+
processors?: ICliCommandProcessor[];
|
|
24
|
+
metadata?: CliProcessorMetadata;
|
|
25
|
+
stateConfiguration?: CliStateConfiguration;
|
|
26
|
+
private todos;
|
|
27
|
+
private nextId;
|
|
28
|
+
getTodos(): TodoItem[];
|
|
29
|
+
constructor();
|
|
30
|
+
processCommand(command: CliProcessCommand, context: ICliExecutionContext): Promise<void>;
|
|
31
|
+
writeDescription(context: ICliExecutionContext): void;
|
|
32
|
+
initialize(context: ICliExecutionContext): Promise<void>;
|
|
33
|
+
private formatTodoLine;
|
|
34
|
+
private registerSubProcessors;
|
|
35
|
+
private loadFromOldStorage;
|
|
36
|
+
private saveToStorage;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
declare class CliTodoIdCompletionProvider implements ICliCompletionProvider {
|
|
40
|
+
private readonly todoProcessor;
|
|
41
|
+
priority: number;
|
|
42
|
+
constructor(todoProcessor: CliTodoCommandProcessor);
|
|
43
|
+
getCompletions(context: ICliCompletionContext): string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare const todoModule: ICliModule;
|
|
47
|
+
|
|
48
|
+
export { CliTodoCommandProcessor, CliTodoIdCompletionProvider, type TodoItem, formatRelativeDate, isOverdue, lineThroughText, migrateTodoItem, parseDueDate, todoModule };
|
package/public-api.d.ts
CHANGED
|
@@ -1,2 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import * as _qodalis_cli_core from '@qodalis/cli-core';
|
|
2
|
+
import { ICliCommandProcessor, CliProcessorMetadata, CliStateConfiguration, CliProcessCommand, ICliExecutionContext, ICliCompletionProvider, ICliCompletionContext, ICliModule } from '@qodalis/cli-core';
|
|
3
|
+
|
|
4
|
+
type TodoItem = {
|
|
5
|
+
id: number;
|
|
6
|
+
text: string;
|
|
7
|
+
completed: boolean;
|
|
8
|
+
createdAt: string;
|
|
9
|
+
completedAt?: string;
|
|
10
|
+
dueDate?: string;
|
|
11
|
+
};
|
|
12
|
+
declare function parseDueDate(input: string): string | null;
|
|
13
|
+
declare function formatRelativeDate(isoDate: string): string;
|
|
14
|
+
declare function isOverdue(todo: TodoItem): boolean;
|
|
15
|
+
declare function migrateTodoItem(item: any): TodoItem;
|
|
16
|
+
declare function lineThroughText(text: string): string;
|
|
17
|
+
|
|
18
|
+
declare class CliTodoCommandProcessor implements ICliCommandProcessor {
|
|
19
|
+
command: string;
|
|
20
|
+
description: string;
|
|
21
|
+
author: _qodalis_cli_core.ICliCommandAuthor;
|
|
22
|
+
version: string;
|
|
23
|
+
processors?: ICliCommandProcessor[];
|
|
24
|
+
metadata?: CliProcessorMetadata;
|
|
25
|
+
stateConfiguration?: CliStateConfiguration;
|
|
26
|
+
private todos;
|
|
27
|
+
private nextId;
|
|
28
|
+
getTodos(): TodoItem[];
|
|
29
|
+
constructor();
|
|
30
|
+
processCommand(command: CliProcessCommand, context: ICliExecutionContext): Promise<void>;
|
|
31
|
+
writeDescription(context: ICliExecutionContext): void;
|
|
32
|
+
initialize(context: ICliExecutionContext): Promise<void>;
|
|
33
|
+
private formatTodoLine;
|
|
34
|
+
private registerSubProcessors;
|
|
35
|
+
private loadFromOldStorage;
|
|
36
|
+
private saveToStorage;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
declare class CliTodoIdCompletionProvider implements ICliCompletionProvider {
|
|
40
|
+
private readonly todoProcessor;
|
|
41
|
+
priority: number;
|
|
42
|
+
constructor(todoProcessor: CliTodoCommandProcessor);
|
|
43
|
+
getCompletions(context: ICliCompletionContext): string[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
declare const todoModule: ICliModule;
|
|
47
|
+
|
|
48
|
+
export { CliTodoCommandProcessor, CliTodoIdCompletionProvider, type TodoItem, formatRelativeDate, isOverdue, lineThroughText, migrateTodoItem, parseDueDate, todoModule };
|
package/public-api.js
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var cliCore = require('@qodalis/cli-core');
|
|
4
|
+
|
|
5
|
+
// src/lib/utilities/index.ts
|
|
6
|
+
var DAY_NAMES = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
|
|
7
|
+
var MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
8
|
+
function startOfDay(date) {
|
|
9
|
+
const d = new Date(date);
|
|
10
|
+
d.setHours(0, 0, 0, 0);
|
|
11
|
+
return d;
|
|
12
|
+
}
|
|
13
|
+
function parseDueDate(input) {
|
|
14
|
+
const trimmed = input.trim().toLowerCase();
|
|
15
|
+
const now = /* @__PURE__ */ new Date();
|
|
16
|
+
const today = startOfDay(now);
|
|
17
|
+
if (trimmed === "today") {
|
|
18
|
+
return today.toISOString();
|
|
19
|
+
}
|
|
20
|
+
if (trimmed === "tomorrow") {
|
|
21
|
+
const d = new Date(today);
|
|
22
|
+
d.setDate(d.getDate() + 1);
|
|
23
|
+
return d.toISOString();
|
|
24
|
+
}
|
|
25
|
+
if (trimmed === "yesterday") {
|
|
26
|
+
const d = new Date(today);
|
|
27
|
+
d.setDate(d.getDate() - 1);
|
|
28
|
+
return d.toISOString();
|
|
29
|
+
}
|
|
30
|
+
if (trimmed === "next week") {
|
|
31
|
+
const d = new Date(today);
|
|
32
|
+
d.setDate(d.getDate() + 7);
|
|
33
|
+
return d.toISOString();
|
|
34
|
+
}
|
|
35
|
+
if (trimmed === "next month") {
|
|
36
|
+
const d = new Date(today);
|
|
37
|
+
d.setMonth(d.getMonth() + 1);
|
|
38
|
+
return d.toISOString();
|
|
39
|
+
}
|
|
40
|
+
const dayIndex = DAY_NAMES.indexOf(trimmed);
|
|
41
|
+
if (dayIndex !== -1) {
|
|
42
|
+
const d = new Date(today);
|
|
43
|
+
const currentDay = d.getDay();
|
|
44
|
+
let daysUntil = dayIndex - currentDay;
|
|
45
|
+
if (daysUntil <= 0) daysUntil += 7;
|
|
46
|
+
d.setDate(d.getDate() + daysUntil);
|
|
47
|
+
return d.toISOString();
|
|
48
|
+
}
|
|
49
|
+
const isoMatch = trimmed.match(/^\d{4}-\d{2}-\d{2}$/);
|
|
50
|
+
if (isoMatch) {
|
|
51
|
+
const d = /* @__PURE__ */ new Date(trimmed + "T00:00:00");
|
|
52
|
+
if (!isNaN(d.getTime())) {
|
|
53
|
+
return startOfDay(d).toISOString();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function formatRelativeDate(isoDate) {
|
|
59
|
+
const target = startOfDay(new Date(isoDate));
|
|
60
|
+
const today = startOfDay(/* @__PURE__ */ new Date());
|
|
61
|
+
const diffMs = target.getTime() - today.getTime();
|
|
62
|
+
const diffDays = Math.round(diffMs / (1e3 * 60 * 60 * 24));
|
|
63
|
+
if (diffDays === 0) return "due: today";
|
|
64
|
+
if (diffDays === 1) return "due: tomorrow";
|
|
65
|
+
if (diffDays === -1) return "overdue: yesterday";
|
|
66
|
+
if (diffDays > 1 && diffDays <= 14) return `due: in ${diffDays} days`;
|
|
67
|
+
if (diffDays < -1) return `overdue: ${Math.abs(diffDays)} days ago`;
|
|
68
|
+
const d = new Date(isoDate);
|
|
69
|
+
return `due: ${MONTH_NAMES[d.getMonth()]} ${d.getDate()}`;
|
|
70
|
+
}
|
|
71
|
+
function isOverdue(todo) {
|
|
72
|
+
if (todo.completed || !todo.dueDate) return false;
|
|
73
|
+
const target = startOfDay(new Date(todo.dueDate));
|
|
74
|
+
const today = startOfDay(/* @__PURE__ */ new Date());
|
|
75
|
+
return target.getTime() < today.getTime();
|
|
76
|
+
}
|
|
77
|
+
function migrateTodoItem(item) {
|
|
78
|
+
return {
|
|
79
|
+
id: item.id,
|
|
80
|
+
text: item.text,
|
|
81
|
+
completed: item.completed ?? false,
|
|
82
|
+
createdAt: item.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
83
|
+
completedAt: item.completedAt,
|
|
84
|
+
dueDate: item.dueDate
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function lineThroughText(text) {
|
|
88
|
+
return text.split("").map((char) => char + "\u0336").join("");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/lib/version.ts
|
|
92
|
+
var LIBRARY_VERSION = "2.0.0-beta.2";
|
|
93
|
+
var API_VERSION = 2;
|
|
94
|
+
|
|
95
|
+
// src/lib/processors/cli-todo-command-processor.ts
|
|
96
|
+
var CliTodoCommandProcessor = class {
|
|
97
|
+
constructor() {
|
|
98
|
+
this.command = "todo";
|
|
99
|
+
this.description = "Manage your tasks from the terminal. Add, list, edit, complete, and remove TODO items.";
|
|
100
|
+
this.author = cliCore.DefaultLibraryAuthor;
|
|
101
|
+
this.version = LIBRARY_VERSION;
|
|
102
|
+
this.processors = [];
|
|
103
|
+
this.metadata = {
|
|
104
|
+
icon: "\u{1F4DD}",
|
|
105
|
+
requiredCoreVersion: ">=2.0.0 <3.0.0",
|
|
106
|
+
requiredCliVersion: ">=2.0.0 <3.0.0"
|
|
107
|
+
};
|
|
108
|
+
this.stateConfiguration = {
|
|
109
|
+
initialState: {
|
|
110
|
+
todos: this.loadFromOldStorage() ?? []
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
this.todos = [];
|
|
114
|
+
this.nextId = 1;
|
|
115
|
+
this.registerSubProcessors();
|
|
116
|
+
}
|
|
117
|
+
getTodos() {
|
|
118
|
+
return this.todos;
|
|
119
|
+
}
|
|
120
|
+
async processCommand(command, context) {
|
|
121
|
+
context.executor.showHelp(command, context);
|
|
122
|
+
}
|
|
123
|
+
writeDescription(context) {
|
|
124
|
+
const { writer } = context;
|
|
125
|
+
writer.writeln(this.description);
|
|
126
|
+
writer.writeln();
|
|
127
|
+
writer.writeln(writer.wrapInColor("Commands:", cliCore.CliForegroundColor.Yellow));
|
|
128
|
+
writer.writeln(` ${writer.wrapInColor("todo ls", cliCore.CliForegroundColor.Cyan)} List all tasks`);
|
|
129
|
+
writer.writeln(` ${writer.wrapInColor("todo ls --pending", cliCore.CliForegroundColor.Cyan)} List pending tasks only`);
|
|
130
|
+
writer.writeln(` ${writer.wrapInColor("todo ls --completed", cliCore.CliForegroundColor.Cyan)} List completed tasks only`);
|
|
131
|
+
writer.writeln(` ${writer.wrapInColor("todo ls --overdue", cliCore.CliForegroundColor.Cyan)} List overdue tasks only`);
|
|
132
|
+
writer.writeln(` ${writer.wrapInColor("todo add <text>", cliCore.CliForegroundColor.Cyan)} Add a new task`);
|
|
133
|
+
writer.writeln(` ${writer.wrapInColor("todo add <text> --due <date>", cliCore.CliForegroundColor.Cyan)} Add with due date`);
|
|
134
|
+
writer.writeln(` ${writer.wrapInColor("todo edit <id> <new text>", cliCore.CliForegroundColor.Cyan)} Edit a task`);
|
|
135
|
+
writer.writeln(` ${writer.wrapInColor("todo done <id>", cliCore.CliForegroundColor.Cyan)} Mark a task as done`);
|
|
136
|
+
writer.writeln(` ${writer.wrapInColor("todo toggle <id>", cliCore.CliForegroundColor.Cyan)} Toggle done/undone`);
|
|
137
|
+
writer.writeln(` ${writer.wrapInColor("todo rm <id>", cliCore.CliForegroundColor.Cyan)} Remove a task`);
|
|
138
|
+
writer.writeln(` ${writer.wrapInColor("todo rm --all", cliCore.CliForegroundColor.Cyan)} Remove all tasks`);
|
|
139
|
+
writer.writeln();
|
|
140
|
+
writer.writeln(writer.wrapInColor("Due date formats:", cliCore.CliForegroundColor.Yellow));
|
|
141
|
+
writer.writeln(` today, tomorrow, monday..sunday, next week, next month, YYYY-MM-DD`);
|
|
142
|
+
writer.writeln();
|
|
143
|
+
writer.writeln(writer.wrapInColor("Examples:", cliCore.CliForegroundColor.Yellow));
|
|
144
|
+
writer.writeln(` todo add Buy groceries --due tomorrow`);
|
|
145
|
+
writer.writeln(` todo ls --pending`);
|
|
146
|
+
writer.writeln(` todo edit 3 Buy organic groceries`);
|
|
147
|
+
writer.writeln(` todo done 1`);
|
|
148
|
+
writer.writeln(` todo toggle 2`);
|
|
149
|
+
}
|
|
150
|
+
async initialize(context) {
|
|
151
|
+
context.state.select((x) => x["todos"]).subscribe((todos) => {
|
|
152
|
+
this.todos = (todos || []).map(migrateTodoItem);
|
|
153
|
+
this.nextId = this.todos.length > 0 ? Math.max(...this.todos.map((t) => t.id)) + 1 : 1;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
formatTodoLine(todo, writer) {
|
|
157
|
+
const check = todo.completed ? writer.wrapInColor(cliCore.CliIcon.CheckIcon, cliCore.CliForegroundColor.Green) : " ";
|
|
158
|
+
const text = todo.completed ? lineThroughText(todo.text) : todo.text;
|
|
159
|
+
let suffix = "";
|
|
160
|
+
if (todo.completed) {
|
|
161
|
+
suffix = writer.wrapInColor(" done", cliCore.CliForegroundColor.Green);
|
|
162
|
+
} else if (todo.dueDate) {
|
|
163
|
+
const rel = formatRelativeDate(todo.dueDate);
|
|
164
|
+
const color = isOverdue(todo) ? cliCore.CliForegroundColor.Red : cliCore.CliForegroundColor.Yellow;
|
|
165
|
+
suffix = " " + writer.wrapInColor(rel, color);
|
|
166
|
+
}
|
|
167
|
+
return ` [${check}] #${todo.id} - ${text}${suffix}`;
|
|
168
|
+
}
|
|
169
|
+
registerSubProcessors() {
|
|
170
|
+
this.processors = [
|
|
171
|
+
// ── ls ──────────────────────────────────────────────
|
|
172
|
+
{
|
|
173
|
+
command: "ls",
|
|
174
|
+
aliases: ["list"],
|
|
175
|
+
description: "List TODO items",
|
|
176
|
+
parameters: [
|
|
177
|
+
{
|
|
178
|
+
name: "pending",
|
|
179
|
+
description: "Show only pending tasks",
|
|
180
|
+
type: "boolean",
|
|
181
|
+
required: false
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "completed",
|
|
185
|
+
description: "Show only completed tasks",
|
|
186
|
+
type: "boolean",
|
|
187
|
+
required: false
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "overdue",
|
|
191
|
+
description: "Show only overdue tasks",
|
|
192
|
+
type: "boolean",
|
|
193
|
+
required: false
|
|
194
|
+
}
|
|
195
|
+
],
|
|
196
|
+
processCommand: async (command, context) => {
|
|
197
|
+
if (this.todos.length === 0) {
|
|
198
|
+
context.writer.writeWarning("No tasks yet.");
|
|
199
|
+
context.writer.writeln('Use "todo add <text>" to create one.');
|
|
200
|
+
context.process.output([]);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
let filtered = [...this.todos];
|
|
204
|
+
const args = command.args;
|
|
205
|
+
if (args["pending"]) {
|
|
206
|
+
filtered = filtered.filter((t) => !t.completed);
|
|
207
|
+
} else if (args["completed"]) {
|
|
208
|
+
filtered = filtered.filter((t) => t.completed);
|
|
209
|
+
} else if (args["overdue"]) {
|
|
210
|
+
filtered = filtered.filter((t) => isOverdue(t));
|
|
211
|
+
}
|
|
212
|
+
const doneCount = this.todos.filter((t) => t.completed).length;
|
|
213
|
+
context.writer.writeln(
|
|
214
|
+
context.writer.wrapInColor(
|
|
215
|
+
`Todos (${doneCount}/${this.todos.length} done)`,
|
|
216
|
+
cliCore.CliForegroundColor.Cyan
|
|
217
|
+
)
|
|
218
|
+
);
|
|
219
|
+
if (filtered.length === 0) {
|
|
220
|
+
context.writer.writeln(" No matching tasks.");
|
|
221
|
+
} else {
|
|
222
|
+
filtered.forEach((todo) => {
|
|
223
|
+
context.writer.writeln(
|
|
224
|
+
this.formatTodoLine(todo, context.writer)
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
context.process.output(filtered);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
// ── add ─────────────────────────────────────────────
|
|
232
|
+
{
|
|
233
|
+
command: "add",
|
|
234
|
+
description: "Add a new task",
|
|
235
|
+
acceptsRawInput: true,
|
|
236
|
+
valueRequired: true,
|
|
237
|
+
parameters: [
|
|
238
|
+
{
|
|
239
|
+
name: "due",
|
|
240
|
+
description: "Due date (today, tomorrow, monday..sunday, next week, next month, YYYY-MM-DD)",
|
|
241
|
+
type: "string",
|
|
242
|
+
required: false
|
|
243
|
+
}
|
|
244
|
+
],
|
|
245
|
+
processCommand: async (command, context) => {
|
|
246
|
+
const text = command.value;
|
|
247
|
+
if (!text) {
|
|
248
|
+
context.writer.writeError("Please provide a task description.");
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
let dueDate;
|
|
252
|
+
const dueArg = command.args["due"];
|
|
253
|
+
if (dueArg) {
|
|
254
|
+
const parsed = parseDueDate(dueArg);
|
|
255
|
+
if (!parsed) {
|
|
256
|
+
context.writer.writeError(
|
|
257
|
+
`Invalid due date: "${dueArg}". Use: today, tomorrow, monday..sunday, next week, next month, or YYYY-MM-DD.`
|
|
258
|
+
);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
dueDate = parsed;
|
|
262
|
+
}
|
|
263
|
+
const newItem = {
|
|
264
|
+
id: this.nextId++,
|
|
265
|
+
text,
|
|
266
|
+
completed: false,
|
|
267
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
268
|
+
dueDate
|
|
269
|
+
};
|
|
270
|
+
this.todos.push(newItem);
|
|
271
|
+
await this.saveToStorage(context);
|
|
272
|
+
let msg = `Added: "${text}"`;
|
|
273
|
+
if (dueDate) {
|
|
274
|
+
msg += ` (${formatRelativeDate(dueDate)})`;
|
|
275
|
+
}
|
|
276
|
+
context.writer.writeSuccess(msg);
|
|
277
|
+
context.process.output(newItem);
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
// ── edit ────────────────────────────────────────────
|
|
281
|
+
{
|
|
282
|
+
command: "edit",
|
|
283
|
+
description: "Edit a task's text or due date",
|
|
284
|
+
acceptsRawInput: true,
|
|
285
|
+
valueRequired: true,
|
|
286
|
+
parameters: [
|
|
287
|
+
{
|
|
288
|
+
name: "due",
|
|
289
|
+
description: 'New due date (or "none" to remove)',
|
|
290
|
+
type: "string",
|
|
291
|
+
required: false
|
|
292
|
+
}
|
|
293
|
+
],
|
|
294
|
+
processCommand: async (command, context) => {
|
|
295
|
+
const raw = command.value || "";
|
|
296
|
+
const spaceIndex = raw.indexOf(" ");
|
|
297
|
+
const idStr = spaceIndex === -1 ? raw : raw.substring(0, spaceIndex);
|
|
298
|
+
const newText = spaceIndex === -1 ? void 0 : raw.substring(spaceIndex + 1).trim();
|
|
299
|
+
const id = parseInt(idStr, 10);
|
|
300
|
+
if (isNaN(id)) {
|
|
301
|
+
context.writer.writeError("Usage: todo edit <id> <new text> [--due <date>]");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const todo = this.todos.find((t) => t.id === id);
|
|
305
|
+
if (!todo) {
|
|
306
|
+
context.writer.writeError(`Task #${id} not found.`);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const dueArg = command.args["due"];
|
|
310
|
+
let changed = false;
|
|
311
|
+
if (newText) {
|
|
312
|
+
todo.text = newText;
|
|
313
|
+
changed = true;
|
|
314
|
+
}
|
|
315
|
+
if (dueArg) {
|
|
316
|
+
if (dueArg === "none") {
|
|
317
|
+
todo.dueDate = void 0;
|
|
318
|
+
changed = true;
|
|
319
|
+
} else {
|
|
320
|
+
const parsed = parseDueDate(dueArg);
|
|
321
|
+
if (!parsed) {
|
|
322
|
+
context.writer.writeError(`Invalid due date: "${dueArg}".`);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
todo.dueDate = parsed;
|
|
326
|
+
changed = true;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (!changed) {
|
|
330
|
+
context.writer.writeError("Nothing to update. Provide new text or --due.");
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
await this.saveToStorage(context);
|
|
334
|
+
context.writer.writeSuccess(`Updated task #${id}.`);
|
|
335
|
+
context.process.output(todo);
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
// ── done ────────────────────────────────────────────
|
|
339
|
+
{
|
|
340
|
+
command: "done",
|
|
341
|
+
aliases: ["complete"],
|
|
342
|
+
description: "Mark a task as done",
|
|
343
|
+
acceptsRawInput: true,
|
|
344
|
+
valueRequired: true,
|
|
345
|
+
processCommand: async (command, context) => {
|
|
346
|
+
const id = parseInt(command.value || "", 10);
|
|
347
|
+
if (isNaN(id)) {
|
|
348
|
+
context.writer.writeError("Please provide a valid task ID.");
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const todo = this.todos.find((t) => t.id === id);
|
|
352
|
+
if (!todo) {
|
|
353
|
+
context.writer.writeError(`Task #${id} not found.`);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (todo.completed) {
|
|
357
|
+
context.writer.writeInfo(`Task #${id} is already done.`);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
todo.completed = true;
|
|
361
|
+
todo.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
362
|
+
await this.saveToStorage(context);
|
|
363
|
+
context.writer.writeSuccess(`Done: "${todo.text}"`);
|
|
364
|
+
context.process.output(todo);
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
// ── toggle ──────────────────────────────────────────
|
|
368
|
+
{
|
|
369
|
+
command: "toggle",
|
|
370
|
+
description: "Toggle a task between done and undone",
|
|
371
|
+
acceptsRawInput: true,
|
|
372
|
+
valueRequired: true,
|
|
373
|
+
processCommand: async (command, context) => {
|
|
374
|
+
const id = parseInt(command.value || "", 10);
|
|
375
|
+
if (isNaN(id)) {
|
|
376
|
+
context.writer.writeError("Please provide a valid task ID.");
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const todo = this.todos.find((t) => t.id === id);
|
|
380
|
+
if (!todo) {
|
|
381
|
+
context.writer.writeError(`Task #${id} not found.`);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
todo.completed = !todo.completed;
|
|
385
|
+
todo.completedAt = todo.completed ? (/* @__PURE__ */ new Date()).toISOString() : void 0;
|
|
386
|
+
await this.saveToStorage(context);
|
|
387
|
+
const status = todo.completed ? "done" : "reopened";
|
|
388
|
+
context.writer.writeSuccess(`Task #${id} marked as ${status}.`);
|
|
389
|
+
context.process.output(todo);
|
|
390
|
+
}
|
|
391
|
+
},
|
|
392
|
+
// ── rm ──────────────────────────────────────────────
|
|
393
|
+
{
|
|
394
|
+
command: "rm",
|
|
395
|
+
aliases: ["remove"],
|
|
396
|
+
description: "Remove a task by ID",
|
|
397
|
+
acceptsRawInput: true,
|
|
398
|
+
parameters: [
|
|
399
|
+
{
|
|
400
|
+
name: "all",
|
|
401
|
+
description: "Remove all tasks",
|
|
402
|
+
type: "boolean",
|
|
403
|
+
defaultValue: false,
|
|
404
|
+
aliases: ["a"],
|
|
405
|
+
required: false
|
|
406
|
+
}
|
|
407
|
+
],
|
|
408
|
+
processCommand: async (command, context) => {
|
|
409
|
+
if (command.args["all"] || command.args["a"]) {
|
|
410
|
+
if (this.todos.length === 0) {
|
|
411
|
+
context.writer.writeInfo("No tasks to remove.");
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
const confirmed = await context.reader.readConfirm(
|
|
415
|
+
`Remove all ${this.todos.length} tasks?`,
|
|
416
|
+
false
|
|
417
|
+
);
|
|
418
|
+
if (!confirmed) {
|
|
419
|
+
context.writer.writeInfo("Cancelled.");
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
this.todos = [];
|
|
423
|
+
await this.saveToStorage(context);
|
|
424
|
+
context.writer.writeSuccess("Removed all tasks.");
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
const id = parseInt(command.value, 10);
|
|
428
|
+
if (isNaN(id)) {
|
|
429
|
+
context.writer.writeError("Please provide a valid task ID.");
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const index = this.todos.findIndex((t) => t.id === id);
|
|
433
|
+
if (index === -1) {
|
|
434
|
+
context.writer.writeError(`Task #${id} not found.`);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const removed = this.todos.splice(index, 1)[0];
|
|
438
|
+
await this.saveToStorage(context);
|
|
439
|
+
context.writer.writeSuccess(`Removed: "${removed.text}"`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
];
|
|
443
|
+
}
|
|
444
|
+
loadFromOldStorage() {
|
|
445
|
+
const data = localStorage.getItem("todo-items");
|
|
446
|
+
if (!data) return [];
|
|
447
|
+
try {
|
|
448
|
+
return JSON.parse(data).map(migrateTodoItem);
|
|
449
|
+
} catch {
|
|
450
|
+
return [];
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
async saveToStorage(context) {
|
|
454
|
+
context.state.updateState({ todos: this.todos });
|
|
455
|
+
await context.state.persist();
|
|
456
|
+
localStorage.removeItem("todo-items");
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// src/lib/completion/cli-todo-id-completion-provider.ts
|
|
461
|
+
var TODO_ID_SUBCOMMANDS = /* @__PURE__ */ new Set(["rm", "done", "complete", "toggle", "edit"]);
|
|
462
|
+
var CliTodoIdCompletionProvider = class {
|
|
463
|
+
constructor(todoProcessor2) {
|
|
464
|
+
this.todoProcessor = todoProcessor2;
|
|
465
|
+
this.priority = 50;
|
|
466
|
+
}
|
|
467
|
+
getCompletions(context) {
|
|
468
|
+
const { tokens, tokenIndex, token } = context;
|
|
469
|
+
if (tokenIndex !== 2 || tokens.length < 2) {
|
|
470
|
+
return [];
|
|
471
|
+
}
|
|
472
|
+
const rootCommand = tokens[0].toLowerCase();
|
|
473
|
+
if (rootCommand !== "todo") {
|
|
474
|
+
return [];
|
|
475
|
+
}
|
|
476
|
+
const subCommand = tokens[1].toLowerCase();
|
|
477
|
+
if (!TODO_ID_SUBCOMMANDS.has(subCommand)) {
|
|
478
|
+
return [];
|
|
479
|
+
}
|
|
480
|
+
const todos = this.todoProcessor.getTodos();
|
|
481
|
+
const lowerPrefix = token.toLowerCase();
|
|
482
|
+
const filtered = subCommand === "done" || subCommand === "complete" ? todos.filter((t) => !t.completed) : todos;
|
|
483
|
+
return filtered.map((t) => String(t.id)).filter((id) => id.startsWith(lowerPrefix)).sort((a, b) => Number(a) - Number(b));
|
|
484
|
+
}
|
|
485
|
+
};
|
|
486
|
+
var todoProcessor = new CliTodoCommandProcessor();
|
|
487
|
+
var todoModule = {
|
|
488
|
+
apiVersion: API_VERSION,
|
|
489
|
+
name: "@qodalis/cli-todo",
|
|
490
|
+
processors: [todoProcessor],
|
|
491
|
+
services: [
|
|
492
|
+
{
|
|
493
|
+
provide: cliCore.ICliCompletionProvider_TOKEN,
|
|
494
|
+
useValue: new CliTodoIdCompletionProvider(todoProcessor),
|
|
495
|
+
multi: true
|
|
496
|
+
}
|
|
497
|
+
],
|
|
498
|
+
translations: {
|
|
499
|
+
es: { "cli.todo.description": "Gestionar tus tareas desde la terminal" },
|
|
500
|
+
fr: { "cli.todo.description": "G\xE9rer vos t\xE2ches depuis le terminal" },
|
|
501
|
+
de: { "cli.todo.description": "Aufgaben \xFCber das Terminal verwalten" },
|
|
502
|
+
pt: { "cli.todo.description": "Gerenciar suas tarefas pelo terminal" },
|
|
503
|
+
it: { "cli.todo.description": "Gestire le attivit\xE0 dal terminale" },
|
|
504
|
+
ja: { "cli.todo.description": "\u30BF\u30FC\u30DF\u30CA\u30EB\u304B\u3089\u30BF\u30B9\u30AF\u3092\u7BA1\u7406" },
|
|
505
|
+
ko: { "cli.todo.description": "\uD130\uBBF8\uB110\uC5D0\uC11C \uD560 \uC77C \uAD00\uB9AC" },
|
|
506
|
+
zh: { "cli.todo.description": "\u4ECE\u7EC8\u7AEF\u7BA1\u7406\u4F60\u7684\u4EFB\u52A1" },
|
|
507
|
+
ru: { "cli.todo.description": "\u0423\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0437\u0430\u0434\u0430\u0447\u0430\u043C\u0438 \u0438\u0437 \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0430" },
|
|
508
|
+
ro: { "cli.todo.description": "Gestioneaz\u0103 sarcinile tale din terminal" }
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
exports.CliTodoCommandProcessor = CliTodoCommandProcessor;
|
|
513
|
+
exports.CliTodoIdCompletionProvider = CliTodoIdCompletionProvider;
|
|
514
|
+
exports.formatRelativeDate = formatRelativeDate;
|
|
515
|
+
exports.isOverdue = isOverdue;
|
|
516
|
+
exports.lineThroughText = lineThroughText;
|
|
517
|
+
exports.migrateTodoItem = migrateTodoItem;
|
|
518
|
+
exports.parseDueDate = parseDueDate;
|
|
519
|
+
exports.todoModule = todoModule;
|
|
520
|
+
//# sourceMappingURL=public-api.js.map
|
|
521
|
+
//# sourceMappingURL=public-api.js.map
|