@tomkapa/tayto 0.6.0 → 0.7.0
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/{chunk-74Q55TOV.js → chunk-5V4TBQ5S.js} +38 -4
- package/dist/chunk-5V4TBQ5S.js.map +1 -0
- package/dist/index.js +29 -21
- package/dist/index.js.map +1 -1
- package/dist/{tui-WMESKCRD.js → tui-NCL4RFFD.js} +443 -181
- package/dist/tui-NCL4RFFD.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-74Q55TOV.js.map +0 -1
- package/dist/tui-WMESKCRD.js.map +0 -1
|
@@ -8,14 +8,14 @@ import {
|
|
|
8
8
|
detectGitRemote,
|
|
9
9
|
isTerminalStatus,
|
|
10
10
|
logger
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-5V4TBQ5S.js";
|
|
12
12
|
|
|
13
13
|
// src/tui/index.tsx
|
|
14
14
|
import { render } from "ink";
|
|
15
15
|
|
|
16
16
|
// src/tui/components/App.tsx
|
|
17
|
-
import { useReducer, useEffect as
|
|
18
|
-
import { Box as
|
|
17
|
+
import { useReducer, useEffect as useEffect4, useCallback as useCallback2, useMemo as useMemo2, useState as useState7, useRef as useRef5 } from "react";
|
|
18
|
+
import { Box as Box19, Text as Text19, useInput as useInput7, useApp, useStdout as useStdout4 } from "ink";
|
|
19
19
|
|
|
20
20
|
// src/tui/types.ts
|
|
21
21
|
var ViewType = {
|
|
@@ -25,6 +25,7 @@ var ViewType = {
|
|
|
25
25
|
TaskEdit: "task-edit",
|
|
26
26
|
ProjectSelector: "project-selector",
|
|
27
27
|
ProjectCreate: "project-create",
|
|
28
|
+
ProjectEdit: "project-edit",
|
|
28
29
|
DependencyList: "dependency-list",
|
|
29
30
|
EpicPicker: "epic-picker",
|
|
30
31
|
ProjectLink: "project-link",
|
|
@@ -177,8 +178,10 @@ var initialState = {
|
|
|
177
178
|
epicSelectedIndex: 0,
|
|
178
179
|
selectedEpicIds: /* @__PURE__ */ new Set(),
|
|
179
180
|
linkingProject: null,
|
|
181
|
+
editingProject: null,
|
|
180
182
|
isEpicReordering: false,
|
|
181
|
-
epicReorderSnapshot: null
|
|
183
|
+
epicReorderSnapshot: null,
|
|
184
|
+
detectedGitRemote: null
|
|
182
185
|
};
|
|
183
186
|
function appReducer(state, action) {
|
|
184
187
|
switch (action.type) {
|
|
@@ -200,6 +203,8 @@ function appReducer(state, action) {
|
|
|
200
203
|
isSearchActive: false,
|
|
201
204
|
formData: null,
|
|
202
205
|
linkingProject: null,
|
|
206
|
+
editingProject: null,
|
|
207
|
+
detectedGitRemote: null,
|
|
203
208
|
focusedPanel: "list"
|
|
204
209
|
};
|
|
205
210
|
}
|
|
@@ -384,6 +389,10 @@ function appReducer(state, action) {
|
|
|
384
389
|
}
|
|
385
390
|
case "SET_LINKING_PROJECT":
|
|
386
391
|
return { ...state, linkingProject: action.project };
|
|
392
|
+
case "SET_EDITING_PROJECT":
|
|
393
|
+
return { ...state, editingProject: action.project };
|
|
394
|
+
case "SET_DETECTED_GIT_REMOTE":
|
|
395
|
+
return { ...state, detectedGitRemote: action.remote };
|
|
387
396
|
}
|
|
388
397
|
}
|
|
389
398
|
|
|
@@ -513,7 +522,8 @@ function getKeyHints(state) {
|
|
|
513
522
|
}
|
|
514
523
|
if (activeView === ViewType.TaskCreate || activeView === ViewType.TaskEdit) {
|
|
515
524
|
return [
|
|
516
|
-
{ key: "tab", desc: "
|
|
525
|
+
{ key: "\u2191\u2193/tab", desc: "navigate" },
|
|
526
|
+
{ key: "\u2190\u2192", desc: "cursor" },
|
|
517
527
|
{ key: "ctrl+s", desc: "save" },
|
|
518
528
|
{ key: "esc", desc: "cancel" }
|
|
519
529
|
];
|
|
@@ -522,6 +532,7 @@ function getKeyHints(state) {
|
|
|
522
532
|
return [
|
|
523
533
|
{ key: "j/k", desc: "nav" },
|
|
524
534
|
{ key: "enter", desc: "select" },
|
|
535
|
+
{ key: "e", desc: "edit" },
|
|
525
536
|
{ key: "c", desc: "create" },
|
|
526
537
|
{ key: "l", desc: "link" },
|
|
527
538
|
{ key: "d", desc: "default" },
|
|
@@ -986,8 +997,8 @@ ${task.additionalRequirements}`;
|
|
|
986
997
|
}
|
|
987
998
|
|
|
988
999
|
// src/tui/components/TaskForm.tsx
|
|
989
|
-
import { useState as useState2, useCallback } from "react";
|
|
990
|
-
import { Box as Box9, Text as Text9, useInput as useInput2 } from "ink";
|
|
1000
|
+
import { useState as useState2, useCallback, useEffect, useRef } from "react";
|
|
1001
|
+
import { Box as Box9, Text as Text9, useInput as useInput2, useStdin } from "ink";
|
|
991
1002
|
|
|
992
1003
|
// src/tui/editor.ts
|
|
993
1004
|
import { writeFileSync as writeFileSync2, readFileSync, unlinkSync, mkdtempSync as mkdtempSync2 } from "fs";
|
|
@@ -997,15 +1008,17 @@ import { spawnSync } from "child_process";
|
|
|
997
1008
|
function getEditor() {
|
|
998
1009
|
return process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
|
|
999
1010
|
}
|
|
1000
|
-
function openInEditor(content, filename) {
|
|
1011
|
+
function openInEditor(content, filename, options) {
|
|
1001
1012
|
const dir = mkdtempSync2(join2(tmpdir2(), "task-"));
|
|
1002
1013
|
const filepath = join2(dir, filename);
|
|
1003
1014
|
writeFileSync2(filepath, content, "utf-8");
|
|
1004
1015
|
const editor = getEditor();
|
|
1016
|
+
options?.beforeOpen?.();
|
|
1005
1017
|
const result = spawnSync(editor, [filepath], {
|
|
1006
1018
|
stdio: "inherit",
|
|
1007
1019
|
shell: true
|
|
1008
1020
|
});
|
|
1021
|
+
options?.afterOpen?.();
|
|
1009
1022
|
if (result.status !== 0) {
|
|
1010
1023
|
try {
|
|
1011
1024
|
unlinkSync(filepath);
|
|
@@ -1222,7 +1235,7 @@ function TaskPicker({ tasks, excludeIds, initialSelection, onConfirm, onCancel }
|
|
|
1222
1235
|
}
|
|
1223
1236
|
|
|
1224
1237
|
// src/tui/components/TaskForm.tsx
|
|
1225
|
-
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1238
|
+
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1226
1239
|
var FIELDS = [
|
|
1227
1240
|
{ label: "Name", key: "name", type: "inline" },
|
|
1228
1241
|
{ label: "Type", key: "type", type: "select", options: TYPE_VALUES },
|
|
@@ -1252,23 +1265,42 @@ function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
|
|
|
1252
1265
|
technicalNotes: editingTask?.technicalNotes ?? "",
|
|
1253
1266
|
additionalRequirements: editingTask?.additionalRequirements ?? ""
|
|
1254
1267
|
});
|
|
1268
|
+
const [cursorPos, setCursorPos] = useState2(() => (editingTask?.name ?? "").length);
|
|
1269
|
+
const cursorRef = useRef(cursorPos);
|
|
1270
|
+
cursorRef.current = cursorPos;
|
|
1255
1271
|
const [editorActive, setEditorActive] = useState2(false);
|
|
1256
1272
|
const [pickerActive, setPickerActive] = useState2(false);
|
|
1257
1273
|
const [pickedDeps, setPickedDeps] = useState2(initialDeps ?? []);
|
|
1274
|
+
const { setRawMode } = useStdin();
|
|
1275
|
+
useEffect(() => {
|
|
1276
|
+
const field2 = FIELDS[focusIndex];
|
|
1277
|
+
if (field2?.type === "inline") {
|
|
1278
|
+
const pos = values[field2.key]?.length ?? 0;
|
|
1279
|
+
setCursorPos(pos);
|
|
1280
|
+
cursorRef.current = pos;
|
|
1281
|
+
}
|
|
1282
|
+
}, [focusIndex]);
|
|
1258
1283
|
const currentField = FIELDS[focusIndex];
|
|
1259
1284
|
const launchEditor = useCallback(
|
|
1260
1285
|
(field2) => {
|
|
1261
1286
|
setEditorActive(true);
|
|
1262
1287
|
setTimeout(() => {
|
|
1263
1288
|
const content = values[field2.key] ?? "";
|
|
1264
|
-
const result = openInEditor(content, field2.editorFilename ?? `${field2.key}.md
|
|
1289
|
+
const result = openInEditor(content, field2.editorFilename ?? `${field2.key}.md`, {
|
|
1290
|
+
beforeOpen: () => {
|
|
1291
|
+
setRawMode(false);
|
|
1292
|
+
},
|
|
1293
|
+
afterOpen: () => {
|
|
1294
|
+
setRawMode(true);
|
|
1295
|
+
}
|
|
1296
|
+
});
|
|
1265
1297
|
if (result !== null) {
|
|
1266
1298
|
setValues((v) => ({ ...v, [field2.key]: result }));
|
|
1267
1299
|
}
|
|
1268
1300
|
setEditorActive(false);
|
|
1269
1301
|
}, 50);
|
|
1270
1302
|
},
|
|
1271
|
-
[values]
|
|
1303
|
+
[values, setRawMode]
|
|
1272
1304
|
);
|
|
1273
1305
|
const handlePickerConfirm = useCallback((selected) => {
|
|
1274
1306
|
setPickedDeps(selected);
|
|
@@ -1308,32 +1340,49 @@ function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
|
|
|
1308
1340
|
}
|
|
1309
1341
|
return;
|
|
1310
1342
|
}
|
|
1343
|
+
if (key.upArrow) {
|
|
1344
|
+
setFocusIndex((i) => Math.max(0, i - 1));
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
if (key.downArrow) {
|
|
1348
|
+
setFocusIndex((i) => Math.min(FIELDS.length - 1, i + 1));
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1311
1351
|
if (currentField.type === "inline") {
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1352
|
+
if (key.leftArrow) {
|
|
1353
|
+
setCursorPos((p) => Math.max(0, p - 1));
|
|
1354
|
+
} else if (key.rightArrow) {
|
|
1355
|
+
setCursorPos((p) => Math.min((values[currentField.key] ?? "").length, p + 1));
|
|
1356
|
+
} else if (key.backspace || key.delete) {
|
|
1357
|
+
const pos = cursorRef.current;
|
|
1358
|
+
if (pos > 0) {
|
|
1359
|
+
setValues((v) => {
|
|
1360
|
+
const cur = v[currentField.key] ?? "";
|
|
1361
|
+
return { ...v, [currentField.key]: cur.slice(0, pos - 1) + cur.slice(pos) };
|
|
1362
|
+
});
|
|
1363
|
+
cursorRef.current = pos - 1;
|
|
1364
|
+
setCursorPos(pos - 1);
|
|
1365
|
+
}
|
|
1315
1366
|
} else if (key.return) {
|
|
1316
1367
|
setFocusIndex((i) => Math.min(FIELDS.length - 1, i + 1));
|
|
1317
1368
|
} else if (input && !key.ctrl && !key.meta) {
|
|
1318
|
-
|
|
1369
|
+
const pos = cursorRef.current;
|
|
1370
|
+
setValues((v) => {
|
|
1371
|
+
const cur = v[currentField.key] ?? "";
|
|
1372
|
+
return { ...v, [currentField.key]: cur.slice(0, pos) + input + cur.slice(pos) };
|
|
1373
|
+
});
|
|
1374
|
+
cursorRef.current = pos + input.length;
|
|
1375
|
+
setCursorPos(pos + input.length);
|
|
1319
1376
|
}
|
|
1320
1377
|
}
|
|
1321
1378
|
if (currentField.type === "picker") {
|
|
1322
1379
|
if (key.return) {
|
|
1323
1380
|
setPickerActive(true);
|
|
1324
|
-
} else if (key.downArrow) {
|
|
1325
|
-
setFocusIndex((i) => Math.min(FIELDS.length - 1, i + 1));
|
|
1326
|
-
} else if (key.upArrow) {
|
|
1327
|
-
setFocusIndex((i) => Math.max(0, i - 1));
|
|
1328
1381
|
}
|
|
1329
1382
|
}
|
|
1330
1383
|
if (currentField.type === "editor") {
|
|
1331
1384
|
if (key.return) {
|
|
1332
1385
|
launchEditor(currentField);
|
|
1333
|
-
} else if (key.downArrow) {
|
|
1334
|
-
setFocusIndex((i) => Math.min(FIELDS.length - 1, i + 1));
|
|
1335
|
-
} else if (key.upArrow) {
|
|
1336
|
-
setFocusIndex((i) => Math.max(0, i - 1));
|
|
1337
1386
|
}
|
|
1338
1387
|
}
|
|
1339
1388
|
if (currentField.type === "select") {
|
|
@@ -1374,37 +1423,37 @@ function TaskForm({ editingTask, allTasks, initialDeps, onSave, onCancel }) {
|
|
|
1374
1423
|
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS.map((field2, i) => {
|
|
1375
1424
|
const isFocused = i === focusIndex;
|
|
1376
1425
|
const value = field2.key === "dependsOn" ? depSummary : values[field2.key] ?? "";
|
|
1377
|
-
const displayValue = value;
|
|
1378
1426
|
return /* @__PURE__ */ jsxs6(Box9, { gap: 1, children: [
|
|
1379
1427
|
/* @__PURE__ */ jsxs6(Text9, { color: isFocused ? theme.dialog.label : theme.yaml.key, bold: isFocused, children: [
|
|
1380
1428
|
isFocused ? ">" : " ",
|
|
1381
1429
|
" ",
|
|
1382
1430
|
field2.label.padEnd(14)
|
|
1383
1431
|
] }),
|
|
1384
|
-
field2.type === "inline" && /* @__PURE__ */
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1432
|
+
field2.type === "inline" && /* @__PURE__ */ jsx9(Text9, { color: isFocused ? theme.yaml.value : theme.table.fg, children: isFocused ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1433
|
+
value.slice(0, cursorPos),
|
|
1434
|
+
/* @__PURE__ */ jsx9(Text9, { color: theme.titleHighlight, children: "_" }),
|
|
1435
|
+
value.slice(cursorPos)
|
|
1436
|
+
] }) : value }),
|
|
1388
1437
|
field2.type === "picker" && /* @__PURE__ */ jsxs6(Text9, { children: [
|
|
1389
|
-
|
|
1438
|
+
value ? /* @__PURE__ */ jsx9(Text9, { color: theme.status.added, children: value.length > 60 ? value.slice(0, 60) + "..." : value }) : /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: isFocused ? "press enter to select" : "none" }),
|
|
1390
1439
|
isFocused && /* @__PURE__ */ jsx9(Text9, { color: theme.menu.key, children: " [enter: open picker]" })
|
|
1391
1440
|
] }),
|
|
1392
1441
|
field2.type === "editor" && /* @__PURE__ */ jsxs6(Text9, { children: [
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1442
|
+
value ? /* @__PURE__ */ jsxs6(Text9, { color: theme.status.added, children: [
|
|
1443
|
+
value.split("\n")[0]?.slice(0, 50),
|
|
1444
|
+
value.length > 50 || value.includes("\n") ? "..." : ""
|
|
1396
1445
|
] }) : /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: isFocused ? "press enter" : "empty" }),
|
|
1397
1446
|
isFocused && /* @__PURE__ */ jsx9(Text9, { color: theme.menu.key, children: " [enter: $EDITOR]" })
|
|
1398
1447
|
] }),
|
|
1399
1448
|
field2.type === "select" && /* @__PURE__ */ jsxs6(Text9, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
|
|
1400
1449
|
isFocused ? "< " : " ",
|
|
1401
|
-
|
|
1450
|
+
value,
|
|
1402
1451
|
isFocused ? " >" : ""
|
|
1403
1452
|
] })
|
|
1404
1453
|
] }, field2.key);
|
|
1405
1454
|
}) }),
|
|
1406
1455
|
/* @__PURE__ */ jsx9(Box9, { flexGrow: 1 }),
|
|
1407
|
-
/* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "tab:
|
|
1456
|
+
/* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "\u2191\u2193/tab: navigate | \u2190\u2192: cursor | ctrl+s: save | esc: cancel" }) }),
|
|
1408
1457
|
editorActive && /* @__PURE__ */ jsx9(Box9, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text9, { color: theme.flash.warn, bold: true, children: "Editor open... save and close to return" }) })
|
|
1409
1458
|
] });
|
|
1410
1459
|
}
|
|
@@ -1418,6 +1467,7 @@ function ProjectSelector({
|
|
|
1418
1467
|
activeProject,
|
|
1419
1468
|
onSelect,
|
|
1420
1469
|
onCreate,
|
|
1470
|
+
onEdit,
|
|
1421
1471
|
onSetDefault,
|
|
1422
1472
|
onLink,
|
|
1423
1473
|
onCancel
|
|
@@ -1443,6 +1493,13 @@ function ProjectSelector({
|
|
|
1443
1493
|
onCreate();
|
|
1444
1494
|
return;
|
|
1445
1495
|
}
|
|
1496
|
+
if (input === "e") {
|
|
1497
|
+
const project = projects[selectedIndex];
|
|
1498
|
+
if (project) {
|
|
1499
|
+
onEdit(project);
|
|
1500
|
+
}
|
|
1501
|
+
return;
|
|
1502
|
+
}
|
|
1446
1503
|
if (input === "d") {
|
|
1447
1504
|
const project = projects[selectedIndex];
|
|
1448
1505
|
if (project) {
|
|
@@ -1487,7 +1544,7 @@ function ProjectSelector({
|
|
|
1487
1544
|
const activeMarker = isActive ? "*" : " ";
|
|
1488
1545
|
const defaultMarker = project.isDefault ? "D" : " ";
|
|
1489
1546
|
const marker = `${activeMarker}${defaultMarker}`;
|
|
1490
|
-
const remoteDisplay = (project.gitRemote ?? "").slice(0, 38).padEnd(40);
|
|
1547
|
+
const remoteDisplay = (project.gitRemote?.value ?? "").slice(0, 38).padEnd(40);
|
|
1491
1548
|
if (isSelected) {
|
|
1492
1549
|
return /* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsxs7(Text10, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
|
|
1493
1550
|
marker,
|
|
@@ -1508,28 +1565,53 @@ function ProjectSelector({
|
|
|
1508
1565
|
] }, project.id);
|
|
1509
1566
|
}),
|
|
1510
1567
|
/* @__PURE__ */ jsx10(Box10, { flexGrow: 1 }),
|
|
1511
|
-
/* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "enter: select | d: set default | l: link git | c: create | esc: back" }) })
|
|
1568
|
+
/* @__PURE__ */ jsx10(Box10, { paddingX: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "enter: select | e: edit | d: set default | l: link git | c: create | esc: back" }) })
|
|
1512
1569
|
] });
|
|
1513
1570
|
}
|
|
1514
1571
|
|
|
1515
1572
|
// src/tui/components/ProjectForm.tsx
|
|
1516
|
-
import { useState as useState4 } from "react";
|
|
1573
|
+
import { useState as useState4, useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
1517
1574
|
import { Box as Box11, Text as Text11, useInput as useInput4 } from "ink";
|
|
1518
|
-
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1575
|
+
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1519
1576
|
var FIELDS2 = [
|
|
1520
1577
|
{ label: "Name", key: "name", type: "inline" },
|
|
1521
1578
|
{ label: "Key", key: "key", type: "inline" },
|
|
1522
1579
|
{ label: "Description", key: "description", type: "inline" },
|
|
1580
|
+
{ label: "Git Remote", key: "gitRemote", type: "inline" },
|
|
1523
1581
|
{ label: "Default", key: "isDefault", type: "toggle" }
|
|
1524
1582
|
];
|
|
1525
|
-
function ProjectForm({ onSave, onCancel }) {
|
|
1583
|
+
function ProjectForm({ editingProject, initialGitRemote, onSave, onCancel }) {
|
|
1584
|
+
const isEditing = !!editingProject;
|
|
1526
1585
|
const [focusIndex, setFocusIndex] = useState4(0);
|
|
1527
|
-
const [values, setValues] = useState4({
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1586
|
+
const [values, setValues] = useState4(() => {
|
|
1587
|
+
if (editingProject) {
|
|
1588
|
+
return {
|
|
1589
|
+
name: editingProject.name,
|
|
1590
|
+
key: editingProject.key,
|
|
1591
|
+
description: editingProject.description,
|
|
1592
|
+
gitRemote: editingProject.gitRemote?.value ?? "",
|
|
1593
|
+
isDefault: editingProject.isDefault ? "yes" : "no"
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
return {
|
|
1597
|
+
name: "",
|
|
1598
|
+
key: "",
|
|
1599
|
+
description: "",
|
|
1600
|
+
gitRemote: initialGitRemote?.value ?? "",
|
|
1601
|
+
isDefault: "no"
|
|
1602
|
+
};
|
|
1532
1603
|
});
|
|
1604
|
+
const [cursorPos, setCursorPos] = useState4(0);
|
|
1605
|
+
const cursorRef = useRef2(cursorPos);
|
|
1606
|
+
cursorRef.current = cursorPos;
|
|
1607
|
+
useEffect2(() => {
|
|
1608
|
+
const field2 = FIELDS2[focusIndex];
|
|
1609
|
+
if (field2?.type === "inline") {
|
|
1610
|
+
const pos = values[field2.key]?.length ?? 0;
|
|
1611
|
+
setCursorPos(pos);
|
|
1612
|
+
cursorRef.current = pos;
|
|
1613
|
+
}
|
|
1614
|
+
}, [focusIndex]);
|
|
1533
1615
|
const currentField = FIELDS2[focusIndex];
|
|
1534
1616
|
useInput4((input, key) => {
|
|
1535
1617
|
if (key.escape) {
|
|
@@ -1552,19 +1634,46 @@ function ProjectForm({ onSave, onCancel }) {
|
|
|
1552
1634
|
name: nameVal,
|
|
1553
1635
|
key: values["key"] ?? "",
|
|
1554
1636
|
description: values["description"] ?? "",
|
|
1637
|
+
gitRemote: values["gitRemote"] ?? "",
|
|
1555
1638
|
isDefault: values["isDefault"] === "yes"
|
|
1556
1639
|
});
|
|
1557
1640
|
}
|
|
1558
1641
|
return;
|
|
1559
1642
|
}
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1643
|
+
if (key.upArrow) {
|
|
1644
|
+
setFocusIndex((i) => Math.max(0, i - 1));
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
if (key.downArrow) {
|
|
1648
|
+
setFocusIndex((i) => Math.min(FIELDS2.length - 1, i + 1));
|
|
1649
|
+
return;
|
|
1650
|
+
}
|
|
1651
|
+
const isReadOnly = isEditing && currentField.key === "key";
|
|
1652
|
+
if (currentField.type === "inline" && !isReadOnly) {
|
|
1653
|
+
if (key.leftArrow) {
|
|
1654
|
+
setCursorPos((p) => Math.max(0, p - 1));
|
|
1655
|
+
} else if (key.rightArrow) {
|
|
1656
|
+
setCursorPos((p) => Math.min((values[currentField.key] ?? "").length, p + 1));
|
|
1657
|
+
} else if (key.backspace || key.delete) {
|
|
1658
|
+
const pos = cursorRef.current;
|
|
1659
|
+
if (pos > 0) {
|
|
1660
|
+
setValues((v) => {
|
|
1661
|
+
const cur = v[currentField.key] ?? "";
|
|
1662
|
+
return { ...v, [currentField.key]: cur.slice(0, pos - 1) + cur.slice(pos) };
|
|
1663
|
+
});
|
|
1664
|
+
cursorRef.current = pos - 1;
|
|
1665
|
+
setCursorPos(pos - 1);
|
|
1666
|
+
}
|
|
1564
1667
|
} else if (key.return) {
|
|
1565
1668
|
setFocusIndex((i) => Math.min(FIELDS2.length - 1, i + 1));
|
|
1566
1669
|
} else if (input && !key.ctrl && !key.meta) {
|
|
1567
|
-
|
|
1670
|
+
const pos = cursorRef.current;
|
|
1671
|
+
setValues((v) => {
|
|
1672
|
+
const cur = v[currentField.key] ?? "";
|
|
1673
|
+
return { ...v, [currentField.key]: cur.slice(0, pos) + input + cur.slice(pos) };
|
|
1674
|
+
});
|
|
1675
|
+
cursorRef.current = pos + input.length;
|
|
1676
|
+
setCursorPos(pos + input.length);
|
|
1568
1677
|
}
|
|
1569
1678
|
}
|
|
1570
1679
|
if (currentField.type === "toggle") {
|
|
@@ -1579,7 +1688,7 @@ function ProjectForm({ onSave, onCancel }) {
|
|
|
1579
1688
|
return /* @__PURE__ */ jsxs8(Box11, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
|
|
1580
1689
|
/* @__PURE__ */ jsx11(Box11, { gap: 0, children: /* @__PURE__ */ jsxs8(Text11, { color: theme.title, bold: true, children: [
|
|
1581
1690
|
" ",
|
|
1582
|
-
"new project"
|
|
1691
|
+
isEditing ? "edit project" : "new project"
|
|
1583
1692
|
] }) }),
|
|
1584
1693
|
/* @__PURE__ */ jsx11(Box11, { flexDirection: "column", paddingX: 1, paddingY: 0, children: FIELDS2.map((field2, i) => {
|
|
1585
1694
|
const isFocused = i === focusIndex;
|
|
@@ -1591,9 +1700,13 @@ function ProjectForm({ onSave, onCancel }) {
|
|
|
1591
1700
|
field2.label.padEnd(14)
|
|
1592
1701
|
] }),
|
|
1593
1702
|
field2.type === "inline" && /* @__PURE__ */ jsxs8(Text11, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1703
|
+
isFocused ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1704
|
+
value.slice(0, cursorPos),
|
|
1705
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.titleHighlight, children: "_" }),
|
|
1706
|
+
value.slice(cursorPos)
|
|
1707
|
+
] }) : value,
|
|
1708
|
+
field2.key === "key" && !value && !isEditing && /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: isFocused ? " (auto from name)" : "" }),
|
|
1709
|
+
field2.key === "key" && isEditing && isFocused && /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: " (read-only)" })
|
|
1597
1710
|
] }),
|
|
1598
1711
|
field2.type === "toggle" && /* @__PURE__ */ jsxs8(Text11, { color: isFocused ? theme.yaml.value : theme.table.fg, children: [
|
|
1599
1712
|
isFocused ? "< " : " ",
|
|
@@ -1603,16 +1716,19 @@ function ProjectForm({ onSave, onCancel }) {
|
|
|
1603
1716
|
] }, field2.key);
|
|
1604
1717
|
}) }),
|
|
1605
1718
|
/* @__PURE__ */ jsx11(Box11, { flexGrow: 1 }),
|
|
1606
|
-
/* @__PURE__ */ jsx11(Box11, { paddingX: 1, children: /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "tab:
|
|
1719
|
+
/* @__PURE__ */ jsx11(Box11, { paddingX: 1, children: /* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "\u2191\u2193/tab: navigate | \u2190\u2192: cursor | ctrl+s: save | esc: cancel" }) })
|
|
1607
1720
|
] });
|
|
1608
1721
|
}
|
|
1609
1722
|
|
|
1610
1723
|
// src/tui/components/ProjectLinkForm.tsx
|
|
1611
|
-
import { useState as useState5 } from "react";
|
|
1724
|
+
import { useState as useState5, useRef as useRef3 } from "react";
|
|
1612
1725
|
import { Box as Box12, Text as Text12, useInput as useInput5 } from "ink";
|
|
1613
1726
|
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1614
1727
|
function ProjectLinkForm({ project, onSave, onUnlink, onDetect, onCancel }) {
|
|
1615
|
-
const [remoteUrl, setRemoteUrl] = useState5(project.gitRemote ?? "");
|
|
1728
|
+
const [remoteUrl, setRemoteUrl] = useState5(project.gitRemote?.value ?? "");
|
|
1729
|
+
const [cursorPos, setCursorPos] = useState5(() => (project.gitRemote?.value ?? "").length);
|
|
1730
|
+
const cursorRef = useRef3(cursorPos);
|
|
1731
|
+
cursorRef.current = cursorPos;
|
|
1616
1732
|
useInput5((input, key) => {
|
|
1617
1733
|
if (key.escape) {
|
|
1618
1734
|
onCancel();
|
|
@@ -1629,6 +1745,8 @@ function ProjectLinkForm({ project, onSave, onUnlink, onDetect, onCancel }) {
|
|
|
1629
1745
|
const detected = onDetect();
|
|
1630
1746
|
if (detected) {
|
|
1631
1747
|
setRemoteUrl(detected);
|
|
1748
|
+
cursorRef.current = detected.length;
|
|
1749
|
+
setCursorPos(detected.length);
|
|
1632
1750
|
}
|
|
1633
1751
|
return;
|
|
1634
1752
|
}
|
|
@@ -1638,12 +1756,28 @@ function ProjectLinkForm({ project, onSave, onUnlink, onDetect, onCancel }) {
|
|
|
1638
1756
|
}
|
|
1639
1757
|
return;
|
|
1640
1758
|
}
|
|
1759
|
+
if (key.leftArrow) {
|
|
1760
|
+
setCursorPos((p) => Math.max(0, p - 1));
|
|
1761
|
+
return;
|
|
1762
|
+
}
|
|
1763
|
+
if (key.rightArrow) {
|
|
1764
|
+
setCursorPos((p) => Math.min(remoteUrl.length, p + 1));
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1641
1767
|
if (key.backspace || key.delete) {
|
|
1642
|
-
|
|
1768
|
+
const pos = cursorRef.current;
|
|
1769
|
+
if (pos > 0) {
|
|
1770
|
+
setRemoteUrl((v) => v.slice(0, pos - 1) + v.slice(pos));
|
|
1771
|
+
cursorRef.current = pos - 1;
|
|
1772
|
+
setCursorPos(pos - 1);
|
|
1773
|
+
}
|
|
1643
1774
|
return;
|
|
1644
1775
|
}
|
|
1645
1776
|
if (input && !key.ctrl && !key.meta) {
|
|
1646
|
-
|
|
1777
|
+
const pos = cursorRef.current;
|
|
1778
|
+
setRemoteUrl((v) => v.slice(0, pos) + input + v.slice(pos));
|
|
1779
|
+
cursorRef.current = pos + input.length;
|
|
1780
|
+
setCursorPos(pos + input.length);
|
|
1647
1781
|
}
|
|
1648
1782
|
});
|
|
1649
1783
|
return /* @__PURE__ */ jsxs9(Box12, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
|
|
@@ -1667,15 +1801,16 @@ function ProjectLinkForm({ project, onSave, onUnlink, onDetect, onCancel }) {
|
|
|
1667
1801
|
{
|
|
1668
1802
|
color: project.gitRemote ? theme.yaml.value : theme.table.fg,
|
|
1669
1803
|
dimColor: !project.gitRemote,
|
|
1670
|
-
children: project.gitRemote ?? "(none)"
|
|
1804
|
+
children: project.gitRemote?.value ?? "(none)"
|
|
1671
1805
|
}
|
|
1672
1806
|
)
|
|
1673
1807
|
] }),
|
|
1674
1808
|
/* @__PURE__ */ jsxs9(Box12, { gap: 1, marginTop: 1, children: [
|
|
1675
1809
|
/* @__PURE__ */ jsx12(Text12, { color: theme.dialog.label, bold: true, children: "Remote URL: " }),
|
|
1676
1810
|
/* @__PURE__ */ jsxs9(Text12, { color: theme.yaml.value, children: [
|
|
1677
|
-
remoteUrl,
|
|
1678
|
-
/* @__PURE__ */ jsx12(Text12, { color: theme.titleHighlight, children: "_" })
|
|
1811
|
+
remoteUrl.slice(0, cursorPos),
|
|
1812
|
+
/* @__PURE__ */ jsx12(Text12, { color: theme.titleHighlight, children: "_" }),
|
|
1813
|
+
remoteUrl.slice(cursorPos)
|
|
1679
1814
|
] })
|
|
1680
1815
|
] })
|
|
1681
1816
|
] }),
|
|
@@ -1684,6 +1819,38 @@ function ProjectLinkForm({ project, onSave, onUnlink, onDetect, onCancel }) {
|
|
|
1684
1819
|
] });
|
|
1685
1820
|
}
|
|
1686
1821
|
|
|
1822
|
+
// src/utils/dismissed-remotes.ts
|
|
1823
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
1824
|
+
function isDismissedRemote(filePath, remote) {
|
|
1825
|
+
try {
|
|
1826
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1827
|
+
const list = JSON.parse(content);
|
|
1828
|
+
if (!Array.isArray(list)) return false;
|
|
1829
|
+
return list.includes(remote.value);
|
|
1830
|
+
} catch {
|
|
1831
|
+
return false;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
function dismissRemote(filePath, remote) {
|
|
1835
|
+
let list = [];
|
|
1836
|
+
try {
|
|
1837
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1838
|
+
const parsed = JSON.parse(content);
|
|
1839
|
+
if (Array.isArray(parsed)) {
|
|
1840
|
+
list = parsed.filter((v) => typeof v === "string");
|
|
1841
|
+
}
|
|
1842
|
+
} catch {
|
|
1843
|
+
}
|
|
1844
|
+
if (list.includes(remote.value)) return;
|
|
1845
|
+
list.push(remote.value);
|
|
1846
|
+
try {
|
|
1847
|
+
writeFileSync3(filePath, JSON.stringify(list), "utf-8");
|
|
1848
|
+
logger.info(`dismissRemote: persisted ${remote.value} to ${filePath}`);
|
|
1849
|
+
} catch (e) {
|
|
1850
|
+
logger.error(`dismissRemote: failed to write ${filePath}`, e instanceof Error ? e : new Error(String(e)));
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1687
1854
|
// src/tui/components/HelpOverlay.tsx
|
|
1688
1855
|
import { Box as Box13, Text as Text13 } from "ink";
|
|
1689
1856
|
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
@@ -1755,10 +1922,10 @@ var ROW2 = [
|
|
|
1755
1922
|
{
|
|
1756
1923
|
title: "FORMS",
|
|
1757
1924
|
keys: [
|
|
1758
|
-
["tab", "
|
|
1759
|
-
["
|
|
1925
|
+
["\u2191\u2193/tab", "Navigate fields"],
|
|
1926
|
+
["\u2190\u2192", "Move cursor"],
|
|
1760
1927
|
["ctrl+s", "Save"],
|
|
1761
|
-
["enter", "Open editor"],
|
|
1928
|
+
["enter", "Open editor / next"],
|
|
1762
1929
|
["esc", "Cancel"]
|
|
1763
1930
|
]
|
|
1764
1931
|
},
|
|
@@ -1849,9 +2016,41 @@ function ConfirmDialog({ task }) {
|
|
|
1849
2016
|
);
|
|
1850
2017
|
}
|
|
1851
2018
|
|
|
1852
|
-
// src/tui/components/
|
|
2019
|
+
// src/tui/components/DetectedProjectDialog.tsx
|
|
1853
2020
|
import { Box as Box15, Text as Text15 } from "ink";
|
|
1854
|
-
import {
|
|
2021
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2022
|
+
function DetectedProjectDialog({ remote }) {
|
|
2023
|
+
return /* @__PURE__ */ jsxs12(
|
|
2024
|
+
Box15,
|
|
2025
|
+
{
|
|
2026
|
+
flexDirection: "column",
|
|
2027
|
+
borderStyle: "bold",
|
|
2028
|
+
borderColor: theme.borderFocus,
|
|
2029
|
+
paddingX: 3,
|
|
2030
|
+
paddingY: 1,
|
|
2031
|
+
alignSelf: "center",
|
|
2032
|
+
children: [
|
|
2033
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.dialog.label, bold: true, children: "<New Repo Detected>" }),
|
|
2034
|
+
/* @__PURE__ */ jsx15(Text15, { children: " " }),
|
|
2035
|
+
/* @__PURE__ */ jsxs12(Text15, { color: theme.dialog.fg, children: [
|
|
2036
|
+
"Git remote: ",
|
|
2037
|
+
/* @__PURE__ */ jsx15(Text15, { bold: true, children: remote.value })
|
|
2038
|
+
] }),
|
|
2039
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.dialog.fg, children: "No project is linked to this repo yet." }),
|
|
2040
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.dialog.fg, children: "Would you like to create one and link it?" }),
|
|
2041
|
+
/* @__PURE__ */ jsx15(Text15, { children: " " }),
|
|
2042
|
+
/* @__PURE__ */ jsxs12(Box15, { gap: 3, children: [
|
|
2043
|
+
/* @__PURE__ */ jsx15(Text15, { backgroundColor: theme.dialog.buttonFocusBg, color: theme.dialog.buttonFocusFg, bold: true, children: " y: Create " }),
|
|
2044
|
+
/* @__PURE__ */ jsx15(Text15, { backgroundColor: theme.dialog.buttonBg, color: theme.dialog.buttonFg, children: " n: Skip " })
|
|
2045
|
+
] })
|
|
2046
|
+
]
|
|
2047
|
+
}
|
|
2048
|
+
);
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
// src/tui/components/DependencyList.tsx
|
|
2052
|
+
import { Box as Box16, Text as Text16 } from "ink";
|
|
2053
|
+
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1855
2054
|
function TaskRow({
|
|
1856
2055
|
task,
|
|
1857
2056
|
globalIndex,
|
|
@@ -1859,16 +2058,16 @@ function TaskRow({
|
|
|
1859
2058
|
}) {
|
|
1860
2059
|
const isSelected = globalIndex === selectedIndex;
|
|
1861
2060
|
const statusColor = STATUS_COLOR[task.status] ?? theme.table.fg;
|
|
1862
|
-
return /* @__PURE__ */
|
|
2061
|
+
return /* @__PURE__ */ jsx16(Box16, { children: isSelected ? /* @__PURE__ */ jsxs13(Text16, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
|
|
1863
2062
|
"> ",
|
|
1864
2063
|
task.id.padEnd(12),
|
|
1865
2064
|
task.status.padEnd(14),
|
|
1866
2065
|
task.name
|
|
1867
|
-
] }) : /* @__PURE__ */
|
|
1868
|
-
/* @__PURE__ */
|
|
1869
|
-
/* @__PURE__ */
|
|
1870
|
-
/* @__PURE__ */
|
|
1871
|
-
/* @__PURE__ */
|
|
2066
|
+
] }) : /* @__PURE__ */ jsxs13(Fragment4, { children: [
|
|
2067
|
+
/* @__PURE__ */ jsx16(Text16, { children: " " }),
|
|
2068
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.yaml.value, children: task.id.padEnd(12) }),
|
|
2069
|
+
/* @__PURE__ */ jsx16(Text16, { color: statusColor, children: task.status.padEnd(14) }),
|
|
2070
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.table.fg, children: task.name })
|
|
1872
2071
|
] }) }, task.id);
|
|
1873
2072
|
}
|
|
1874
2073
|
function DependencyList({
|
|
@@ -1889,23 +2088,23 @@ function DependencyList({
|
|
|
1889
2088
|
const relatedOffset = offset;
|
|
1890
2089
|
offset += related.length;
|
|
1891
2090
|
const duplicatesOffset = offset;
|
|
1892
|
-
return /* @__PURE__ */
|
|
1893
|
-
/* @__PURE__ */
|
|
1894
|
-
/* @__PURE__ */
|
|
2091
|
+
return /* @__PURE__ */ jsxs13(Box16, { flexDirection: "column", flexGrow: 1, borderStyle: "bold", borderColor: theme.borderFocus, children: [
|
|
2092
|
+
/* @__PURE__ */ jsxs13(Box16, { gap: 0, children: [
|
|
2093
|
+
/* @__PURE__ */ jsxs13(Text16, { color: theme.title, bold: true, children: [
|
|
1895
2094
|
" ",
|
|
1896
2095
|
"dependencies"
|
|
1897
2096
|
] }),
|
|
1898
|
-
/* @__PURE__ */
|
|
1899
|
-
/* @__PURE__ */
|
|
1900
|
-
/* @__PURE__ */
|
|
2097
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.fg, children: "(" }),
|
|
2098
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.titleHighlight, bold: true, children: task.name }),
|
|
2099
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.fg, children: ")" })
|
|
1901
2100
|
] }),
|
|
1902
|
-
/* @__PURE__ */
|
|
1903
|
-
/* @__PURE__ */
|
|
2101
|
+
/* @__PURE__ */ jsxs13(Box16, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
|
|
2102
|
+
/* @__PURE__ */ jsxs13(Text16, { color: theme.table.headerFg, bold: true, children: [
|
|
1904
2103
|
"BLOCKED BY (",
|
|
1905
2104
|
blockers.length,
|
|
1906
2105
|
")"
|
|
1907
2106
|
] }),
|
|
1908
|
-
blockers.length === 0 ? /* @__PURE__ */
|
|
2107
|
+
blockers.length === 0 ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: " No blockers" }) : blockers.map((t, i) => /* @__PURE__ */ jsx16(
|
|
1909
2108
|
TaskRow,
|
|
1910
2109
|
{
|
|
1911
2110
|
task: t,
|
|
@@ -1915,13 +2114,13 @@ function DependencyList({
|
|
|
1915
2114
|
t.id
|
|
1916
2115
|
))
|
|
1917
2116
|
] }),
|
|
1918
|
-
/* @__PURE__ */
|
|
1919
|
-
/* @__PURE__ */
|
|
2117
|
+
/* @__PURE__ */ jsxs13(Box16, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
|
|
2118
|
+
/* @__PURE__ */ jsxs13(Text16, { color: theme.table.headerFg, bold: true, children: [
|
|
1920
2119
|
"BLOCKS (",
|
|
1921
2120
|
dependents.length,
|
|
1922
2121
|
")"
|
|
1923
2122
|
] }),
|
|
1924
|
-
dependents.length === 0 ? /* @__PURE__ */
|
|
2123
|
+
dependents.length === 0 ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: " No dependents" }) : dependents.map((t, i) => /* @__PURE__ */ jsx16(
|
|
1925
2124
|
TaskRow,
|
|
1926
2125
|
{
|
|
1927
2126
|
task: t,
|
|
@@ -1931,13 +2130,13 @@ function DependencyList({
|
|
|
1931
2130
|
t.id
|
|
1932
2131
|
))
|
|
1933
2132
|
] }),
|
|
1934
|
-
/* @__PURE__ */
|
|
1935
|
-
/* @__PURE__ */
|
|
2133
|
+
/* @__PURE__ */ jsxs13(Box16, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
|
|
2134
|
+
/* @__PURE__ */ jsxs13(Text16, { color: theme.table.headerFg, bold: true, children: [
|
|
1936
2135
|
"RELATES TO (",
|
|
1937
2136
|
related.length,
|
|
1938
2137
|
")"
|
|
1939
2138
|
] }),
|
|
1940
|
-
related.length === 0 ? /* @__PURE__ */
|
|
2139
|
+
related.length === 0 ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: " No related tasks" }) : related.map((t, i) => /* @__PURE__ */ jsx16(
|
|
1941
2140
|
TaskRow,
|
|
1942
2141
|
{
|
|
1943
2142
|
task: t,
|
|
@@ -1947,13 +2146,13 @@ function DependencyList({
|
|
|
1947
2146
|
t.id
|
|
1948
2147
|
))
|
|
1949
2148
|
] }),
|
|
1950
|
-
/* @__PURE__ */
|
|
1951
|
-
/* @__PURE__ */
|
|
2149
|
+
/* @__PURE__ */ jsxs13(Box16, { flexDirection: "column", paddingX: 1, paddingTop: 1, children: [
|
|
2150
|
+
/* @__PURE__ */ jsxs13(Text16, { color: theme.table.headerFg, bold: true, children: [
|
|
1952
2151
|
"DUPLICATES (",
|
|
1953
2152
|
duplicates.length,
|
|
1954
2153
|
")"
|
|
1955
2154
|
] }),
|
|
1956
|
-
duplicates.length === 0 ? /* @__PURE__ */
|
|
2155
|
+
duplicates.length === 0 ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: " No duplicate tasks" }) : duplicates.map((t, i) => /* @__PURE__ */ jsx16(
|
|
1957
2156
|
TaskRow,
|
|
1958
2157
|
{
|
|
1959
2158
|
task: t,
|
|
@@ -1963,19 +2162,19 @@ function DependencyList({
|
|
|
1963
2162
|
t.id
|
|
1964
2163
|
))
|
|
1965
2164
|
] }),
|
|
1966
|
-
/* @__PURE__ */
|
|
1967
|
-
isAddingDep && /* @__PURE__ */
|
|
1968
|
-
/* @__PURE__ */
|
|
1969
|
-
/* @__PURE__ */
|
|
1970
|
-
/* @__PURE__ */
|
|
2165
|
+
/* @__PURE__ */ jsx16(Box16, { flexGrow: 1 }),
|
|
2166
|
+
isAddingDep && /* @__PURE__ */ jsxs13(Box16, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
|
|
2167
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.prompt, children: "depends on (id or id:type): " }),
|
|
2168
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.prompt, children: addDepInput }),
|
|
2169
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.promptSuggest, children: "_" })
|
|
1971
2170
|
] }),
|
|
1972
|
-
/* @__PURE__ */
|
|
2171
|
+
/* @__PURE__ */ jsx16(Box16, { paddingX: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "a: add dep (id or id:relates-to) | x: remove selected | enter: go to task | esc: back" }) })
|
|
1973
2172
|
] });
|
|
1974
2173
|
}
|
|
1975
2174
|
|
|
1976
2175
|
// src/tui/components/EpicPanel.tsx
|
|
1977
|
-
import { Box as
|
|
1978
|
-
import { jsx as
|
|
2176
|
+
import { Box as Box17, Text as Text17 } from "ink";
|
|
2177
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1979
2178
|
var PAGE_SIZE2 = 20;
|
|
1980
2179
|
function EpicPanel({
|
|
1981
2180
|
epics,
|
|
@@ -1988,34 +2187,34 @@ function EpicPanel({
|
|
|
1988
2187
|
const currentPage = Math.floor(selectedIndex / PAGE_SIZE2);
|
|
1989
2188
|
const viewStart = currentPage * PAGE_SIZE2;
|
|
1990
2189
|
const visibleEpics = epics.slice(viewStart, viewStart + PAGE_SIZE2);
|
|
1991
|
-
return /* @__PURE__ */
|
|
1992
|
-
|
|
2190
|
+
return /* @__PURE__ */ jsxs14(
|
|
2191
|
+
Box17,
|
|
1993
2192
|
{
|
|
1994
2193
|
flexDirection: "column",
|
|
1995
2194
|
width: 48,
|
|
1996
2195
|
borderStyle: "bold",
|
|
1997
2196
|
borderColor: isFocused ? theme.borderFocus : theme.border,
|
|
1998
2197
|
children: [
|
|
1999
|
-
/* @__PURE__ */
|
|
2000
|
-
/* @__PURE__ */
|
|
2198
|
+
/* @__PURE__ */ jsxs14(Box17, { children: [
|
|
2199
|
+
/* @__PURE__ */ jsxs14(Text17, { color: theme.title, bold: true, children: [
|
|
2001
2200
|
" ",
|
|
2002
2201
|
"epics"
|
|
2003
2202
|
] }),
|
|
2004
|
-
/* @__PURE__ */
|
|
2203
|
+
/* @__PURE__ */ jsxs14(Text17, { color: theme.titleCounter, bold: true, children: [
|
|
2005
2204
|
"[",
|
|
2006
2205
|
epics.length,
|
|
2007
2206
|
"]"
|
|
2008
2207
|
] }),
|
|
2009
|
-
isReordering && /* @__PURE__ */
|
|
2208
|
+
isReordering && /* @__PURE__ */ jsxs14(Text17, { color: theme.flash.warn, bold: true, children: [
|
|
2010
2209
|
" ",
|
|
2011
2210
|
"REORDER"
|
|
2012
2211
|
] }),
|
|
2013
|
-
filterActive && /* @__PURE__ */
|
|
2212
|
+
filterActive && /* @__PURE__ */ jsxs14(Text17, { color: theme.titleFilter, children: [
|
|
2014
2213
|
" *",
|
|
2015
2214
|
selectedEpicIds.size
|
|
2016
2215
|
] })
|
|
2017
2216
|
] }),
|
|
2018
|
-
/* @__PURE__ */
|
|
2217
|
+
/* @__PURE__ */ jsx17(Box17, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: epics.length === 0 ? /* @__PURE__ */ jsx17(Box17, { paddingX: 1, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "No epics" }) }) : visibleEpics.map((epic, i) => {
|
|
2019
2218
|
const actualIndex = viewStart + i;
|
|
2020
2219
|
const isSelected = actualIndex === selectedIndex && isFocused;
|
|
2021
2220
|
const isChecked = selectedEpicIds.has(epic.id);
|
|
@@ -2023,21 +2222,21 @@ function EpicPanel({
|
|
|
2023
2222
|
const statusColor = STATUS_COLOR[epic.status] ?? theme.table.fg;
|
|
2024
2223
|
if (isSelected) {
|
|
2025
2224
|
const cursorBg = isReordering ? theme.flash.warn : theme.table.cursorBg;
|
|
2026
|
-
return /* @__PURE__ */
|
|
2225
|
+
return /* @__PURE__ */ jsx17(Box17, { children: /* @__PURE__ */ jsxs14(Text17, { backgroundColor: cursorBg, color: theme.table.cursorFg, bold: true, children: [
|
|
2027
2226
|
isReordering ? "~ " : " ",
|
|
2028
2227
|
marker,
|
|
2029
2228
|
" ",
|
|
2030
2229
|
epic.name
|
|
2031
2230
|
] }) }, epic.id);
|
|
2032
2231
|
}
|
|
2033
|
-
return /* @__PURE__ */
|
|
2232
|
+
return /* @__PURE__ */ jsx17(Box17, { children: /* @__PURE__ */ jsxs14(Text17, { color: isChecked ? theme.titleHighlight : statusColor, children: [
|
|
2034
2233
|
" ",
|
|
2035
2234
|
marker,
|
|
2036
2235
|
" ",
|
|
2037
2236
|
epic.name
|
|
2038
2237
|
] }) }, epic.id);
|
|
2039
2238
|
}) }),
|
|
2040
|
-
epics.length > PAGE_SIZE2 && /* @__PURE__ */
|
|
2239
|
+
epics.length > PAGE_SIZE2 && /* @__PURE__ */ jsx17(Box17, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs14(Text17, { dimColor: true, children: [
|
|
2041
2240
|
"[",
|
|
2042
2241
|
viewStart + 1,
|
|
2043
2242
|
"-",
|
|
@@ -2053,8 +2252,8 @@ function EpicPanel({
|
|
|
2053
2252
|
|
|
2054
2253
|
// src/tui/components/EpicPicker.tsx
|
|
2055
2254
|
import { useState as useState6 } from "react";
|
|
2056
|
-
import { Box as
|
|
2057
|
-
import { Fragment as
|
|
2255
|
+
import { Box as Box18, Text as Text18, useInput as useInput6, useStdout as useStdout3 } from "ink";
|
|
2256
|
+
import { Fragment as Fragment5, jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2058
2257
|
function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
|
|
2059
2258
|
const { stdout } = useStdout3();
|
|
2060
2259
|
const termHeight = stdout.rows > 0 ? stdout.rows : 24;
|
|
@@ -2127,53 +2326,53 @@ function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
|
|
|
2127
2326
|
return;
|
|
2128
2327
|
}
|
|
2129
2328
|
});
|
|
2130
|
-
return /* @__PURE__ */
|
|
2131
|
-
/* @__PURE__ */
|
|
2132
|
-
/* @__PURE__ */
|
|
2329
|
+
return /* @__PURE__ */ jsxs15(Box18, { flexDirection: "column", borderStyle: "bold", borderColor: theme.borderFocus, flexGrow: 1, children: [
|
|
2330
|
+
/* @__PURE__ */ jsxs15(Box18, { gap: 0, children: [
|
|
2331
|
+
/* @__PURE__ */ jsxs15(Text18, { color: theme.title, bold: true, children: [
|
|
2133
2332
|
" ",
|
|
2134
2333
|
"assign to epic"
|
|
2135
2334
|
] }),
|
|
2136
|
-
/* @__PURE__ */
|
|
2335
|
+
/* @__PURE__ */ jsxs15(Text18, { color: theme.titleCounter, bold: true, children: [
|
|
2137
2336
|
" ",
|
|
2138
2337
|
"[",
|
|
2139
2338
|
epics.length,
|
|
2140
2339
|
"]"
|
|
2141
2340
|
] })
|
|
2142
2341
|
] }),
|
|
2143
|
-
isSearching ? /* @__PURE__ */
|
|
2144
|
-
/* @__PURE__ */
|
|
2145
|
-
/* @__PURE__ */
|
|
2146
|
-
/* @__PURE__ */
|
|
2147
|
-
] }) : searchQuery ? /* @__PURE__ */
|
|
2342
|
+
isSearching ? /* @__PURE__ */ jsxs15(Box18, { borderStyle: "round", borderColor: theme.prompt, paddingX: 1, children: [
|
|
2343
|
+
/* @__PURE__ */ jsx18(Text18, { color: theme.prompt, children: "/" }),
|
|
2344
|
+
/* @__PURE__ */ jsx18(Text18, { color: theme.prompt, children: searchQuery }),
|
|
2345
|
+
/* @__PURE__ */ jsx18(Text18, { color: theme.promptSuggest, children: "_" })
|
|
2346
|
+
] }) : searchQuery ? /* @__PURE__ */ jsx18(Box18, { paddingX: 1, children: /* @__PURE__ */ jsxs15(Text18, { color: theme.titleFilter, children: [
|
|
2148
2347
|
"/",
|
|
2149
2348
|
searchQuery
|
|
2150
2349
|
] }) }) : null,
|
|
2151
|
-
/* @__PURE__ */
|
|
2350
|
+
/* @__PURE__ */ jsx18(Box18, { paddingX: 1, children: /* @__PURE__ */ jsxs15(Text18, { color: theme.table.headerFg, bold: true, children: [
|
|
2152
2351
|
" ",
|
|
2153
2352
|
"ID".padEnd(14),
|
|
2154
2353
|
"STATUS".padEnd(14),
|
|
2155
2354
|
"NAME"
|
|
2156
2355
|
] }) }),
|
|
2157
|
-
filtered.length === 0 ? /* @__PURE__ */
|
|
2356
|
+
filtered.length === 0 ? /* @__PURE__ */ jsx18(Box18, { paddingX: 2, paddingY: 1, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "No epics match the filter" }) }) : visible.map((epic, i) => {
|
|
2158
2357
|
const actualIndex = viewStart + i;
|
|
2159
2358
|
const isCursor = actualIndex === cursorIndex;
|
|
2160
2359
|
const isCurrent = epic.id === currentEpicId;
|
|
2161
2360
|
const marker = isCurrent ? "* " : " ";
|
|
2162
2361
|
const statusColor = STATUS_COLOR[epic.status] ?? theme.table.fg;
|
|
2163
|
-
return /* @__PURE__ */
|
|
2362
|
+
return /* @__PURE__ */ jsx18(Box18, { paddingX: 1, children: isCursor ? /* @__PURE__ */ jsxs15(Text18, { backgroundColor: theme.table.cursorBg, color: theme.table.cursorFg, bold: true, children: [
|
|
2164
2363
|
"> ",
|
|
2165
2364
|
epic.id.padEnd(14),
|
|
2166
2365
|
epic.status.padEnd(14),
|
|
2167
2366
|
epic.name
|
|
2168
|
-
] }) : /* @__PURE__ */
|
|
2169
|
-
/* @__PURE__ */
|
|
2170
|
-
/* @__PURE__ */
|
|
2171
|
-
/* @__PURE__ */
|
|
2172
|
-
/* @__PURE__ */
|
|
2367
|
+
] }) : /* @__PURE__ */ jsxs15(Fragment5, { children: [
|
|
2368
|
+
/* @__PURE__ */ jsx18(Text18, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: marker }),
|
|
2369
|
+
/* @__PURE__ */ jsx18(Text18, { color: theme.yaml.value, children: epic.id.padEnd(14) }),
|
|
2370
|
+
/* @__PURE__ */ jsx18(Text18, { color: statusColor, children: epic.status.padEnd(14) }),
|
|
2371
|
+
/* @__PURE__ */ jsx18(Text18, { color: isCurrent ? theme.titleHighlight : theme.table.fg, children: epic.name })
|
|
2173
2372
|
] }) }, epic.id);
|
|
2174
2373
|
}),
|
|
2175
|
-
/* @__PURE__ */
|
|
2176
|
-
filtered.length > maxVisible && /* @__PURE__ */
|
|
2374
|
+
/* @__PURE__ */ jsx18(Box18, { flexGrow: 1 }),
|
|
2375
|
+
filtered.length > maxVisible && /* @__PURE__ */ jsx18(Box18, { justifyContent: "flex-end", paddingRight: 1, children: /* @__PURE__ */ jsxs15(Text18, { dimColor: true, children: [
|
|
2177
2376
|
"[",
|
|
2178
2377
|
viewStart + 1,
|
|
2179
2378
|
"-",
|
|
@@ -2182,19 +2381,19 @@ function EpicPicker({ epics, currentEpicId, onSelect, onCancel }) {
|
|
|
2182
2381
|
filtered.length,
|
|
2183
2382
|
"]"
|
|
2184
2383
|
] }) }),
|
|
2185
|
-
/* @__PURE__ */
|
|
2384
|
+
/* @__PURE__ */ jsx18(Box18, { paddingX: 1, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "enter: assign | x: unassign | /: search | esc: cancel" }) })
|
|
2186
2385
|
] });
|
|
2187
2386
|
}
|
|
2188
2387
|
|
|
2189
2388
|
// src/tui/useAutoRefetch.ts
|
|
2190
|
-
import { useEffect, useRef } from "react";
|
|
2389
|
+
import { useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
2191
2390
|
import { watchFile, unwatchFile } from "fs";
|
|
2192
2391
|
var POLL_INTERVAL_MS = 1e3;
|
|
2193
2392
|
var DEBOUNCE_MS = 200;
|
|
2194
2393
|
function useAutoRefetch(dbPath, onRefetch) {
|
|
2195
|
-
const callbackRef =
|
|
2394
|
+
const callbackRef = useRef4(onRefetch);
|
|
2196
2395
|
callbackRef.current = onRefetch;
|
|
2197
|
-
|
|
2396
|
+
useEffect3(() => {
|
|
2198
2397
|
let debounceTimer = null;
|
|
2199
2398
|
const handleChange = () => {
|
|
2200
2399
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
@@ -2222,7 +2421,7 @@ function useAutoRefetch(dbPath, onRefetch) {
|
|
|
2222
2421
|
}
|
|
2223
2422
|
|
|
2224
2423
|
// src/tui/components/App.tsx
|
|
2225
|
-
import { jsx as
|
|
2424
|
+
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2226
2425
|
var STATUS_CYCLE = [
|
|
2227
2426
|
TaskStatus.Backlog,
|
|
2228
2427
|
TaskStatus.Todo,
|
|
@@ -2236,7 +2435,7 @@ function App({ container, initialProject, latestVersion }) {
|
|
|
2236
2435
|
const { stdout } = useStdout4();
|
|
2237
2436
|
const [state, dispatch] = useReducer(appReducer, initialState);
|
|
2238
2437
|
const [, setResizeTick] = useState7(0);
|
|
2239
|
-
|
|
2438
|
+
useEffect4(() => {
|
|
2240
2439
|
const onResize = () => {
|
|
2241
2440
|
setResizeTick((n) => n + 1);
|
|
2242
2441
|
};
|
|
@@ -2401,10 +2600,10 @@ function App({ container, initialProject, latestVersion }) {
|
|
|
2401
2600
|
loadEpics();
|
|
2402
2601
|
}, [loadProjects, loadTasks, loadEpics]);
|
|
2403
2602
|
useAutoRefetch(container.dbPath, refetchAll);
|
|
2404
|
-
|
|
2603
|
+
useEffect4(() => {
|
|
2405
2604
|
loadProjects();
|
|
2406
2605
|
}, [loadProjects]);
|
|
2407
|
-
|
|
2606
|
+
useEffect4(() => {
|
|
2408
2607
|
if (state.projects.length > 0 && !state.activeProject) {
|
|
2409
2608
|
logger.info(`TUI.resolveProject: resolving initialProject=${initialProject ?? "(default)"}`);
|
|
2410
2609
|
const result = container.projectService.resolveProject(initialProject);
|
|
@@ -2421,13 +2620,29 @@ function App({ container, initialProject, latestVersion }) {
|
|
|
2421
2620
|
}
|
|
2422
2621
|
}
|
|
2423
2622
|
}, [state.projects, state.activeProject, initialProject, container]);
|
|
2424
|
-
|
|
2623
|
+
const gitRemoteCheckedRef = useRef5(false);
|
|
2624
|
+
useEffect4(() => {
|
|
2625
|
+
if (state.projects.length > 0 && !gitRemoteCheckedRef.current && !initialProject) {
|
|
2626
|
+
gitRemoteCheckedRef.current = true;
|
|
2627
|
+
const remoteResult = detectGitRemote();
|
|
2628
|
+
if (remoteResult.ok && remoteResult.value) {
|
|
2629
|
+
const remote = remoteResult.value;
|
|
2630
|
+
const alreadyLinked = state.projects.some((p) => p.gitRemote?.equals(remote));
|
|
2631
|
+
const dismissed = isDismissedRemote(container.dismissedGitRemotesPath, remote);
|
|
2632
|
+
if (!alreadyLinked && !dismissed) {
|
|
2633
|
+
logger.info(`TUI.detectGitRemote: unlinked remote detected: ${remote.value}`);
|
|
2634
|
+
dispatch({ type: "SET_DETECTED_GIT_REMOTE", remote });
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
}, [state.projects, initialProject]);
|
|
2639
|
+
useEffect4(() => {
|
|
2425
2640
|
if (state.activeProject) {
|
|
2426
2641
|
loadTasks();
|
|
2427
2642
|
loadEpics();
|
|
2428
2643
|
}
|
|
2429
2644
|
}, [state.activeProject, state.filter, loadTasks, loadEpics]);
|
|
2430
|
-
|
|
2645
|
+
useEffect4(() => {
|
|
2431
2646
|
if (state.flash) {
|
|
2432
2647
|
const timer = setTimeout(() => {
|
|
2433
2648
|
dispatch({ type: "CLEAR_FLASH" });
|
|
@@ -2459,11 +2674,20 @@ function App({ container, initialProject, latestVersion }) {
|
|
|
2459
2674
|
}
|
|
2460
2675
|
return;
|
|
2461
2676
|
}
|
|
2677
|
+
if (state.detectedGitRemote && state.activeView === ViewType.TaskList) {
|
|
2678
|
+
if (input === "y") {
|
|
2679
|
+
dispatch({ type: "NAVIGATE_TO", view: ViewType.ProjectCreate });
|
|
2680
|
+
} else if (input === "n" || key.escape) {
|
|
2681
|
+
dismissRemote(container.dismissedGitRemotesPath, state.detectedGitRemote);
|
|
2682
|
+
dispatch({ type: "SET_DETECTED_GIT_REMOTE", remote: null });
|
|
2683
|
+
}
|
|
2684
|
+
return;
|
|
2685
|
+
}
|
|
2462
2686
|
if (state.activeView === ViewType.Help) {
|
|
2463
2687
|
dispatch({ type: "GO_BACK" });
|
|
2464
2688
|
return;
|
|
2465
2689
|
}
|
|
2466
|
-
if (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit || state.activeView === ViewType.ProjectSelector || state.activeView === ViewType.ProjectCreate || state.activeView === ViewType.ProjectLink || state.activeView === ViewType.EpicPicker) {
|
|
2690
|
+
if (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit || state.activeView === ViewType.ProjectSelector || state.activeView === ViewType.ProjectCreate || state.activeView === ViewType.ProjectEdit || state.activeView === ViewType.ProjectLink || state.activeView === ViewType.EpicPicker) {
|
|
2467
2691
|
return;
|
|
2468
2692
|
}
|
|
2469
2693
|
if (state.activeView === ViewType.DependencyList && state.isAddingDep) {
|
|
@@ -3037,35 +3261,57 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3037
3261
|
const handleProjectCreate = useCallback2(() => {
|
|
3038
3262
|
dispatch({ type: "NAVIGATE_TO", view: ViewType.ProjectCreate });
|
|
3039
3263
|
}, []);
|
|
3264
|
+
const handleProjectEdit = useCallback2((project) => {
|
|
3265
|
+
dispatch({ type: "SET_EDITING_PROJECT", project });
|
|
3266
|
+
dispatch({ type: "NAVIGATE_TO", view: ViewType.ProjectEdit });
|
|
3267
|
+
}, []);
|
|
3040
3268
|
const handleProjectFormSave = useCallback2(
|
|
3041
3269
|
(data) => {
|
|
3042
|
-
const
|
|
3270
|
+
const editing = state.activeView === ViewType.ProjectEdit ? state.editingProject : null;
|
|
3271
|
+
const result = editing ? container.projectService.updateProject(editing.id, {
|
|
3272
|
+
name: data.name,
|
|
3273
|
+
description: data.description,
|
|
3274
|
+
isDefault: data.isDefault,
|
|
3275
|
+
gitRemote: data.gitRemote || null
|
|
3276
|
+
}) : container.projectService.createProject({
|
|
3043
3277
|
name: data.name,
|
|
3044
3278
|
key: data.key || void 0,
|
|
3045
3279
|
description: data.description || void 0,
|
|
3046
|
-
isDefault: data.isDefault
|
|
3280
|
+
isDefault: data.isDefault,
|
|
3281
|
+
gitRemote: data.gitRemote || void 0
|
|
3047
3282
|
});
|
|
3048
3283
|
if (result.ok) {
|
|
3049
|
-
|
|
3284
|
+
const verb = editing ? "updated" : "created";
|
|
3285
|
+
logger.info(
|
|
3286
|
+
`TUI.${editing ? "editProject" : "createProject"}: ${verb} key=${result.value.key} name=${result.value.name}`
|
|
3287
|
+
);
|
|
3050
3288
|
dispatch({
|
|
3051
3289
|
type: "FLASH",
|
|
3052
|
-
message: `Project
|
|
3290
|
+
message: `Project ${verb}: ${result.value.name}`,
|
|
3053
3291
|
level: "info"
|
|
3054
3292
|
});
|
|
3055
|
-
|
|
3056
|
-
|
|
3293
|
+
if (editing && state.activeProject?.id === result.value.id) {
|
|
3294
|
+
dispatch({ type: "SET_ACTIVE_PROJECT", project: result.value });
|
|
3295
|
+
}
|
|
3296
|
+
if (!editing) {
|
|
3297
|
+
dispatch({ type: "SET_ACTIVE_PROJECT", project: result.value });
|
|
3298
|
+
dispatch({ type: "GO_BACK" });
|
|
3299
|
+
}
|
|
3057
3300
|
dispatch({ type: "GO_BACK" });
|
|
3058
3301
|
loadProjects();
|
|
3059
3302
|
} else {
|
|
3060
|
-
logger.error("
|
|
3303
|
+
logger.error(`TUI.${editing ? "editProject" : "createProject"}: failed`, result.error);
|
|
3061
3304
|
dispatch({ type: "FLASH", message: result.error.message, level: "error" });
|
|
3062
3305
|
}
|
|
3063
3306
|
},
|
|
3064
|
-
[container, loadProjects]
|
|
3307
|
+
[container, state.activeView, state.editingProject, state.activeProject, loadProjects]
|
|
3065
3308
|
);
|
|
3066
3309
|
const handleProjectFormCancel = useCallback2(() => {
|
|
3310
|
+
if (state.activeView === ViewType.ProjectCreate && state.detectedGitRemote) {
|
|
3311
|
+
dismissRemote(container.dismissedGitRemotesPath, state.detectedGitRemote);
|
|
3312
|
+
}
|
|
3067
3313
|
dispatch({ type: "GO_BACK" });
|
|
3068
|
-
}, []);
|
|
3314
|
+
}, [state.activeView, state.detectedGitRemote, container]);
|
|
3069
3315
|
const handleProjectLink = useCallback2((project) => {
|
|
3070
3316
|
dispatch({ type: "SET_LINKING_PROJECT", project });
|
|
3071
3317
|
dispatch({ type: "NAVIGATE_TO", view: ViewType.ProjectLink });
|
|
@@ -3076,11 +3322,11 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3076
3322
|
const result = container.projectService.linkGitRemote(state.linkingProject.id, remote);
|
|
3077
3323
|
if (result.ok) {
|
|
3078
3324
|
logger.info(
|
|
3079
|
-
`TUI.linkGitRemote: linked project=${state.linkingProject.id} remote=${result.value.gitRemote}`
|
|
3325
|
+
`TUI.linkGitRemote: linked project=${state.linkingProject.id} remote=${result.value.gitRemote?.value}`
|
|
3080
3326
|
);
|
|
3081
3327
|
dispatch({
|
|
3082
3328
|
type: "FLASH",
|
|
3083
|
-
message: `Linked to: ${result.value.gitRemote}`,
|
|
3329
|
+
message: `Linked to: ${result.value.gitRemote?.value}`,
|
|
3084
3330
|
level: "info"
|
|
3085
3331
|
});
|
|
3086
3332
|
dispatch({ type: "GO_BACK" });
|
|
@@ -3108,8 +3354,8 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3108
3354
|
const handleLinkDetect = useCallback2(() => {
|
|
3109
3355
|
const result = detectGitRemote();
|
|
3110
3356
|
if (result.ok && result.value) {
|
|
3111
|
-
dispatch({ type: "FLASH", message: `Detected: ${result.value}`, level: "info" });
|
|
3112
|
-
return result.value;
|
|
3357
|
+
dispatch({ type: "FLASH", message: `Detected: ${result.value.value}`, level: "info" });
|
|
3358
|
+
return result.value.value;
|
|
3113
3359
|
}
|
|
3114
3360
|
dispatch({ type: "FLASH", message: "No git remote detected in cwd", level: "warn" });
|
|
3115
3361
|
return null;
|
|
@@ -3138,17 +3384,17 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3138
3384
|
return result.ok ? result.value : [];
|
|
3139
3385
|
}, [container, state.activeProject, state.tasks]);
|
|
3140
3386
|
const previewTaskId = previewTask?.id ?? null;
|
|
3141
|
-
|
|
3387
|
+
useEffect4(() => {
|
|
3142
3388
|
if (state.activeView === ViewType.TaskList && previewTaskId) {
|
|
3143
3389
|
loadDeps(previewTaskId);
|
|
3144
3390
|
}
|
|
3145
3391
|
}, [state.activeView, previewTaskId, loadDeps]);
|
|
3146
|
-
return /* @__PURE__ */
|
|
3147
|
-
/* @__PURE__ */
|
|
3148
|
-
/* @__PURE__ */
|
|
3149
|
-
state.confirmDelete && /* @__PURE__ */
|
|
3150
|
-
!state.confirmDelete && state.activeView === ViewType.TaskList && /* @__PURE__ */
|
|
3151
|
-
/* @__PURE__ */
|
|
3392
|
+
return /* @__PURE__ */ jsxs16(Box19, { flexDirection: "column", height: stdout.rows, children: [
|
|
3393
|
+
/* @__PURE__ */ jsx19(Header, { state, latestVersion }),
|
|
3394
|
+
/* @__PURE__ */ jsxs16(Box19, { flexDirection: "column", flexGrow: 1, overflowY: "hidden", children: [
|
|
3395
|
+
state.confirmDelete && /* @__PURE__ */ jsx19(ConfirmDialog, { task: state.confirmDelete }),
|
|
3396
|
+
!state.confirmDelete && state.activeView === ViewType.TaskList && (state.detectedGitRemote ? /* @__PURE__ */ jsx19(DetectedProjectDialog, { remote: state.detectedGitRemote }) : /* @__PURE__ */ jsxs16(Box19, { flexDirection: "row", flexGrow: 1, children: [
|
|
3397
|
+
/* @__PURE__ */ jsx19(
|
|
3152
3398
|
EpicPanel,
|
|
3153
3399
|
{
|
|
3154
3400
|
epics: state.epics,
|
|
@@ -3158,7 +3404,7 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3158
3404
|
isReordering: state.isEpicReordering
|
|
3159
3405
|
}
|
|
3160
3406
|
),
|
|
3161
|
-
/* @__PURE__ */
|
|
3407
|
+
/* @__PURE__ */ jsx19(Box19, { width: taskListWidth, children: /* @__PURE__ */ jsx19(
|
|
3162
3408
|
TaskList,
|
|
3163
3409
|
{
|
|
3164
3410
|
tasks: state.tasks,
|
|
@@ -3179,7 +3425,7 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3179
3425
|
epicFilterActive: state.selectedEpicIds.size > 0
|
|
3180
3426
|
}
|
|
3181
3427
|
) }),
|
|
3182
|
-
/* @__PURE__ */
|
|
3428
|
+
/* @__PURE__ */ jsx19(Box19, { width: taskDetailWidth, children: previewTask ? /* @__PURE__ */ jsx19(
|
|
3183
3429
|
TaskDetail,
|
|
3184
3430
|
{
|
|
3185
3431
|
task: previewTask,
|
|
@@ -3190,24 +3436,24 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3190
3436
|
isFocused: state.focusedPanel === "detail",
|
|
3191
3437
|
scrollOffset: state.detailScrollOffset
|
|
3192
3438
|
}
|
|
3193
|
-
) : /* @__PURE__ */
|
|
3194
|
-
|
|
3439
|
+
) : /* @__PURE__ */ jsxs16(
|
|
3440
|
+
Box19,
|
|
3195
3441
|
{
|
|
3196
3442
|
flexDirection: "column",
|
|
3197
3443
|
flexGrow: 1,
|
|
3198
3444
|
borderStyle: "bold",
|
|
3199
3445
|
borderColor: theme.border,
|
|
3200
3446
|
children: [
|
|
3201
|
-
/* @__PURE__ */
|
|
3447
|
+
/* @__PURE__ */ jsx19(Box19, { children: /* @__PURE__ */ jsxs16(Text19, { color: theme.title, bold: true, children: [
|
|
3202
3448
|
" ",
|
|
3203
3449
|
"detail"
|
|
3204
3450
|
] }) }),
|
|
3205
|
-
/* @__PURE__ */
|
|
3451
|
+
/* @__PURE__ */ jsx19(Box19, { flexGrow: 1, justifyContent: "center", alignItems: "center", children: /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: "No task selected" }) })
|
|
3206
3452
|
]
|
|
3207
3453
|
}
|
|
3208
3454
|
) })
|
|
3209
|
-
] }),
|
|
3210
|
-
!state.confirmDelete && state.activeView === ViewType.TaskDetail && state.selectedTask && /* @__PURE__ */
|
|
3455
|
+
] })),
|
|
3456
|
+
!state.confirmDelete && state.activeView === ViewType.TaskDetail && state.selectedTask && /* @__PURE__ */ jsx19(
|
|
3211
3457
|
TaskDetail,
|
|
3212
3458
|
{
|
|
3213
3459
|
task: state.selectedTask,
|
|
@@ -3218,7 +3464,7 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3218
3464
|
scrollOffset: state.detailScrollOffset
|
|
3219
3465
|
}
|
|
3220
3466
|
),
|
|
3221
|
-
!state.confirmDelete && state.activeView === ViewType.DependencyList && state.selectedTask && /* @__PURE__ */
|
|
3467
|
+
!state.confirmDelete && state.activeView === ViewType.DependencyList && state.selectedTask && /* @__PURE__ */ jsx19(
|
|
3222
3468
|
DependencyList,
|
|
3223
3469
|
{
|
|
3224
3470
|
task: state.selectedTask,
|
|
@@ -3231,7 +3477,7 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3231
3477
|
addDepInput: state.addDepInput
|
|
3232
3478
|
}
|
|
3233
3479
|
),
|
|
3234
|
-
!state.confirmDelete && (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit) && /* @__PURE__ */
|
|
3480
|
+
!state.confirmDelete && (state.activeView === ViewType.TaskCreate || state.activeView === ViewType.TaskEdit) && /* @__PURE__ */ jsx19(
|
|
3235
3481
|
TaskForm,
|
|
3236
3482
|
{
|
|
3237
3483
|
editingTask: state.activeView === ViewType.TaskEdit ? state.selectedTask : null,
|
|
@@ -3241,7 +3487,7 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3241
3487
|
onCancel: handleFormCancel
|
|
3242
3488
|
}
|
|
3243
3489
|
),
|
|
3244
|
-
!state.confirmDelete && state.activeView === ViewType.EpicPicker && state.selectedTask && /* @__PURE__ */
|
|
3490
|
+
!state.confirmDelete && state.activeView === ViewType.EpicPicker && state.selectedTask && /* @__PURE__ */ jsx19(
|
|
3245
3491
|
EpicPicker,
|
|
3246
3492
|
{
|
|
3247
3493
|
epics: state.epics,
|
|
@@ -3250,20 +3496,36 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3250
3496
|
onCancel: handleEpicPickerCancel
|
|
3251
3497
|
}
|
|
3252
3498
|
),
|
|
3253
|
-
!state.confirmDelete && state.activeView === ViewType.ProjectSelector && /* @__PURE__ */
|
|
3499
|
+
!state.confirmDelete && state.activeView === ViewType.ProjectSelector && /* @__PURE__ */ jsx19(
|
|
3254
3500
|
ProjectSelector,
|
|
3255
3501
|
{
|
|
3256
3502
|
projects: state.projects,
|
|
3257
3503
|
activeProject: state.activeProject,
|
|
3258
3504
|
onSelect: handleProjectSelect,
|
|
3259
3505
|
onCreate: handleProjectCreate,
|
|
3506
|
+
onEdit: handleProjectEdit,
|
|
3260
3507
|
onSetDefault: handleSetDefault,
|
|
3261
3508
|
onLink: handleProjectLink,
|
|
3262
3509
|
onCancel: handleProjectCancel
|
|
3263
3510
|
}
|
|
3264
3511
|
),
|
|
3265
|
-
!state.confirmDelete && state.activeView === ViewType.ProjectCreate && /* @__PURE__ */
|
|
3266
|
-
|
|
3512
|
+
!state.confirmDelete && state.activeView === ViewType.ProjectCreate && /* @__PURE__ */ jsx19(
|
|
3513
|
+
ProjectForm,
|
|
3514
|
+
{
|
|
3515
|
+
initialGitRemote: state.detectedGitRemote ?? void 0,
|
|
3516
|
+
onSave: handleProjectFormSave,
|
|
3517
|
+
onCancel: handleProjectFormCancel
|
|
3518
|
+
}
|
|
3519
|
+
),
|
|
3520
|
+
!state.confirmDelete && state.activeView === ViewType.ProjectEdit && state.editingProject && /* @__PURE__ */ jsx19(
|
|
3521
|
+
ProjectForm,
|
|
3522
|
+
{
|
|
3523
|
+
editingProject: state.editingProject,
|
|
3524
|
+
onSave: handleProjectFormSave,
|
|
3525
|
+
onCancel: handleProjectFormCancel
|
|
3526
|
+
}
|
|
3527
|
+
),
|
|
3528
|
+
!state.confirmDelete && state.activeView === ViewType.ProjectLink && state.linkingProject && /* @__PURE__ */ jsx19(
|
|
3267
3529
|
ProjectLinkForm,
|
|
3268
3530
|
{
|
|
3269
3531
|
project: state.linkingProject,
|
|
@@ -3273,18 +3535,18 @@ ${state.selectedTask.additionalRequirements}`;
|
|
|
3273
3535
|
onCancel: handleLinkCancel
|
|
3274
3536
|
}
|
|
3275
3537
|
),
|
|
3276
|
-
!state.confirmDelete && state.activeView === ViewType.Help && /* @__PURE__ */
|
|
3538
|
+
!state.confirmDelete && state.activeView === ViewType.Help && /* @__PURE__ */ jsx19(HelpOverlay, {})
|
|
3277
3539
|
] }),
|
|
3278
|
-
/* @__PURE__ */
|
|
3279
|
-
state.flash && /* @__PURE__ */
|
|
3540
|
+
/* @__PURE__ */ jsx19(Crumbs, { breadcrumbs: state.breadcrumbs }),
|
|
3541
|
+
state.flash && /* @__PURE__ */ jsx19(FlashMessage, { message: state.flash.message, level: state.flash.level })
|
|
3280
3542
|
] });
|
|
3281
3543
|
}
|
|
3282
3544
|
|
|
3283
3545
|
// src/tui/index.tsx
|
|
3284
|
-
import { jsx as
|
|
3546
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
3285
3547
|
async function launchTUI(container, initialProject, latestVersion) {
|
|
3286
3548
|
const instance = render(
|
|
3287
|
-
/* @__PURE__ */
|
|
3549
|
+
/* @__PURE__ */ jsx20(App, { container, initialProject, latestVersion }),
|
|
3288
3550
|
{
|
|
3289
3551
|
exitOnCtrlC: true
|
|
3290
3552
|
}
|
|
@@ -3294,4 +3556,4 @@ async function launchTUI(container, initialProject, latestVersion) {
|
|
|
3294
3556
|
export {
|
|
3295
3557
|
launchTUI
|
|
3296
3558
|
};
|
|
3297
|
-
//# sourceMappingURL=tui-
|
|
3559
|
+
//# sourceMappingURL=tui-NCL4RFFD.js.map
|