wave-code 0.8.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +26 -7
- package/dist/components/HelpView.d.ts.map +1 -1
- package/dist/components/HelpView.js +2 -0
- package/dist/components/InputBox.js +4 -3
- package/dist/components/MessageBlockItem.d.ts.map +1 -1
- package/dist/components/MessageBlockItem.js +1 -1
- package/dist/components/MessageList.d.ts +2 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +14 -4
- package/dist/components/QueuedMessageList.d.ts +3 -0
- package/dist/components/QueuedMessageList.d.ts.map +1 -0
- package/dist/components/QueuedMessageList.js +17 -0
- package/dist/components/ReasoningDisplay.d.ts +1 -0
- package/dist/components/ReasoningDisplay.d.ts.map +1 -1
- package/dist/components/ReasoningDisplay.js +3 -3
- package/dist/components/RewindCommand.d.ts.map +1 -1
- package/dist/components/RewindCommand.js +10 -4
- package/dist/components/ToolDisplay.js +1 -1
- package/dist/contexts/useChat.d.ts +7 -0
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +17 -1
- package/dist/hooks/useInputManager.d.ts +3 -3
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +10 -9
- package/dist/managers/inputHandlers.d.ts +7 -4
- package/dist/managers/inputHandlers.d.ts.map +1 -1
- package/dist/managers/inputHandlers.js +165 -42
- package/package.json +2 -2
- package/src/components/ChatInterface.tsx +42 -15
- package/src/components/HelpView.tsx +2 -0
- package/src/components/InputBox.tsx +17 -17
- package/src/components/MessageBlockItem.tsx +8 -3
- package/src/components/MessageList.tsx +16 -3
- package/src/components/QueuedMessageList.tsx +31 -0
- package/src/components/ReasoningDisplay.tsx +8 -2
- package/src/components/RewindCommand.tsx +21 -5
- package/src/components/ToolDisplay.tsx +2 -2
- package/src/contexts/useChat.tsx +29 -1
- package/src/hooks/useInputManager.ts +9 -21
- package/src/managers/inputHandlers.ts +197 -56
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAatE,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAuKjC,CAAC"}
|
|
@@ -5,6 +5,7 @@ import { MessageList } from "./MessageList.js";
|
|
|
5
5
|
import { InputBox } from "./InputBox.js";
|
|
6
6
|
import { LoadingIndicator } from "./LoadingIndicator.js";
|
|
7
7
|
import { TaskList } from "./TaskList.js";
|
|
8
|
+
import { QueuedMessageList } from "./QueuedMessageList.js";
|
|
8
9
|
import { ConfirmationDetails } from "./ConfirmationDetails.js";
|
|
9
10
|
import { ConfirmationSelector } from "./ConfirmationSelector.js";
|
|
10
11
|
import { useChat } from "../contexts/useChat.js";
|
|
@@ -12,6 +13,7 @@ export const ChatInterface = () => {
|
|
|
12
13
|
const { stdout } = useStdout();
|
|
13
14
|
const [detailsHeight, setDetailsHeight] = useState(0);
|
|
14
15
|
const [selectorHeight, setSelectorHeight] = useState(0);
|
|
16
|
+
const [dynamicBlocksHeight, setDynamicBlocksHeight] = useState(0);
|
|
15
17
|
const [isConfirmationTooTall, setIsConfirmationTooTall] = useState(false);
|
|
16
18
|
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
19
|
const model = getModelConfig().model;
|
|
@@ -21,16 +23,33 @@ export const ChatInterface = () => {
|
|
|
21
23
|
const handleSelectorHeightMeasured = useCallback((height) => {
|
|
22
24
|
setSelectorHeight(height);
|
|
23
25
|
}, []);
|
|
26
|
+
const handleDynamicBlocksHeightMeasured = useCallback((height) => {
|
|
27
|
+
setDynamicBlocksHeight(height);
|
|
28
|
+
}, []);
|
|
24
29
|
useLayoutEffect(() => {
|
|
30
|
+
if (!isConfirmationVisible) {
|
|
31
|
+
setIsConfirmationTooTall(false);
|
|
32
|
+
setDetailsHeight(0);
|
|
33
|
+
setSelectorHeight(0);
|
|
34
|
+
setDynamicBlocksHeight(0);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (isConfirmationTooTall) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
25
40
|
const terminalHeight = stdout?.rows || 24;
|
|
26
|
-
const totalHeight = detailsHeight + selectorHeight;
|
|
41
|
+
const totalHeight = detailsHeight + selectorHeight + dynamicBlocksHeight;
|
|
27
42
|
if (totalHeight > terminalHeight) {
|
|
28
43
|
setIsConfirmationTooTall(true);
|
|
29
44
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
}, [
|
|
46
|
+
detailsHeight,
|
|
47
|
+
selectorHeight,
|
|
48
|
+
dynamicBlocksHeight,
|
|
49
|
+
stdout?.rows,
|
|
50
|
+
isConfirmationVisible,
|
|
51
|
+
isConfirmationTooTall,
|
|
52
|
+
]);
|
|
34
53
|
const handleConfirmationCancel = useCallback(() => {
|
|
35
54
|
if (isConfirmationTooTall) {
|
|
36
55
|
setWasLastDetailsTooTall((prev) => prev + 1);
|
|
@@ -55,7 +74,7 @@ export const ChatInterface = () => {
|
|
|
55
74
|
]);
|
|
56
75
|
if (!sessionId)
|
|
57
76
|
return null;
|
|
58
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(MessageList, { messages: messages, isExpanded: isExpanded, forceStatic: isConfirmationVisible && isConfirmationTooTall, version: version, workdir: workdir, model: model }), (isLoading || isCommandRunning || isCompressing) &&
|
|
77
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(MessageList, { messages: messages, isExpanded: isExpanded, forceStatic: isConfirmationVisible && isConfirmationTooTall, version: version, workdir: workdir, model: model, onDynamicBlocksHeightMeasured: handleDynamicBlocksHeightMeasured }), (isLoading || isCommandRunning || isCompressing) &&
|
|
59
78
|
!isConfirmationVisible &&
|
|
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 }))] }));
|
|
79
|
+
!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 && (_jsxs(_Fragment, { children: [_jsx(QueuedMessageList, {}), _jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, sendMessage: sendMessage, abortMessage: abortMessage, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand })] }))] }));
|
|
61
80
|
};
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,CAqK5C,CAAC"}
|
|
@@ -40,11 +40,13 @@ export const HelpView = ({ onCancel, commands = [], }) => {
|
|
|
40
40
|
const helpItems = [
|
|
41
41
|
{ key: "@", description: "Reference files" },
|
|
42
42
|
{ key: "/", description: "Commands" },
|
|
43
|
+
{ key: "!", description: "Shell commands (e.g. !ls)" },
|
|
43
44
|
{ key: "Ctrl+R", description: "Search history" },
|
|
44
45
|
{ key: "Ctrl+O", description: "Expand/collapse messages" },
|
|
45
46
|
{ key: "Ctrl+T", description: "Toggle task list" },
|
|
46
47
|
{ key: "Ctrl+B", description: "Background current task" },
|
|
47
48
|
{ key: "Ctrl+V", description: "Paste image" },
|
|
49
|
+
{ key: "Ctrl+J", description: "Newline" },
|
|
48
50
|
{ key: "Shift+Tab", description: "Cycle permission mode" },
|
|
49
51
|
{
|
|
50
52
|
key: "Esc",
|
|
@@ -14,7 +14,7 @@ import { useInputManager } from "../hooks/useInputManager.js";
|
|
|
14
14
|
import { useChat } from "../contexts/useChat.js";
|
|
15
15
|
export const INPUT_PLACEHOLDER_TEXT = "Type your message (use /help for more info)...";
|
|
16
16
|
export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(0, 10);
|
|
17
|
-
export const InputBox = ({
|
|
17
|
+
export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, }) => {
|
|
18
18
|
const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, clearMessages, } = useChat();
|
|
19
19
|
// Input manager with all input state and functionality (including images)
|
|
20
20
|
const { inputText, cursorPosition,
|
|
@@ -47,7 +47,7 @@ export const InputBox = ({ isLoading = false, isCommandRunning = false, sendMess
|
|
|
47
47
|
}, [chatPermissionMode, setPermissionMode]);
|
|
48
48
|
// Use the InputManager's unified input handler
|
|
49
49
|
useInput(async (input, key) => {
|
|
50
|
-
await handleInput(input, key, attachedImages,
|
|
50
|
+
await handleInput(input, key, attachedImages, clearImages);
|
|
51
51
|
});
|
|
52
52
|
const handleRewindCancel = () => {
|
|
53
53
|
if (setShowRewindManager) {
|
|
@@ -56,6 +56,7 @@ export const InputBox = ({ isLoading = false, isCommandRunning = false, sendMess
|
|
|
56
56
|
};
|
|
57
57
|
const isPlaceholder = !inputText;
|
|
58
58
|
const placeholderText = INPUT_PLACEHOLDER_TEXT;
|
|
59
|
+
const isShellCommand = inputText?.startsWith("!") && !inputText.includes("\n");
|
|
59
60
|
// handleCommandSelectorInsert is already memoized in useInputManager, no need to wrap again
|
|
60
61
|
// Split text into three parts: before cursor, cursor position, after cursor
|
|
61
62
|
const displayText = isPlaceholder ? placeholderText : inputText;
|
|
@@ -87,5 +88,5 @@ export const InputBox = ({ isLoading = false, isCommandRunning = false, sendMess
|
|
|
87
88
|
showMcpManager ||
|
|
88
89
|
showRewindManager ||
|
|
89
90
|
showHelp ||
|
|
90
|
-
showStatusCommand || (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { borderStyle: "single", borderColor: "gray", borderLeft: false, borderRight: false, children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }), _jsx(Box, { paddingRight: 1, justifyContent: "space-between", width: "100%", children: _jsxs(Text, { color: "gray", children: ["Mode:", " ", _jsx(Text, { color: permissionMode === "plan" ? "yellow" : "cyan", children: permissionMode }), " ", "(Shift+Tab to cycle)"] }) })] }))] }));
|
|
91
|
+
showStatusCommand || (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { borderStyle: "single", borderColor: "gray", borderLeft: false, borderRight: false, children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }), _jsx(Box, { paddingRight: 1, justifyContent: "space-between", width: "100%", children: isShellCommand ? (_jsxs(Text, { color: "gray", children: ["Shell: ", _jsx(Text, { color: "yellow", children: "Run shell command" })] })) : (_jsxs(Text, { color: "gray", children: ["Mode:", " ", _jsx(Text, { color: permissionMode === "plan" ? "yellow" : "cyan", children: permissionMode }), " ", "(Shift+Tab to cycle)"] })) })] }))] }));
|
|
91
92
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageBlockItem.d.ts","sourceRoot":"","sources":["../../src/components/MessageBlockItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ5D,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,gBAAgB,GAAI,6CAK9B,qBAAqB,
|
|
1
|
+
{"version":3,"file":"MessageBlockItem.d.ts","sourceRoot":"","sources":["../../src/components/MessageBlockItem.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ5D,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,YAAY,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,gBAAgB,GAAI,6CAK9B,qBAAqB,4CAiEvB,CAAC"}
|
|
@@ -7,5 +7,5 @@ import { CompressDisplay } from "./CompressDisplay.js";
|
|
|
7
7
|
import { ReasoningDisplay } from "./ReasoningDisplay.js";
|
|
8
8
|
import { Markdown } from "./Markdown.js";
|
|
9
9
|
export const MessageBlockItem = ({ block, message, isExpanded, paddingTop = 0, }) => {
|
|
10
|
-
return (_jsxs(Box, { flexDirection: "column", paddingTop: paddingTop, children: [block.type === "text" && block.content.trim() && (_jsxs(Box, { children: [block.customCommandContent && (_jsxs(Text, { color: "cyan", bold: true, children: ["$", " "] })), block.source === MessageSource.HOOK && (_jsxs(Text, { color: "magenta", bold: true, children: ["~", " "] })), message.role === "user" ? (_jsx(Text, { backgroundColor: "gray", color: "white", children: block.content })) : (_jsx(Markdown, { children: block.content }))] })), block.type === "error" && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", block.content] }) })), block.type === "bang" && (_jsx(BangDisplay, { block: block, isExpanded: isExpanded })), block.type === "tool" && (_jsx(ToolDisplay, { block: block, isExpanded: isExpanded })), block.type === "image" && (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "# Image" }), block.imageUrls && block.imageUrls.length > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "(", block.imageUrls.length, ")"] }))] })), block.type === "compress" && (_jsx(CompressDisplay, { block: block, isExpanded: isExpanded })), block.type === "reasoning" && _jsx(ReasoningDisplay, { block: block })] }));
|
|
10
|
+
return (_jsxs(Box, { flexDirection: "column", paddingTop: paddingTop, children: [block.type === "text" && block.content.trim() && (_jsxs(Box, { children: [block.customCommandContent && (_jsxs(Text, { color: "cyan", bold: true, children: ["$", " "] })), block.source === MessageSource.HOOK && (_jsxs(Text, { color: "magenta", bold: true, children: ["~", " "] })), message.role === "user" || isExpanded ? (_jsx(Text, { backgroundColor: message.role === "user" ? "gray" : undefined, color: "white", children: block.content })) : (_jsx(Markdown, { children: block.content }))] })), block.type === "error" && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", block.content] }) })), block.type === "bang" && (_jsx(BangDisplay, { block: block, isExpanded: isExpanded })), block.type === "tool" && (_jsx(ToolDisplay, { block: block, isExpanded: isExpanded })), block.type === "image" && (_jsxs(Box, { children: [_jsx(Text, { color: "magenta", bold: true, children: "# Image" }), block.imageUrls && block.imageUrls.length > 0 && (_jsxs(Text, { color: "gray", dimColor: true, children: [" ", "(", block.imageUrls.length, ")"] }))] })), block.type === "compress" && (_jsx(CompressDisplay, { block: block, isExpanded: isExpanded })), block.type === "reasoning" && (_jsx(ReasoningDisplay, { block: block, isExpanded: isExpanded }))] }));
|
|
11
11
|
};
|
|
@@ -7,6 +7,7 @@ export interface MessageListProps {
|
|
|
7
7
|
version?: string;
|
|
8
8
|
workdir?: string;
|
|
9
9
|
model?: string;
|
|
10
|
+
onDynamicBlocksHeightMeasured?: (height: number) => void;
|
|
10
11
|
}
|
|
11
|
-
export declare const MessageList: React.MemoExoticComponent<({ messages, isExpanded, forceStatic, version, workdir, model, }: MessageListProps) => import("react/jsx-runtime").JSX.Element>;
|
|
12
|
+
export declare const MessageList: React.MemoExoticComponent<({ messages, isExpanded, forceStatic, version, workdir, model, onDynamicBlocksHeightMeasured, }: MessageListProps) => import("react/jsx-runtime").JSX.Element>;
|
|
12
13
|
//# sourceMappingURL=MessageList.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkC,MAAM,OAAO,CAAC;AAGvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG9C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED,eAAO,MAAM,WAAW,6HASnB,gBAAgB,6CA8GpB,CAAC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import React from "react";
|
|
2
|
+
import React, { useLayoutEffect, useRef } from "react";
|
|
3
3
|
import os from "os";
|
|
4
|
-
import { Box, Text, Static } from "ink";
|
|
4
|
+
import { Box, Text, Static, measureElement } from "ink";
|
|
5
5
|
import { MessageBlockItem } from "./MessageBlockItem.js";
|
|
6
|
-
export const MessageList = React.memo(({ messages, isExpanded = false, forceStatic = false, version, workdir, model, }) => {
|
|
6
|
+
export const MessageList = React.memo(({ messages, isExpanded = false, forceStatic = false, version, workdir, model, onDynamicBlocksHeightMeasured, }) => {
|
|
7
7
|
const welcomeMessage = (_jsxs(Box, { flexDirection: "column", paddingTop: 1, children: [_jsxs(Text, { color: "gray", children: ["WAVE", version ? ` v${version}` : "", model ? ` • ${model}` : ""] }), workdir && (_jsx(Text, { color: "gray", wrap: "truncate-middle", children: workdir.replace(os.homedir(), "~") }))] }));
|
|
8
8
|
// Limit messages when expanded to prevent long rendering times
|
|
9
9
|
const maxExpandedMessages = 20;
|
|
@@ -35,6 +35,16 @@ export const MessageList = React.memo(({ messages, isExpanded = false, forceStat
|
|
|
35
35
|
});
|
|
36
36
|
const staticBlocks = blocksWithStatus.filter((b) => !b.isDynamic);
|
|
37
37
|
const dynamicBlocks = blocksWithStatus.filter((b) => b.isDynamic);
|
|
38
|
+
const dynamicBlocksRef = useRef(null);
|
|
39
|
+
useLayoutEffect(() => {
|
|
40
|
+
if (dynamicBlocksRef.current) {
|
|
41
|
+
const { height } = measureElement(dynamicBlocksRef.current);
|
|
42
|
+
onDynamicBlocksHeightMeasured?.(height);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
onDynamicBlocksHeightMeasured?.(0);
|
|
46
|
+
}
|
|
47
|
+
}, [dynamicBlocks, isExpanded, onDynamicBlocksHeightMeasured]);
|
|
38
48
|
const staticItems = [
|
|
39
49
|
{ isWelcome: true, key: "welcome", block: undefined, message: undefined },
|
|
40
50
|
...staticBlocks.map((b) => ({ ...b, isWelcome: false })),
|
|
@@ -44,7 +54,7 @@ export const MessageList = React.memo(({ messages, isExpanded = false, forceStat
|
|
|
44
54
|
return (_jsx(React.Fragment, { children: welcomeMessage }, item.key));
|
|
45
55
|
}
|
|
46
56
|
return (_jsx(MessageBlockItem, { block: item.block, message: item.message, isExpanded: isExpanded, paddingTop: 1 }, item.key));
|
|
47
|
-
} })), dynamicBlocks.length > 0 && (_jsx(Box, { flexDirection: "column", children: dynamicBlocks.map((item) => (_jsx(MessageBlockItem, { block: item.block, message: item.message, isExpanded: isExpanded, paddingTop: 1 }, item.key))) }))] }));
|
|
57
|
+
} })), dynamicBlocks.length > 0 && (_jsx(Box, { ref: dynamicBlocksRef, flexDirection: "column", children: dynamicBlocks.map((item) => (_jsx(MessageBlockItem, { block: item.block, message: item.message, isExpanded: isExpanded, paddingTop: 1 }, item.key))) }))] }));
|
|
48
58
|
});
|
|
49
59
|
// Add display name for debugging
|
|
50
60
|
MessageList.displayName = "MessageList";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QueuedMessageList.d.ts","sourceRoot":"","sources":["../../src/components/QueuedMessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EA0BrC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useChat } from "../contexts/useChat.js";
|
|
3
|
+
import { Box, Text } from "ink";
|
|
4
|
+
export const QueuedMessageList = () => {
|
|
5
|
+
const { queuedMessages = [] } = useChat();
|
|
6
|
+
if (queuedMessages.length === 0) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
return (_jsx(Box, { flexDirection: "column", children: queuedMessages.map((msg, index) => {
|
|
10
|
+
const content = msg.content.trim();
|
|
11
|
+
const hasImages = msg.images && msg.images.length > 0;
|
|
12
|
+
const displayText = content || (hasImages ? "[Images]" : "");
|
|
13
|
+
return (_jsx(Box, { children: _jsx(Text, { color: "gray", italic: true, children: displayText.length > 60
|
|
14
|
+
? `${displayText.substring(0, 57)}...`
|
|
15
|
+
: displayText }) }, index));
|
|
16
|
+
}) }));
|
|
17
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReasoningDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ReasoningDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,UAAU,qBAAqB;IAC7B,KAAK,EAAE,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"ReasoningDisplay.d.ts","sourceRoot":"","sources":["../../src/components/ReasoningDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,UAAU,qBAAqB;IAC7B,KAAK,EAAE,cAAc,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CA4B5D,CAAC"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Box } from "ink";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import { Markdown } from "./Markdown.js";
|
|
4
|
-
export const ReasoningDisplay = ({ block, }) => {
|
|
4
|
+
export const ReasoningDisplay = ({ block, isExpanded = false, }) => {
|
|
5
5
|
const { content } = block;
|
|
6
6
|
if (!content || !content.trim()) {
|
|
7
7
|
return null;
|
|
8
8
|
}
|
|
9
|
-
return (_jsx(Box, { borderRight: false, borderTop: false, borderBottom: false, borderStyle: "classic", borderColor: "blue", paddingLeft: 1, children: _jsx(Box, { flexDirection: "column", children: _jsx(Markdown, { children: content }) }) }));
|
|
9
|
+
return (_jsx(Box, { borderRight: false, borderTop: false, borderBottom: false, borderStyle: "classic", borderColor: "blue", paddingLeft: 1, children: _jsx(Box, { flexDirection: "column", children: isExpanded ? (_jsx(Text, { color: "white", children: content })) : (_jsx(Markdown, { children: content })) }) }));
|
|
10
10
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RewindCommand.d.ts","sourceRoot":"","sources":["../../src/components/RewindCommand.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,gBAAgB,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC;QACnC,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC,CAAC;CACJ;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,
|
|
1
|
+
{"version":3,"file":"RewindCommand.d.ts","sourceRoot":"","sources":["../../src/components/RewindCommand.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,gBAAgB,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC;QACnC,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC,CAAC;CACJ;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAwJtD,CAAC"}
|
|
@@ -16,11 +16,15 @@ export const RewindCommand = ({ messages: initialMessages, onSelect, onCancel, g
|
|
|
16
16
|
const checkpoints = messages
|
|
17
17
|
.map((msg, index) => ({ msg, index }))
|
|
18
18
|
.filter(({ msg }) => msg.role === "user");
|
|
19
|
+
const MAX_VISIBLE_ITEMS = 3;
|
|
19
20
|
const [selectedIndex, setSelectedIndex] = useState(checkpoints.length - 1);
|
|
20
21
|
// Update selectedIndex when checkpoints change (after loading full thread)
|
|
21
22
|
React.useEffect(() => {
|
|
22
23
|
setSelectedIndex(checkpoints.length - 1);
|
|
23
24
|
}, [checkpoints.length]);
|
|
25
|
+
// Calculate visible window
|
|
26
|
+
const startIndex = Math.max(0, Math.min(selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2), Math.max(0, checkpoints.length - MAX_VISIBLE_ITEMS)));
|
|
27
|
+
const visibleCheckpoints = checkpoints.slice(startIndex, startIndex + MAX_VISIBLE_ITEMS);
|
|
24
28
|
useInput((input, key) => {
|
|
25
29
|
if (key.return) {
|
|
26
30
|
if (checkpoints.length > 0 && selectedIndex >= 0) {
|
|
@@ -47,13 +51,15 @@ export const RewindCommand = ({ messages: initialMessages, onSelect, onCancel, g
|
|
|
47
51
|
if (checkpoints.length === 0) {
|
|
48
52
|
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, borderStyle: "single", borderColor: "yellow", borderLeft: false, borderRight: false, children: [_jsx(Text, { color: "yellow", children: "No user messages found to rewind to." }), _jsx(Text, { dimColor: true, children: "Press Escape to cancel" })] }));
|
|
49
53
|
}
|
|
50
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, gap: 1, borderStyle: "single", borderColor: "cyan", borderLeft: false, borderRight: false, children: [_jsx(Box, { children: _jsx(Text, { color: "cyan", bold: true, children: "Rewind: Select a message to revert to" }) }), _jsx(Box, { flexDirection: "column", children:
|
|
51
|
-
const
|
|
54
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, gap: 1, borderStyle: "single", borderColor: "cyan", borderLeft: false, borderRight: false, children: [_jsx(Box, { children: _jsx(Text, { color: "cyan", bold: true, children: "Rewind: Select a message to revert to" }) }), _jsx(Box, { flexDirection: "column", children: visibleCheckpoints.map((checkpoint, index) => {
|
|
55
|
+
const actualIndex = startIndex + index;
|
|
56
|
+
const isSelected = actualIndex === selectedIndex;
|
|
52
57
|
const content = checkpoint.msg.blocks
|
|
53
58
|
.filter((b) => b.type === "text")
|
|
54
59
|
.map((b) => b.content)
|
|
55
60
|
.join(" ")
|
|
61
|
+
.replace(/\n/g, "\\n")
|
|
56
62
|
.substring(0, 60);
|
|
57
|
-
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "cyan" : undefined, children: [isSelected ? "▶ " : " ", "[", checkpoint.index, "]", " ", content || "(No text content)",
|
|
58
|
-
}) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate \u2022 Enter to rewind \u2022 Esc to cancel" }) }), _jsx(Box, { children: _jsx(Text, { color: "red", dimColor: true, children: "
|
|
63
|
+
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? "black" : "white", backgroundColor: isSelected ? "cyan" : undefined, children: [isSelected ? "▶ " : " ", "[", checkpoint.index, "]", " ", content || "(No text content)", actualIndex === checkpoints.length - 1 ? " (Latest)" : ""] }) }, checkpoint.index));
|
|
64
|
+
}) }), _jsx(Box, { children: _jsx(Text, { dimColor: true, children: "\u2191\u2193 navigate \u2022 Enter to rewind \u2022 Esc to cancel" }) }), _jsx(Box, { children: _jsx(Text, { color: "red", dimColor: true, children: "Warning: This will delete all subsequent messages and revert file and task list changes." }) })] }));
|
|
59
65
|
};
|
|
@@ -40,5 +40,5 @@ export const ToolDisplay = ({ block, isExpanded = false, }) => {
|
|
|
40
40
|
return null;
|
|
41
41
|
};
|
|
42
42
|
const shortResult = getShortResult();
|
|
43
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Box, { flexShrink: 0, children: [_jsx(Text, { color: getStatusColor(), children: "\u25CF " }), _jsx(Text, { color: "white", children: toolName })] }), !isExpanded && compactParams && (_jsxs(Text, { color: "gray", children: [" ", compactParams] })), hasImages() && _jsxs(Text, { color: "blue", children: [" ", getImageIndicator()] })] }), !isExpanded && shortResult && !error && (_jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: shortResult.split("\n").map((line, index) => (_jsx(Text, { color: "gray", children: line }, index))) })), isExpanded && parameters && (_jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Parameters:" }), _jsx(Text, { color: "gray", children: parameters })] })), isExpanded && result && (_jsx(Box, { flexDirection: "column", children: _jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "green", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Result:" }), _jsx(Text, { color: "white", children: result })] }) })), error && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", typeof error === "string" ? error : String(error)] }) })), stage === "end" && success && (_jsx(DiffDisplay, { toolName: name, parameters: parameters, startLineNumber: block.startLineNumber }))] }));
|
|
43
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Box, { flexShrink: 0, children: [_jsx(Text, { color: getStatusColor(), children: "\u25CF " }), _jsx(Text, { color: "white", children: toolName })] }), !isExpanded && compactParams && (_jsxs(Text, { color: "gray", children: [" ", compactParams] })), hasImages() && _jsxs(Text, { color: "blue", children: [" ", getImageIndicator()] })] }), !isExpanded && shortResult && !error && (_jsx(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: shortResult.split("\n").map((line, index) => (_jsx(Text, { color: "gray", children: line }, index))) })), isExpanded && parameters && (_jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "gray", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Parameters:" }), _jsx(Text, { color: "gray", children: parameters })] })), isExpanded && result && (_jsx(Box, { flexDirection: "column", children: _jsxs(Box, { paddingLeft: 2, borderLeft: true, borderColor: "green", flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Result:" }), _jsx(Text, { color: "white", children: result })] }) })), error && (_jsx(Box, { children: _jsxs(Text, { color: "red", children: ["Error: ", typeof error === "string" ? error : String(error)] }) })), !isExpanded && stage === "end" && success && (_jsx(DiffDisplay, { toolName: name, parameters: parameters, startLineNumber: block.startLineNumber }))] }));
|
|
44
44
|
};
|
|
@@ -9,6 +9,13 @@ export interface ChatContextType {
|
|
|
9
9
|
isExpanded: boolean;
|
|
10
10
|
isTaskListVisible: boolean;
|
|
11
11
|
setIsTaskListVisible: (visible: boolean) => void;
|
|
12
|
+
queuedMessages: Array<{
|
|
13
|
+
content: string;
|
|
14
|
+
images?: Array<{
|
|
15
|
+
path: string;
|
|
16
|
+
mimeType: string;
|
|
17
|
+
}>;
|
|
18
|
+
}>;
|
|
12
19
|
sessionId: string;
|
|
13
20
|
sendMessage: (content: string, images?: Array<{
|
|
14
21
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EACV,OAAO,EACP,eAAe,EACf,cAAc,EACd,IAAI,EACJ,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACf,MAAM,gBAAgB,CAAC;AASxB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IAEvB,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,oBAAoB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/contexts/useChat.tsx"],"names":[],"mappings":"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"}
|
package/dist/contexts/useChat.js
CHANGED
|
@@ -22,6 +22,7 @@ export const ChatProvider = ({ children, bypassPermissions, pluginDirs, tools, w
|
|
|
22
22
|
isExpandedRef.current = isExpanded;
|
|
23
23
|
}, [isExpanded]);
|
|
24
24
|
const [isTaskListVisible, setIsTaskListVisible] = useState(true);
|
|
25
|
+
const [queuedMessages, setQueuedMessages] = useState([]);
|
|
25
26
|
// AI State
|
|
26
27
|
const [messages, setMessages] = useState([]);
|
|
27
28
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -203,6 +204,10 @@ export const ChatProvider = ({ children, bypassPermissions, pluginDirs, tools, w
|
|
|
203
204
|
const hasImageAttachments = images && images.length > 0;
|
|
204
205
|
if (!hasTextContent && !hasImageAttachments)
|
|
205
206
|
return;
|
|
207
|
+
if (isLoading || isCommandRunning) {
|
|
208
|
+
setQueuedMessages((prev) => [...prev, { content, images }]);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
206
211
|
try {
|
|
207
212
|
// Handle bash mode - check if it's a bash command (starts with ! and only one line)
|
|
208
213
|
if (content.startsWith("!") && !content.includes("\n")) {
|
|
@@ -238,12 +243,22 @@ export const ChatProvider = ({ children, bypassPermissions, pluginDirs, tools, w
|
|
|
238
243
|
console.error("Failed to send message:", error);
|
|
239
244
|
// Loading state will be automatically updated by the useEffect that watches messages
|
|
240
245
|
}
|
|
241
|
-
}, []);
|
|
246
|
+
}, [isLoading, isCommandRunning]);
|
|
247
|
+
// Process queued messages when idle
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
if (!isLoading && !isCommandRunning && queuedMessages.length > 0) {
|
|
250
|
+
const nextMessage = queuedMessages[0];
|
|
251
|
+
setQueuedMessages((prev) => prev.slice(1));
|
|
252
|
+
sendMessage(nextMessage.content, nextMessage.images);
|
|
253
|
+
}
|
|
254
|
+
}, [isLoading, isCommandRunning, queuedMessages, sendMessage]);
|
|
242
255
|
// Unified interrupt method, interrupt both AI messages and command execution
|
|
243
256
|
const abortMessage = useCallback(() => {
|
|
257
|
+
setQueuedMessages([]);
|
|
244
258
|
agentRef.current?.abortMessage();
|
|
245
259
|
}, []);
|
|
246
260
|
const clearMessages = useCallback(() => {
|
|
261
|
+
setQueuedMessages([]);
|
|
247
262
|
agentRef.current?.clearMessages();
|
|
248
263
|
}, []);
|
|
249
264
|
// Permission management methods
|
|
@@ -390,6 +405,7 @@ export const ChatProvider = ({ children, bypassPermissions, pluginDirs, tools, w
|
|
|
390
405
|
isExpanded,
|
|
391
406
|
isTaskListVisible,
|
|
392
407
|
setIsTaskListVisible,
|
|
408
|
+
queuedMessages,
|
|
393
409
|
sessionId,
|
|
394
410
|
sendMessage,
|
|
395
411
|
abortMessage,
|
|
@@ -48,7 +48,7 @@ export declare const useInputManager: (callbacks?: Partial<InputManagerCallbacks
|
|
|
48
48
|
checkForSlashDeletion: (cursorPos: number) => boolean;
|
|
49
49
|
handleHistorySearchSelect: (prompt: string) => void;
|
|
50
50
|
handleCancelHistorySearch: () => void;
|
|
51
|
-
|
|
51
|
+
processSelectorInput: (char: string) => void;
|
|
52
52
|
setShowBackgroundTaskManager: (show: boolean) => void;
|
|
53
53
|
setShowMcpManager: (show: boolean) => void;
|
|
54
54
|
setShowRewindManager: (show: boolean) => void;
|
|
@@ -64,14 +64,14 @@ export declare const useInputManager: (callbacks?: Partial<InputManagerCallbacks
|
|
|
64
64
|
id: number;
|
|
65
65
|
path: string;
|
|
66
66
|
mimeType: string;
|
|
67
|
-
}
|
|
67
|
+
}>) => Promise<void>;
|
|
68
68
|
expandLongTextPlaceholders: (text: string) => string;
|
|
69
69
|
clearLongTextMap: () => void;
|
|
70
70
|
handleInput: (input: string, key: Key, attachedImages: Array<{
|
|
71
71
|
id: number;
|
|
72
72
|
path: string;
|
|
73
73
|
mimeType: string;
|
|
74
|
-
}>,
|
|
74
|
+
}>, clearImages?: () => void) => Promise<boolean>;
|
|
75
75
|
setInputText: (text: string) => void;
|
|
76
76
|
setCursorPosition: (position: number) => void;
|
|
77
77
|
manager: null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useInputManager.d.ts","sourceRoot":"","sources":["../../src/hooks/useInputManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAGL,qBAAqB,EACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAkC,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhF,eAAO,MAAM,eAAe,GAC1B,YAAW,OAAO,CAAC,qBAAqB,CAAM;;;;;;;;;;;;;;;;;;;;+BAsIA,MAAM;;;;;qCAoBA,MAAM;iCAIV,MAAM;;;;;mCAaJ,MAAM;oCAIL,MAAM;wCAIF,MAAM;mCAIX,MAAM;;;;mCASN,MAAM;;;;;
|
|
1
|
+
{"version":3,"file":"useInputManager.d.ts","sourceRoot":"","sources":["../../src/hooks/useInputManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAGL,qBAAqB,EACtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAkC,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhF,eAAO,MAAM,eAAe,GAC1B,YAAW,OAAO,CAAC,qBAAqB,CAAM;;;;;;;;;;;;;;;;;;;;+BAsIA,MAAM;;;;;qCAoBA,MAAM;iCAIV,MAAM;;;;;mCAaJ,MAAM;oCAIL,MAAM;wCAIF,MAAM;mCAIX,MAAM;;;;mCASN,MAAM;;;;;sCAkCH,MAAM;uCAIL,MAAM;wCAQL,MAAM;;iCAUb,MAAM;yCAYE,OAAO;8BAIlB,OAAO;iCAIJ,OAAO;wBAIhB,OAAO;iCAIE,OAAO;8BAIV,cAAc;0BAKlB,MAAM,YAAY,MAAM;2BAIvB,MAAM;;;8BAYH,MAAM;mCAW/B,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;uCAYnB,MAAM;;yBAajD,MAAM,OACR,GAAG,kBACQ,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,gBACvD,MAAM,IAAI;yBAxFY,MAAM;kCAIG,MAAM;;CAmLxD,CAAC"}
|
|
@@ -135,9 +135,10 @@ export const useInputManager = (callbacks = {}) => {
|
|
|
135
135
|
const handleCommandInsert = useCallback((command) => {
|
|
136
136
|
const currentState = stateRef.current;
|
|
137
137
|
if (currentState.slashPosition >= 0) {
|
|
138
|
+
const wordEnd = handlers.getWordEnd(currentState.inputText, currentState.slashPosition);
|
|
138
139
|
const beforeSlash = currentState.inputText.substring(0, currentState.slashPosition);
|
|
139
|
-
const
|
|
140
|
-
const newInput = beforeSlash + `/${command} ` +
|
|
140
|
+
const afterWord = currentState.inputText.substring(wordEnd);
|
|
141
|
+
const newInput = beforeSlash + `/${command} ` + afterWord;
|
|
141
142
|
const newCursorPosition = beforeSlash.length + command.length + 2;
|
|
142
143
|
dispatch({ type: "SET_INPUT_TEXT", payload: newInput });
|
|
143
144
|
dispatch({ type: "SET_CURSOR_POSITION", payload: newCursorPosition });
|
|
@@ -168,8 +169,8 @@ export const useInputManager = (callbacks = {}) => {
|
|
|
168
169
|
const handleCancelHistorySearch = useCallback(() => {
|
|
169
170
|
dispatch({ type: "CANCEL_HISTORY_SEARCH" });
|
|
170
171
|
}, []);
|
|
171
|
-
const
|
|
172
|
-
handlers.
|
|
172
|
+
const processSelectorInput = useCallback((char) => {
|
|
173
|
+
handlers.processSelectorInput(stateRef.current, dispatch, char);
|
|
173
174
|
}, []);
|
|
174
175
|
const setInputText = useCallback((text) => {
|
|
175
176
|
dispatch({ type: "SET_INPUT_TEXT", payload: text });
|
|
@@ -211,8 +212,8 @@ export const useInputManager = (callbacks = {}) => {
|
|
|
211
212
|
const handlePasteInput = useCallback((input) => {
|
|
212
213
|
handlers.handlePasteInput(stateRef.current, dispatch, callbacksRef.current, input);
|
|
213
214
|
}, []);
|
|
214
|
-
const handleSubmit = useCallback(async (attachedImages
|
|
215
|
-
await handlers.handleSubmit(stateRef.current, dispatch, callbacksRef.current,
|
|
215
|
+
const handleSubmit = useCallback(async (attachedImages) => {
|
|
216
|
+
await handlers.handleSubmit(stateRef.current, dispatch, callbacksRef.current, attachedImages);
|
|
216
217
|
}, []);
|
|
217
218
|
const expandLongTextPlaceholders = useCallback((text) => {
|
|
218
219
|
return handlers.expandLongTextPlaceholders(text, stateRef.current.longTextMap);
|
|
@@ -220,8 +221,8 @@ export const useInputManager = (callbacks = {}) => {
|
|
|
220
221
|
const clearLongTextMap = useCallback(() => {
|
|
221
222
|
dispatch({ type: "CLEAR_LONG_TEXT_MAP" });
|
|
222
223
|
}, []);
|
|
223
|
-
const handleInput = useCallback(async (input, key, attachedImages,
|
|
224
|
-
return await handlers.handleInput(stateRef.current, dispatch, callbacksRef.current, input, key,
|
|
224
|
+
const handleInput = useCallback(async (input, key, attachedImages, clearImages) => {
|
|
225
|
+
return await handlers.handleInput(stateRef.current, dispatch, callbacksRef.current, input, key, clearImages);
|
|
225
226
|
}, []);
|
|
226
227
|
return {
|
|
227
228
|
// State
|
|
@@ -267,7 +268,7 @@ export const useInputManager = (callbacks = {}) => {
|
|
|
267
268
|
handleHistorySearchSelect,
|
|
268
269
|
handleCancelHistorySearch,
|
|
269
270
|
// Special handling
|
|
270
|
-
|
|
271
|
+
processSelectorInput,
|
|
271
272
|
// Bash/MCP Manager
|
|
272
273
|
setShowBackgroundTaskManager,
|
|
273
274
|
setShowMcpManager,
|
|
@@ -2,16 +2,19 @@ import { Key } from "ink";
|
|
|
2
2
|
import { PermissionMode } from "wave-agent-sdk";
|
|
3
3
|
import { InputState, InputAction, InputManagerCallbacks } from "./inputReducer.js";
|
|
4
4
|
export declare const expandLongTextPlaceholders: (text: string, longTextMap: Record<string, string>) => string;
|
|
5
|
-
export declare const handleSubmit: (state: InputState, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>,
|
|
5
|
+
export declare const handleSubmit: (state: InputState, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>, attachedImagesOverride?: Array<{
|
|
6
6
|
id: number;
|
|
7
7
|
path: string;
|
|
8
8
|
mimeType: string;
|
|
9
9
|
}>) => Promise<void>;
|
|
10
10
|
export declare const handlePasteImage: (dispatch: React.Dispatch<InputAction>) => Promise<boolean>;
|
|
11
11
|
export declare const cyclePermissionMode: (currentMode: PermissionMode, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>) => void;
|
|
12
|
+
export declare const getAtSelectorPosition: (text: string, cursorPosition: number) => number;
|
|
13
|
+
export declare const getSlashSelectorPosition: (text: string, cursorPosition: number) => number;
|
|
12
14
|
export declare const updateSearchQueriesForActiveSelectors: (state: InputState, dispatch: React.Dispatch<InputAction>, inputText: string, cursorPosition: number) => void;
|
|
13
|
-
export declare const
|
|
15
|
+
export declare const processSelectorInput: (state: InputState, dispatch: React.Dispatch<InputAction>, char: string) => void;
|
|
14
16
|
export declare const handlePasteInput: (state: InputState, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>, input: string) => void;
|
|
17
|
+
export declare const getWordEnd: (text: string, startPos: number) => number;
|
|
15
18
|
export declare const handleCommandSelect: (state: InputState, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>, command: string) => {
|
|
16
19
|
newInput: string;
|
|
17
20
|
newCursorPosition: number;
|
|
@@ -23,6 +26,6 @@ export declare const handleFileSelect: (state: InputState, dispatch: React.Dispa
|
|
|
23
26
|
export declare const checkForAtDeletion: (state: InputState, dispatch: React.Dispatch<InputAction>, cursorPosition: number) => boolean;
|
|
24
27
|
export declare const checkForSlashDeletion: (state: InputState, dispatch: React.Dispatch<InputAction>, cursorPosition: number) => boolean;
|
|
25
28
|
export declare const handleSelectorInput: (state: InputState, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>, input: string, key: Key) => boolean;
|
|
26
|
-
export declare const handleNormalInput: (state: InputState, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>, input: string, key: Key,
|
|
27
|
-
export declare const handleInput: (state: InputState, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>, input: string, key: Key,
|
|
29
|
+
export declare const handleNormalInput: (state: InputState, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>, input: string, key: Key, clearImages?: () => void) => Promise<boolean>;
|
|
30
|
+
export declare const handleInput: (state: InputState, dispatch: React.Dispatch<InputAction>, callbacks: Partial<InputManagerCallbacks>, input: string, key: Key, clearImages?: () => void) => Promise<boolean>;
|
|
28
31
|
//# sourceMappingURL=inputHandlers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inputHandlers.d.ts","sourceRoot":"","sources":["../../src/managers/inputHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,EAAwB,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEtE,OAAO,EACL,UAAU,EACV,WAAW,EACX,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,0BAA0B,GACrC,MAAM,MAAM,EACZ,aAAa,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAClC,MAcF,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,OAAO,UAAU,EACjB,UAAU,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,EACrC,WAAW,OAAO,CAAC,qBAAqB,CAAC,EACzC,
|
|
1
|
+
{"version":3,"file":"inputHandlers.d.ts","sourceRoot":"","sources":["../../src/managers/inputHandlers.ts"],"names":[],"mappings":"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"}
|