loop-task 2.0.2 → 2.0.4
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/config/constants.js +10 -0
- package/dist/i18n/en.json +12 -0
- package/dist/loop-config.js +59 -0
- package/dist/shared/useUndoRedo.js +79 -0
- package/dist/tui/components/CodeEditorModal.js +245 -0
- package/dist/tui/components/CodeEditorPreview.js +14 -0
- package/dist/tui/components/CreateForm.js +12 -10
- package/dist/tui/components/TaskForm.js +12 -12
- package/dist/tui/utils/syntax.js +128 -0
- package/package.json +2 -2
- package/dist/board/App.js +0 -405
- package/dist/board/board-log.js +0 -18
- package/dist/board/components/ActionButtons.js +0 -46
- package/dist/board/components/BoardButton.js +0 -58
- package/dist/board/components/ConfirmModal.js +0 -36
- package/dist/board/components/ContextHelpModal.js +0 -22
- package/dist/board/components/CreateForm.js +0 -271
- package/dist/board/components/CreateProjectModal.js +0 -97
- package/dist/board/components/DeleteProjectConfirm.js +0 -53
- package/dist/board/components/DetailView.js +0 -25
- package/dist/board/components/EditProjectModal.js +0 -99
- package/dist/board/components/FilterBar.js +0 -17
- package/dist/board/components/Footer.js +0 -52
- package/dist/board/components/Header.js +0 -46
- package/dist/board/components/HelpModal.js +0 -65
- package/dist/board/components/Inspector.js +0 -12
- package/dist/board/components/LogModal.js +0 -82
- package/dist/board/components/Navigator.js +0 -84
- package/dist/board/components/ProjectsModal.js +0 -70
- package/dist/board/components/ProjectsPage.js +0 -134
- package/dist/board/components/RunHistory.js +0 -83
- package/dist/board/components/SearchBox.js +0 -10
- package/dist/board/components/SearchSelect.js +0 -132
- package/dist/board/components/TaskBrowser.js +0 -95
- package/dist/board/components/TaskFilterBar.js +0 -6
- package/dist/board/components/TaskForm.js +0 -155
- package/dist/board/components/Timeline.js +0 -5
- package/dist/board/daemon.js +0 -117
- package/dist/board/focus-context.js +0 -98
- package/dist/board/hooks/useBoardKeybindings.js +0 -344
- package/dist/board/hooks/useBreakpoint.js +0 -6
- package/dist/board/hooks/useHoverState.js +0 -11
- package/dist/board/hooks/useInputShortcuts.js +0 -61
- package/dist/board/hooks/useLogStream.js +0 -32
- package/dist/board/hooks/useLoopPolling.js +0 -28
- package/dist/board/hooks/useTabNav.js +0 -60
- package/dist/board/hooks/useTaskKeybindings.js +0 -120
- package/dist/board/index.js +0 -32
- package/dist/board/router.js +0 -16
- package/dist/board/state.js +0 -99
- package/dist/board/toast.js +0 -65
- package/dist/board/types.js +0 -1
- package/dist/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/client/commands.d.ts +0 -11
- package/dist/client/commands.d.ts.map +0 -1
- package/dist/client/commands.js.map +0 -1
- package/dist/client/ipc.d.ts +0 -5
- package/dist/client/ipc.d.ts.map +0 -1
- package/dist/client/ipc.js.map +0 -1
- package/dist/daemon/index.d.ts +0 -2
- package/dist/daemon/index.d.ts.map +0 -1
- package/dist/daemon/index.js.map +0 -1
- package/dist/daemon/manager.d.ts +0 -17
- package/dist/daemon/manager.d.ts.map +0 -1
- package/dist/daemon/manager.js.map +0 -1
- package/dist/daemon/server.d.ts +0 -15
- package/dist/daemon/server.d.ts.map +0 -1
- package/dist/daemon/server.js.map +0 -1
- package/dist/daemon/spawner.d.ts +0 -3
- package/dist/daemon/spawner.d.ts.map +0 -1
- package/dist/daemon/spawner.js.map +0 -1
- package/dist/daemon/state.d.ts +0 -15
- package/dist/daemon/state.d.ts.map +0 -1
- package/dist/daemon/state.js.map +0 -1
- package/dist/duration.d.ts +0 -3
- package/dist/duration.d.ts.map +0 -1
- package/dist/duration.js.map +0 -1
- package/dist/logger.d.ts +0 -11
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/loop-config.d.ts +0 -13
- package/dist/loop-config.d.ts.map +0 -1
- package/dist/loop-config.js.map +0 -1
- package/dist/loop.d.ts +0 -46
- package/dist/loop.d.ts.map +0 -1
- package/dist/loop.js +0 -363
- package/dist/loop.js.map +0 -1
- package/dist/open-board.d.ts +0 -2
- package/dist/open-board.d.ts.map +0 -1
- package/dist/open-board.js +0 -19
- package/dist/open-board.js.map +0 -1
- package/dist/tui/app.d.ts +0 -57
- package/dist/tui/app.d.ts.map +0 -1
- package/dist/tui/app.js.map +0 -1
- package/dist/tui/components/ActionButtons.js +0 -49
- package/dist/tui/components/CommandBuilderField.js +0 -143
- package/dist/tui/components/CommandEditorModal.js +0 -159
- package/dist/tui/components/ConfirmModal.js +0 -9
- package/dist/tui/components/FilterBar.js +0 -13
- package/dist/tui/components/FocusableSearchSelect.js +0 -82
- package/dist/tui/components/Footer.js +0 -42
- package/dist/tui/components/InlineCommandEditor.js +0 -143
- package/dist/tui/components/PatchEditForm.js +0 -78
- package/dist/tui/components/SearchSelect.js +0 -67
- package/dist/tui/components/TaskFilterBar.js +0 -9
- package/dist/tui/create-loop.d.ts +0 -3
- package/dist/tui/create-loop.d.ts.map +0 -1
- package/dist/tui/create-loop.js +0 -90
- package/dist/tui/create-loop.js.map +0 -1
- package/dist/tui/dashboard.d.ts +0 -2
- package/dist/tui/dashboard.d.ts.map +0 -1
- package/dist/tui/dashboard.js +0 -45
- package/dist/tui/dashboard.js.map +0 -1
- package/dist/tui/events.d.ts +0 -3
- package/dist/tui/events.d.ts.map +0 -1
- package/dist/tui/events.js +0 -244
- package/dist/tui/events.js.map +0 -1
- package/dist/tui/popup.d.ts +0 -3
- package/dist/tui/popup.d.ts.map +0 -1
- package/dist/tui/popup.js +0 -58
- package/dist/tui/popup.js.map +0 -1
- package/dist/tui/render.d.ts +0 -9
- package/dist/tui/render.d.ts.map +0 -1
- package/dist/tui/render.js +0 -305
- package/dist/tui/render.js.map +0 -1
- package/dist/types.d.ts +0 -94
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- /package/dist/tui/{app.js → App.js} +0 -0
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useHoverState } from "../hooks/useHoverState.js";
|
|
3
|
-
const PRIMARY_REST = "#1e3a5f";
|
|
4
|
-
const PRIMARY_HOVER = "#2a4f7a";
|
|
5
|
-
const PRIMARY_FOCUSED = "#1e3a8a";
|
|
6
|
-
const DEFAULT_HOVER = "#334155";
|
|
7
|
-
const DEFAULT_FOCUSED = "#374151";
|
|
8
|
-
export function BoardButton(props) {
|
|
9
|
-
const { label, onMouseDown, focused, variant = "default", marginRight } = props;
|
|
10
|
-
const { isHovered, hoverProps } = useHoverState();
|
|
11
|
-
let bg;
|
|
12
|
-
const fg = "#e5e7eb";
|
|
13
|
-
let border;
|
|
14
|
-
if (variant === "primary") {
|
|
15
|
-
if (focused) {
|
|
16
|
-
bg = PRIMARY_FOCUSED;
|
|
17
|
-
border = "#38bdf8";
|
|
18
|
-
}
|
|
19
|
-
else if (isHovered) {
|
|
20
|
-
bg = PRIMARY_HOVER;
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
bg = PRIMARY_REST;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
else if (variant === "danger") {
|
|
27
|
-
if (focused) {
|
|
28
|
-
bg = "#7f1d1d";
|
|
29
|
-
border = "#f87171";
|
|
30
|
-
}
|
|
31
|
-
else if (isHovered) {
|
|
32
|
-
bg = "#7f1d1d";
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
bg = "#0b0b0b";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
if (focused) {
|
|
40
|
-
bg = DEFAULT_FOCUSED;
|
|
41
|
-
border = "#38bdf8";
|
|
42
|
-
}
|
|
43
|
-
else if (isHovered) {
|
|
44
|
-
bg = DEFAULT_HOVER;
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
bg = "#0b0b0b";
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return (_jsx("box", { border: true, onMouseDown: onMouseDown, borderColor: border, style: {
|
|
51
|
-
paddingLeft: 2,
|
|
52
|
-
paddingRight: 2,
|
|
53
|
-
marginRight,
|
|
54
|
-
backgroundColor: bg,
|
|
55
|
-
justifyContent: "center",
|
|
56
|
-
alignItems: "center",
|
|
57
|
-
}, ...hoverProps, children: _jsx("text", { fg: fg, children: _jsx("strong", { children: label }) }) }));
|
|
58
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useTerminalDimensions } from "@opentui/react";
|
|
3
|
-
import { t } from "../../i18n/index.js";
|
|
4
|
-
import { useHoverState } from "../hooks/useHoverState.js";
|
|
5
|
-
import { HOVER_BG } from "../../config/constants.js";
|
|
6
|
-
export function ConfirmModal(props) {
|
|
7
|
-
const { message, choice, onYes, onNo } = props;
|
|
8
|
-
const { width } = useTerminalDimensions();
|
|
9
|
-
return (_jsx("box", { style: {
|
|
10
|
-
position: "absolute",
|
|
11
|
-
top: 0,
|
|
12
|
-
left: 0,
|
|
13
|
-
width: "100%",
|
|
14
|
-
height: "100%",
|
|
15
|
-
justifyContent: "center",
|
|
16
|
-
alignItems: "center",
|
|
17
|
-
zIndex: 100,
|
|
18
|
-
}, children: _jsxs("box", { title: t("board.confirmTitle"), border: true, style: {
|
|
19
|
-
flexDirection: "column",
|
|
20
|
-
padding: 1,
|
|
21
|
-
minWidth: Math.min(44, width - 4),
|
|
22
|
-
backgroundColor: "#111827",
|
|
23
|
-
}, children: [_jsx("text", { children: message }), _jsx("text", { children: " " }), _jsxs("box", { style: { flexDirection: "row", justifyContent: "center" }, children: [_jsx(ConfirmButton, { label: t("board.yes"), onMouseDown: onYes, selected: choice === 1, width: 12, marginRight: 1 }), _jsx(ConfirmButton, { label: t("board.no"), onMouseDown: onNo, selected: choice === 0, width: 12 })] })] }) }));
|
|
24
|
-
}
|
|
25
|
-
function ConfirmButton(props) {
|
|
26
|
-
const { isHovered, hoverProps } = useHoverState();
|
|
27
|
-
const bg = props.selected ? "#1e3a8a" : isHovered ? HOVER_BG : undefined;
|
|
28
|
-
const borderColor = props.selected ? "#38bdf8" : undefined;
|
|
29
|
-
return (_jsx("box", { border: true, onMouseDown: props.onMouseDown, borderColor: borderColor, style: {
|
|
30
|
-
width: props.width,
|
|
31
|
-
backgroundColor: bg,
|
|
32
|
-
justifyContent: "center",
|
|
33
|
-
alignItems: "center",
|
|
34
|
-
marginRight: props.marginRight,
|
|
35
|
-
}, ...hoverProps, children: _jsx("text", { fg: "#e5e7eb", children: _jsx("strong", { children: props.label }) }) }));
|
|
36
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useTerminalDimensions } from "@opentui/react";
|
|
3
|
-
import { t } from "../../i18n/index.js";
|
|
4
|
-
export function ContextHelpModal(props) {
|
|
5
|
-
const { onClose } = props;
|
|
6
|
-
const { width } = useTerminalDimensions();
|
|
7
|
-
return (_jsx("box", { onMouseDown: onClose, style: {
|
|
8
|
-
position: "absolute",
|
|
9
|
-
top: 0,
|
|
10
|
-
left: 0,
|
|
11
|
-
width: "100%",
|
|
12
|
-
height: "100%",
|
|
13
|
-
justifyContent: "center",
|
|
14
|
-
alignItems: "center",
|
|
15
|
-
zIndex: 90,
|
|
16
|
-
}, children: _jsxs("box", { title: t("context.helpTitle"), border: true, style: {
|
|
17
|
-
flexDirection: "column",
|
|
18
|
-
padding: 1,
|
|
19
|
-
minWidth: Math.min(72, width - 4),
|
|
20
|
-
backgroundColor: "#111827",
|
|
21
|
-
}, children: [_jsx("text", { fg: "#e5e7eb", children: t("context.helpRules") }), _jsx("text", { children: " " }), _jsx("text", { fg: "#38bdf8", children: "{{key}} Templates" }), _jsx("text", { fg: "#e5e7eb", children: t("context.helpTemplates") }), _jsx("text", { children: " " }), _jsx("text", { fg: "#f59e0b", children: "Caveat" }), _jsx("text", { fg: "#e5e7eb", children: t("context.helpCaveat") }), _jsx("text", { children: " " }), _jsx("text", { fg: "#34d399", children: "jq Tip" }), _jsx("text", { fg: "#e5e7eb", children: t("context.helpJqTip") }), _jsx("text", { children: " " }), _jsx("text", { fg: "#9ca3af", children: "Press any key or click to close" })] }) }));
|
|
22
|
-
}
|
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useRef, useState } from "react";
|
|
3
|
-
import { useKeyboard, useTerminalDimensions } from "@opentui/react";
|
|
4
|
-
import fs from "node:fs";
|
|
5
|
-
import { buildLoopOptions, parseCommandLine } from "../../loop-config.js";
|
|
6
|
-
import { t } from "../../i18n/index.js";
|
|
7
|
-
import { commandLine } from "../format.js";
|
|
8
|
-
import { createLoop, updateLoop, listTasks } from "../daemon.js";
|
|
9
|
-
import { useHoverState } from "../hooks/useHoverState.js";
|
|
10
|
-
import { useInputShortcuts } from "../hooks/useInputShortcuts.js";
|
|
11
|
-
import { useTabNav } from "../hooks/useTabNav.js";
|
|
12
|
-
import { SearchSelect } from "./SearchSelect.js";
|
|
13
|
-
import { HOVER_BG } from "../../config/constants.js";
|
|
14
|
-
export const TASK_MODE_INLINE = "inline";
|
|
15
|
-
export const TASK_MODE_EXISTING = "existing";
|
|
16
|
-
export const createFields = ["interval", "taskMode", "command", "cwd", "taskId", "description", "runNow", "maxRuns", "project"];
|
|
17
|
-
export function createInitialValues(loop, currentProjectId) {
|
|
18
|
-
if (!loop) {
|
|
19
|
-
return {
|
|
20
|
-
interval: "30m",
|
|
21
|
-
taskMode: TASK_MODE_INLINE,
|
|
22
|
-
command: "",
|
|
23
|
-
cwd: process.cwd(),
|
|
24
|
-
taskId: "",
|
|
25
|
-
description: "",
|
|
26
|
-
runNow: "n",
|
|
27
|
-
maxRuns: "",
|
|
28
|
-
project: currentProjectId ?? "default",
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
interval: loop.intervalHuman,
|
|
33
|
-
taskMode: loop.taskId ? TASK_MODE_EXISTING : TASK_MODE_INLINE,
|
|
34
|
-
command: commandLine(loop.command, loop.commandArgs),
|
|
35
|
-
cwd: loop.cwd ?? "",
|
|
36
|
-
taskId: loop.taskId ?? "",
|
|
37
|
-
description: loop.description ?? "",
|
|
38
|
-
runNow: loop.immediate ? "y" : "n",
|
|
39
|
-
maxRuns: loop.maxRuns !== null ? String(loop.maxRuns) : "",
|
|
40
|
-
project: loop.projectId ?? currentProjectId ?? "default",
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export function CreateView(props) {
|
|
44
|
-
const [values, setValues] = useState({
|
|
45
|
-
...props.initial,
|
|
46
|
-
project: props.initial.project ?? props.currentProjectId ?? "default",
|
|
47
|
-
});
|
|
48
|
-
const valuesRef = useRef(values);
|
|
49
|
-
const [error, setError] = useState("");
|
|
50
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
51
|
-
const [selectedTaskName, setSelectedTaskName] = useState(props.selectedTaskName ?? null);
|
|
52
|
-
const inputRef = useRef(null);
|
|
53
|
-
const { width: termWidth } = useTerminalDimensions();
|
|
54
|
-
const btnWidth = Math.max(10, Math.min(14, Math.floor(termWidth / 6)));
|
|
55
|
-
const isInline = values.taskMode === TASK_MODE_INLINE;
|
|
56
|
-
const visibleFields = props.mode === "edit"
|
|
57
|
-
? createFields.filter((f) => f !== "runNow")
|
|
58
|
-
: [...createFields];
|
|
59
|
-
const filteredFields = visibleFields.filter((f) => {
|
|
60
|
-
if (f === "command")
|
|
61
|
-
return isInline;
|
|
62
|
-
if (f === "taskId")
|
|
63
|
-
return !isInline;
|
|
64
|
-
return true;
|
|
65
|
-
});
|
|
66
|
-
const navItems = [...filteredFields, "save", "cancel"];
|
|
67
|
-
const { setFocusIndex, focusedItem, isFocused } = useTabNav(navItems);
|
|
68
|
-
const focusedItemRef = useRef(focusedItem);
|
|
69
|
-
focusedItemRef.current = focusedItem;
|
|
70
|
-
useInputShortcuts(() => {
|
|
71
|
-
const fi = focusedItemRef.current;
|
|
72
|
-
if (fi === "save" || fi === "cancel" || fi === "project")
|
|
73
|
-
return null;
|
|
74
|
-
return inputRef.current;
|
|
75
|
-
});
|
|
76
|
-
useState(() => {
|
|
77
|
-
if (values.taskId && !selectedTaskName) {
|
|
78
|
-
void listTasks().then((tasks) => {
|
|
79
|
-
const found = tasks.find((t) => t.id === values.taskId);
|
|
80
|
-
if (found)
|
|
81
|
-
setSelectedTaskName(found.name);
|
|
82
|
-
}).catch(() => { });
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
function updateValues(next) {
|
|
86
|
-
valuesRef.current = next;
|
|
87
|
-
setValues(next);
|
|
88
|
-
}
|
|
89
|
-
if (props.selectedTaskId && props.selectedTaskId !== values.taskId) {
|
|
90
|
-
const next = { ...valuesRef.current, taskId: props.selectedTaskId, taskMode: TASK_MODE_EXISTING };
|
|
91
|
-
valuesRef.current = next;
|
|
92
|
-
setValues(next);
|
|
93
|
-
setSelectedTaskName(props.selectedTaskName ?? props.selectedTaskId);
|
|
94
|
-
}
|
|
95
|
-
useKeyboard((key) => {
|
|
96
|
-
if (key.name !== "return" && key.name !== "enter" && key.name !== " ")
|
|
97
|
-
return;
|
|
98
|
-
const fi = focusedItemRef.current;
|
|
99
|
-
if (fi === "taskMode") {
|
|
100
|
-
const next = valuesRef.current.taskMode === TASK_MODE_INLINE ? TASK_MODE_EXISTING : TASK_MODE_INLINE;
|
|
101
|
-
updateValues({ ...valuesRef.current, taskMode: next });
|
|
102
|
-
key.preventDefault();
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
if (fi === "runNow") {
|
|
106
|
-
const next = valuesRef.current.runNow === "y" ? "n" : "y";
|
|
107
|
-
updateValues({ ...valuesRef.current, runNow: next });
|
|
108
|
-
key.preventDefault();
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (fi === "taskId" && !isInline) {
|
|
112
|
-
props.onChooseTask();
|
|
113
|
-
key.preventDefault();
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
if (fi === "save") {
|
|
117
|
-
void submit(valuesRef.current);
|
|
118
|
-
key.preventDefault();
|
|
119
|
-
}
|
|
120
|
-
else if (fi === "cancel") {
|
|
121
|
-
props.onCancel();
|
|
122
|
-
key.preventDefault();
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
async function submit(current) {
|
|
126
|
-
if (isSubmitting)
|
|
127
|
-
return;
|
|
128
|
-
try {
|
|
129
|
-
setIsSubmitting(true);
|
|
130
|
-
setError("");
|
|
131
|
-
if (current.taskMode === TASK_MODE_INLINE) {
|
|
132
|
-
const cwd = current.cwd.trim();
|
|
133
|
-
if (cwd && !fs.existsSync(cwd)) {
|
|
134
|
-
setError(t("board.cwdMissing", { cwd }));
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
const tokens = parseCommandLine(current.command.trim());
|
|
138
|
-
const [command, ...commandArgs] = tokens;
|
|
139
|
-
if (!command) {
|
|
140
|
-
setError(t("errors.commandEmpty"));
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
if (!current.description.trim()) {
|
|
144
|
-
setError(t("errors.descriptionEmpty"));
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
const built = buildLoopOptions(current.interval.trim(), {
|
|
148
|
-
now: props.mode === "create" && current.runNow === "y",
|
|
149
|
-
maxRuns: current.maxRuns.trim() || null,
|
|
150
|
-
verbose: false,
|
|
151
|
-
description: current.description.trim(),
|
|
152
|
-
command,
|
|
153
|
-
commandArgs,
|
|
154
|
-
cwd,
|
|
155
|
-
});
|
|
156
|
-
built.options.projectId = current.project || "default";
|
|
157
|
-
const request = props.mode === "edit" && props.editId
|
|
158
|
-
? updateLoop(props.editId, built.options, built.intervalHuman)
|
|
159
|
-
: createLoop(built.options, built.intervalHuman);
|
|
160
|
-
const id = await request;
|
|
161
|
-
props.onDone(props.mode === "edit", id, current.description.trim());
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
if (!current.taskId) {
|
|
165
|
-
setError(t("errors.commandEmpty"));
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (!current.description.trim()) {
|
|
169
|
-
setError(t("errors.descriptionEmpty"));
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
const cwd = current.cwd.trim();
|
|
173
|
-
if (cwd && !fs.existsSync(cwd)) {
|
|
174
|
-
setError(t("board.cwdMissing", { cwd }));
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
const built = buildLoopOptions(current.interval.trim(), {
|
|
178
|
-
now: props.mode === "create" && current.runNow === "y",
|
|
179
|
-
maxRuns: current.maxRuns.trim() || null,
|
|
180
|
-
verbose: false,
|
|
181
|
-
description: current.description.trim(),
|
|
182
|
-
taskId: current.taskId,
|
|
183
|
-
cwd,
|
|
184
|
-
});
|
|
185
|
-
built.options.projectId = current.project || "default";
|
|
186
|
-
const request = props.mode === "edit" && props.editId
|
|
187
|
-
? updateLoop(props.editId, built.options, built.intervalHuman)
|
|
188
|
-
: createLoop(built.options, built.intervalHuman);
|
|
189
|
-
const id = await request;
|
|
190
|
-
props.onDone(props.mode === "edit", id, current.description.trim());
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
catch (e) {
|
|
194
|
-
setError(e instanceof Error ? e.message : String(e));
|
|
195
|
-
}
|
|
196
|
-
finally {
|
|
197
|
-
setIsSubmitting(false);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
const labels = {
|
|
201
|
-
interval: t("board.labelInterval"),
|
|
202
|
-
taskMode: t("board.labelTaskMode"),
|
|
203
|
-
command: t("board.labelCommand"),
|
|
204
|
-
cwd: t("board.labelCwd"),
|
|
205
|
-
taskId: t("board.labelTask"),
|
|
206
|
-
description: t("board.labelDescription"),
|
|
207
|
-
runNow: t("board.labelRunNow"),
|
|
208
|
-
maxRuns: t("board.labelMaxRuns"),
|
|
209
|
-
project: t("project.projectsLabel"),
|
|
210
|
-
};
|
|
211
|
-
const hints = {
|
|
212
|
-
interval: t("board.hintInterval"),
|
|
213
|
-
taskMode: t("board.hintTaskMode"),
|
|
214
|
-
command: t("board.hintCommand"),
|
|
215
|
-
cwd: t("board.hintCwd"),
|
|
216
|
-
taskId: t("board.hintTask"),
|
|
217
|
-
description: t("board.hintDescription"),
|
|
218
|
-
runNow: t("board.hintRunNow"),
|
|
219
|
-
maxRuns: t("board.hintMaxRuns"),
|
|
220
|
-
project: t("project.hintColor"),
|
|
221
|
-
};
|
|
222
|
-
const examples = {
|
|
223
|
-
interval: t("board.exampleInterval"),
|
|
224
|
-
taskMode: "",
|
|
225
|
-
command: t("board.exampleCommand"),
|
|
226
|
-
cwd: "",
|
|
227
|
-
taskId: "",
|
|
228
|
-
description: t("board.exampleDescription"),
|
|
229
|
-
runNow: "",
|
|
230
|
-
maxRuns: "",
|
|
231
|
-
project: "",
|
|
232
|
-
};
|
|
233
|
-
const taskModeOptions = [
|
|
234
|
-
{ name: t("board.taskModeInline"), description: "", value: TASK_MODE_INLINE },
|
|
235
|
-
{ name: t("board.taskModeExisting"), description: "", value: TASK_MODE_EXISTING },
|
|
236
|
-
];
|
|
237
|
-
const runNowOptions = [
|
|
238
|
-
{ name: t("board.runNowNo"), description: "", value: "n" },
|
|
239
|
-
{ name: t("board.runNowYes"), description: "", value: "y" },
|
|
240
|
-
];
|
|
241
|
-
const projectOptions = (props.projects ?? []).length > 0
|
|
242
|
-
? props.projects.map((p) => ({ name: p.name, value: p.id, color: p.color }))
|
|
243
|
-
: [{ name: "Default", value: "default", color: "#ffffff" }];
|
|
244
|
-
const title = props.mode === "edit" ? t("board.editTitle") : t("board.createTitle");
|
|
245
|
-
return (_jsxs("box", { title: title, border: true, style: { flexDirection: "column", flexGrow: 1, padding: 1, backgroundColor: "#0b0b0b" }, children: [_jsxs("box", { style: { flexDirection: "column", marginBottom: 1 }, children: [_jsx("text", { fg: "#9ca3af", children: t("board.exampleHeading") }), _jsx("text", { fg: "#d1d5db", children: t("board.exampleFull") })] }), _jsx("box", { style: { flexDirection: "column", flexGrow: 1 }, children: Array.from({ length: Math.ceil(filteredFields.length / 2) }, (_, row) => {
|
|
246
|
-
const leftField = filteredFields[row * 2];
|
|
247
|
-
const rightField = row * 2 + 1 < filteredFields.length ? filteredFields[row * 2 + 1] : null;
|
|
248
|
-
return (_jsxs("box", { style: { flexDirection: "row", marginBottom: 1 }, children: [_jsx(FormRow, { field: leftField, index: row * 2, isFocused: isFocused(leftField), values: values, valuesRef: valuesRef, updateValues: updateValues, setFocusIndex: setFocusIndex, submit: submit, labels: labels, hints: hints, examples: examples, taskModeOptions: taskModeOptions, runNowOptions: runNowOptions, projectOptions: projectOptions, selectedTaskName: selectedTaskName, onChooseTask: props.onChooseTask, inputRef: inputRef, style: { width: "50%", paddingRight: 1 } }), rightField ? (_jsx(FormRow, { field: rightField, index: row * 2 + 1, isFocused: isFocused(rightField), values: values, valuesRef: valuesRef, updateValues: updateValues, setFocusIndex: setFocusIndex, submit: submit, labels: labels, hints: hints, examples: examples, taskModeOptions: taskModeOptions, runNowOptions: runNowOptions, projectOptions: projectOptions, selectedTaskName: selectedTaskName, onChooseTask: props.onChooseTask, inputRef: inputRef, style: { width: "50%" } })) : (_jsx("box", { style: { width: "50%" } }))] }, row));
|
|
249
|
-
}) }), _jsxs("box", { style: { flexDirection: "row", height: 3, marginBottom: 1, backgroundColor: "#0b0b0b" }, children: [_jsx(HoverButton, { label: isSubmitting ? t("board.saving") : props.mode === "edit" ? t("board.save") : t("board.create"), onMouseDown: () => void submit(valuesRef.current), selected: isFocused("save"), width: btnWidth, marginRight: 1 }), _jsx(HoverButton, { label: t("board.cancel"), onMouseDown: props.onCancel, selected: isFocused("cancel"), width: btnWidth })] }), _jsx("text", { fg: "#9ca3af", children: t("board.formNav") }), error ? _jsx("text", { fg: "#f87171", children: error }) : null] }));
|
|
250
|
-
}
|
|
251
|
-
function HoverButton(props) {
|
|
252
|
-
const { isHovered, hoverProps } = useHoverState();
|
|
253
|
-
const bg = props.selected ? "#1e3a8a" : isHovered ? HOVER_BG : undefined;
|
|
254
|
-
const borderColor = props.selected ? "#38bdf8" : undefined;
|
|
255
|
-
return (_jsx("box", { border: true, onMouseDown: props.onMouseDown, borderColor: borderColor, style: { width: props.width, justifyContent: "center", alignItems: "center", marginRight: props.marginRight, backgroundColor: bg }, ...hoverProps, children: _jsx("text", { fg: "#e5e7eb", children: _jsx("strong", { children: props.label }) }) }));
|
|
256
|
-
}
|
|
257
|
-
function FormRow(props) {
|
|
258
|
-
const { field, index, isFocused, values, valuesRef, updateValues, setFocusIndex, submit, labels, hints, examples, taskModeOptions, runNowOptions, projectOptions, selectedTaskName, onChooseTask, inputRef, style } = props;
|
|
259
|
-
const { isHovered, hoverProps } = useHoverState();
|
|
260
|
-
const isToggleField = field === "taskMode" || field === "runNow";
|
|
261
|
-
const isTaskButton = field === "taskId";
|
|
262
|
-
const isProjectField = field === "project";
|
|
263
|
-
const toggleOptions = field === "taskMode" ? taskModeOptions : runNowOptions;
|
|
264
|
-
const toggleValue = field === "taskMode" ? values.taskMode : values.runNow;
|
|
265
|
-
return (_jsxs("box", { style: { flexDirection: "column", ...style }, children: [_jsx("text", { fg: isFocused ? "#38bdf8" : "#e5e7eb", children: labels[field] }), _jsx("text", { fg: "#6b7280", children: hints[field] }), isToggleField ? (_jsx("box", { border: true, borderColor: isFocused ? "#38bdf8" : undefined, style: { height: 3, flexDirection: "row", backgroundColor: "#0b0b0b", alignItems: "center" }, children: toggleOptions.map((opt) => {
|
|
266
|
-
const isActive = toggleValue === opt.value;
|
|
267
|
-
return (_jsx("box", { onMouseDown: () => updateValues({ ...valuesRef.current, [field]: opt.value }), style: { backgroundColor: isActive ? "#1e3a8a" : undefined, paddingLeft: 1, paddingRight: 1, marginRight: 1 }, ...hoverProps, children: _jsx("text", { fg: isActive ? "#ffffff" : isHovered ? "#e5e7eb" : "#9ca3af", children: _jsx("strong", { children: opt.name }) }) }, opt.value));
|
|
268
|
-
}) })) : isTaskButton ? (_jsx("box", { border: true, borderColor: isFocused ? "#38bdf8" : undefined, style: { height: 3, flexDirection: "row", backgroundColor: "#0b0b0b", alignItems: "center", paddingLeft: 1 }, onMouseDown: () => { setFocusIndex(index); onChooseTask(); }, children: _jsx("text", { fg: values.taskId ? "#4ade80" : "#9ca3af", children: values.taskId
|
|
269
|
-
? t("board.selectedTask", { name: selectedTaskName ?? values.taskId })
|
|
270
|
-
: t("board.chooseTask") }) })) : isProjectField ? (_jsx(SearchSelect, { options: projectOptions, value: values.project, onChange: (v) => updateValues({ ...valuesRef.current, project: v }), focused: isFocused })) : (_jsx("box", { border: true, borderColor: isFocused ? "#38bdf8" : undefined, style: { height: 3, backgroundColor: "#0b0b0b" }, children: _jsx("input", { ref: inputRef, focused: isFocused, value: values[field], placeholder: examples[field] ? t("board.placeholderExample", { example: examples[field] }) : t("board.placeholderBlank"), onInput: (value) => updateValues({ ...valuesRef.current, [field]: value }), onSubmit: () => { void submit(valuesRef.current); } }) }))] }));
|
|
271
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useRef } from "react";
|
|
3
|
-
import { useTabNav } from "../hooks/useTabNav.js";
|
|
4
|
-
import { useKeyboard, useTerminalDimensions } from "@opentui/react";
|
|
5
|
-
import { t } from "../../i18n/index.js";
|
|
6
|
-
import { createProject } from "../daemon.js";
|
|
7
|
-
import { PROJECT_COLORS, PROJECT_COLOR_KEYS } from "../../config/constants.js";
|
|
8
|
-
export function CreateProjectModal(props) {
|
|
9
|
-
const { onDone, onCancel } = props;
|
|
10
|
-
const { width } = useTerminalDimensions();
|
|
11
|
-
const defaultColorKey = PROJECT_COLOR_KEYS.indexOf("cyan") >= 0 ? "cyan" : PROJECT_COLOR_KEYS[0] ?? "white";
|
|
12
|
-
const [name, setName] = useState("");
|
|
13
|
-
const [selectedColorKey, setSelectedColorKey] = useState(defaultColorKey);
|
|
14
|
-
const [error, setError] = useState("");
|
|
15
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
16
|
-
const { focusedItem: focusField, setFocusIndex } = useTabNav(["name", "color", "save", "cancel"]);
|
|
17
|
-
const focusFieldRef = useRef(focusField);
|
|
18
|
-
focusFieldRef.current = focusField;
|
|
19
|
-
useKeyboard((key) => {
|
|
20
|
-
if (key.name === "escape") {
|
|
21
|
-
onCancel();
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (key.name === "return" || key.name === "enter") {
|
|
25
|
-
if (focusFieldRef.current === "cancel") {
|
|
26
|
-
onCancel();
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
void submit();
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
if (focusFieldRef.current === "color") {
|
|
33
|
-
if (key.name === "left" || key.name === "right") {
|
|
34
|
-
const idx = PROJECT_COLOR_KEYS.indexOf(selectedColorKey);
|
|
35
|
-
const dir = key.name === "left" ? -1 : 1;
|
|
36
|
-
setSelectedColorKey(PROJECT_COLOR_KEYS[(idx + dir + PROJECT_COLOR_KEYS.length) % PROJECT_COLOR_KEYS.length] ?? selectedColorKey);
|
|
37
|
-
key.preventDefault();
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
async function submit() {
|
|
43
|
-
if (isSubmitting)
|
|
44
|
-
return;
|
|
45
|
-
if (!name.trim()) {
|
|
46
|
-
setError(t("project.error.nameRequired"));
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
try {
|
|
50
|
-
setIsSubmitting(true);
|
|
51
|
-
setError("");
|
|
52
|
-
const color = PROJECT_COLORS[selectedColorKey] ?? "#ffffff";
|
|
53
|
-
const project = await createProject(name.trim(), color);
|
|
54
|
-
onDone(project);
|
|
55
|
-
}
|
|
56
|
-
catch (err) {
|
|
57
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
58
|
-
}
|
|
59
|
-
finally {
|
|
60
|
-
setIsSubmitting(false);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return (_jsx("box", { style: {
|
|
64
|
-
position: "absolute",
|
|
65
|
-
top: 0,
|
|
66
|
-
left: 0,
|
|
67
|
-
width: "100%",
|
|
68
|
-
height: "100%",
|
|
69
|
-
justifyContent: "center",
|
|
70
|
-
alignItems: "center",
|
|
71
|
-
zIndex: 100,
|
|
72
|
-
}, children: _jsxs("box", { title: t("project.createTitle"), border: true, style: {
|
|
73
|
-
flexDirection: "column",
|
|
74
|
-
padding: 1,
|
|
75
|
-
minWidth: Math.min(44, width - 4),
|
|
76
|
-
backgroundColor: "#111827",
|
|
77
|
-
}, children: [_jsx("text", { fg: focusField === "name" ? "#38bdf8" : "#e5e7eb", children: t("project.labelName") }), _jsx("text", { fg: "#6b7280", children: t("project.hintName") }), _jsx("box", { border: true, borderColor: focusField === "name" ? "#38bdf8" : undefined, style: { height: 3, backgroundColor: "#0b0b0b", marginBottom: 1 }, children: _jsx("input", { focused: focusField === "name", value: name, placeholder: "My Project", onInput: (value) => setName(value) }) }), _jsx("text", { fg: focusField === "color" ? "#38bdf8" : "#e5e7eb", children: t("project.labelColor") }), _jsx("text", { fg: "#6b7280", children: t("project.hintColor") }), _jsx("box", { border: true, borderColor: focusField === "color" ? "#38bdf8" : undefined, style: { height: 3, flexDirection: "row", backgroundColor: "#0b0b0b", alignItems: "center", marginBottom: 1 }, children: PROJECT_COLOR_KEYS.map((colorKey) => {
|
|
78
|
-
const isActive = selectedColorKey === colorKey;
|
|
79
|
-
return (_jsx("box", { onMouseDown: () => { setFocusIndex(1); setSelectedColorKey(colorKey); }, style: {
|
|
80
|
-
backgroundColor: isActive ? "#1e3a8a" : undefined,
|
|
81
|
-
paddingLeft: 1,
|
|
82
|
-
paddingRight: 1,
|
|
83
|
-
marginRight: 1,
|
|
84
|
-
}, children: _jsxs("text", { children: [_jsx("span", { fg: PROJECT_COLORS[colorKey] ?? "#ffffff", children: "\u25CF" }), ` ${colorKey}`] }) }, colorKey));
|
|
85
|
-
}) }), error ? _jsx("text", { fg: "#f87171", children: error }) : null, _jsxs("box", { style: { flexDirection: "row", marginTop: 1 }, children: [_jsx("box", { border: true, borderColor: focusField === "save" ? "#38bdf8" : undefined, onMouseDown: () => void submit(), style: {
|
|
86
|
-
width: 12,
|
|
87
|
-
justifyContent: "center",
|
|
88
|
-
alignItems: "center",
|
|
89
|
-
marginRight: 1,
|
|
90
|
-
backgroundColor: focusField === "save" ? "#1e3a8a" : "#0b0b0b",
|
|
91
|
-
}, children: _jsx("text", { fg: "#e5e7eb", children: _jsx("strong", { children: isSubmitting ? t("board.saving") : t("board.create") }) }) }), _jsx("box", { border: true, borderColor: focusField === "cancel" ? "#38bdf8" : undefined, onMouseDown: onCancel, style: {
|
|
92
|
-
width: 12,
|
|
93
|
-
justifyContent: "center",
|
|
94
|
-
alignItems: "center",
|
|
95
|
-
backgroundColor: focusField === "cancel" ? "#1e3a8a" : "#0b0b0b",
|
|
96
|
-
}, children: _jsx("text", { fg: "#e5e7eb", children: _jsx("strong", { children: t("board.cancel") }) }) })] })] }) }));
|
|
97
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
3
|
-
import { useKeyboard, useTerminalDimensions } from "@opentui/react";
|
|
4
|
-
import { t } from "../../i18n/index.js";
|
|
5
|
-
import { deleteProject } from "../daemon.js";
|
|
6
|
-
export function DeleteProjectConfirm(props) {
|
|
7
|
-
const { project, loopCount, onDone, onCancel } = props;
|
|
8
|
-
const { width } = useTerminalDimensions();
|
|
9
|
-
const [isDeleting, setIsDeleting] = useState(false);
|
|
10
|
-
const [error, setError] = useState("");
|
|
11
|
-
useKeyboard((key) => {
|
|
12
|
-
if (key.name === "escape") {
|
|
13
|
-
onCancel();
|
|
14
|
-
}
|
|
15
|
-
if (key.name === "return" || key.name === "enter") {
|
|
16
|
-
void handleDelete();
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
async function handleDelete() {
|
|
20
|
-
if (isDeleting)
|
|
21
|
-
return;
|
|
22
|
-
try {
|
|
23
|
-
setIsDeleting(true);
|
|
24
|
-
setError("");
|
|
25
|
-
await deleteProject(project.id);
|
|
26
|
-
onDone();
|
|
27
|
-
}
|
|
28
|
-
catch (err) {
|
|
29
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
30
|
-
setIsDeleting(false);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return (_jsx("box", { style: {
|
|
34
|
-
position: "absolute",
|
|
35
|
-
top: 0,
|
|
36
|
-
left: 0,
|
|
37
|
-
width: "100%",
|
|
38
|
-
height: "100%",
|
|
39
|
-
justifyContent: "center",
|
|
40
|
-
alignItems: "center",
|
|
41
|
-
zIndex: 100,
|
|
42
|
-
}, children: _jsxs("box", { title: t("project.deleteTitle"), border: true, style: {
|
|
43
|
-
flexDirection: "column",
|
|
44
|
-
padding: 1,
|
|
45
|
-
minWidth: Math.min(50, width - 4),
|
|
46
|
-
backgroundColor: "#111827",
|
|
47
|
-
}, children: [_jsx("text", { fg: "#e5e7eb", children: t("project.deleteConfirm", { name: project.name }) }), loopCount > 0 ? (_jsx("text", { fg: "#f59e0b", children: t("project.deleteWarning", { count: String(loopCount) }) })) : null, error ? _jsx("text", { fg: "#f87171", children: error }) : null, _jsx("text", { children: " " }), _jsxs("box", { style: { flexDirection: "row" }, children: [_jsx("box", { border: true, onMouseDown: () => void handleDelete(), style: {
|
|
48
|
-
paddingLeft: 1,
|
|
49
|
-
paddingRight: 1,
|
|
50
|
-
marginRight: 1,
|
|
51
|
-
backgroundColor: "#7f1d1d",
|
|
52
|
-
}, children: _jsx("text", { fg: "#fca5a5", children: _jsx("strong", { children: isDeleting ? "Deleting..." : t("project.moveToDefault") }) }) }), _jsx("box", { border: true, onMouseDown: onCancel, style: { paddingLeft: 1, paddingRight: 1, backgroundColor: "#0b0b0b" }, children: _jsx("text", { fg: "#e5e7eb", children: _jsx("strong", { children: t("board.cancel") }) }) })] })] }) }));
|
|
53
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from "react";
|
|
3
|
-
import { t } from "../../i18n/index.js";
|
|
4
|
-
import { commandLine, describeLoop, statusColor, statusLabel, timingLabel } from "../format.js";
|
|
5
|
-
import { RunHistory } from "./RunHistory.js";
|
|
6
|
-
export function DetailView(props) {
|
|
7
|
-
const { loop, selectedRunIndex, onSelectRun, onOpenRun } = props;
|
|
8
|
-
const [, setTick] = useState(0);
|
|
9
|
-
const cmd = commandLine(loop.command, loop.commandArgs);
|
|
10
|
-
const maxRuns = loop.maxRuns !== null ? String(loop.maxRuns) : t("board.unlimited");
|
|
11
|
-
const summary = [
|
|
12
|
-
`${t("board.detailSummaryStatus")} ${statusLabel(loop.status)}`,
|
|
13
|
-
`${t("board.detailSummaryTiming")} ${timingLabel(loop)}`,
|
|
14
|
-
`${t("board.detailSummaryInterval")} ${loop.intervalHuman}`,
|
|
15
|
-
`${t("board.detailSummaryRuns")} ${loop.runCount}/${maxRuns}`,
|
|
16
|
-
`${t("board.detailSummaryExit")} ${loop.lastExitCode ?? t("format.dash")}`,
|
|
17
|
-
].join(" | ");
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
const timer = setInterval(() => {
|
|
20
|
-
setTick((tick) => tick + 1);
|
|
21
|
-
}, 1000);
|
|
22
|
-
return () => clearInterval(timer);
|
|
23
|
-
}, []);
|
|
24
|
-
return (_jsxs("box", { style: { flexDirection: "column", flexGrow: 1, backgroundColor: "#0b0b0b" }, children: [_jsxs("box", { title: t("board.detailTitle"), border: true, style: { flexDirection: "column", backgroundColor: "#0b0b0b" }, children: [_jsx("text", { fg: "#9ca3af", children: summary }), _jsx("text", { children: " " }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldId") }), loop.id] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldDesc") }), describeLoop(loop)] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldCommand") }), cmd] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldDir") }), loop.cwd || t("board.inherit")] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldInterval") }), loop.intervalHuman] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldStatus") }), _jsx("span", { fg: statusColor(loop.status), children: statusLabel(loop.status) })] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldRuns") }), loop.runCount, " / ", maxRuns] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldCreated") }), loop.createdAt] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldLastRun") }), loop.lastRunAt ?? t("format.dash")] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldLastExit") }), loop.lastExitCode ?? t("format.dash")] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldNextRun") }), loop.nextRunAt ?? t("format.dash")] }), _jsxs("text", { children: [_jsx("strong", { children: t("board.detailFieldPid") }), loop.pid] })] }), _jsx(RunHistory, { loop: loop, selectedRunIndex: selectedRunIndex, focused: true, onSelectRun: onSelectRun, onOpenRun: onOpenRun })] }));
|
|
25
|
-
}
|