@rushinglabs/taskfile 0.2.2 → 0.2.3
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/dist/index.js +0 -1
- package/dist/store.js +16 -0
- package/dist/tui/App.js +47 -4
- package/dist/tui/TodosPage.js +1 -1
- package/dist/tui/index.js +3 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -6,7 +6,6 @@ process.on("warning", (warning) => {
|
|
|
6
6
|
}
|
|
7
7
|
console.warn(warning);
|
|
8
8
|
});
|
|
9
|
-
import "./production-env.js"; // must stay first: sets NODE_ENV before react is loaded
|
|
10
9
|
import { readFileSync } from "node:fs";
|
|
11
10
|
import { dirname, join } from "node:path";
|
|
12
11
|
import { fileURLToPath } from "node:url";
|
package/dist/store.js
CHANGED
|
@@ -156,6 +156,22 @@ export function renameList(id, name) {
|
|
|
156
156
|
getDb().prepare("UPDATE lists SET name = ? WHERE id = ?").run(trimmed, id);
|
|
157
157
|
return { status: "ok", list: { ...rowToList(row), name: trimmed } };
|
|
158
158
|
}
|
|
159
|
+
// Deletes a list and every task in it. The last remaining list can't be deleted: the app always
|
|
160
|
+
// needs at least one list to land on.
|
|
161
|
+
export function deleteList(id) {
|
|
162
|
+
const row = getDb().prepare("SELECT id, name, createdAt FROM lists WHERE id = ?").get(id);
|
|
163
|
+
if (!row) {
|
|
164
|
+
return { status: "not-found" };
|
|
165
|
+
}
|
|
166
|
+
if (loadLists().length <= 1) {
|
|
167
|
+
return { status: "blocked", reason: "Can't delete the only list." };
|
|
168
|
+
}
|
|
169
|
+
// Sub-items reference their parent task, so they go first to keep the foreign key satisfied.
|
|
170
|
+
getDb().prepare("DELETE FROM tasks WHERE listId = ? AND parentId IS NOT NULL").run(id);
|
|
171
|
+
getDb().prepare("DELETE FROM tasks WHERE listId = ?").run(id);
|
|
172
|
+
getDb().prepare("DELETE FROM lists WHERE id = ?").run(id);
|
|
173
|
+
return { status: "ok", list: rowToList(row) };
|
|
174
|
+
}
|
|
159
175
|
export function getDefaultListId() {
|
|
160
176
|
return loadLists()[0].id;
|
|
161
177
|
}
|
package/dist/tui/App.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useState } from "react";
|
|
3
|
-
import { useApp, useInput, useWindowSize } from "ink";
|
|
3
|
+
import { Text, useApp, useInput, useWindowSize } from "ink";
|
|
4
4
|
import { Frame } from "./Frame.js";
|
|
5
5
|
import { TodosPage } from "./TodosPage.js";
|
|
6
6
|
import { SettingsPage } from "./SettingsPage.js";
|
|
@@ -9,9 +9,10 @@ import { SnapshotsModal } from "./SnapshotsModal.js";
|
|
|
9
9
|
import { SnapshotDetailView } from "./SnapshotDetailView.js";
|
|
10
10
|
import { fitFrameToWindow } from "./frameSize.js";
|
|
11
11
|
import { loadSettings, saveSettings } from "../settings.js";
|
|
12
|
-
import { computeProgress, createList, loadLists, loadTasks, renameList } from "../store.js";
|
|
12
|
+
import { computeProgress, createList, deleteList, loadLists, loadTasks, renameList } from "../store.js";
|
|
13
13
|
import { deleteSnapshot, listSnapshots, loadSnapshotDetail, } from "../snapshots.js";
|
|
14
|
-
const NAV_HINT = "↑/↓ cycle pages · ←/→ switch focus · r rename list · Tab switch · Ctrl+N new list · h snapshots · q quit";
|
|
14
|
+
const NAV_HINT = "↑/↓ cycle pages · ←/→ switch focus · r rename list · D delete list · Tab switch · Ctrl+N new list · h snapshots · q quit";
|
|
15
|
+
const CONFIRM_DELETE_LIST_HINT = "y/enter confirm delete · n/esc cancel";
|
|
15
16
|
const SNAPSHOT_MODAL_HINT = "↑/k ↓/j select · enter/space view · d delete · esc close";
|
|
16
17
|
const SNAPSHOT_CONFIRM_DELETE_HINT = "y confirm delete · n/esc cancel";
|
|
17
18
|
const VIEW_SNAPSHOT_HINT = "esc/h back to live view";
|
|
@@ -49,6 +50,7 @@ export function App() {
|
|
|
49
50
|
const [snapshotModalMode, setSnapshotModalMode] = useState("list");
|
|
50
51
|
const [pendingDeleteSnapshotId, setPendingDeleteSnapshotId] = useState(undefined);
|
|
51
52
|
const [viewingSnapshot, setViewingSnapshot] = useState(undefined);
|
|
53
|
+
const [pendingDeleteListId, setPendingDeleteListId] = useState(undefined);
|
|
52
54
|
useEffect(() => {
|
|
53
55
|
if (page.type === "list") {
|
|
54
56
|
setSettings(saveSettings({ lastActiveListId: page.listId }));
|
|
@@ -58,6 +60,22 @@ export function App() {
|
|
|
58
60
|
setLists(loadLists());
|
|
59
61
|
}
|
|
60
62
|
const renamingListId = mode === "nav-rename-list" && page.type === "list" ? page.listId : undefined;
|
|
63
|
+
const pendingDeleteList = mode === "confirm-delete-list" ? lists.find((list) => list.id === pendingDeleteListId) : undefined;
|
|
64
|
+
const pendingDeleteTaskCount = pendingDeleteList ? loadTasks(pendingDeleteList.id).length : 0;
|
|
65
|
+
function confirmDeleteList(listId) {
|
|
66
|
+
const index = lists.findIndex((list) => list.id === listId);
|
|
67
|
+
const result = deleteList(listId);
|
|
68
|
+
if (result.status === "blocked") {
|
|
69
|
+
setMessage(result.reason);
|
|
70
|
+
}
|
|
71
|
+
else if (result.status === "ok") {
|
|
72
|
+
const nextLists = loadLists();
|
|
73
|
+
setLists(nextLists);
|
|
74
|
+
// Land on the list that took the deleted one's place (or the new last list).
|
|
75
|
+
const neighbor = nextLists[Math.min(Math.max(index, 0), nextLists.length - 1)];
|
|
76
|
+
setPage({ type: "list", listId: neighbor.id });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
61
79
|
function cyclePage(delta) {
|
|
62
80
|
const pages = buildPages(lists);
|
|
63
81
|
const currentIndex = pages.findIndex((candidate) => pagesEqual(candidate, page));
|
|
@@ -132,6 +150,20 @@ export function App() {
|
|
|
132
150
|
}
|
|
133
151
|
return;
|
|
134
152
|
}
|
|
153
|
+
if (mode === "confirm-delete-list") {
|
|
154
|
+
if (input === "y" || key.return) {
|
|
155
|
+
if (pendingDeleteListId !== undefined) {
|
|
156
|
+
confirmDeleteList(pendingDeleteListId);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
else if (!(input === "n" || key.escape)) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
setPendingDeleteListId(undefined);
|
|
163
|
+
setMode("normal");
|
|
164
|
+
setHint(NAV_HINT);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
135
167
|
if (mode === "new-list" || mode === "nav-rename-list") {
|
|
136
168
|
if (key.escape) {
|
|
137
169
|
setMode("normal");
|
|
@@ -151,6 +183,17 @@ export function App() {
|
|
|
151
183
|
setHint(SNAPSHOT_MODAL_HINT);
|
|
152
184
|
return;
|
|
153
185
|
}
|
|
186
|
+
if (input === "D" && page.type === "list") {
|
|
187
|
+
setMessage(undefined);
|
|
188
|
+
if (lists.length <= 1) {
|
|
189
|
+
setMessage("Can't delete the only list.");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
setPendingDeleteListId(page.listId);
|
|
193
|
+
setMode("confirm-delete-list");
|
|
194
|
+
setHint(CONFIRM_DELETE_LIST_HINT);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
154
197
|
if (key.ctrl && input === "n") {
|
|
155
198
|
setMessage(undefined);
|
|
156
199
|
setHint("enter save · esc cancel");
|
|
@@ -198,7 +241,7 @@ export function App() {
|
|
|
198
241
|
setLists(loadLists());
|
|
199
242
|
setPage({ type: "list", listId: list.id });
|
|
200
243
|
setMode("normal");
|
|
201
|
-
} })), renamingListId !== undefined && (_jsx(AddTaskInput, { label: "Rename list: ", initialValue: lists.find((l) => l.id === renamingListId)?.name ?? "", onSubmit: (text) => {
|
|
244
|
+
} })), pendingDeleteList && (_jsxs(Text, { children: ["Delete list \"", pendingDeleteList.name, "\" and its ", pendingDeleteTaskCount, " task(s)? (y/n)"] })), renamingListId !== undefined && (_jsx(AddTaskInput, { label: "Rename list: ", initialValue: lists.find((l) => l.id === renamingListId)?.name ?? "", onSubmit: (text) => {
|
|
202
245
|
const result = renameList(renamingListId, text);
|
|
203
246
|
if (result.status === "blocked") {
|
|
204
247
|
setMessage(result.reason);
|
package/dist/tui/TodosPage.js
CHANGED
|
@@ -5,7 +5,7 @@ import { addTask, computeProgress, hasSubItems, loadTasks, removeTask, renameLis
|
|
|
5
5
|
import { TaskList } from "./TaskList.js";
|
|
6
6
|
import { AddTaskInput } from "./AddTaskInput.js";
|
|
7
7
|
import { EditTaskModal } from "./EditTaskModal.js";
|
|
8
|
-
const LIST_HINT = "↑/k ↓/j move · space/enter toggle · a add · s add sub-item · e edit · d delete · r rename list · ←/→ nav focus · Tab switch · Ctrl+N new list · q quit";
|
|
8
|
+
const LIST_HINT = "↑/k ↓/j move · space/enter toggle · a add · s add sub-item · e edit · d delete · r rename list · D delete list · ←/→ nav focus · Tab switch · Ctrl+N new list · q quit";
|
|
9
9
|
const EDIT_HINT = "tab cycle · space toggle priority · enter save · esc cancel";
|
|
10
10
|
const RENAME_LIST_HINT = "enter save · esc cancel";
|
|
11
11
|
function resolveGroupParentId(tasks, index) {
|
package/dist/tui/index.js
CHANGED
|
@@ -5,6 +5,8 @@ import { loadSettings } from "../settings.js";
|
|
|
5
5
|
import { runStartupSnapshotMaintenance } from "../snapshots.js";
|
|
6
6
|
export async function runTui() {
|
|
7
7
|
runStartupSnapshotMaintenance(loadSettings());
|
|
8
|
-
|
|
8
|
+
// Use the terminal's alternate screen (like vim/htop) so earlier shell output
|
|
9
|
+
// isn't visible around the app, and is restored when the TUI exits.
|
|
10
|
+
const { waitUntilExit } = render(_jsx(App, {}), { alternateScreen: true });
|
|
9
11
|
await waitUntilExit();
|
|
10
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rushinglabs/taskfile",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "A simple todo list CLI, runnable via npx",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"commander": "^15.0.0",
|
|
35
|
-
"ink": "^
|
|
35
|
+
"ink": "^8.0.0",
|
|
36
36
|
"ink-text-input": "^6.0.0",
|
|
37
|
-
"react": "^19.
|
|
37
|
+
"react": "^19.3.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^26.2.0",
|
|
41
|
-
"@types/react": "^19.
|
|
41
|
+
"@types/react": "^19.3.0",
|
|
42
42
|
"ink-testing-library": "^4.0.0",
|
|
43
43
|
"tsx": "^4.23.12",
|
|
44
44
|
"typescript": "^7.0.2",
|