@tomkapa/tayto 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/dist/index.js +58 -16
- package/dist/index.js.map +1 -1
- package/dist/{tui-FTXYP3HM.js → tui-5JJH67YY.js} +321 -246
- package/dist/tui-5JJH67YY.js.map +1 -0
- package/package.json +1 -1
- package/dist/tui-FTXYP3HM.js.map +0 -1
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
import { render } from "ink";
|
|
14
14
|
|
|
15
15
|
// src/tui/components/App.tsx
|
|
16
|
-
import { useReducer, useEffect, useCallback as useCallback2, useMemo } from "react";
|
|
17
|
-
import { Box as Box16, Text as Text16, useInput as useInput6, useApp } from "ink";
|
|
16
|
+
import { useReducer, useEffect as useEffect2, useCallback as useCallback2, useMemo as useMemo2, useState as useState6 } from "react";
|
|
17
|
+
import { Box as Box16, Text as Text16, useInput as useInput6, useApp, useStdout as useStdout4 } from "ink";
|
|
18
18
|
|
|
19
19
|
// src/tui/types.ts
|
|
20
20
|
var ViewType = {
|
|
@@ -29,6 +29,140 @@ var ViewType = {
|
|
|
29
29
|
Help: "help"
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
+
// src/tui/theme.ts
|
|
33
|
+
var theme = {
|
|
34
|
+
// Body
|
|
35
|
+
fg: "#1E90FF",
|
|
36
|
+
// dodgerblue
|
|
37
|
+
bg: "black",
|
|
38
|
+
logo: "#FFA500",
|
|
39
|
+
// orange
|
|
40
|
+
// Table
|
|
41
|
+
table: {
|
|
42
|
+
fg: "#00FFFF",
|
|
43
|
+
// aqua
|
|
44
|
+
cursorFg: "black",
|
|
45
|
+
cursorBg: "#00FFFF",
|
|
46
|
+
// aqua
|
|
47
|
+
headerFg: "white",
|
|
48
|
+
markColor: "#98FB98",
|
|
49
|
+
// palegreen
|
|
50
|
+
depHighlightBg: "#1A5276",
|
|
51
|
+
// dark steel blue – non-terminal dep rows
|
|
52
|
+
blockedCursorBg: "#C0392B"
|
|
53
|
+
// strong red – selected row blocked by non-terminal dep
|
|
54
|
+
},
|
|
55
|
+
// Status colors (row-level)
|
|
56
|
+
status: {
|
|
57
|
+
new: "#87CEFA",
|
|
58
|
+
// lightskyblue
|
|
59
|
+
modified: "#ADFF2F",
|
|
60
|
+
// greenyellow
|
|
61
|
+
added: "#1E90FF",
|
|
62
|
+
// dodgerblue
|
|
63
|
+
error: "#FF4500",
|
|
64
|
+
// orangered
|
|
65
|
+
pending: "#FF8C00",
|
|
66
|
+
// darkorange
|
|
67
|
+
highlight: "#00FFFF",
|
|
68
|
+
// aqua
|
|
69
|
+
kill: "#9370DB",
|
|
70
|
+
// mediumpurple
|
|
71
|
+
completed: "#778899"
|
|
72
|
+
// lightslategray
|
|
73
|
+
},
|
|
74
|
+
// Frame / borders
|
|
75
|
+
border: "#1E90FF",
|
|
76
|
+
// dodgerblue
|
|
77
|
+
borderFocus: "#00FFFF",
|
|
78
|
+
// aqua
|
|
79
|
+
// Title
|
|
80
|
+
title: "#00FFFF",
|
|
81
|
+
// aqua
|
|
82
|
+
titleHighlight: "#FF00FF",
|
|
83
|
+
// fuchsia
|
|
84
|
+
titleCounter: "#FFEFD5",
|
|
85
|
+
// papayawhip
|
|
86
|
+
titleFilter: "#2E8B57",
|
|
87
|
+
// seagreen
|
|
88
|
+
// Prompt
|
|
89
|
+
prompt: "#5F9EA0",
|
|
90
|
+
// cadetblue
|
|
91
|
+
promptSuggest: "#1E90FF",
|
|
92
|
+
// dodgerblue
|
|
93
|
+
// Dialog
|
|
94
|
+
dialog: {
|
|
95
|
+
fg: "#1E90FF",
|
|
96
|
+
// dodgerblue
|
|
97
|
+
buttonFg: "black",
|
|
98
|
+
buttonBg: "#1E90FF",
|
|
99
|
+
buttonFocusFg: "white",
|
|
100
|
+
buttonFocusBg: "#FF00FF",
|
|
101
|
+
// fuchsia
|
|
102
|
+
label: "#FF00FF",
|
|
103
|
+
// fuchsia
|
|
104
|
+
field: "#1E90FF"
|
|
105
|
+
},
|
|
106
|
+
// Detail/YAML
|
|
107
|
+
yaml: {
|
|
108
|
+
key: "#4682B4",
|
|
109
|
+
// steelblue
|
|
110
|
+
colon: "white",
|
|
111
|
+
value: "#FFEFD5"
|
|
112
|
+
// papayawhip
|
|
113
|
+
},
|
|
114
|
+
// Crumbs
|
|
115
|
+
crumb: {
|
|
116
|
+
fg: "black",
|
|
117
|
+
bg: "#4682B4",
|
|
118
|
+
// steelblue
|
|
119
|
+
activeBg: "#FFA500"
|
|
120
|
+
// orange
|
|
121
|
+
},
|
|
122
|
+
// Flash
|
|
123
|
+
flash: {
|
|
124
|
+
info: "#FFDEAD",
|
|
125
|
+
// navajowhite
|
|
126
|
+
warn: "#FFA500",
|
|
127
|
+
// orange
|
|
128
|
+
error: "#FF4500"
|
|
129
|
+
// orangered
|
|
130
|
+
},
|
|
131
|
+
// Menu / key hints
|
|
132
|
+
menu: {
|
|
133
|
+
key: "#1E90FF",
|
|
134
|
+
// dodgerblue
|
|
135
|
+
numKey: "#FF00FF",
|
|
136
|
+
// fuchsia
|
|
137
|
+
desc: "white"
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// src/tui/constants.ts
|
|
142
|
+
var STATUS_VALUES = Object.values(TaskStatus);
|
|
143
|
+
var TYPE_VALUES = Object.values(TaskType);
|
|
144
|
+
var PAGE_SIZE = 20;
|
|
145
|
+
var STATUS_COLOR = {
|
|
146
|
+
[TaskStatus.Backlog]: theme.status.completed,
|
|
147
|
+
[TaskStatus.Todo]: theme.status.new,
|
|
148
|
+
[TaskStatus.InProgress]: theme.status.pending,
|
|
149
|
+
[TaskStatus.Review]: theme.status.modified,
|
|
150
|
+
[TaskStatus.Done]: theme.status.added,
|
|
151
|
+
[TaskStatus.Cancelled]: theme.status.kill
|
|
152
|
+
};
|
|
153
|
+
var TYPE_COLOR = {
|
|
154
|
+
[TaskType.Epic]: theme.status.modified,
|
|
155
|
+
[TaskType.Story]: theme.status.highlight,
|
|
156
|
+
[TaskType.TechDebt]: theme.status.pending,
|
|
157
|
+
[TaskType.Bug]: theme.status.error
|
|
158
|
+
};
|
|
159
|
+
var DEP_TYPE_LABEL = {
|
|
160
|
+
[DependencyType.Blocks]: "blocks",
|
|
161
|
+
[DependencyType.RelatesTo]: "relates-to",
|
|
162
|
+
[DependencyType.Duplicates]: "duplicates",
|
|
163
|
+
[UIDependencyType.BlockedBy]: "blocked-by"
|
|
164
|
+
};
|
|
165
|
+
|
|
32
166
|
// src/tui/state.ts
|
|
33
167
|
var initialState = {
|
|
34
168
|
activeView: ViewType.TaskList,
|
|
@@ -121,6 +255,11 @@ function appReducer(state, action) {
|
|
|
121
255
|
const newIndex = action.direction === "up" ? Math.max(0, state.selectedIndex - 1) : Math.min(maxIndex, state.selectedIndex + 1);
|
|
122
256
|
return { ...state, selectedIndex: newIndex, detailScrollOffset: 0 };
|
|
123
257
|
}
|
|
258
|
+
case "PAGE_CURSOR": {
|
|
259
|
+
const maxIndex = Math.max(0, state.tasks.length - 1);
|
|
260
|
+
const newIndex = action.direction === "up" ? Math.max(0, state.selectedIndex - PAGE_SIZE) : Math.min(maxIndex, state.selectedIndex + PAGE_SIZE);
|
|
261
|
+
return { ...state, selectedIndex: newIndex, detailScrollOffset: 0 };
|
|
262
|
+
}
|
|
124
263
|
case "SET_CURSOR": {
|
|
125
264
|
const maxIndex = Math.max(0, state.tasks.length - 1);
|
|
126
265
|
return { ...state, selectedIndex: Math.max(0, Math.min(action.index, maxIndex)) };
|
|
@@ -260,139 +399,6 @@ function appReducer(state, action) {
|
|
|
260
399
|
}
|
|
261
400
|
}
|
|
262
401
|
|
|
263
|
-
// src/tui/theme.ts
|
|
264
|
-
var theme = {
|
|
265
|
-
// Body
|
|
266
|
-
fg: "#1E90FF",
|
|
267
|
-
// dodgerblue
|
|
268
|
-
bg: "black",
|
|
269
|
-
logo: "#FFA500",
|
|
270
|
-
// orange
|
|
271
|
-
// Table
|
|
272
|
-
table: {
|
|
273
|
-
fg: "#00FFFF",
|
|
274
|
-
// aqua
|
|
275
|
-
cursorFg: "black",
|
|
276
|
-
cursorBg: "#00FFFF",
|
|
277
|
-
// aqua
|
|
278
|
-
headerFg: "white",
|
|
279
|
-
markColor: "#98FB98",
|
|
280
|
-
// palegreen
|
|
281
|
-
depHighlightBg: "#1A5276",
|
|
282
|
-
// dark steel blue – non-terminal dep rows
|
|
283
|
-
blockedCursorBg: "#C0392B"
|
|
284
|
-
// strong red – selected row blocked by non-terminal dep
|
|
285
|
-
},
|
|
286
|
-
// Status colors (row-level)
|
|
287
|
-
status: {
|
|
288
|
-
new: "#87CEFA",
|
|
289
|
-
// lightskyblue
|
|
290
|
-
modified: "#ADFF2F",
|
|
291
|
-
// greenyellow
|
|
292
|
-
added: "#1E90FF",
|
|
293
|
-
// dodgerblue
|
|
294
|
-
error: "#FF4500",
|
|
295
|
-
// orangered
|
|
296
|
-
pending: "#FF8C00",
|
|
297
|
-
// darkorange
|
|
298
|
-
highlight: "#00FFFF",
|
|
299
|
-
// aqua
|
|
300
|
-
kill: "#9370DB",
|
|
301
|
-
// mediumpurple
|
|
302
|
-
completed: "#778899"
|
|
303
|
-
// lightslategray
|
|
304
|
-
},
|
|
305
|
-
// Frame / borders
|
|
306
|
-
border: "#1E90FF",
|
|
307
|
-
// dodgerblue
|
|
308
|
-
borderFocus: "#00FFFF",
|
|
309
|
-
// aqua
|
|
310
|
-
// Title
|
|
311
|
-
title: "#00FFFF",
|
|
312
|
-
// aqua
|
|
313
|
-
titleHighlight: "#FF00FF",
|
|
314
|
-
// fuchsia
|
|
315
|
-
titleCounter: "#FFEFD5",
|
|
316
|
-
// papayawhip
|
|
317
|
-
titleFilter: "#2E8B57",
|
|
318
|
-
// seagreen
|
|
319
|
-
// Prompt
|
|
320
|
-
prompt: "#5F9EA0",
|
|
321
|
-
// cadetblue
|
|
322
|
-
promptSuggest: "#1E90FF",
|
|
323
|
-
// dodgerblue
|
|
324
|
-
// Dialog
|
|
325
|
-
dialog: {
|
|
326
|
-
fg: "#1E90FF",
|
|
327
|
-
// dodgerblue
|
|
328
|
-
buttonFg: "black",
|
|
329
|
-
buttonBg: "#1E90FF",
|
|
330
|
-
buttonFocusFg: "white",
|
|
331
|
-
buttonFocusBg: "#FF00FF",
|
|
332
|
-
// fuchsia
|
|
333
|
-
label: "#FF00FF",
|
|
334
|
-
// fuchsia
|
|
335
|
-
field: "#1E90FF"
|
|
336
|
-
},
|
|
337
|
-
// Detail/YAML
|
|
338
|
-
yaml: {
|
|
339
|
-
key: "#4682B4",
|
|
340
|
-
// steelblue
|
|
341
|
-
colon: "white",
|
|
342
|
-
value: "#FFEFD5"
|
|
343
|
-
// papayawhip
|
|
344
|
-
},
|
|
345
|
-
// Crumbs
|
|
346
|
-
crumb: {
|
|
347
|
-
fg: "black",
|
|
348
|
-
bg: "#4682B4",
|
|
349
|
-
// steelblue
|
|
350
|
-
activeBg: "#FFA500"
|
|
351
|
-
// orange
|
|
352
|
-
},
|
|
353
|
-
// Flash
|
|
354
|
-
flash: {
|
|
355
|
-
info: "#FFDEAD",
|
|
356
|
-
// navajowhite
|
|
357
|
-
warn: "#FFA500",
|
|
358
|
-
// orange
|
|
359
|
-
error: "#FF4500"
|
|
360
|
-
// orangered
|
|
361
|
-
},
|
|
362
|
-
// Menu / key hints
|
|
363
|
-
menu: {
|
|
364
|
-
key: "#1E90FF",
|
|
365
|
-
// dodgerblue
|
|
366
|
-
numKey: "#FF00FF",
|
|
367
|
-
// fuchsia
|
|
368
|
-
desc: "white"
|
|
369
|
-
}
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// src/tui/constants.ts
|
|
373
|
-
var STATUS_VALUES = Object.values(TaskStatus);
|
|
374
|
-
var TYPE_VALUES = Object.values(TaskType);
|
|
375
|
-
var STATUS_COLOR = {
|
|
376
|
-
[TaskStatus.Backlog]: theme.status.completed,
|
|
377
|
-
[TaskStatus.Todo]: theme.status.new,
|
|
378
|
-
[TaskStatus.InProgress]: theme.status.pending,
|
|
379
|
-
[TaskStatus.Review]: theme.status.modified,
|
|
380
|
-
[TaskStatus.Done]: theme.status.added,
|
|
381
|
-
[TaskStatus.Cancelled]: theme.status.kill
|
|
382
|
-
};
|
|
383
|
-
var TYPE_COLOR = {
|
|
384
|
-
[TaskType.Epic]: theme.status.modified,
|
|
385
|
-
[TaskType.Story]: theme.status.highlight,
|
|
386
|
-
[TaskType.TechDebt]: theme.status.pending,
|
|
387
|
-
[TaskType.Bug]: theme.status.error
|
|
388
|
-
};
|
|
389
|
-
var DEP_TYPE_LABEL = {
|
|
390
|
-
[DependencyType.Blocks]: "blocks",
|
|
391
|
-
[DependencyType.RelatesTo]: "relates-to",
|
|
392
|
-
[DependencyType.Duplicates]: "duplicates",
|
|
393
|
-
[UIDependencyType.BlockedBy]: "blocked-by"
|
|
394
|
-
};
|
|
395
|
-
|
|
396
402
|
// src/tui/components/Header.tsx
|
|
397
403
|
import { Box, Text } from "ink";
|
|
398
404
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -428,6 +434,7 @@ function getKeyHints(view, isSearchActive, focusedPanel) {
|
|
|
428
434
|
{ key: "p", desc: "project" },
|
|
429
435
|
{ key: "f", desc: "status-f" },
|
|
430
436
|
{ key: "t", desc: "type-f" },
|
|
437
|
+
{ key: "PgDn/Up", desc: "page" },
|
|
431
438
|
{ key: "tab", desc: "panel" },
|
|
432
439
|
{ key: "?", desc: "help" },
|
|
433
440
|
{ key: "q", desc: "quit" }
|
|
@@ -517,7 +524,6 @@ function FlashMessage({ message, level }) {
|
|
|
517
524
|
// src/tui/components/TaskList.tsx
|
|
518
525
|
import { Box as Box4, Text as Text4 } from "ink";
|
|
519
526
|
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
520
|
-
var PAGE_SIZE = 20;
|
|
521
527
|
var COL = {
|
|
522
528
|
rank: 5,
|
|
523
529
|
type: 12,
|
|
@@ -588,7 +594,7 @@ function TaskList({
|
|
|
588
594
|
/* @__PURE__ */ jsx4(Text4, { color: theme.table.headerFg, bold: true, children: "STATUS".padEnd(COL.status) }),
|
|
589
595
|
/* @__PURE__ */ jsx4(Text4, { color: theme.table.headerFg, bold: true, children: "NAME" })
|
|
590
596
|
] }),
|
|
591
|
-
tasks.length === 0 ? /* @__PURE__ */ jsx4(Box4, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.fg, children: "No tasks found. Press 'c' to create one." }) }) : visibleTasks.map((task, i) => {
|
|
597
|
+
/* @__PURE__ */ jsx4(Box4, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: tasks.length === 0 ? /* @__PURE__ */ jsx4(Box4, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.fg, children: "No tasks found. Press 'c' to create one." }) }) : visibleTasks.map((task, i) => {
|
|
592
598
|
const actualIndex = viewStart + i;
|
|
593
599
|
const isSelected = actualIndex === selectedIndex;
|
|
594
600
|
const isNonTerminalBlocker = nonTerminalBlockerIds.has(task.id);
|
|
@@ -622,8 +628,7 @@ function TaskList({
|
|
|
622
628
|
/* @__PURE__ */ jsx4(Text4, { color: STATUS_COLOR[task.status] ?? rowColor, children: task.status.padEnd(COL.status) }),
|
|
623
629
|
/* @__PURE__ */ jsx4(Text4, { color: rowColor, children: task.name })
|
|
624
630
|
] }, task.id);
|
|
625
|
-
}),
|
|
626
|
-
/* @__PURE__ */ jsx4(Box4, { flexGrow: 1 }),
|
|
631
|
+
}) }),
|
|
627
632
|
tasks.length > PAGE_SIZE && /* @__PURE__ */ jsx4(Box4, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs2(Text4, { dimColor: true, children: [
|
|
628
633
|
"[",
|
|
629
634
|
viewStart + 1,
|
|
@@ -639,7 +644,9 @@ function TaskList({
|
|
|
639
644
|
}
|
|
640
645
|
|
|
641
646
|
// src/tui/components/TaskDetail.tsx
|
|
647
|
+
import { useMemo } from "react";
|
|
642
648
|
import { Box as Box6, Text as Text6, useStdout } from "ink";
|
|
649
|
+
import chalk2 from "chalk";
|
|
643
650
|
|
|
644
651
|
// src/tui/components/Markdown.tsx
|
|
645
652
|
import { Text as Text5, Box as Box5 } from "ink";
|
|
@@ -731,15 +738,10 @@ function createRenderer() {
|
|
|
731
738
|
);
|
|
732
739
|
}
|
|
733
740
|
var marked = createRenderer();
|
|
734
|
-
function
|
|
735
|
-
if (!content.trim())
|
|
736
|
-
return /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "No content" });
|
|
737
|
-
}
|
|
741
|
+
function renderMarkdown(content) {
|
|
742
|
+
if (!content.trim()) return "";
|
|
738
743
|
const rendered = marked.parse(content);
|
|
739
|
-
|
|
740
|
-
return /* @__PURE__ */ jsx5(Text5, { children: content });
|
|
741
|
-
}
|
|
742
|
-
return /* @__PURE__ */ jsx5(Text5, { children: rendered.trimEnd() });
|
|
744
|
+
return typeof rendered === "string" ? rendered.trimEnd() : content;
|
|
743
745
|
}
|
|
744
746
|
function MermaidHint({ content }) {
|
|
745
747
|
const blocks = extractMermaidBlocks(content);
|
|
@@ -768,19 +770,74 @@ function openAllMermaidDiagrams(content) {
|
|
|
768
770
|
|
|
769
771
|
// src/tui/components/TaskDetail.tsx
|
|
770
772
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
773
|
+
var PAD = " ";
|
|
774
|
+
function field(label, value) {
|
|
775
|
+
return PAD + chalk2.hex(theme.yaml.key).bold(label) + chalk2.hex(theme.yaml.colon)(": ") + chalk2.hex(theme.yaml.value)(value);
|
|
776
|
+
}
|
|
777
|
+
function sectionHeader(title) {
|
|
778
|
+
return PAD + chalk2.hex(theme.title).bold(`--- ${title} ---`);
|
|
779
|
+
}
|
|
780
|
+
function markdownLines(content) {
|
|
781
|
+
const rendered = renderMarkdown(content);
|
|
782
|
+
if (!rendered) return [];
|
|
783
|
+
return rendered.split("\n").map((line) => PAD + line);
|
|
784
|
+
}
|
|
785
|
+
function buildContentLines(task, blockers, dependents, related, duplicates) {
|
|
786
|
+
const lines = [];
|
|
787
|
+
lines.push(field("id", task.id));
|
|
788
|
+
lines.push(field("type", task.type));
|
|
789
|
+
lines.push(field("status", task.status));
|
|
790
|
+
lines.push(field("created", new Date(task.createdAt).toLocaleString()));
|
|
791
|
+
lines.push(field("updated", new Date(task.updatedAt).toLocaleString()));
|
|
792
|
+
if (task.parentId) lines.push(field("parent", task.parentId));
|
|
793
|
+
const hasDeps = blockers.length > 0 || dependents.length > 0 || related.length > 0 || duplicates.length > 0;
|
|
794
|
+
if (hasDeps) {
|
|
795
|
+
lines.push(sectionHeader("dependencies"));
|
|
796
|
+
if (blockers.length > 0) {
|
|
797
|
+
lines.push(
|
|
798
|
+
PAD + chalk2.hex(theme.status.error).bold("blocked by: ") + chalk2.hex(theme.yaml.value)(blockers.map((t) => t.id).join(", "))
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
if (dependents.length > 0) {
|
|
802
|
+
lines.push(
|
|
803
|
+
PAD + chalk2.hex(theme.status.added).bold("blocks: ") + chalk2.hex(theme.yaml.value)(dependents.map((t) => t.id).join(", "))
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
if (related.length > 0) {
|
|
807
|
+
lines.push(
|
|
808
|
+
PAD + chalk2.hex(theme.status.new).bold("relates to: ") + chalk2.hex(theme.yaml.value)(related.map((t) => t.id).join(", "))
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
if (duplicates.length > 0) {
|
|
812
|
+
lines.push(
|
|
813
|
+
PAD + chalk2.hex(theme.status.pending).bold("duplicates: ") + chalk2.hex(theme.yaml.value)(duplicates.map((t) => t.id).join(", "))
|
|
814
|
+
);
|
|
815
|
+
}
|
|
816
|
+
lines.push(PAD + chalk2.dim("press D to manage dependencies"));
|
|
817
|
+
}
|
|
818
|
+
lines.push(sectionHeader("description"));
|
|
819
|
+
if (task.description.trim()) {
|
|
820
|
+
lines.push(...markdownLines(task.description));
|
|
821
|
+
} else {
|
|
822
|
+
lines.push(PAD + chalk2.dim("No description"));
|
|
823
|
+
}
|
|
824
|
+
if (task.technicalNotes.trim()) {
|
|
825
|
+
lines.push(sectionHeader("technical notes"));
|
|
826
|
+
lines.push(...markdownLines(task.technicalNotes));
|
|
827
|
+
}
|
|
828
|
+
if (task.additionalRequirements.trim()) {
|
|
829
|
+
lines.push(sectionHeader("requirements"));
|
|
830
|
+
lines.push(...markdownLines(task.additionalRequirements));
|
|
831
|
+
}
|
|
832
|
+
return lines;
|
|
777
833
|
}
|
|
834
|
+
var CHROME_LINES = 8;
|
|
778
835
|
function TaskDetail({
|
|
779
836
|
task,
|
|
780
|
-
blockers,
|
|
781
|
-
dependents,
|
|
782
|
-
related,
|
|
783
|
-
duplicates,
|
|
837
|
+
blockers = [],
|
|
838
|
+
dependents = [],
|
|
839
|
+
related = [],
|
|
840
|
+
duplicates = [],
|
|
784
841
|
isFocused = true,
|
|
785
842
|
scrollOffset = 0
|
|
786
843
|
}) {
|
|
@@ -788,7 +845,12 @@ function TaskDetail({
|
|
|
788
845
|
const allText = `${task.description}
|
|
789
846
|
${task.technicalNotes}
|
|
790
847
|
${task.additionalRequirements}`;
|
|
791
|
-
const
|
|
848
|
+
const contentLines = useMemo(
|
|
849
|
+
() => buildContentLines(task, blockers, dependents, related, duplicates),
|
|
850
|
+
[task, blockers, dependents, related, duplicates]
|
|
851
|
+
);
|
|
852
|
+
const viewportHeight = Math.max(1, (stdout.rows > 0 ? stdout.rows : 24) - CHROME_LINES);
|
|
853
|
+
const visibleLines = contentLines.slice(scrollOffset, scrollOffset + viewportHeight);
|
|
792
854
|
return /* @__PURE__ */ jsxs4(
|
|
793
855
|
Box6,
|
|
794
856
|
{
|
|
@@ -810,60 +872,7 @@ ${task.additionalRequirements}`;
|
|
|
810
872
|
scrollOffset
|
|
811
873
|
] })
|
|
812
874
|
] }),
|
|
813
|
-
/* @__PURE__ */ jsx6(
|
|
814
|
-
/* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", paddingX: 1, paddingY: 0, children: [
|
|
815
|
-
/* @__PURE__ */ jsx6(Field, { label: "id", value: task.id }),
|
|
816
|
-
/* @__PURE__ */ jsx6(Field, { label: "type", value: task.type }),
|
|
817
|
-
/* @__PURE__ */ jsx6(Field, { label: "status", value: task.status }),
|
|
818
|
-
/* @__PURE__ */ jsx6(Field, { label: "created", value: new Date(task.createdAt).toLocaleString() }),
|
|
819
|
-
/* @__PURE__ */ jsx6(Field, { label: "updated", value: new Date(task.updatedAt).toLocaleString() }),
|
|
820
|
-
task.parentId && /* @__PURE__ */ jsx6(Field, { label: "parent", value: task.parentId })
|
|
821
|
-
] }),
|
|
822
|
-
(blockers && blockers.length > 0 || dependents && dependents.length > 0 || related && related.length > 0 || duplicates && duplicates.length > 0) && /* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", paddingX: 1, children: [
|
|
823
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.title, bold: true, children: "--- dependencies ---" }),
|
|
824
|
-
blockers && blockers.length > 0 && /* @__PURE__ */ jsxs4(Box6, { gap: 0, children: [
|
|
825
|
-
/* @__PURE__ */ jsxs4(Text6, { color: theme.status.error, bold: true, children: [
|
|
826
|
-
"blocked by:",
|
|
827
|
-
" "
|
|
828
|
-
] }),
|
|
829
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.yaml.value, children: blockers.map((t) => t.id).join(", ") })
|
|
830
|
-
] }),
|
|
831
|
-
dependents && dependents.length > 0 && /* @__PURE__ */ jsxs4(Box6, { gap: 0, children: [
|
|
832
|
-
/* @__PURE__ */ jsxs4(Text6, { color: theme.status.added, bold: true, children: [
|
|
833
|
-
"blocks:",
|
|
834
|
-
" "
|
|
835
|
-
] }),
|
|
836
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.yaml.value, children: dependents.map((t) => t.id).join(", ") })
|
|
837
|
-
] }),
|
|
838
|
-
related && related.length > 0 && /* @__PURE__ */ jsxs4(Box6, { gap: 0, children: [
|
|
839
|
-
/* @__PURE__ */ jsxs4(Text6, { color: theme.status.new, bold: true, children: [
|
|
840
|
-
"relates to:",
|
|
841
|
-
" "
|
|
842
|
-
] }),
|
|
843
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.yaml.value, children: related.map((t) => t.id).join(", ") })
|
|
844
|
-
] }),
|
|
845
|
-
duplicates && duplicates.length > 0 && /* @__PURE__ */ jsxs4(Box6, { gap: 0, children: [
|
|
846
|
-
/* @__PURE__ */ jsxs4(Text6, { color: theme.status.pending, bold: true, children: [
|
|
847
|
-
"duplicates:",
|
|
848
|
-
" "
|
|
849
|
-
] }),
|
|
850
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.yaml.value, children: duplicates.map((t) => t.id).join(", ") })
|
|
851
|
-
] }),
|
|
852
|
-
/* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "press D to manage dependencies" })
|
|
853
|
-
] }),
|
|
854
|
-
/* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", paddingX: 1, children: [
|
|
855
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.title, bold: true, children: "--- description ---" }),
|
|
856
|
-
task.description.trim() ? /* @__PURE__ */ jsx6(Markdown, { content: task.description }) : /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "No description" })
|
|
857
|
-
] }),
|
|
858
|
-
task.technicalNotes.trim() && /* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", paddingX: 1, children: [
|
|
859
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.title, bold: true, children: "--- technical notes ---" }),
|
|
860
|
-
/* @__PURE__ */ jsx6(Markdown, { content: task.technicalNotes })
|
|
861
|
-
] }),
|
|
862
|
-
task.additionalRequirements.trim() && /* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", paddingX: 1, children: [
|
|
863
|
-
/* @__PURE__ */ jsx6(Text6, { color: theme.title, bold: true, children: "--- requirements ---" }),
|
|
864
|
-
/* @__PURE__ */ jsx6(Markdown, { content: task.additionalRequirements })
|
|
865
|
-
] })
|
|
866
|
-
] }) }),
|
|
875
|
+
/* @__PURE__ */ jsx6(Text6, { children: visibleLines.join("\n") }),
|
|
867
876
|
/* @__PURE__ */ jsx6(Box6, { flexGrow: 1 }),
|
|
868
877
|
/* @__PURE__ */ jsx6(Box6, { paddingX: 1, children: /* @__PURE__ */ jsx6(MermaidHint, { content: allText }) })
|
|
869
878
|
]
|
|
@@ -1143,13 +1152,13 @@ function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
|
|
|
1143
1152
|
const [pickedDeps, setPickedDeps] = useState2(initialDeps ?? []);
|
|
1144
1153
|
const currentField = FIELDS[focusIndex];
|
|
1145
1154
|
const launchEditor = useCallback(
|
|
1146
|
-
(
|
|
1155
|
+
(field2) => {
|
|
1147
1156
|
setEditorActive(true);
|
|
1148
1157
|
setTimeout(() => {
|
|
1149
|
-
const content = values[
|
|
1150
|
-
const result = openInEditor(content,
|
|
1158
|
+
const content = values[field2.key] ?? "";
|
|
1159
|
+
const result = openInEditor(content, field2.editorFilename ?? `${field2.key}.md`);
|
|
1151
1160
|
if (result !== null) {
|
|
1152
|
-
setValues((v) => ({ ...v, [
|
|
1161
|
+
setValues((v) => ({ ...v, [field2.key]: result }));
|
|
1153
1162
|
}
|
|
1154
1163
|
setEditorActive(false);
|
|
1155
1164
|
}, 50);
|
|
@@ -1257,37 +1266,37 @@ function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
|
|
|
1257
1266
|
" ",
|
|
1258
1267
|
isEdit ? "edit" : "create"
|
|
1259
1268
|
] }) }),
|
|
1260
|
-
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS.map((
|
|
1269
|
+
/* @__PURE__ */ jsx8(Box8, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS.map((field2, i) => {
|
|
1261
1270
|
const isFocused = i === focusIndex;
|
|
1262
|
-
const value =
|
|
1271
|
+
const value = field2.key === "dependsOn" ? depSummary : values[field2.key] ?? "";
|
|
1263
1272
|
const displayValue = value;
|
|
1264
1273
|
return /* @__PURE__ */ jsxs6(Box8, { gap: 1, children: [
|
|
1265
1274
|
/* @__PURE__ */ jsxs6(Text8, { color: isFocused ? theme.dialog.label : theme.yaml.key, bold: isFocused, children: [
|
|
1266
1275
|
isFocused ? ">" : " ",
|
|
1267
1276
|
" ",
|
|
1268
|
-
|
|
1277
|
+
field2.label.padEnd(14)
|
|
1269
1278
|
] }),
|
|
1270
|
-
|
|
1279
|
+
field2.type === "inline" && /* @__PURE__ */ jsxs6(Text8, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
|
|
1271
1280
|
displayValue,
|
|
1272
1281
|
isFocused ? /* @__PURE__ */ jsx8(Text8, { color: theme.titleHighlight, children: "_" }) : ""
|
|
1273
1282
|
] }),
|
|
1274
|
-
|
|
1283
|
+
field2.type === "picker" && /* @__PURE__ */ jsxs6(Text8, { children: [
|
|
1275
1284
|
displayValue ? /* @__PURE__ */ jsx8(Text8, { color: theme.status.added, children: displayValue.length > 60 ? displayValue.slice(0, 60) + "..." : displayValue }) : /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: isFocused ? "press enter to select" : "none" }),
|
|
1276
1285
|
isFocused && /* @__PURE__ */ jsx8(Text8, { color: theme.menu.key, children: " [enter: open picker]" })
|
|
1277
1286
|
] }),
|
|
1278
|
-
|
|
1287
|
+
field2.type === "editor" && /* @__PURE__ */ jsxs6(Text8, { children: [
|
|
1279
1288
|
displayValue ? /* @__PURE__ */ jsxs6(Text8, { color: theme.status.added, children: [
|
|
1280
1289
|
displayValue.split("\n")[0]?.slice(0, 50),
|
|
1281
1290
|
displayValue.length > 50 || displayValue.includes("\n") ? "..." : ""
|
|
1282
1291
|
] }) : /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: isFocused ? "press enter" : "empty" }),
|
|
1283
1292
|
isFocused && /* @__PURE__ */ jsx8(Text8, { color: theme.menu.key, children: " [enter: $EDITOR]" })
|
|
1284
1293
|
] }),
|
|
1285
|
-
|
|
1294
|
+
field2.type === "select" && /* @__PURE__ */ jsxs6(Text8, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
|
|
1286
1295
|
isFocused ? "< " : " ",
|
|
1287
1296
|
displayValue,
|
|
1288
1297
|
isFocused ? " >" : ""
|
|
1289
1298
|
] })
|
|
1290
|
-
] },
|
|
1299
|
+
] }, field2.key);
|
|
1291
1300
|
}) }),
|
|
1292
1301
|
/* @__PURE__ */ jsx8(Box8, { flexGrow: 1 }),
|
|
1293
1302
|
/* @__PURE__ */ jsx8(Box8, { paddingX: 1, children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "tab: next | shift+tab: prev | ctrl+s: save | esc: cancel" }) }),
|
|
@@ -1455,26 +1464,26 @@ function ProjectForm({ onSave, onCancel }) {
|
|
|
1455
1464
|
" ",
|
|
1456
1465
|
"new project"
|
|
1457
1466
|
] }) }),
|
|
1458
|
-
/* @__PURE__ */ jsx10(Box10, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS2.map((
|
|
1467
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS2.map((field2, i) => {
|
|
1459
1468
|
const isFocused = i === focusIndex;
|
|
1460
|
-
const value = values[
|
|
1469
|
+
const value = values[field2.key] ?? "";
|
|
1461
1470
|
return /* @__PURE__ */ jsxs8(Box10, { gap: 1, children: [
|
|
1462
1471
|
/* @__PURE__ */ jsxs8(Text10, { color: isFocused ? theme.dialog.label : theme.yaml.key, bold: isFocused, children: [
|
|
1463
1472
|
isFocused ? ">" : " ",
|
|
1464
1473
|
" ",
|
|
1465
|
-
|
|
1474
|
+
field2.label.padEnd(14)
|
|
1466
1475
|
] }),
|
|
1467
|
-
|
|
1476
|
+
field2.type === "inline" && /* @__PURE__ */ jsxs8(Text10, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
|
|
1468
1477
|
value,
|
|
1469
1478
|
isFocused ? /* @__PURE__ */ jsx10(Text10, { color: theme.titleHighlight, children: "_" }) : "",
|
|
1470
|
-
|
|
1479
|
+
field2.key === "key" && !value && /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: isFocused ? " (auto from name)" : "" })
|
|
1471
1480
|
] }),
|
|
1472
|
-
|
|
1481
|
+
field2.type === "toggle" && /* @__PURE__ */ jsxs8(Text10, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
|
|
1473
1482
|
isFocused ? "< " : " ",
|
|
1474
1483
|
value,
|
|
1475
1484
|
isFocused ? " >" : ""
|
|
1476
1485
|
] })
|
|
1477
|
-
] },
|
|
1486
|
+
] }, field2.key);
|
|
1478
1487
|
}) }),
|
|
1479
1488
|
/* @__PURE__ */ jsx10(Box10, { flexGrow: 1 }),
|
|
1480
1489
|
/* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "tab: next | shift+tab: prev | ctrl+s: save | esc: cancel" }) })
|
|
@@ -1490,6 +1499,8 @@ var SECTIONS = [
|
|
|
1490
1499
|
keys: [
|
|
1491
1500
|
["j/k", "Up/Down"],
|
|
1492
1501
|
["g/G", "Top/Bottom"],
|
|
1502
|
+
["PgDn", "Page down"],
|
|
1503
|
+
["PgUp", "Page up"],
|
|
1493
1504
|
["enter", "View"],
|
|
1494
1505
|
["esc", "Back"]
|
|
1495
1506
|
]
|
|
@@ -1778,7 +1789,7 @@ function EpicPanel({
|
|
|
1778
1789
|
selectedEpicIds.size
|
|
1779
1790
|
] })
|
|
1780
1791
|
] }),
|
|
1781
|
-
epics.length === 0 ? /* @__PURE__ */ jsx14(Box14, { paddingX: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "No epics" }) }) : visibleEpics.map((epic, i) => {
|
|
1792
|
+
/* @__PURE__ */ jsx14(Box14, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: epics.length === 0 ? /* @__PURE__ */ jsx14(Box14, { paddingX: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "No epics" }) }) : visibleEpics.map((epic, i) => {
|
|
1782
1793
|
const actualIndex = viewStart + i;
|
|
1783
1794
|
const isSelected = actualIndex === selectedIndex && isFocused;
|
|
1784
1795
|
const isChecked = selectedEpicIds.has(epic.id);
|
|
@@ -1799,8 +1810,7 @@ function EpicPanel({
|
|
|
1799
1810
|
" ",
|
|
1800
1811
|
epic.name
|
|
1801
1812
|
] }) }, epic.id);
|
|
1802
|
-
}),
|
|
1803
|
-
/* @__PURE__ */ jsx14(Box14, { flexGrow: 1 }),
|
|
1813
|
+
}) }),
|
|
1804
1814
|
epics.length > PAGE_SIZE2 && /* @__PURE__ */ jsx14(Box14, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs12(Text14, { dimColor: true, children: [
|
|
1805
1815
|
"[",
|
|
1806
1816
|
viewStart + 1,
|
|
@@ -1950,6 +1960,41 @@ function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
|
|
|
1950
1960
|
] });
|
|
1951
1961
|
}
|
|
1952
1962
|
|
|
1963
|
+
// src/tui/useAutoRefetch.ts
|
|
1964
|
+
import { useEffect, useRef } from "react";
|
|
1965
|
+
import { watchFile, unwatchFile } from "fs";
|
|
1966
|
+
var POLL_INTERVAL_MS = 1e3;
|
|
1967
|
+
var DEBOUNCE_MS = 200;
|
|
1968
|
+
function useAutoRefetch(dbPath, onRefetch) {
|
|
1969
|
+
const callbackRef = useRef(onRefetch);
|
|
1970
|
+
callbackRef.current = onRefetch;
|
|
1971
|
+
useEffect(() => {
|
|
1972
|
+
let debounceTimer = null;
|
|
1973
|
+
const handleChange = () => {
|
|
1974
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
1975
|
+
debounceTimer = setTimeout(() => {
|
|
1976
|
+
logger.info("useAutoRefetch: external db change detected, refetching");
|
|
1977
|
+
callbackRef.current();
|
|
1978
|
+
}, DEBOUNCE_MS);
|
|
1979
|
+
};
|
|
1980
|
+
const walPath = `${dbPath}-wal`;
|
|
1981
|
+
const listener = (curr, prev) => {
|
|
1982
|
+
if (curr.mtimeMs !== prev.mtimeMs) {
|
|
1983
|
+
handleChange();
|
|
1984
|
+
}
|
|
1985
|
+
};
|
|
1986
|
+
watchFile(dbPath, { interval: POLL_INTERVAL_MS }, listener);
|
|
1987
|
+
watchFile(walPath, { interval: POLL_INTERVAL_MS }, listener);
|
|
1988
|
+
logger.info(`useAutoRefetch: watching ${dbPath} (poll ${POLL_INTERVAL_MS}ms)`);
|
|
1989
|
+
return () => {
|
|
1990
|
+
unwatchFile(dbPath, listener);
|
|
1991
|
+
unwatchFile(walPath, listener);
|
|
1992
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
1993
|
+
logger.info("useAutoRefetch: stopped watching");
|
|
1994
|
+
};
|
|
1995
|
+
}, [dbPath]);
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1953
1998
|
// src/tui/components/App.tsx
|
|
1954
1999
|
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1955
2000
|
var STATUS_CYCLE = [
|
|
@@ -1959,9 +2004,25 @@ var STATUS_CYCLE = [
|
|
|
1959
2004
|
TaskStatus.Review,
|
|
1960
2005
|
TaskStatus.Done
|
|
1961
2006
|
];
|
|
2007
|
+
var EPIC_PANEL_WIDTH = 48;
|
|
1962
2008
|
function App({ container, initialProject }) {
|
|
1963
2009
|
const { exit } = useApp();
|
|
2010
|
+
const { stdout } = useStdout4();
|
|
1964
2011
|
const [state, dispatch] = useReducer(appReducer, initialState);
|
|
2012
|
+
const [, setResizeTick] = useState6(0);
|
|
2013
|
+
useEffect2(() => {
|
|
2014
|
+
const onResize = () => {
|
|
2015
|
+
setResizeTick((n) => n + 1);
|
|
2016
|
+
};
|
|
2017
|
+
stdout.on("resize", onResize);
|
|
2018
|
+
return () => {
|
|
2019
|
+
stdout.off("resize", onResize);
|
|
2020
|
+
};
|
|
2021
|
+
}, [stdout]);
|
|
2022
|
+
const termWidth = stdout.columns > 0 ? stdout.columns : 120;
|
|
2023
|
+
const remaining = Math.max(0, termWidth - EPIC_PANEL_WIDTH - 2);
|
|
2024
|
+
const taskListWidth = Math.floor(remaining * 0.6);
|
|
2025
|
+
const taskDetailWidth = remaining - taskListWidth;
|
|
1965
2026
|
const loadProjects = useCallback2(() => {
|
|
1966
2027
|
const result = container.projectService.listProjects();
|
|
1967
2028
|
if (result.ok) {
|
|
@@ -2070,10 +2131,16 @@ function App({ container, initialProject }) {
|
|
|
2070
2131
|
});
|
|
2071
2132
|
loadEpics();
|
|
2072
2133
|
}, [container, state.epics, state.epicSelectedIndex, loadEpics]);
|
|
2073
|
-
|
|
2134
|
+
const refetchAll = useCallback2(() => {
|
|
2135
|
+
loadProjects();
|
|
2136
|
+
loadTasks();
|
|
2137
|
+
loadEpics();
|
|
2138
|
+
}, [loadProjects, loadTasks, loadEpics]);
|
|
2139
|
+
useAutoRefetch(container.dbPath, refetchAll);
|
|
2140
|
+
useEffect2(() => {
|
|
2074
2141
|
loadProjects();
|
|
2075
2142
|
}, [loadProjects]);
|
|
2076
|
-
|
|
2143
|
+
useEffect2(() => {
|
|
2077
2144
|
if (state.projects.length > 0 && !state.activeProject) {
|
|
2078
2145
|
logger.info(`TUI.resolveProject: resolving initialProject=${initialProject ?? "(default)"}`);
|
|
2079
2146
|
const result = container.projectService.resolveProject(initialProject);
|
|
@@ -2090,13 +2157,13 @@ function App({ container, initialProject }) {
|
|
|
2090
2157
|
}
|
|
2091
2158
|
}
|
|
2092
2159
|
}, [state.projects, state.activeProject, initialProject, container]);
|
|
2093
|
-
|
|
2160
|
+
useEffect2(() => {
|
|
2094
2161
|
if (state.activeProject) {
|
|
2095
2162
|
loadTasks();
|
|
2096
2163
|
loadEpics();
|
|
2097
2164
|
}
|
|
2098
2165
|
}, [state.activeProject, state.filter, loadTasks, loadEpics]);
|
|
2099
|
-
|
|
2166
|
+
useEffect2(() => {
|
|
2100
2167
|
if (state.flash) {
|
|
2101
2168
|
const timer = setTimeout(() => {
|
|
2102
2169
|
dispatch({ type: "CLEAR_FLASH" });
|
|
@@ -2319,6 +2386,14 @@ function App({ container, initialProject }) {
|
|
|
2319
2386
|
dispatch({ type: "SET_CURSOR", index: state.tasks.length - 1 });
|
|
2320
2387
|
return;
|
|
2321
2388
|
}
|
|
2389
|
+
if (key.pageDown || key.ctrl && input === "d") {
|
|
2390
|
+
dispatch({ type: "PAGE_CURSOR", direction: "down" });
|
|
2391
|
+
return;
|
|
2392
|
+
}
|
|
2393
|
+
if (key.pageUp || key.ctrl && input === "u") {
|
|
2394
|
+
dispatch({ type: "PAGE_CURSOR", direction: "up" });
|
|
2395
|
+
return;
|
|
2396
|
+
}
|
|
2322
2397
|
if (key.return) {
|
|
2323
2398
|
const task = state.tasks[state.selectedIndex];
|
|
2324
2399
|
if (task) {
|
|
@@ -2709,7 +2784,7 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
2709
2784
|
dispatch({ type: "GO_BACK" });
|
|
2710
2785
|
}, []);
|
|
2711
2786
|
const previewTask = state.tasks[state.selectedIndex] ?? null;
|
|
2712
|
-
const initialDepsForEdit =
|
|
2787
|
+
const initialDepsForEdit = useMemo2(() => {
|
|
2713
2788
|
return [
|
|
2714
2789
|
...state.depBlockers.map((t) => ({ id: t.id, name: t.name, type: DependencyType.Blocks })),
|
|
2715
2790
|
...state.depRelated.map((t) => ({ id: t.id, name: t.name, type: DependencyType.RelatesTo })),
|
|
@@ -2720,20 +2795,20 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
2720
2795
|
}))
|
|
2721
2796
|
];
|
|
2722
2797
|
}, [state.depBlockers, state.depRelated, state.depDuplicates]);
|
|
2723
|
-
const allProjectTasks =
|
|
2798
|
+
const allProjectTasks = useMemo2(() => {
|
|
2724
2799
|
if (!state.activeProject) return [];
|
|
2725
2800
|
const result = container.taskService.listTasks({ projectId: state.activeProject.id });
|
|
2726
2801
|
return result.ok ? result.value : [];
|
|
2727
2802
|
}, [container, state.activeProject, state.tasks]);
|
|
2728
2803
|
const previewTaskId = previewTask?.id ?? null;
|
|
2729
|
-
|
|
2804
|
+
useEffect2(() => {
|
|
2730
2805
|
if (state.activeView === ViewType.TaskList && previewTaskId) {
|
|
2731
2806
|
loadDeps(previewTaskId);
|
|
2732
2807
|
}
|
|
2733
2808
|
}, [state.activeView, previewTaskId, loadDeps]);
|
|
2734
|
-
return /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", height:
|
|
2809
|
+
return /* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", height: stdout.rows, children: [
|
|
2735
2810
|
/* @__PURE__ */ jsx16(Header, { state }),
|
|
2736
|
-
/* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", flexGrow: 1, children: [
|
|
2811
|
+
/* @__PURE__ */ jsxs14(Box16, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: [
|
|
2737
2812
|
state.confirmDelete && /* @__PURE__ */ jsx16(ConfirmDialog, { task: state.confirmDelete }),
|
|
2738
2813
|
!state.confirmDelete && state.activeView === ViewType.TaskList && /* @__PURE__ */ jsxs14(Box16, { flexDirection: "row", flexGrow: 1, children: [
|
|
2739
2814
|
/* @__PURE__ */ jsx16(
|
|
@@ -2746,7 +2821,7 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
2746
2821
|
isReordering: state.isEpicReordering
|
|
2747
2822
|
}
|
|
2748
2823
|
),
|
|
2749
|
-
/* @__PURE__ */ jsx16(Box16, {
|
|
2824
|
+
/* @__PURE__ */ jsx16(Box16, { width: taskListWidth, children: /* @__PURE__ */ jsx16(
|
|
2750
2825
|
TaskList,
|
|
2751
2826
|
{
|
|
2752
2827
|
tasks: state.tasks,
|
|
@@ -2767,7 +2842,7 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
2767
2842
|
epicFilterActive: state.selectedEpicIds.size > 0
|
|
2768
2843
|
}
|
|
2769
2844
|
) }),
|
|
2770
|
-
/* @__PURE__ */ jsx16(Box16, {
|
|
2845
|
+
/* @__PURE__ */ jsx16(Box16, { width: taskDetailWidth, children: previewTask ? /* @__PURE__ */ jsx16(
|
|
2771
2846
|
TaskDetail,
|
|
2772
2847
|
{
|
|
2773
2848
|
task: previewTask,
|
|
@@ -2868,4 +2943,4 @@ async function launchTUI(container, initialProject) {
|
|
|
2868
2943
|
export {
|
|
2869
2944
|
launchTUI
|
|
2870
2945
|
};
|
|
2871
|
-
//# sourceMappingURL=tui-
|
|
2946
|
+
//# sourceMappingURL=tui-5JJH67YY.js.map
|