@plainconceptsplatform/loop-task 2.6.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/LICENSE +21 -0
- package/README.md +534 -0
- package/dist/app/App.js +132 -0
- package/dist/app/BoardLayout.js +5 -0
- package/dist/app/index.js +48 -0
- package/dist/app/providers/index.js +1 -0
- package/dist/app/router/index.js +16 -0
- package/dist/app/types.js +1 -0
- package/dist/cli/import-validator.js +152 -0
- package/dist/cli/import-writer.js +81 -0
- package/dist/cli.js +573 -0
- package/dist/client/cli-format.js +7 -0
- package/dist/client/commands.js +162 -0
- package/dist/client/ipc.js +96 -0
- package/dist/client/project-commands.js +164 -0
- package/dist/core/command/command-runner.js +300 -0
- package/dist/core/command/process-tree.js +73 -0
- package/dist/core/command/resolve-cwd.js +8 -0
- package/dist/core/command/stdout-capture-transform.js +73 -0
- package/dist/core/context/context-parser.js +65 -0
- package/dist/core/context/log-context.js +3 -0
- package/dist/core/context/template.js +20 -0
- package/dist/core/context/validate-context.js +19 -0
- package/dist/core/foreground/index.js +81 -0
- package/dist/core/logging/bounded-log-reader.js +368 -0
- package/dist/core/logging/log-follower.js +26 -0
- package/dist/core/logging/log-parser.js +29 -0
- package/dist/core/logging/log-rotator.js +34 -0
- package/dist/core/logging/rotating-log-stream.js +137 -0
- package/dist/core/loop/chain-executor.js +171 -0
- package/dist/core/loop/delay-utils.js +65 -0
- package/dist/core/loop/loop-controller.js +240 -0
- package/dist/core/loop/loop-runner.js +194 -0
- package/dist/core/loop/run-executor.js +144 -0
- package/dist/core/loop/types.js +1 -0
- package/dist/core/scheduling/index.js +16 -0
- package/dist/daemon/daemon-log.js +11 -0
- package/dist/daemon/diagnostics.js +85 -0
- package/dist/daemon/http/helpers.js +81 -0
- package/dist/daemon/http/openapi.js +119 -0
- package/dist/daemon/http/route-loops.js +311 -0
- package/dist/daemon/http/route-misc.js +60 -0
- package/dist/daemon/http/route-projects.js +55 -0
- package/dist/daemon/http/route-tasks.js +141 -0
- package/dist/daemon/http/routes.js +19 -0
- package/dist/daemon/http/server.js +82 -0
- package/dist/daemon/http/sse.js +36 -0
- package/dist/daemon/index.js +189 -0
- package/dist/daemon/ipc/send.js +5 -0
- package/dist/daemon/managers/loop-entry.js +27 -0
- package/dist/daemon/managers/loop-manager.js +346 -0
- package/dist/daemon/managers/loop-options.js +17 -0
- package/dist/daemon/managers/loop-serialization.js +53 -0
- package/dist/daemon/managers/project-manager.js +170 -0
- package/dist/daemon/managers/task-manager.js +94 -0
- package/dist/daemon/mcp/index.js +2 -0
- package/dist/daemon/mcp/openapi-sync.js +50 -0
- package/dist/daemon/mcp/server.js +167 -0
- package/dist/daemon/mcp/tools.js +382 -0
- package/dist/daemon/recipe/deferred-reload.js +32 -0
- package/dist/daemon/recipe/diagram-reader.js +26 -0
- package/dist/daemon/recipe/file-writer.js +52 -0
- package/dist/daemon/recipe/id-remapper.js +46 -0
- package/dist/daemon/recipe/runtime-state.js +23 -0
- package/dist/daemon/recipe/scanner.js +165 -0
- package/dist/daemon/recipe/task-store.js +33 -0
- package/dist/daemon/recipe/validator.js +35 -0
- package/dist/daemon/server/handlers/diagnostics-handlers.js +7 -0
- package/dist/daemon/server/handlers/index.js +101 -0
- package/dist/daemon/server/handlers/log-handlers.js +129 -0
- package/dist/daemon/server/handlers/loop-handlers.js +64 -0
- package/dist/daemon/server/handlers/project-handlers.js +42 -0
- package/dist/daemon/server/handlers/settings-handlers.js +8 -0
- package/dist/daemon/server/handlers/task-handlers.js +20 -0
- package/dist/daemon/server/handlers/telemetry-handlers.js +5 -0
- package/dist/daemon/server/index.js +117 -0
- package/dist/daemon/settings-manager.js +73 -0
- package/dist/daemon/spawner/index.js +204 -0
- package/dist/daemon/state/index.js +209 -0
- package/dist/daemon/telemetry/agent-integrations/agent-integration.js +1 -0
- package/dist/daemon/telemetry/agent-integrations/claude-code-integration.js +55 -0
- package/dist/daemon/telemetry/agent-integrations/detect-agent-integration.js +20 -0
- package/dist/daemon/telemetry/agent-integrations/index.js +3 -0
- package/dist/daemon/telemetry/agent-integrations/opencode-integration.js +72 -0
- package/dist/daemon/telemetry/index.js +6 -0
- package/dist/daemon/telemetry/noop-telemetry-adapter.js +53 -0
- package/dist/daemon/telemetry/open-telemetry-adapter.js +464 -0
- package/dist/daemon/telemetry/telemetry-manager.js +147 -0
- package/dist/daemon/telemetry/telemetry-redaction.js +67 -0
- package/dist/daemon/telemetry/telemetry-types.js +45 -0
- package/dist/daemon/telemetry/telemetry.js +1 -0
- package/dist/daemon/watcher/index.js +250 -0
- package/dist/duration.js +24 -0
- package/dist/entities/loops/filters.js +109 -0
- package/dist/entities/loops/index.js +1 -0
- package/dist/entities/projects/filters.js +69 -0
- package/dist/entities/projects/index.js +1 -0
- package/dist/entities/tasks/filters.js +41 -0
- package/dist/entities/tasks/index.js +1 -0
- package/dist/entry.js +16 -0
- package/dist/esm-loader.js +39 -0
- package/dist/features/chain-editor/ChainEditor.js +116 -0
- package/dist/features/chain-editor/mermaidToAscii.js +375 -0
- package/dist/features/chain-editor/renderChainDiagram.js +106 -0
- package/dist/features/code-editor/CodeEditorModal.js +84 -0
- package/dist/features/code-editor/CodeEditorPreview.js +14 -0
- package/dist/features/code-editor/useEditorKeyboard.js +150 -0
- package/dist/features/code-editor/useModalDimensions.js +9 -0
- package/dist/features/commands/commands.js +132 -0
- package/dist/features/commands/useCommandHandlers.js +280 -0
- package/dist/features/commands/useContextualActions.js +78 -0
- package/dist/features/commands/useGlobalShortcuts.js +154 -0
- package/dist/features/forms/FormRouter.js +46 -0
- package/dist/features/overlays/ContextHelpModal.js +8 -0
- package/dist/features/overlays/DiagramModal.js +45 -0
- package/dist/features/overlays/ExportModal.js +51 -0
- package/dist/features/overlays/HelpGuideModal.js +23 -0
- package/dist/features/overlays/HelpModal.js +59 -0
- package/dist/features/overlays/OverlayStack.js +10 -0
- package/dist/features/overlays/ProjectsModal.js +42 -0
- package/dist/features/overlays/TaskPickerModal.js +62 -0
- package/dist/features/overlays/WelcomeScreen.js +74 -0
- package/dist/features/overlays/useOverlayStack.js +55 -0
- package/dist/features/state/useAppState.js +151 -0
- package/dist/logger.js +25 -0
- package/dist/loop-config.js +152 -0
- package/dist/shared/clipboard.js +111 -0
- package/dist/shared/config/constants.js +104 -0
- package/dist/shared/config/paths.js +67 -0
- package/dist/shared/container/index.js +24 -0
- package/dist/shared/fs-utils.js +44 -0
- package/dist/shared/hooks/useBreakpoint.js +11 -0
- package/dist/shared/hooks/useDaemonSettings.js +41 -0
- package/dist/shared/hooks/useInject.js +6 -0
- package/dist/shared/hooks/useLogStream.js +37 -0
- package/dist/shared/hooks/useLoopFormValidation.js +131 -0
- package/dist/shared/hooks/useLoopPolling.js +30 -0
- package/dist/shared/hooks/useMouseScroll.js +56 -0
- package/dist/shared/hooks/useUndoRedo.js +77 -0
- package/dist/shared/i18n/en.json +709 -0
- package/dist/shared/i18n/index.js +11 -0
- package/dist/shared/providers/InversifyProvider.js +6 -0
- package/dist/shared/services/export-service.js +43 -0
- package/dist/shared/services/log-service.js +28 -0
- package/dist/shared/services/loop-service.js +78 -0
- package/dist/shared/services/project-service.js +39 -0
- package/dist/shared/services/settings-service.js +102 -0
- package/dist/shared/services/task-service.js +41 -0
- package/dist/shared/services/types.js +8 -0
- package/dist/shared/sleep.js +17 -0
- package/dist/shared/tail.js +4 -0
- package/dist/shared/ui/Button.js +18 -0
- package/dist/shared/ui/DebugPanel.js +9 -0
- package/dist/shared/ui/FocusableButton.js +18 -0
- package/dist/shared/ui/FocusableInput.js +72 -0
- package/dist/shared/ui/FocusableList.js +48 -0
- package/dist/shared/ui/Modal.js +13 -0
- package/dist/shared/ui/SelectModal.js +78 -0
- package/dist/shared/ui/Toast.js +37 -0
- package/dist/shared/ui/format.js +117 -0
- package/dist/shared/ui/hooks/useHoverState.js +11 -0
- package/dist/shared/ui/state.js +9 -0
- package/dist/shared/ui/theme.js +96 -0
- package/dist/shared/utils/log-lines.js +13 -0
- package/dist/shared/utils/paste.js +37 -0
- package/dist/shared/utils/syntax.js +128 -0
- package/dist/shared/utils/validation.js +56 -0
- package/dist/types.js +1 -0
- package/dist/visual-evidence/capture.js +51 -0
- package/dist/visual-evidence/cli.js +85 -0
- package/dist/visual-evidence/evidence-required.js +71 -0
- package/dist/visual-evidence/index.js +10 -0
- package/dist/visual-evidence/launch.js +61 -0
- package/dist/visual-evidence/manifest.js +32 -0
- package/dist/visual-evidence/openspec-resolver.js +45 -0
- package/dist/visual-evidence/publish.js +126 -0
- package/dist/visual-evidence/run.js +121 -0
- package/dist/visual-evidence/scenario-registry.js +132 -0
- package/dist/widgets/command-input/CommandDropdown.js +69 -0
- package/dist/widgets/command-input/CommandInput.js +133 -0
- package/dist/widgets/command-input/HintBar.js +9 -0
- package/dist/widgets/command-input/InputModes.js +72 -0
- package/dist/widgets/commands-browser/CommandsBrowserModal.js +85 -0
- package/dist/widgets/header/Header.js +54 -0
- package/dist/widgets/header/TabBar.js +20 -0
- package/dist/widgets/left-panel/LeftPanel.js +34 -0
- package/dist/widgets/left-panel/Navigator.js +107 -0
- package/dist/widgets/left-panel/ProjectsPage.js +76 -0
- package/dist/widgets/left-panel/ProjectsPageParts.js +34 -0
- package/dist/widgets/left-panel/TaskBrowser.js +132 -0
- package/dist/widgets/log-modal/LogModal.js +211 -0
- package/dist/widgets/loop-form/CreateForm.js +82 -0
- package/dist/widgets/loop-form/TextField.js +9 -0
- package/dist/widgets/loop-form/WizardForm.js +199 -0
- package/dist/widgets/loop-form/useCreateSteps.js +135 -0
- package/dist/widgets/loop-form/useHandleComplete.js +94 -0
- package/dist/widgets/project-form/ProjectForm.js +83 -0
- package/dist/widgets/right-panel/Inspector.js +41 -0
- package/dist/widgets/right-panel/RightPanel.js +38 -0
- package/dist/widgets/right-panel/RunHistory.js +187 -0
- package/dist/widgets/task-form/TaskForm.js +272 -0
- package/package.json +119 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { commandLine } from "../../shared/ui/format.js";
|
|
2
|
+
/**
|
|
3
|
+
* Render a task chain as ASCII art, starting from a root task.
|
|
4
|
+
* Handles cycles (shows "(cycle to TaskName)"), missing tasks
|
|
5
|
+
* (shows "(missing task)"), and silent chain markers ("[s]").
|
|
6
|
+
*/
|
|
7
|
+
export function renderChainDiagram(rootTaskId, tasks) {
|
|
8
|
+
const taskMap = new Map();
|
|
9
|
+
for (const task of tasks) {
|
|
10
|
+
taskMap.set(task.id, task);
|
|
11
|
+
}
|
|
12
|
+
const rootTask = taskMap.get(rootTaskId);
|
|
13
|
+
if (!rootTask) {
|
|
14
|
+
return renderMissingTaskBox(rootTaskId);
|
|
15
|
+
}
|
|
16
|
+
const visited = new Set();
|
|
17
|
+
function renderTask(task, _branch) {
|
|
18
|
+
if (visited.has(task.id)) {
|
|
19
|
+
const name = task.name || task.id;
|
|
20
|
+
return `(cycle to ${name})`;
|
|
21
|
+
}
|
|
22
|
+
visited.add(task.id);
|
|
23
|
+
const lines = [];
|
|
24
|
+
const silent = task.silentChain ? " [s]" : "";
|
|
25
|
+
// Name line
|
|
26
|
+
lines.push(`${task.name}${silent}`);
|
|
27
|
+
// Steps or command
|
|
28
|
+
if (task.steps && task.steps.length > 0) {
|
|
29
|
+
for (let i = 0; i < task.steps.length; i++) {
|
|
30
|
+
const step = task.steps[i];
|
|
31
|
+
for (const cmd of step.commands) {
|
|
32
|
+
lines.push(` Step ${i + 1}: ${commandLine(cmd.command, cmd.commandArgs)}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
lines.push(` ${commandLine(task.command, task.commandArgs)}`);
|
|
38
|
+
}
|
|
39
|
+
// Branch annotations
|
|
40
|
+
const successTarget = task.onSuccessTaskId
|
|
41
|
+
? resolveBranchLabel(task.onSuccessTaskId, taskMap, visited)
|
|
42
|
+
: "(none)";
|
|
43
|
+
const failureTarget = task.onFailureTaskId
|
|
44
|
+
? resolveBranchLabel(task.onFailureTaskId, taskMap, visited)
|
|
45
|
+
: "(none)";
|
|
46
|
+
lines.push(` onSuccess -> ${successTarget}`);
|
|
47
|
+
lines.push(` onFailure -> ${failureTarget}`);
|
|
48
|
+
// Build box
|
|
49
|
+
const box = renderBox(lines);
|
|
50
|
+
// Recurse into children that haven't been visited yet
|
|
51
|
+
const children = [];
|
|
52
|
+
if (task.onSuccessTaskId) {
|
|
53
|
+
const child = taskMap.get(task.onSuccessTaskId);
|
|
54
|
+
if (child && !visited.has(child.id)) {
|
|
55
|
+
children.push(renderTask(child, "success"));
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (task.onFailureTaskId) {
|
|
59
|
+
const child = taskMap.get(task.onFailureTaskId);
|
|
60
|
+
if (child && !visited.has(child.id)) {
|
|
61
|
+
children.push(renderTask(child, "failure"));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (children.length > 0) {
|
|
65
|
+
return box + "\n" + children.map((c) => connector() + "\n" + c).join("\n");
|
|
66
|
+
}
|
|
67
|
+
return box;
|
|
68
|
+
}
|
|
69
|
+
const result = renderTask(rootTask, null);
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
function resolveBranchLabel(targetId, taskMap, visited) {
|
|
73
|
+
const target = taskMap.get(targetId);
|
|
74
|
+
if (!target)
|
|
75
|
+
return `${targetId} (missing task)`;
|
|
76
|
+
if (visited.has(targetId))
|
|
77
|
+
return `${target.name || targetId} (cycle)`;
|
|
78
|
+
const silent = target.silentChain ? " (silent)" : "";
|
|
79
|
+
return `${target.name || targetId}${silent}`;
|
|
80
|
+
}
|
|
81
|
+
function renderBox(lines) {
|
|
82
|
+
const maxWidth = Math.max(3, ...lines.map((l) => stripAnsi(l).length)) + 4;
|
|
83
|
+
const border = "+" + "-".repeat(maxWidth) + "+";
|
|
84
|
+
const boxed = [border];
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
const displayLen = stripAnsi(line).length;
|
|
87
|
+
const padding = maxWidth - displayLen - 2;
|
|
88
|
+
boxed.push(`| ${line}${" ".repeat(Math.max(0, padding))} |`);
|
|
89
|
+
}
|
|
90
|
+
boxed.push(border);
|
|
91
|
+
return boxed.join("\n");
|
|
92
|
+
}
|
|
93
|
+
function renderMissingTaskBox(taskId) {
|
|
94
|
+
const lines = [taskId, " (missing task)"];
|
|
95
|
+
return renderBox(lines);
|
|
96
|
+
}
|
|
97
|
+
function connector() {
|
|
98
|
+
return " | \n v";
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Strip ANSI escape codes for length calculation.
|
|
102
|
+
* Safe no-op for plain strings.
|
|
103
|
+
*/
|
|
104
|
+
function stripAnsi(str) {
|
|
105
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "");
|
|
106
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useCallback, useMemo } from "react";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
import { darkTheme as theme } from "../../shared/ui/theme.js";
|
|
5
|
+
import { t } from "../../shared/i18n/index.js";
|
|
6
|
+
import { useUndoRedo } from "../../shared/hooks/useUndoRedo.js";
|
|
7
|
+
import { highlightSegments } from "../../shared/utils/syntax.js";
|
|
8
|
+
import { joinCommandLines } from "../../loop-config.js";
|
|
9
|
+
import { CODE_EDITOR_UNDO_LIMIT, CODE_EDITOR_SYNTAX_COLORS } from "../../shared/config/constants.js";
|
|
10
|
+
import { useModalDimensions } from "./useModalDimensions.js";
|
|
11
|
+
import { useEditorKeyboard } from "./useEditorKeyboard.js";
|
|
12
|
+
export function CodeEditorModal(props) {
|
|
13
|
+
const { initialValue, onSave, onCancel } = props;
|
|
14
|
+
const { modalWidth, modalHeight, editorVisibleLines: EDITOR_VISIBLE_LINES } = useModalDimensions();
|
|
15
|
+
const { value, setValue, undo, redo } = useUndoRedo(initialValue, CODE_EDITOR_UNDO_LIMIT);
|
|
16
|
+
const [cursorRow, setCursorRow] = useState(() => {
|
|
17
|
+
const lines = initialValue.split("\n");
|
|
18
|
+
return Math.max(0, lines.length - 1);
|
|
19
|
+
});
|
|
20
|
+
const [cursorCol, setCursorCol] = useState(() => {
|
|
21
|
+
const lines = initialValue.split("\n");
|
|
22
|
+
return (lines[lines.length - 1] ?? "").length;
|
|
23
|
+
});
|
|
24
|
+
const [flashMsg, setFlashMsg] = useState(null);
|
|
25
|
+
const lines = useMemo(() => (value ? value.split("\n") : [""]), [value]);
|
|
26
|
+
const lineCount = lines.length;
|
|
27
|
+
const lineNumWidth = String(lineCount).length;
|
|
28
|
+
React.useEffect(() => {
|
|
29
|
+
if (flashMsg) {
|
|
30
|
+
const timer = setTimeout(() => setFlashMsg(null), 1500);
|
|
31
|
+
return () => clearTimeout(timer);
|
|
32
|
+
}
|
|
33
|
+
}, [flashMsg]);
|
|
34
|
+
const scrollStart = useMemo(() => {
|
|
35
|
+
if (cursorRow < EDITOR_VISIBLE_LINES)
|
|
36
|
+
return 0;
|
|
37
|
+
return cursorRow - EDITOR_VISIBLE_LINES + 1;
|
|
38
|
+
}, [cursorRow]);
|
|
39
|
+
const visibleLines = useMemo(() => lines.slice(scrollStart, scrollStart + EDITOR_VISIBLE_LINES), [lines, scrollStart]);
|
|
40
|
+
const applyMutation = useCallback((nextLines, newCursorRow, newCursorCol) => {
|
|
41
|
+
setValue(nextLines.join("\n"));
|
|
42
|
+
setCursorRow(newCursorRow);
|
|
43
|
+
setCursorCol(newCursorCol);
|
|
44
|
+
}, [setValue]);
|
|
45
|
+
useEditorKeyboard({
|
|
46
|
+
lines,
|
|
47
|
+
cursorRow,
|
|
48
|
+
cursorCol,
|
|
49
|
+
value,
|
|
50
|
+
setValue,
|
|
51
|
+
setCursorRow,
|
|
52
|
+
setCursorCol,
|
|
53
|
+
undo,
|
|
54
|
+
redo,
|
|
55
|
+
onSave,
|
|
56
|
+
onCancel,
|
|
57
|
+
setFlashMsg,
|
|
58
|
+
applyMutation,
|
|
59
|
+
});
|
|
60
|
+
const joined = joinCommandLines(value);
|
|
61
|
+
const innerWidth = modalWidth - 2;
|
|
62
|
+
const truncatedPreview = joined.length > innerWidth
|
|
63
|
+
? joined.slice(0, innerWidth - 1) + t("codeEditor.previewTruncated")
|
|
64
|
+
: joined;
|
|
65
|
+
const accent = theme.accent.brand;
|
|
66
|
+
return (_jsx(Box, { position: "absolute", top: 0, left: 0, width: "100%", height: "100%", justifyContent: "center", alignItems: "center", children: _jsxs(Box, { width: modalWidth, height: modalHeight, flexDirection: "column", backgroundColor: theme.bg.elevated, borderStyle: "round", borderColor: accent, paddingX: 1, children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { color: accent, bold: true, children: t("codeEditor.title") }), _jsxs(Text, { color: theme.text.muted, children: [lineCount, " ", t("cmdEditor.lines")] })] }), _jsx(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: visibleLines.map((line, visIdx) => {
|
|
67
|
+
const rowIdx = scrollStart + visIdx;
|
|
68
|
+
const isCursor = rowIdx === cursorRow;
|
|
69
|
+
const lineNum = String(rowIdx + 1).padStart(lineNumWidth, " ");
|
|
70
|
+
if (isCursor) {
|
|
71
|
+
const before = line.slice(0, cursorCol);
|
|
72
|
+
const cur = cursorCol < line.length ? line[cursorCol] : " ";
|
|
73
|
+
const after = cursorCol < line.length ? line.slice(cursorCol + 1) : "";
|
|
74
|
+
// Cursor line: render with cursor (no syntax highlight on cursor line)
|
|
75
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: theme.text.muted, children: [lineNum, " "] }), _jsx(Text, { color: theme.text.primary, children: before }), _jsx(Text, { inverse: true, children: cur }), _jsx(Text, { color: theme.text.primary, children: after })] }, rowIdx));
|
|
76
|
+
}
|
|
77
|
+
// Non-cursor line: syntax highlighted (whitespace preserved)
|
|
78
|
+
const segments = highlightSegments(line, CODE_EDITOR_SYNTAX_COLORS, theme.text.muted);
|
|
79
|
+
if (segments.length === 0) {
|
|
80
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: theme.text.muted, children: [lineNum, " "] }), _jsx(Text, { children: " " })] }, rowIdx));
|
|
81
|
+
}
|
|
82
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: theme.text.muted, children: [lineNum, " "] }), segments.map((seg, ti) => (_jsx(Text, { color: seg.color, children: seg.value }, ti)))] }, rowIdx));
|
|
83
|
+
}) }), _jsxs(Box, { children: [_jsxs(Text, { color: theme.text.muted, children: [t("codeEditor.previewLabel"), " "] }), _jsx(Text, { color: theme.text.secondary, children: truncatedPreview || t("codeEditor.emptyPlaceholder") })] }), _jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Box, { gap: 2, children: [_jsx(Text, { color: theme.accent.brand, children: t("codeEditor.buttonCopy") }), _jsx(Text, { color: theme.text.muted, children: "ctrl+x" }), _jsx(Text, { color: theme.accent.brand, children: t("codeEditor.buttonPaste") }), _jsx(Text, { color: theme.text.muted, children: "ctrl+v" }), _jsx(Text, { color: theme.accent.brand, children: t("codeEditor.buttonClear") }), _jsx(Text, { color: theme.text.muted, children: "ctrl+l" })] }), flashMsg ? (_jsx(Text, { color: theme.semantic.success, children: flashMsg })) : null] }), _jsx(Box, { children: _jsx(Text, { color: theme.text.muted, children: t("codeEditor.hint") }) })] }) }));
|
|
84
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { darkTheme as theme } from "../../shared/ui/theme.js";
|
|
4
|
+
import { t } from "../../shared/i18n/index.js";
|
|
5
|
+
import { CODE_EDITOR_MAX_VISIBLE } from "../../shared/config/constants.js";
|
|
6
|
+
export function CodeEditorPreview(props) {
|
|
7
|
+
const { value, hint, isActive, onActivate } = props;
|
|
8
|
+
const lines = value ? value.split("\n") : [];
|
|
9
|
+
const visibleLines = lines.slice(0, CODE_EDITOR_MAX_VISIBLE);
|
|
10
|
+
const truncated = lines.length > CODE_EDITOR_MAX_VISIBLE;
|
|
11
|
+
const hasValue = lines.length > 0;
|
|
12
|
+
void onActivate; // consumed by parent via useInput when active
|
|
13
|
+
return (_jsxs(Box, { flexDirection: "column", width: "100%", children: [_jsx(Box, { borderStyle: "single", borderColor: isActive ? theme.accent.brand : theme.border.dim, backgroundColor: isActive ? theme.bg.input : undefined, paddingLeft: 1, overflow: "hidden", width: "100%", flexDirection: "column", children: hasValue ? (_jsxs(_Fragment, { children: [visibleLines.map((line, i) => (_jsx(Text, { color: theme.text.primary, children: line }, i))), truncated ? (_jsx(Text, { color: theme.text.muted, children: t("codeEditor.previewTruncated") })) : null] })) : (_jsx(Text, { color: theme.text.muted, children: hint })) }), isActive ? (_jsxs(Box, { marginTop: 0, children: [_jsx(Text, { color: theme.accent.brand, children: "› " }), _jsx(Text, { color: theme.text.muted, children: t("codeEditor.fieldHint") })] })) : null] }));
|
|
14
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { useInput } from "ink";
|
|
2
|
+
import { t } from "../../shared/i18n/index.js";
|
|
3
|
+
import { copyToClipboard, readFromClipboard } from "../../shared/clipboard.js";
|
|
4
|
+
import { sanitizeMultilinePaste, wrapCommand } from "../../shared/utils/paste.js";
|
|
5
|
+
export function useEditorKeyboard(params) {
|
|
6
|
+
const { lines, cursorRow, cursorCol, value, setValue, setCursorRow, setCursorCol, undo, redo, onSave, onCancel, setFlashMsg, applyMutation, } = params;
|
|
7
|
+
useInput((input, key) => {
|
|
8
|
+
if (input.includes("\x1b[200~")) {
|
|
9
|
+
const pasted = wrapCommand(sanitizeMultilinePaste(input));
|
|
10
|
+
if (pasted) {
|
|
11
|
+
const next = [...lines];
|
|
12
|
+
const line = next[cursorRow] ?? "";
|
|
13
|
+
next[cursorRow] =
|
|
14
|
+
line.slice(0, cursorCol) + pasted + line.slice(cursorCol);
|
|
15
|
+
applyMutation(next, cursorRow, cursorCol + pasted.length);
|
|
16
|
+
}
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Swallow SGR mouse sequences
|
|
20
|
+
if (input.includes("[<"))
|
|
21
|
+
return;
|
|
22
|
+
if (key.escape) {
|
|
23
|
+
onCancel();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if ((key.ctrl && input === "s") || input === "\x13") {
|
|
27
|
+
onSave(value);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if ((key.ctrl && !key.shift && input === "z") || input === "\x1a") {
|
|
31
|
+
undo();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (key.ctrl && key.shift && input === "z") {
|
|
35
|
+
redo();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if ((key.ctrl && input === "x") || input === "\x18") {
|
|
39
|
+
copyToClipboard(value);
|
|
40
|
+
setValue("");
|
|
41
|
+
setCursorRow(0);
|
|
42
|
+
setCursorCol(0);
|
|
43
|
+
setFlashMsg(t("codeEditor.copied"));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if ((key.ctrl && input === "v") || input === "\x16") {
|
|
47
|
+
const clip = readFromClipboard();
|
|
48
|
+
if (clip) {
|
|
49
|
+
const pasted = sanitizeMultilinePaste(clip);
|
|
50
|
+
if (pasted) {
|
|
51
|
+
const next = [...lines];
|
|
52
|
+
const line = next[cursorRow] ?? "";
|
|
53
|
+
next[cursorRow] =
|
|
54
|
+
line.slice(0, cursorCol) + pasted + line.slice(cursorCol);
|
|
55
|
+
applyMutation(next, cursorRow, cursorCol + pasted.length);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
setFlashMsg(t("codeEditor.clipboardEmpty"));
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if ((key.ctrl && input === "l") || input === "\x0c") {
|
|
64
|
+
setValue("");
|
|
65
|
+
setCursorRow(0);
|
|
66
|
+
setCursorCol(0);
|
|
67
|
+
setFlashMsg(t("codeEditor.cleared"));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (key.return) {
|
|
71
|
+
const next = [...lines];
|
|
72
|
+
const line = next[cursorRow] ?? "";
|
|
73
|
+
next.splice(cursorRow, 1, line.slice(0, cursorCol), line.slice(cursorCol));
|
|
74
|
+
applyMutation(next, cursorRow + 1, 0);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (key.upArrow) {
|
|
78
|
+
if (cursorRow > 0) {
|
|
79
|
+
const newRow = cursorRow - 1;
|
|
80
|
+
setCursorRow(newRow);
|
|
81
|
+
setCursorCol((c) => Math.min(c, (lines[newRow] ?? "").length));
|
|
82
|
+
}
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (key.downArrow) {
|
|
86
|
+
if (cursorRow < lines.length - 1) {
|
|
87
|
+
const newRow = cursorRow + 1;
|
|
88
|
+
setCursorRow(newRow);
|
|
89
|
+
setCursorCol((c) => Math.min(c, (lines[newRow] ?? "").length));
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (key.leftArrow) {
|
|
94
|
+
if (cursorCol > 0) {
|
|
95
|
+
setCursorCol((c) => c - 1);
|
|
96
|
+
}
|
|
97
|
+
else if (cursorRow > 0) {
|
|
98
|
+
setCursorRow((r) => r - 1);
|
|
99
|
+
setCursorCol((lines[cursorRow - 1] ?? "").length);
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (key.rightArrow) {
|
|
104
|
+
const curLen = (lines[cursorRow] ?? "").length;
|
|
105
|
+
if (cursorCol < curLen) {
|
|
106
|
+
setCursorCol((c) => c + 1);
|
|
107
|
+
}
|
|
108
|
+
else if (cursorRow < lines.length - 1) {
|
|
109
|
+
setCursorRow((r) => r + 1);
|
|
110
|
+
setCursorCol(0);
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (key.backspace || key.delete) {
|
|
115
|
+
if (cursorCol > 0) {
|
|
116
|
+
const next = [...lines];
|
|
117
|
+
const line = next[cursorRow] ?? "";
|
|
118
|
+
next[cursorRow] =
|
|
119
|
+
line.slice(0, cursorCol - 1) + line.slice(cursorCol);
|
|
120
|
+
applyMutation(next, cursorRow, cursorCol - 1);
|
|
121
|
+
}
|
|
122
|
+
else if (cursorRow > 0) {
|
|
123
|
+
const next = [...lines];
|
|
124
|
+
const prev = next[cursorRow - 1] ?? "";
|
|
125
|
+
const cur = next[cursorRow] ?? "";
|
|
126
|
+
next.splice(cursorRow - 1, 2, prev + cur);
|
|
127
|
+
applyMutation(next, cursorRow - 1, prev.length);
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (input.length > 1 && !key.meta) {
|
|
132
|
+
const pasted = wrapCommand(sanitizeMultilinePaste(input));
|
|
133
|
+
if (pasted) {
|
|
134
|
+
const next = [...lines];
|
|
135
|
+
const line = next[cursorRow] ?? "";
|
|
136
|
+
next[cursorRow] =
|
|
137
|
+
line.slice(0, cursorCol) + pasted + line.slice(cursorCol);
|
|
138
|
+
applyMutation(next, cursorRow, cursorCol + pasted.length);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (input.length === 1 && input >= " " && input <= "~") {
|
|
143
|
+
const next = [...lines];
|
|
144
|
+
const line = next[cursorRow] ?? "";
|
|
145
|
+
next[cursorRow] =
|
|
146
|
+
line.slice(0, cursorCol) + input + line.slice(cursorCol);
|
|
147
|
+
applyMutation(next, cursorRow, cursorCol + 1);
|
|
148
|
+
}
|
|
149
|
+
}, { isActive: true });
|
|
150
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CODE_EDITOR_MODAL_HEIGHT, CODE_EDITOR_MODAL_WIDTH, } from "../../shared/config/constants.js";
|
|
2
|
+
export function useModalDimensions() {
|
|
3
|
+
const termRows = process.stdout.rows || 24;
|
|
4
|
+
const termCols = process.stdout.columns || 80;
|
|
5
|
+
const modalHeight = Math.min(CODE_EDITOR_MODAL_HEIGHT, termRows - 2);
|
|
6
|
+
const modalWidth = Math.min(CODE_EDITOR_MODAL_WIDTH, termCols - 2);
|
|
7
|
+
const editorVisibleLines = Math.max(3, modalHeight - 6);
|
|
8
|
+
return { modalWidth, modalHeight, editorVisibleLines };
|
|
9
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { COMMAND_TIER_ACTION, COMMAND_TIER_CONFIRM, COMMAND_TIER_GLOBAL, COMMAND_CATEGORY_GLOBAL, COMMAND_CATEGORY_FILTERS, COMMAND_CATEGORY_LOOP, COMMAND_CATEGORY_TASK, COMMAND_CATEGORY_PROJECT, } from '../../shared/config/constants.js';
|
|
2
|
+
import { t } from '../../shared/i18n/index.js';
|
|
3
|
+
function globalCommands() {
|
|
4
|
+
return [
|
|
5
|
+
{ label: t('cmd.help'), value: 'help', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL, shortcut: 'ctrl+p' },
|
|
6
|
+
{ label: t('cmd.debug'), value: 'debug', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL, shortcut: 'ctrl+b' },
|
|
7
|
+
{ label: t('cmd.toggleApi'), value: 'toggle-api', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL },
|
|
8
|
+
{ label: t('cmd.api'), value: 'api', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL },
|
|
9
|
+
{ label: t('cmd.toggleMcp'), value: 'toggle-mcp', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL },
|
|
10
|
+
{ label: t('cmd.mcp'), value: 'mcp', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL },
|
|
11
|
+
{ label: t('cmd.toggleTelemetry'), value: 'toggle-telemetry', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL },
|
|
12
|
+
{ label: t('cmd.telemetryDiagnostics'), value: 'telemetry-diagnostics', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL },
|
|
13
|
+
{ label: t('cmd.telemetry'), value: 'telemetry', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL },
|
|
14
|
+
{ label: t('cmd.export'), value: 'export', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL, shortcut: 'ctrl+x' },
|
|
15
|
+
{ label: t('cmd.import'), value: 'import', hint: '', tier: COMMAND_TIER_GLOBAL, category: COMMAND_CATEGORY_GLOBAL, shortcut: 'ctrl+i' },
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
function loopFilterCommands() {
|
|
19
|
+
return [
|
|
20
|
+
{ label: t('cmd.search'), value: 'search', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_FILTERS, shortcut: 'ctrl+f+s' },
|
|
21
|
+
{ label: t('cmd.filterStatus'), value: 'filter-status', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_FILTERS, shortcut: 'ctrl+f+t' },
|
|
22
|
+
{ label: t('cmd.sort'), value: 'sort', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_FILTERS, shortcut: 'ctrl+f+o' },
|
|
23
|
+
{ label: t('cmd.project'), value: 'filter-project', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_FILTERS, shortcut: 'ctrl+f+p' },
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
function taskFilterCommands() {
|
|
27
|
+
return [
|
|
28
|
+
{ label: t('cmd.searchTasks'), value: 'search', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_FILTERS, shortcut: 'ctrl+f+s' },
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
function projectFilterCommands() {
|
|
32
|
+
return [
|
|
33
|
+
{ label: t('cmd.searchProjects'), value: 'search', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_FILTERS, shortcut: 'ctrl+f+s' },
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Re-rank autocomplete options: exact match → prefix match → fuzzy (existing order).
|
|
38
|
+
* Stable within each group. Exported for testing.
|
|
39
|
+
*/
|
|
40
|
+
export function rankCommands(input, options) {
|
|
41
|
+
if (input.length === 0)
|
|
42
|
+
return options;
|
|
43
|
+
const q = input.toLowerCase();
|
|
44
|
+
const exact = [];
|
|
45
|
+
const prefix = [];
|
|
46
|
+
const fuzzy = [];
|
|
47
|
+
for (const opt of options) {
|
|
48
|
+
const l = opt.label.toLowerCase();
|
|
49
|
+
const v = opt.value.toLowerCase();
|
|
50
|
+
if (l === q || v === q) {
|
|
51
|
+
exact.push(opt);
|
|
52
|
+
}
|
|
53
|
+
else if (l.startsWith(q) || v.startsWith(q)) {
|
|
54
|
+
prefix.push(opt);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
fuzzy.push(opt);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return [...exact, ...prefix, ...fuzzy];
|
|
61
|
+
}
|
|
62
|
+
export function buildCommands(context) {
|
|
63
|
+
const commands = [...globalCommands()];
|
|
64
|
+
if (context.activeTab === 'loops') {
|
|
65
|
+
commands.push(...loopFilterCommands());
|
|
66
|
+
commands.push({
|
|
67
|
+
label: t('cmd.newLoop'), value: 'new-loop', hint: '',
|
|
68
|
+
tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+n',
|
|
69
|
+
});
|
|
70
|
+
if (context.selectedLoop !== null) {
|
|
71
|
+
const loop = context.selectedLoop;
|
|
72
|
+
const desc = loop.description || loop.id;
|
|
73
|
+
commands.push({ label: t('cmd.edit'), value: 'edit', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+e' }, { label: t('cmd.pause'), value: 'pause', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+p' }, { label: t('cmd.play'), value: 'play', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+r' }, { label: t('cmd.stop'), value: 'stop', hint: t('confirm.stopLoop', { name: desc }), tier: COMMAND_TIER_CONFIRM, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+s' }, { label: t('cmd.trigger'), value: 'trigger', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+t' }, { label: t('cmd.clone'), value: 'clone', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+c' }, { label: t('cmd.delete'), value: 'delete', hint: t('confirm.deleteLoop', { name: desc }), tier: COMMAND_TIER_CONFIRM, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+d' }, { label: t('cmd.logs'), value: 'logs', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+o' });
|
|
74
|
+
if (loop.taskId && loop.isRecipe) {
|
|
75
|
+
commands.push({
|
|
76
|
+
label: t('cmd.diagram'), value: 'diagram', hint: '',
|
|
77
|
+
tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (context.activeTab === 'tasks') {
|
|
83
|
+
commands.push(...taskFilterCommands());
|
|
84
|
+
commands.push({
|
|
85
|
+
label: t('cmd.newTask'), value: 'new-task', hint: '',
|
|
86
|
+
tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_TASK, shortcut: 'ctrl+a+n',
|
|
87
|
+
});
|
|
88
|
+
if (context.selectedTask !== null) {
|
|
89
|
+
const task = context.selectedTask;
|
|
90
|
+
commands.push({ label: t('cmd.editTask'), value: 'edit', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_TASK, shortcut: 'ctrl+a+e' }, { label: t('cmd.deleteTask'), value: 'delete', hint: t('confirm.deleteTask', { id: task.id }), tier: COMMAND_TIER_CONFIRM, category: COMMAND_CATEGORY_TASK, shortcut: 'ctrl+a+d' });
|
|
91
|
+
if (task.isRecipeTask) {
|
|
92
|
+
// Remove edit and delete for recipe tasks — they are immutable
|
|
93
|
+
commands.pop();
|
|
94
|
+
commands.pop();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (context.activeTab === 'projects') {
|
|
99
|
+
commands.push(...projectFilterCommands());
|
|
100
|
+
commands.push({
|
|
101
|
+
label: t('cmd.newProject'), value: 'new-project', hint: '',
|
|
102
|
+
tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_PROJECT, shortcut: 'ctrl+a+n',
|
|
103
|
+
});
|
|
104
|
+
if (context.selectedProject !== null) {
|
|
105
|
+
const project = context.selectedProject;
|
|
106
|
+
commands.push({ label: t('cmd.editProject'), value: 'edit', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_PROJECT, shortcut: 'ctrl+a+e' }, { label: t('cmd.deleteProject'), value: 'delete', hint: t('confirm.deleteProject', { name: project.name }), tier: COMMAND_TIER_CONFIRM, category: COMMAND_CATEGORY_PROJECT, shortcut: 'ctrl+a+d' });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return commands;
|
|
110
|
+
}
|
|
111
|
+
export function buildTabCommands(context) {
|
|
112
|
+
const commands = [...globalCommands()];
|
|
113
|
+
if (context.activeTab === 'loops') {
|
|
114
|
+
commands.push(...loopFilterCommands());
|
|
115
|
+
commands.push({ label: t('cmd.newLoop'), value: 'new-loop', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+n' }, { label: t('cmd.edit'), value: 'edit', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+e' }, { label: t('cmd.pause'), value: 'pause', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+p' }, { label: t('cmd.play'), value: 'play', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+r' }, { label: t('cmd.stop'), value: 'stop', hint: '', tier: COMMAND_TIER_CONFIRM, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+s' }, { label: t('cmd.trigger'), value: 'trigger', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+t' }, { label: t('cmd.clone'), value: 'clone', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+c' }, { label: t('cmd.delete'), value: 'delete', hint: '', tier: COMMAND_TIER_CONFIRM, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+d' }, { label: t('cmd.logs'), value: 'logs', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP, shortcut: 'ctrl+a+o' });
|
|
116
|
+
if (context.selectedLoop?.taskId && context.selectedLoop?.isRecipe) {
|
|
117
|
+
commands.push({
|
|
118
|
+
label: t('cmd.diagram'), value: 'diagram', hint: '',
|
|
119
|
+
tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_LOOP,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (context.activeTab === 'tasks') {
|
|
124
|
+
commands.push(...taskFilterCommands());
|
|
125
|
+
commands.push({ label: t('cmd.newTask'), value: 'new-task', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_TASK, shortcut: 'ctrl+a+n' }, { label: t('cmd.editTask'), value: 'edit', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_TASK, shortcut: 'ctrl+a+e' }, { label: t('cmd.deleteTask'), value: 'delete', hint: '', tier: COMMAND_TIER_CONFIRM, category: COMMAND_CATEGORY_TASK, shortcut: 'ctrl+a+d' });
|
|
126
|
+
}
|
|
127
|
+
if (context.activeTab === 'projects') {
|
|
128
|
+
commands.push(...projectFilterCommands());
|
|
129
|
+
commands.push({ label: t('cmd.newProject'), value: 'new-project', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_PROJECT, shortcut: 'ctrl+a+n' }, { label: t('cmd.editProject'), value: 'edit', hint: '', tier: COMMAND_TIER_ACTION, category: COMMAND_CATEGORY_PROJECT, shortcut: 'ctrl+a+e' }, { label: t('cmd.deleteProject'), value: 'delete', hint: '', tier: COMMAND_TIER_CONFIRM, category: COMMAND_CATEGORY_PROJECT, shortcut: 'ctrl+a+d' });
|
|
130
|
+
}
|
|
131
|
+
return commands;
|
|
132
|
+
}
|