wave-code 0.7.2 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +2 -4
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +24 -52
- package/dist/components/App.d.ts +3 -4
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +49 -6
- package/dist/components/BangDisplay.d.ts +9 -0
- package/dist/components/BangDisplay.d.ts.map +1 -0
- package/dist/components/{CommandOutputDisplay.js → BangDisplay.js} +1 -1
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +3 -2
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +7 -28
- package/dist/components/ConfirmationSelector.d.ts.map +1 -1
- package/dist/components/ConfirmationSelector.js +116 -11
- package/dist/components/HelpView.d.ts +2 -0
- package/dist/components/HelpView.d.ts.map +1 -1
- package/dist/components/HelpView.js +49 -3
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +10 -4
- package/dist/components/MarketplaceAddForm.d.ts.map +1 -1
- package/dist/components/MarketplaceAddForm.js +13 -6
- package/dist/components/MarketplaceDetail.d.ts.map +1 -1
- package/dist/components/MarketplaceDetail.js +8 -3
- package/dist/components/MessageBlockItem.js +2 -2
- package/dist/components/MessageList.d.ts +4 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +15 -8
- package/dist/components/PluginDetail.d.ts.map +1 -1
- package/dist/components/PluginDetail.js +14 -3
- package/dist/components/PluginManagerShell.d.ts.map +1 -1
- package/dist/components/PluginManagerShell.js +3 -3
- package/dist/components/PluginManagerTypes.d.ts +2 -0
- package/dist/components/PluginManagerTypes.d.ts.map +1 -1
- package/dist/components/SessionSelector.d.ts.map +1 -1
- package/dist/components/SessionSelector.js +5 -5
- package/dist/components/StatusCommand.d.ts +6 -0
- package/dist/components/StatusCommand.d.ts.map +1 -0
- package/dist/components/StatusCommand.js +28 -0
- package/dist/components/WorktreeExitPrompt.d.ts +13 -0
- package/dist/components/WorktreeExitPrompt.d.ts.map +1 -0
- package/dist/components/WorktreeExitPrompt.js +26 -0
- package/dist/constants/commands.d.ts +3 -0
- package/dist/constants/commands.d.ts.map +1 -0
- package/dist/constants/commands.js +38 -0
- package/dist/contexts/useChat.d.ts +9 -5
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +38 -8
- package/dist/contracts/status.d.ts +8 -0
- package/dist/contracts/status.d.ts.map +1 -0
- package/dist/contracts/status.js +1 -0
- package/dist/hooks/useInputManager.d.ts +2 -0
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +12 -0
- package/dist/hooks/usePluginManager.d.ts.map +1 -1
- package/dist/hooks/usePluginManager.js +41 -13
- package/dist/hooks/useTasks.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -4
- package/dist/managers/InputManager.d.ts +6 -0
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +32 -13
- package/dist/print-cli.d.ts +2 -4
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +31 -2
- package/dist/session-selector-cli.d.ts +3 -1
- package/dist/session-selector-cli.d.ts.map +1 -1
- package/dist/session-selector-cli.js +2 -2
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils/highlightUtils.d.ts.map +1 -1
- package/dist/utils/highlightUtils.js +66 -42
- package/dist/utils/worktree.d.ts +23 -0
- package/dist/utils/worktree.d.ts.map +1 -0
- package/dist/utils/worktree.js +135 -0
- package/package.json +2 -2
- package/src/cli.tsx +36 -59
- package/src/components/App.tsx +99 -11
- package/src/components/{CommandOutputDisplay.tsx → BangDisplay.tsx} +4 -4
- package/src/components/ChatInterface.tsx +8 -0
- package/src/components/CommandSelector.tsx +7 -29
- package/src/components/ConfirmationSelector.tsx +131 -12
- package/src/components/HelpView.tsx +129 -14
- package/src/components/InputBox.tsx +14 -2
- package/src/components/MarketplaceAddForm.tsx +21 -8
- package/src/components/MarketplaceDetail.tsx +19 -4
- package/src/components/MessageBlockItem.tsx +3 -3
- package/src/components/MessageList.tsx +47 -23
- package/src/components/PluginDetail.tsx +30 -6
- package/src/components/PluginManagerShell.tsx +24 -6
- package/src/components/PluginManagerTypes.ts +2 -0
- package/src/components/SessionSelector.tsx +33 -16
- package/src/components/StatusCommand.tsx +94 -0
- package/src/components/WorktreeExitPrompt.tsx +86 -0
- package/src/constants/commands.ts +41 -0
- package/src/contexts/useChat.tsx +57 -13
- package/src/contracts/status.ts +7 -0
- package/src/hooks/useInputManager.ts +12 -0
- package/src/hooks/usePluginManager.ts +47 -13
- package/src/hooks/useTasks.ts +2 -2
- package/src/index.ts +71 -12
- package/src/managers/InputManager.ts +37 -15
- package/src/print-cli.ts +48 -5
- package/src/session-selector-cli.tsx +6 -2
- package/src/types.ts +11 -0
- package/src/utils/highlightUtils.ts +66 -42
- package/src/utils/worktree.ts +164 -0
- package/dist/components/CommandOutputDisplay.d.ts +0 -9
- package/dist/components/CommandOutputDisplay.d.ts.map +0 -1
package/dist/cli.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { BaseAppProps } from "./types.js";
|
|
2
|
+
export interface CliOptions extends BaseAppProps {
|
|
2
3
|
restoreSessionId?: string;
|
|
3
4
|
continueLastSession?: boolean;
|
|
4
|
-
bypassPermissions?: boolean;
|
|
5
|
-
pluginDirs?: string[];
|
|
6
|
-
tools?: string[];
|
|
7
5
|
}
|
|
8
6
|
export declare function startCli(options: CliOptions): Promise<void>;
|
|
9
7
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":"AAKA,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC9C,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DjE"}
|
package/dist/cli.js
CHANGED
|
@@ -2,61 +2,33 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { render } from "ink";
|
|
3
3
|
import { App } from "./components/App.js";
|
|
4
4
|
import { cleanupLogs } from "./utils/logger.js";
|
|
5
|
+
import { removeWorktree } from "./utils/worktree.js";
|
|
5
6
|
export async function startCli(options) {
|
|
6
|
-
const { restoreSessionId, continueLastSession, bypassPermissions, pluginDirs, tools, } = options;
|
|
7
|
+
const { restoreSessionId, continueLastSession, bypassPermissions, pluginDirs, tools, worktreeSession, workdir, version, model, } = options;
|
|
7
8
|
// Continue with ink-based UI for normal mode
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (isCleaningUp)
|
|
13
|
-
return;
|
|
14
|
-
isCleaningUp = true;
|
|
15
|
-
console.log("\nShutting down gracefully...");
|
|
16
|
-
try {
|
|
17
|
-
// Clean up old log files
|
|
18
|
-
await cleanupLogs().catch((error) => {
|
|
19
|
-
console.warn("Failed to cleanup old logs:", error);
|
|
20
|
-
});
|
|
21
|
-
// Unmount the React app to trigger cleanup
|
|
22
|
-
if (!appUnmounted) {
|
|
23
|
-
unmount();
|
|
24
|
-
appUnmounted = true;
|
|
25
|
-
// Give React time to cleanup
|
|
26
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
27
|
-
}
|
|
28
|
-
process.exit(0);
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
console.error("Error during cleanup:", error);
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
9
|
+
let shouldRemoveWorktree = false;
|
|
10
|
+
const handleExit = (shouldRemove) => {
|
|
11
|
+
shouldRemoveWorktree = shouldRemove;
|
|
12
|
+
unmount();
|
|
34
13
|
};
|
|
35
|
-
// Handle process signals
|
|
36
|
-
process.on("SIGINT", cleanup);
|
|
37
|
-
process.on("SIGTERM", cleanup);
|
|
38
|
-
// Handle uncaught exceptions
|
|
39
|
-
process.on("uncaughtException", (error) => {
|
|
40
|
-
console.error("Uncaught exception:", error);
|
|
41
|
-
cleanup();
|
|
42
|
-
});
|
|
43
|
-
process.on("unhandledRejection", (reason, promise) => {
|
|
44
|
-
console.error("Unhandled rejection at:", promise, "reason:", reason);
|
|
45
|
-
cleanup();
|
|
46
|
-
});
|
|
47
14
|
// Render the application
|
|
48
|
-
const { unmount } = render(_jsx(App, { restoreSessionId: restoreSessionId, continueLastSession: continueLastSession, bypassPermissions: bypassPermissions, pluginDirs: pluginDirs, tools: tools }));
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
15
|
+
const { unmount, waitUntilExit } = render(_jsx(App, { restoreSessionId: restoreSessionId, continueLastSession: continueLastSession, bypassPermissions: bypassPermissions, pluginDirs: pluginDirs, tools: tools, worktreeSession: worktreeSession, workdir: workdir, version: version, model: model, onExit: handleExit }), { exitOnCtrlC: false });
|
|
16
|
+
// Wait for the app to finish unmounting
|
|
17
|
+
await waitUntilExit();
|
|
18
|
+
try {
|
|
19
|
+
// Clean up old log files
|
|
20
|
+
await cleanupLogs().catch((error) => {
|
|
21
|
+
console.warn("Failed to cleanup old logs:", error);
|
|
22
|
+
});
|
|
23
|
+
// Cleanup worktree if requested
|
|
24
|
+
if (shouldRemoveWorktree && worktreeSession) {
|
|
25
|
+
process.chdir(worktreeSession.repoRoot);
|
|
26
|
+
removeWorktree(worktreeSession);
|
|
58
27
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error("Error during cleanup:", error);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
62
34
|
}
|
package/dist/components/App.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
|
|
2
|
+
import { BaseAppProps } from "../types.js";
|
|
3
|
+
interface AppProps extends BaseAppProps {
|
|
3
4
|
restoreSessionId?: string;
|
|
4
5
|
continueLastSession?: boolean;
|
|
5
|
-
|
|
6
|
-
pluginDirs?: string[];
|
|
7
|
-
tools?: string[];
|
|
6
|
+
onExit: (shouldRemove: boolean) => void;
|
|
8
7
|
}
|
|
9
8
|
export declare const App: React.FC<AppProps>;
|
|
10
9
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAWxE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,UAAU,QAAS,SAAQ,YAAY;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;CACzC;AA0ID,eAAO,MAAM,GAAG,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CA6BlC,CAAC"}
|
package/dist/components/App.js
CHANGED
|
@@ -1,11 +1,54 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect, useRef } from "react";
|
|
3
|
-
import { useStdout } from "ink";
|
|
2
|
+
import { useState, useEffect, useRef, useCallback } from "react";
|
|
3
|
+
import { useStdout, useInput } from "ink";
|
|
4
4
|
import { ChatInterface } from "./ChatInterface.js";
|
|
5
5
|
import { ChatProvider, useChat } from "../contexts/useChat.js";
|
|
6
6
|
import { AppProvider } from "../contexts/useAppConfig.js";
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
import { WorktreeExitPrompt } from "./WorktreeExitPrompt.js";
|
|
8
|
+
import { hasUncommittedChanges, hasNewCommits, getDefaultRemoteBranch, } from "wave-agent-sdk";
|
|
9
|
+
const AppWithProviders = ({ bypassPermissions, pluginDirs, tools, worktreeSession, workdir, version, model, onExit, }) => {
|
|
10
|
+
const [isExiting, setIsExiting] = useState(false);
|
|
11
|
+
const [worktreeStatus, setWorktreeStatus] = useState(null);
|
|
12
|
+
const handleSignal = useCallback(async () => {
|
|
13
|
+
if (worktreeSession) {
|
|
14
|
+
const cwd = workdir || worktreeSession.path;
|
|
15
|
+
const baseBranch = getDefaultRemoteBranch(cwd);
|
|
16
|
+
const hasChanges = hasUncommittedChanges(cwd);
|
|
17
|
+
const hasCommits = hasNewCommits(cwd, baseBranch);
|
|
18
|
+
if (hasChanges || hasCommits) {
|
|
19
|
+
setWorktreeStatus({
|
|
20
|
+
hasUncommittedChanges: hasChanges,
|
|
21
|
+
hasNewCommits: hasCommits,
|
|
22
|
+
});
|
|
23
|
+
setIsExiting(true);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
onExit(true);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
onExit(false);
|
|
31
|
+
}
|
|
32
|
+
}, [worktreeSession, workdir, onExit]);
|
|
33
|
+
useInput((input, key) => {
|
|
34
|
+
if (input === "c" && key.ctrl) {
|
|
35
|
+
handleSignal();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const onSigInt = () => handleSignal();
|
|
40
|
+
const onSigTerm = () => handleSignal();
|
|
41
|
+
process.on("SIGINT", onSigInt);
|
|
42
|
+
process.on("SIGTERM", onSigTerm);
|
|
43
|
+
return () => {
|
|
44
|
+
process.off("SIGINT", onSigInt);
|
|
45
|
+
process.off("SIGTERM", onSigTerm);
|
|
46
|
+
};
|
|
47
|
+
}, [handleSignal]);
|
|
48
|
+
if (isExiting && worktreeSession && worktreeStatus) {
|
|
49
|
+
return (_jsx(WorktreeExitPrompt, { name: worktreeSession.name, path: worktreeSession.path, hasUncommittedChanges: worktreeStatus.hasUncommittedChanges, hasNewCommits: worktreeStatus.hasNewCommits, onKeep: () => onExit(false), onRemove: () => onExit(true), onCancel: () => setIsExiting(false) }));
|
|
50
|
+
}
|
|
51
|
+
return (_jsx(ChatProvider, { bypassPermissions: bypassPermissions, pluginDirs: pluginDirs, tools: tools, workdir: workdir, worktreeSession: worktreeSession, version: version, model: model, children: _jsx(ChatInterfaceWithRemount, {}) }));
|
|
9
52
|
};
|
|
10
53
|
const ChatInterfaceWithRemount = () => {
|
|
11
54
|
const { stdout } = useStdout();
|
|
@@ -43,6 +86,6 @@ const ChatInterfaceWithRemount = () => {
|
|
|
43
86
|
]);
|
|
44
87
|
return _jsx(ChatInterface, {}, remountKey);
|
|
45
88
|
};
|
|
46
|
-
export const App = ({ restoreSessionId, continueLastSession, bypassPermissions, pluginDirs, tools, }) => {
|
|
47
|
-
return (_jsx(AppProvider, { restoreSessionId: restoreSessionId, continueLastSession: continueLastSession, children: _jsx(AppWithProviders, { bypassPermissions: bypassPermissions, pluginDirs: pluginDirs, tools: tools }) }));
|
|
89
|
+
export const App = ({ restoreSessionId, continueLastSession, bypassPermissions, pluginDirs, tools, worktreeSession, workdir, version, model, onExit, }) => {
|
|
90
|
+
return (_jsx(AppProvider, { restoreSessionId: restoreSessionId, continueLastSession: continueLastSession, children: _jsx(AppWithProviders, { bypassPermissions: bypassPermissions, pluginDirs: pluginDirs, tools: tools, worktreeSession: worktreeSession, workdir: workdir, version: version, model: model, onExit: onExit }) }));
|
|
48
91
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { BangBlock } from "wave-agent-sdk";
|
|
3
|
+
interface BangDisplayProps {
|
|
4
|
+
block: BangBlock;
|
|
5
|
+
isExpanded?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const BangDisplay: React.FC<BangDisplayProps>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=BangDisplay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BangDisplay.d.ts","sourceRoot":"","sources":["../../src/components/BangDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAEnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,UAAU,gBAAgB;IACxB,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAiDlD,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from "react";
|
|
3
3
|
import { Box, Text } from "ink";
|
|
4
|
-
export const
|
|
4
|
+
export const BangDisplay = ({ block, isExpanded = false, }) => {
|
|
5
5
|
const { command, output, isRunning, exitCode } = block;
|
|
6
6
|
const [isOverflowing, setIsOverflowing] = useState(false);
|
|
7
7
|
const MAX_LINES = 3; // Set maximum display lines
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAYtE,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAYtE,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EA6IjC,CAAC"}
|
|
@@ -13,7 +13,8 @@ export const ChatInterface = () => {
|
|
|
13
13
|
const [detailsHeight, setDetailsHeight] = useState(0);
|
|
14
14
|
const [selectorHeight, setSelectorHeight] = useState(0);
|
|
15
15
|
const [isConfirmationTooTall, setIsConfirmationTooTall] = useState(false);
|
|
16
|
-
const { messages, isLoading, isCommandRunning, isCompressing, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, slashCommands, hasSlashCommand, isConfirmationVisible, confirmingTool, handleConfirmationDecision, handleConfirmationCancel: originalHandleConfirmationCancel, setWasLastDetailsTooTall, } = useChat();
|
|
16
|
+
const { messages, isLoading, isCommandRunning, isCompressing, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, slashCommands, hasSlashCommand, isConfirmationVisible, confirmingTool, handleConfirmationDecision, handleConfirmationCancel: originalHandleConfirmationCancel, setWasLastDetailsTooTall, version, workdir, getModelConfig, } = useChat();
|
|
17
|
+
const model = getModelConfig().model;
|
|
17
18
|
const handleDetailsHeightMeasured = useCallback((height) => {
|
|
18
19
|
setDetailsHeight(height);
|
|
19
20
|
}, []);
|
|
@@ -54,7 +55,7 @@ export const ChatInterface = () => {
|
|
|
54
55
|
]);
|
|
55
56
|
if (!sessionId)
|
|
56
57
|
return null;
|
|
57
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(MessageList, { messages: messages, isExpanded: isExpanded, hideDynamicBlocks: isConfirmationVisible }), (isLoading || isCommandRunning || isCompressing) &&
|
|
58
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(MessageList, { messages: messages, isExpanded: isExpanded, hideDynamicBlocks: isConfirmationVisible, version: version, workdir: workdir, model: model }), (isLoading || isCommandRunning || isCompressing) &&
|
|
58
59
|
!isConfirmationVisible &&
|
|
59
60
|
!isExpanded && (_jsx(LoadingIndicator, { isLoading: isLoading, isCommandRunning: isCommandRunning, isCompressing: isCompressing, latestTotalTokens: latestTotalTokens })), !isConfirmationVisible && !isExpanded && _jsx(TaskList, {}), isConfirmationVisible && (_jsxs(_Fragment, { children: [_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, isExpanded: isExpanded, onHeightMeasured: handleDetailsHeightMeasured, isStatic: isConfirmationTooTall }), _jsx(ConfirmationSelector, { toolName: confirmingTool.name, toolInput: confirmingTool.input, suggestedPrefix: confirmingTool.suggestedPrefix, hidePersistentOption: confirmingTool.hidePersistentOption, isExpanded: isExpanded, onDecision: wrappedHandleConfirmationDecision, onCancel: handleConfirmationCancel, onAbort: abortMessage, onHeightMeasured: handleSelectorHeightMeasured })] })), !isConfirmationVisible && !isExpanded && (_jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, sendMessage: sendMessage, abortMessage: abortMessage, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand }))] }));
|
|
60
61
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandSelector.d.ts","sourceRoot":"","sources":["../../src/components/CommandSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"CommandSelector.d.ts","sourceRoot":"","sources":["../../src/components/CommandSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA+I1D,CAAC"}
|
|
@@ -1,38 +1,17 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from "react";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
|
-
|
|
5
|
-
{
|
|
6
|
-
id: "tasks",
|
|
7
|
-
name: "tasks",
|
|
8
|
-
description: "View and manage background tasks (shells and subagents)",
|
|
9
|
-
handler: () => { }, // Handler here won't be used, actual processing is in the hook
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
id: "mcp",
|
|
13
|
-
name: "mcp",
|
|
14
|
-
description: "View and manage MCP servers",
|
|
15
|
-
handler: () => { }, // Handler here won't be used, actual processing is in the hook
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
id: "rewind",
|
|
19
|
-
name: "rewind",
|
|
20
|
-
description: "Revert conversation and file changes to a previous checkpoint",
|
|
21
|
-
handler: () => { }, // Handler here won't be used, actual processing is in the hook
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: "help",
|
|
25
|
-
name: "help",
|
|
26
|
-
description: "Show help and key bindings",
|
|
27
|
-
handler: () => { }, // Handler here won't be used, actual processing is in the hook
|
|
28
|
-
},
|
|
29
|
-
];
|
|
4
|
+
import { AVAILABLE_COMMANDS } from "../constants/commands.js";
|
|
30
5
|
export const CommandSelector = ({ searchQuery, onSelect, onInsert, onCancel, commands = [], // Default to empty array
|
|
31
6
|
}) => {
|
|
32
7
|
const MAX_VISIBLE_ITEMS = 3;
|
|
33
8
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
9
|
+
// Reset selected index when search query changes
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
setSelectedIndex(0);
|
|
12
|
+
}, [searchQuery]);
|
|
34
13
|
// Merge agent commands and local commands
|
|
35
|
-
const allCommands = [...
|
|
14
|
+
const allCommands = [...AVAILABLE_COMMANDS, ...commands];
|
|
36
15
|
// Filter command list
|
|
37
16
|
const filteredCommands = allCommands.filter((command) => !searchQuery ||
|
|
38
17
|
command.id.toLowerCase().includes(searchQuery.toLowerCase()));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmationSelector.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ConfirmationSelector.d.ts","sourceRoot":"","sources":["../../src/components/ConfirmationSelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuD,MAAM,OAAO,CAAC;AAE5E,OAAO,KAAK,EAAE,kBAAkB,EAAwB,MAAM,gBAAgB,CAAC;AAgB/E,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AASD,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CA6mBpE,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useLayoutEffect, useRef, useState } from "react";
|
|
2
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
3
|
import { Box, Text, useInput, useStdout, measureElement } from "ink";
|
|
4
4
|
import { BASH_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ASK_USER_QUESTION_TOOL_NAME, } from "wave-agent-sdk";
|
|
5
5
|
const getHeaderColor = (header) => {
|
|
@@ -32,6 +32,15 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
32
32
|
userAnswers: {},
|
|
33
33
|
otherText: "",
|
|
34
34
|
otherCursorPosition: 0,
|
|
35
|
+
savedStates: {},
|
|
36
|
+
});
|
|
37
|
+
const pendingDecisionRef = useRef(null);
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (pendingDecisionRef.current) {
|
|
40
|
+
const decision = pendingDecisionRef.current;
|
|
41
|
+
pendingDecisionRef.current = null;
|
|
42
|
+
onDecision(decision);
|
|
43
|
+
}
|
|
35
44
|
});
|
|
36
45
|
const questions = toolInput?.questions || [];
|
|
37
46
|
const currentQuestion = questions[questionState.currentQuestionIndex];
|
|
@@ -87,24 +96,72 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
87
96
|
[currentQuestion.question]: answer,
|
|
88
97
|
};
|
|
89
98
|
if (prev.currentQuestionIndex < questions.length - 1) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
const nextIndex = prev.currentQuestionIndex + 1;
|
|
100
|
+
const savedStates = {
|
|
101
|
+
...prev.savedStates,
|
|
102
|
+
[prev.currentQuestionIndex]: {
|
|
103
|
+
selectedOptionIndex: prev.selectedOptionIndex,
|
|
104
|
+
selectedOptionIndices: prev.selectedOptionIndices,
|
|
105
|
+
otherText: prev.otherText,
|
|
106
|
+
otherCursorPosition: prev.otherCursorPosition,
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const nextState = savedStates[nextIndex] || {
|
|
93
110
|
selectedOptionIndex: 0,
|
|
94
111
|
selectedOptionIndices: new Set(),
|
|
95
|
-
userAnswers: newAnswers,
|
|
96
112
|
otherText: "",
|
|
97
113
|
otherCursorPosition: 0,
|
|
98
114
|
};
|
|
115
|
+
return {
|
|
116
|
+
...prev,
|
|
117
|
+
currentQuestionIndex: nextIndex,
|
|
118
|
+
...nextState,
|
|
119
|
+
userAnswers: newAnswers,
|
|
120
|
+
savedStates,
|
|
121
|
+
};
|
|
99
122
|
}
|
|
100
123
|
else {
|
|
101
|
-
|
|
124
|
+
const finalAnswers = { ...newAnswers };
|
|
125
|
+
// Also collect from savedStates for any questions that were skipped via Tab
|
|
126
|
+
for (const [idxStr, s] of Object.entries(prev.savedStates)) {
|
|
127
|
+
const idx = parseInt(idxStr);
|
|
128
|
+
const q = questions[idx];
|
|
129
|
+
if (q && !finalAnswers[q.question]) {
|
|
130
|
+
const opts = [...q.options, { label: "Other" }];
|
|
131
|
+
let a = "";
|
|
132
|
+
if (q.multiSelect) {
|
|
133
|
+
const selectedLabels = Array.from(s.selectedOptionIndices)
|
|
134
|
+
.filter((i) => i < q.options.length)
|
|
135
|
+
.map((i) => q.options[i].label);
|
|
136
|
+
const isOtherChecked = s.selectedOptionIndices.has(opts.length - 1);
|
|
137
|
+
if (isOtherChecked && s.otherText.trim()) {
|
|
138
|
+
selectedLabels.push(s.otherText.trim());
|
|
139
|
+
}
|
|
140
|
+
a = selectedLabels.join(", ");
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
if (s.selectedOptionIndex === opts.length - 1) {
|
|
144
|
+
a = s.otherText.trim();
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
a = opts[s.selectedOptionIndex].label;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (a)
|
|
151
|
+
finalAnswers[q.question] = a;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Only submit if all questions have been answered
|
|
155
|
+
const allAnswered = questions.every((q) => finalAnswers[q.question]);
|
|
156
|
+
if (!allAnswered)
|
|
157
|
+
return prev;
|
|
158
|
+
pendingDecisionRef.current = {
|
|
102
159
|
behavior: "allow",
|
|
103
|
-
message: JSON.stringify(
|
|
104
|
-
}
|
|
160
|
+
message: JSON.stringify(finalAnswers),
|
|
161
|
+
};
|
|
105
162
|
return {
|
|
106
163
|
...prev,
|
|
107
|
-
userAnswers:
|
|
164
|
+
userAnswers: finalAnswers,
|
|
108
165
|
};
|
|
109
166
|
}
|
|
110
167
|
});
|
|
@@ -144,6 +201,40 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
144
201
|
}));
|
|
145
202
|
return;
|
|
146
203
|
}
|
|
204
|
+
if (key.tab) {
|
|
205
|
+
setQuestionState((prev) => {
|
|
206
|
+
const direction = key.shift ? -1 : 1;
|
|
207
|
+
let nextIndex = prev.currentQuestionIndex + direction;
|
|
208
|
+
if (nextIndex < 0)
|
|
209
|
+
nextIndex = questions.length - 1;
|
|
210
|
+
if (nextIndex >= questions.length)
|
|
211
|
+
nextIndex = 0;
|
|
212
|
+
if (nextIndex === prev.currentQuestionIndex)
|
|
213
|
+
return prev;
|
|
214
|
+
const savedStates = {
|
|
215
|
+
...prev.savedStates,
|
|
216
|
+
[prev.currentQuestionIndex]: {
|
|
217
|
+
selectedOptionIndex: prev.selectedOptionIndex,
|
|
218
|
+
selectedOptionIndices: prev.selectedOptionIndices,
|
|
219
|
+
otherText: prev.otherText,
|
|
220
|
+
otherCursorPosition: prev.otherCursorPosition,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
const nextState = savedStates[nextIndex] || {
|
|
224
|
+
selectedOptionIndex: 0,
|
|
225
|
+
selectedOptionIndices: new Set(),
|
|
226
|
+
otherText: "",
|
|
227
|
+
otherCursorPosition: 0,
|
|
228
|
+
};
|
|
229
|
+
return {
|
|
230
|
+
...prev,
|
|
231
|
+
currentQuestionIndex: nextIndex,
|
|
232
|
+
...nextState,
|
|
233
|
+
savedStates,
|
|
234
|
+
};
|
|
235
|
+
});
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
147
238
|
setQuestionState((prev) => {
|
|
148
239
|
const isOtherFocused = prev.selectedOptionIndex === options.length - 1;
|
|
149
240
|
if (isOtherFocused) {
|
|
@@ -258,6 +349,20 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
258
349
|
}
|
|
259
350
|
return;
|
|
260
351
|
}
|
|
352
|
+
if (key.tab) {
|
|
353
|
+
const currentIndex = availableOptions.indexOf(state.selectedOption);
|
|
354
|
+
const direction = key.shift ? -1 : 1;
|
|
355
|
+
let nextIndex = currentIndex + direction;
|
|
356
|
+
if (nextIndex < 0)
|
|
357
|
+
nextIndex = availableOptions.length - 1;
|
|
358
|
+
if (nextIndex >= availableOptions.length)
|
|
359
|
+
nextIndex = 0;
|
|
360
|
+
setState((prev) => ({
|
|
361
|
+
...prev,
|
|
362
|
+
selectedOption: availableOptions[nextIndex],
|
|
363
|
+
}));
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
261
366
|
if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
|
|
262
367
|
setState((prev) => {
|
|
263
368
|
const nextText = prev.alternativeText.slice(0, prev.alternativeCursorPosition) +
|
|
@@ -306,8 +411,8 @@ export const ConfirmationSelector = ({ toolName, toolInput, suggestedPrefix, hid
|
|
|
306
411
|
? "[x] "
|
|
307
412
|
: "[ ] "
|
|
308
413
|
: "", option.label, option.description ? ` - ${option.description}` : "", isOther && isSelected && (_jsxs(Text, { children: [":", " ", questionState.otherText ? (_jsxs(_Fragment, { children: [questionState.otherText.slice(0, questionState.otherCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: questionState.otherText[questionState.otherCursorPosition] || " " }), questionState.otherText.slice(questionState.otherCursorPosition + 1)] })) : (_jsx(Text, { color: "gray", dimColor: true, children: "[Type your answer...]" }))] }))] }) }, index));
|
|
309
|
-
}) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Question ", questionState.currentQuestionIndex + 1, " of", " ", questions.length, " \u2022", currentQuestion.multiSelect ? " Space to toggle •" : "", " Use \u2191\u2193 to navigate \u2022 Enter to confirm"] }) })] })), toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Do you want to proceed?" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [toolName === EXIT_PLAN_MODE_TOOL_NAME && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "clear" ? "black" : "white", backgroundColor: state.selectedOption === "clear" ? "yellow" : undefined, bold: state.selectedOption === "clear", children: [state.selectedOption === "clear" ? "> " : " ", "Yes, clear context and auto-accept edits"] }) }, "clear-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "allow" ? "black" : "white", backgroundColor: state.selectedOption === "allow" ? "yellow" : undefined, bold: state.selectedOption === "allow", children: [state.selectedOption === "allow" ? "> " : " ", toolName === EXIT_PLAN_MODE_TOOL_NAME
|
|
414
|
+
}) }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Question ", questionState.currentQuestionIndex + 1, " of", " ", questions.length, " \u2022", currentQuestion.multiSelect ? " Space to toggle •" : "", " Use \u2191\u2193 or Tab to navigate \u2022 Enter to confirm"] }) })] })), toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (_jsxs(_Fragment, { children: [_jsx(Box, { marginTop: 1, children: _jsx(Text, { children: "Do you want to proceed?" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [toolName === EXIT_PLAN_MODE_TOOL_NAME && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "clear" ? "black" : "white", backgroundColor: state.selectedOption === "clear" ? "yellow" : undefined, bold: state.selectedOption === "clear", children: [state.selectedOption === "clear" ? "> " : " ", "Yes, clear context and auto-accept edits"] }) }, "clear-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "allow" ? "black" : "white", backgroundColor: state.selectedOption === "allow" ? "yellow" : undefined, bold: state.selectedOption === "allow", children: [state.selectedOption === "allow" ? "> " : " ", toolName === EXIT_PLAN_MODE_TOOL_NAME
|
|
310
415
|
? "Yes, manually approve edits"
|
|
311
|
-
: "Yes, proceed"] }) }, "allow-option"), !hidePersistentOption && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "auto" ? "black" : "white", backgroundColor: state.selectedOption === "auto" ? "yellow" : undefined, bold: state.selectedOption === "auto", children: [state.selectedOption === "auto" ? "> " : " ", getAutoOptionText()] }) }, "auto-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "alternative" ? "black" : "white", backgroundColor: state.selectedOption === "alternative" ? "yellow" : undefined, bold: state.selectedOption === "alternative", children: [state.selectedOption === "alternative" ? "> " : " ", showPlaceholder ? (_jsx(Text, { color: "gray", dimColor: true, children: placeholderText })) : (_jsx(Text, { children: state.alternativeText ? (_jsxs(_Fragment, { children: [state.alternativeText.slice(0, state.alternativeCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: state.alternativeText[state.alternativeCursorPosition] || " " }), state.alternativeText.slice(state.alternativeCursorPosition + 1)] })) : ("Type here to tell Wave what to change") }))] }) }, "alternative-option")] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 to navigate \u2022 ESC to cancel" }) })] }))] }));
|
|
416
|
+
: "Yes, proceed"] }) }, "allow-option"), !hidePersistentOption && (_jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "auto" ? "black" : "white", backgroundColor: state.selectedOption === "auto" ? "yellow" : undefined, bold: state.selectedOption === "auto", children: [state.selectedOption === "auto" ? "> " : " ", getAutoOptionText()] }) }, "auto-option")), _jsx(Box, { children: _jsxs(Text, { color: state.selectedOption === "alternative" ? "black" : "white", backgroundColor: state.selectedOption === "alternative" ? "yellow" : undefined, bold: state.selectedOption === "alternative", children: [state.selectedOption === "alternative" ? "> " : " ", showPlaceholder ? (_jsx(Text, { color: "gray", dimColor: true, children: placeholderText })) : (_jsx(Text, { children: state.alternativeText ? (_jsxs(_Fragment, { children: [state.alternativeText.slice(0, state.alternativeCursorPosition), _jsx(Text, { backgroundColor: "white", color: "black", children: state.alternativeText[state.alternativeCursorPosition] || " " }), state.alternativeText.slice(state.alternativeCursorPosition + 1)] })) : ("Type here to tell Wave what to change") }))] }) }, "alternative-option")] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use \u2191\u2193 or Tab to navigate \u2022 ESC to cancel" }) })] }))] }));
|
|
312
417
|
};
|
|
313
418
|
ConfirmationSelector.displayName = "ConfirmationSelector";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import type { SlashCommand } from "wave-agent-sdk";
|
|
2
3
|
export interface HelpViewProps {
|
|
3
4
|
onCancel: () => void;
|
|
5
|
+
commands?: SlashCommand[];
|
|
4
6
|
}
|
|
5
7
|
export declare const HelpView: React.FC<HelpViewProps>;
|
|
6
8
|
//# sourceMappingURL=HelpView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HelpView.d.ts","sourceRoot":"","sources":["../../src/components/HelpView.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"HelpView.d.ts","sourceRoot":"","sources":["../../src/components/HelpView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAmK5C,CAAC"}
|
|
@@ -1,9 +1,40 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
2
3
|
import { Box, Text, useInput } from "ink";
|
|
3
|
-
|
|
4
|
+
import { AVAILABLE_COMMANDS } from "../constants/commands.js";
|
|
5
|
+
export const HelpView = ({ onCancel, commands = [], }) => {
|
|
6
|
+
const [activeTab, setActiveTab] = useState("general");
|
|
7
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
8
|
+
const MAX_VISIBLE_ITEMS = 10;
|
|
9
|
+
const tabs = [
|
|
10
|
+
"general",
|
|
11
|
+
"commands",
|
|
12
|
+
];
|
|
13
|
+
if (commands.length > 0) {
|
|
14
|
+
tabs.push("custom-commands");
|
|
15
|
+
}
|
|
4
16
|
useInput((_, key) => {
|
|
5
|
-
if (key.escape
|
|
17
|
+
if (key.escape) {
|
|
6
18
|
onCancel();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (key.tab) {
|
|
22
|
+
setActiveTab((prev) => {
|
|
23
|
+
const currentIndex = tabs.indexOf(prev);
|
|
24
|
+
const nextIndex = (currentIndex + 1) % tabs.length;
|
|
25
|
+
return tabs[nextIndex];
|
|
26
|
+
});
|
|
27
|
+
setSelectedIndex(0);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (activeTab === "commands" || activeTab === "custom-commands") {
|
|
31
|
+
const currentCommands = activeTab === "commands" ? AVAILABLE_COMMANDS : commands;
|
|
32
|
+
if (key.upArrow) {
|
|
33
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
34
|
+
}
|
|
35
|
+
else if (key.downArrow) {
|
|
36
|
+
setSelectedIndex((prev) => Math.min(currentCommands.length - 1, prev + 1));
|
|
37
|
+
}
|
|
7
38
|
}
|
|
8
39
|
});
|
|
9
40
|
const helpItems = [
|
|
@@ -20,5 +51,20 @@ export const HelpView = ({ onCancel }) => {
|
|
|
20
51
|
description: "Interrupt AI or command / Cancel selector / Close help",
|
|
21
52
|
},
|
|
22
53
|
];
|
|
23
|
-
|
|
54
|
+
// Calculate visible window for commands
|
|
55
|
+
const currentCommands = activeTab === "commands" ? AVAILABLE_COMMANDS : commands;
|
|
56
|
+
const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2), Math.max(0, currentCommands.length - MAX_VISIBLE_ITEMS)));
|
|
57
|
+
const visibleCommands = currentCommands.slice(startIndex, startIndex + MAX_VISIBLE_ITEMS);
|
|
58
|
+
const footerText = [
|
|
59
|
+
"Tab switch",
|
|
60
|
+
activeTab !== "general" && "↑↓ navigate",
|
|
61
|
+
"Esc close",
|
|
62
|
+
]
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join(" • ");
|
|
65
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "cyan", borderLeft: false, borderRight: false, paddingX: 1, width: "100%", children: [_jsxs(Box, { marginBottom: 1, gap: 2, children: [_jsx(Text, { color: activeTab === "general" ? "cyan" : "gray", bold: true, underline: activeTab === "general", children: "General" }), _jsx(Text, { color: activeTab === "commands" ? "cyan" : "gray", bold: true, underline: activeTab === "commands", children: "Commands" }), commands.length > 0 && (_jsx(Text, { color: activeTab === "custom-commands" ? "cyan" : "gray", bold: true, underline: activeTab === "custom-commands", children: "Custom Commands" }))] }), activeTab === "general" ? (_jsx(Box, { flexDirection: "column", children: helpItems.map((item, index) => (_jsxs(Box, { children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "yellow", children: item.key }) }), _jsx(Text, { color: "white", children: item.description })] }, index))) })) : (_jsx(Box, { flexDirection: "column", children: visibleCommands.map((command, index) => {
|
|
66
|
+
const actualIndex = startIndex + index;
|
|
67
|
+
const isSelected = actualIndex === selectedIndex;
|
|
68
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "cyan" : undefined, children: [isSelected ? "▶ " : " ", "/", command.id] }), isSelected && (_jsx(Box, { marginLeft: 4, children: _jsx(Text, { color: "gray", dimColor: true, children: command.description }) }))] }, command.id));
|
|
69
|
+
}) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: footerText }) })] }));
|
|
24
70
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAczC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,mDACe,CAAC;AAEnD,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,KAC/C,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAE1B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAClD;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAkO5C,CAAC"}
|