tatr-ql 0.2.0 → 0.3.1
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/README.md +27 -2
- package/bin/index.js +1 -1
- package/package.json +4 -2
- package/src/cli.js +36 -45
- package/src/components/Help.js +150 -0
- package/src/components/ShowTask.js +11 -0
- package/src/components/StartPager.js +54 -0
- package/src/components/index.js +3 -0
- package/tasks/20260912-153638/TASK.md +6 -0
- package/tasks/20260912-165231/TASK.md +6 -0
package/README.md
CHANGED
|
@@ -1,2 +1,27 @@
|
|
|
1
|
-
# tatr
|
|
2
|
-
Task Tracker (
|
|
1
|
+
# tatr-ql
|
|
2
|
+
tatr-ql is a Node.js reimplementation of [tsoding/tatr](https://github.com/tsoding/tatr), a Task Tracker Query Language created by [Tsoding](https://github.com/tsoding).
|
|
3
|
+
|
|
4
|
+
The original project is implemented in C/C++. This project brings the same concept and functionality to the JavaScript/Node.js ecosystem.
|
|
5
|
+
|
|
6
|
+
> [!NOTE]
|
|
7
|
+
> This is my first piece of AI slop. The tests were AI-generated. Honestly, the main things I implemented myself were the `Zod` schema validation and the integration of [TermDom](https://termdom.org/) with [ZikoJS](https://github.com/zikojs/ziko/) for the UI.
|
|
8
|
+
I could build the rest from scratch, but I don't have the time right now. I mainly needed the API to monitor my 50+ ZikoJS packages on npm.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```console
|
|
13
|
+
npm i tatr-ql -g
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Task.md
|
|
17
|
+
|
|
18
|
+
```md
|
|
19
|
+
# <title>
|
|
20
|
+
|
|
21
|
+
- STATUS: (OPEN|CLOSED)
|
|
22
|
+
- PRIORITY: <number>
|
|
23
|
+
- TAGS: <comma-and-whitespace-separated-list-of-tags>
|
|
24
|
+
[other properties]
|
|
25
|
+
|
|
26
|
+
[description]
|
|
27
|
+
```
|
package/bin/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tatr-ql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Task Tracker (Nodejs Version)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"task",
|
|
@@ -27,9 +27,11 @@
|
|
|
27
27
|
"test:run": "vitest run"
|
|
28
28
|
},
|
|
29
29
|
"bin": {
|
|
30
|
-
"tatr": "./bin/
|
|
30
|
+
"tatr": "./bin/index.js"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
+
"@b9g/termdom": "^0.1.6",
|
|
34
|
+
"vanjs-core": "^1.6.1",
|
|
33
35
|
"ziko": "^2.0.0-beta.10",
|
|
34
36
|
"zod": "^4.6.2"
|
|
35
37
|
},
|
package/src/cli.js
CHANGED
|
@@ -14,45 +14,21 @@ import { parseTags } from "./task.js";
|
|
|
14
14
|
import { matchesQuery } from "./query.js";
|
|
15
15
|
import packageJson from "../package.json" with { type: "json" };
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
import {TermDOM} from "@b9g/termdom";
|
|
18
|
+
const term = new TermDOM();
|
|
19
|
+
term.attach();
|
|
20
|
+
const {document, window} = term;
|
|
21
|
+
globalThis.document = document
|
|
22
|
+
globalThis.window = window
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
StartPager,
|
|
27
|
+
Help,
|
|
28
|
+
ShowTask
|
|
29
|
+
} from "./components/index.js";
|
|
18
30
|
|
|
19
|
-
const
|
|
20
|
-
tatr ${VERSION} — filesystem task tracker
|
|
21
|
-
|
|
22
|
-
Usage:
|
|
23
|
-
tatr <command> [options]
|
|
24
|
-
|
|
25
|
-
Commands:
|
|
26
|
-
new <title> Create a task
|
|
27
|
-
ls [query] List tasks, optionally filtered by TQL
|
|
28
|
-
show <id> Show a task
|
|
29
|
-
edit <id> Edit TASK.md in $EDITOR
|
|
30
|
-
close <id> Mark a task CLOSED
|
|
31
|
-
reopen <id> Mark a task OPEN
|
|
32
|
-
rm <id> Delete a task
|
|
33
|
-
tags Show tag descriptions
|
|
34
|
-
init Create tasks/
|
|
35
|
-
help Show this help
|
|
36
|
-
version Show the version
|
|
37
|
-
|
|
38
|
-
new options:
|
|
39
|
-
--priority <n> Task priority (default: 0)
|
|
40
|
-
--tags <a,b,c> Comma-separated tags
|
|
41
|
-
--suffix <name> HUID suffix
|
|
42
|
-
--description <x> Initial description
|
|
43
|
-
|
|
44
|
-
ls options:
|
|
45
|
-
--all Include CLOSED tasks (default is all; kept for compatibility)
|
|
46
|
-
--open Only OPEN tasks
|
|
47
|
-
--closed Only CLOSED tasks
|
|
48
|
-
|
|
49
|
-
TQL examples:
|
|
50
|
-
tatr ls :bug
|
|
51
|
-
tatr ls ':bug and not :ui'
|
|
52
|
-
tatr ls 'not tagged'
|
|
53
|
-
tatr ls ':bug and priority lt 50'
|
|
54
|
-
tatr ls '[:bug or :enhancement] and priority ge 10'
|
|
55
|
-
`.trim());
|
|
31
|
+
const {version : VERSION} = packageJson;
|
|
56
32
|
|
|
57
33
|
const parseFlags = (args) => {
|
|
58
34
|
const positionals = [];
|
|
@@ -131,11 +107,25 @@ const commandShow = async (id) => {
|
|
|
131
107
|
|
|
132
108
|
const task = await readTask(id);
|
|
133
109
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
console.log(`
|
|
110
|
+
const {title, status, priority, tags} = task
|
|
111
|
+
|
|
112
|
+
ShowTask({id, title, status, priority, tags}).mount(document.body)
|
|
113
|
+
|
|
114
|
+
// console.log(`ID: ${task.id}`);
|
|
115
|
+
// console.log(`TITLE: ${task.title}`);
|
|
116
|
+
// console.log(`STATUS: ${task.status}`);
|
|
117
|
+
// console.log(`PRIORITY: ${task.priority}`);
|
|
118
|
+
// console.log(`TAGS: ${task.tags.join(", ")}`);
|
|
119
|
+
|
|
120
|
+
// table(
|
|
121
|
+
// tbody(
|
|
122
|
+
// tr(td("ID"), td(task.id)),
|
|
123
|
+
// tr(td("TITLE"), td(task.title)),
|
|
124
|
+
// tr(td("STATUS"), td(task.status)),
|
|
125
|
+
// tr(td("PRIORITY"), td(task.priority)),
|
|
126
|
+
// tr(td("TAGS"), td(task.tags.join(", ")))
|
|
127
|
+
// )
|
|
128
|
+
// ).mount(document.body)
|
|
139
129
|
|
|
140
130
|
if (task.description) console.log(`\n${task.description}`);
|
|
141
131
|
};
|
|
@@ -193,10 +183,11 @@ export const main = async (argv = process.argv.slice(2)) => {
|
|
|
193
183
|
const [command = "help", ...args] = argv;
|
|
194
184
|
|
|
195
185
|
switch (command) {
|
|
196
|
-
case "help":
|
|
186
|
+
case "help":
|
|
197
187
|
case "-h":
|
|
198
188
|
case "--help":
|
|
199
|
-
|
|
189
|
+
Help({version : VERSION}).mount(document.body);
|
|
190
|
+
await StartPager()
|
|
200
191
|
break;
|
|
201
192
|
case "version":
|
|
202
193
|
case "-v":
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { tags } from "ziko/dom";
|
|
2
|
+
const {
|
|
3
|
+
div, h1, span, h2, section, pre, table, tbody, tr, td
|
|
4
|
+
} = tags
|
|
5
|
+
export const Help = ({ version = '0.1.0'} = {}) =>
|
|
6
|
+
div(
|
|
7
|
+
pre(`
|
|
8
|
+
-
|
|
9
|
+
`),
|
|
10
|
+
h1(
|
|
11
|
+
`tatr ${version}`,
|
|
12
|
+
span(" — filesystem task tracker")
|
|
13
|
+
).style({
|
|
14
|
+
color : 'blue'
|
|
15
|
+
}),
|
|
16
|
+
|
|
17
|
+
section(
|
|
18
|
+
h2("Usage"),
|
|
19
|
+
|
|
20
|
+
pre(
|
|
21
|
+
"tatr <command> [options]"
|
|
22
|
+
)
|
|
23
|
+
),
|
|
24
|
+
|
|
25
|
+
section(
|
|
26
|
+
h2("Commands"),
|
|
27
|
+
|
|
28
|
+
table(
|
|
29
|
+
tbody(
|
|
30
|
+
tr(
|
|
31
|
+
td("new <title>"),
|
|
32
|
+
td("Create a task")
|
|
33
|
+
),
|
|
34
|
+
|
|
35
|
+
tr(
|
|
36
|
+
td("ls [query]"),
|
|
37
|
+
td("List tasks, optionally filtered by TQL")
|
|
38
|
+
),
|
|
39
|
+
|
|
40
|
+
tr(
|
|
41
|
+
td("show <id>"),
|
|
42
|
+
td("Show a task")
|
|
43
|
+
),
|
|
44
|
+
|
|
45
|
+
tr(
|
|
46
|
+
td("edit <id>"),
|
|
47
|
+
td("Edit TASK.md in $EDITOR")
|
|
48
|
+
),
|
|
49
|
+
|
|
50
|
+
tr(
|
|
51
|
+
td("close <id>"),
|
|
52
|
+
td("Mark a task CLOSED")
|
|
53
|
+
),
|
|
54
|
+
|
|
55
|
+
tr(
|
|
56
|
+
td("reopen <id>"),
|
|
57
|
+
td("Mark a task OPEN")
|
|
58
|
+
),
|
|
59
|
+
|
|
60
|
+
tr(
|
|
61
|
+
td("rm <id>"),
|
|
62
|
+
td("Delete a task")
|
|
63
|
+
),
|
|
64
|
+
|
|
65
|
+
tr(
|
|
66
|
+
td("tags"),
|
|
67
|
+
td("Show tag descriptions")
|
|
68
|
+
),
|
|
69
|
+
|
|
70
|
+
tr(
|
|
71
|
+
td("init"),
|
|
72
|
+
td("Create tasks/")
|
|
73
|
+
),
|
|
74
|
+
|
|
75
|
+
tr(
|
|
76
|
+
td("help"),
|
|
77
|
+
td("Show this help")
|
|
78
|
+
),
|
|
79
|
+
|
|
80
|
+
tr(
|
|
81
|
+
td("version"),
|
|
82
|
+
td("Show the version")
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
),
|
|
87
|
+
|
|
88
|
+
section(
|
|
89
|
+
h2("new options"),
|
|
90
|
+
|
|
91
|
+
table(
|
|
92
|
+
tbody(
|
|
93
|
+
tr(
|
|
94
|
+
td("--priority <n>"),
|
|
95
|
+
td("Task priority (default: 0)")
|
|
96
|
+
),
|
|
97
|
+
|
|
98
|
+
tr(
|
|
99
|
+
td("--tags <a,b,c>"),
|
|
100
|
+
td("Comma-separated tags")
|
|
101
|
+
),
|
|
102
|
+
|
|
103
|
+
tr(
|
|
104
|
+
td("--suffix <name>"),
|
|
105
|
+
td("HUID suffix")
|
|
106
|
+
),
|
|
107
|
+
|
|
108
|
+
tr(
|
|
109
|
+
td("--description <x>"),
|
|
110
|
+
td("Initial description")
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
),
|
|
115
|
+
|
|
116
|
+
section(
|
|
117
|
+
h2("ls options"),
|
|
118
|
+
|
|
119
|
+
table(
|
|
120
|
+
tbody(
|
|
121
|
+
tr(
|
|
122
|
+
td("--all"),
|
|
123
|
+
td("Include CLOSED tasks (default is all; kept for compatibility)")
|
|
124
|
+
),
|
|
125
|
+
|
|
126
|
+
tr(
|
|
127
|
+
td("--open"),
|
|
128
|
+
td("Only OPEN tasks")
|
|
129
|
+
),
|
|
130
|
+
|
|
131
|
+
tr(
|
|
132
|
+
td("--closed"),
|
|
133
|
+
td("Only CLOSED tasks")
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
),
|
|
138
|
+
|
|
139
|
+
section(
|
|
140
|
+
h2("TQL examples"),
|
|
141
|
+
|
|
142
|
+
pre(
|
|
143
|
+
`tatr ls :bug
|
|
144
|
+
tatr ls ':bug and not :ui'
|
|
145
|
+
tatr ls 'not tagged'
|
|
146
|
+
tatr ls ':bug and priority lt 50'
|
|
147
|
+
tatr ls '[:bug or :enhancement] and priority ge 10'`
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { tags } from "ziko/dom"
|
|
2
|
+
const { table, tbody, tr, td} = tags
|
|
3
|
+
export const ShowTask = ({id, title, status, priority, tags} = {}) => table(
|
|
4
|
+
tbody(
|
|
5
|
+
tr(td("ID"), td(id)),
|
|
6
|
+
tr(td("TITLE"), td(title)),
|
|
7
|
+
tr(td("STATUS"), td(status)),
|
|
8
|
+
tr(td("PRIORITY"), td(priority)),
|
|
9
|
+
tr(td("TAGS"), td(tags.join(", ")))
|
|
10
|
+
)
|
|
11
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const StartPager = async () => {
|
|
2
|
+
// Let TermDOM finish its initial layout.
|
|
3
|
+
await new Promise((resolve) =>
|
|
4
|
+
window.requestAnimationFrame(resolve)
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
await new Promise((resolve) =>
|
|
8
|
+
window.requestAnimationFrame(resolve)
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
// Reset the camera after layout has settled.
|
|
12
|
+
window.scrollTo(0, 0);
|
|
13
|
+
|
|
14
|
+
const page = () => Math.max(1, window.innerHeight - 1);
|
|
15
|
+
|
|
16
|
+
const height = () =>
|
|
17
|
+
Math.max(
|
|
18
|
+
0,
|
|
19
|
+
document.body.scrollHeight - window.innerHeight
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const bindings = {
|
|
23
|
+
" ": () => window.scrollBy(0, page()),
|
|
24
|
+
f: () => window.scrollBy(0, page()),
|
|
25
|
+
PageDown: () => window.scrollBy(0, page()),
|
|
26
|
+
|
|
27
|
+
b: () => window.scrollBy(0, -page()),
|
|
28
|
+
PageUp: () => window.scrollBy(0, -page()),
|
|
29
|
+
|
|
30
|
+
j: () => window.scrollBy(0, 1),
|
|
31
|
+
ArrowDown: () => window.scrollBy(0, 1),
|
|
32
|
+
|
|
33
|
+
k: () => window.scrollBy(0, -1),
|
|
34
|
+
ArrowUp: () => window.scrollBy(0, -1),
|
|
35
|
+
|
|
36
|
+
g: () => window.scrollTo(0, 0),
|
|
37
|
+
|
|
38
|
+
G: () =>
|
|
39
|
+
window.scrollTo(
|
|
40
|
+
0,
|
|
41
|
+
height()
|
|
42
|
+
),
|
|
43
|
+
|
|
44
|
+
q: () =>
|
|
45
|
+
term.window.close()
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
document.addEventListener(
|
|
49
|
+
"keydown",
|
|
50
|
+
(event) => {
|
|
51
|
+
bindings[event.key]?.();
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
};
|