wave-code 0.2.1 → 0.5.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/commands/plugin/disable.d.ts +2 -1
- package/dist/commands/plugin/disable.d.ts.map +1 -1
- package/dist/commands/plugin/disable.js +3 -2
- package/dist/commands/plugin/enable.d.ts +2 -1
- package/dist/commands/plugin/enable.d.ts.map +1 -1
- package/dist/commands/plugin/enable.js +3 -2
- package/dist/commands/plugin/install.d.ts +2 -1
- package/dist/commands/plugin/install.d.ts.map +1 -1
- package/dist/commands/plugin/list.d.ts.map +1 -1
- package/dist/commands/plugin/list.js +15 -3
- package/dist/commands/plugin/marketplace.d.ts +3 -0
- package/dist/commands/plugin/marketplace.d.ts.map +1 -1
- package/dist/commands/plugin/marketplace.js +15 -1
- package/dist/commands/plugin/uninstall.d.ts +4 -0
- package/dist/commands/plugin/uninstall.d.ts.map +1 -0
- package/dist/commands/plugin/uninstall.js +29 -0
- package/dist/commands/plugin/update.d.ts +4 -0
- package/dist/commands/plugin/update.d.ts.map +1 -0
- package/dist/commands/plugin/update.js +15 -0
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +2 -2
- package/dist/components/CommandSelector.d.ts.map +1 -1
- package/dist/components/CommandSelector.js +9 -3
- package/dist/components/Confirmation.d.ts.map +1 -1
- package/dist/components/Confirmation.js +73 -20
- package/dist/components/DiffDisplay.d.ts +0 -1
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +38 -59
- package/dist/components/DiscoverView.d.ts +3 -0
- package/dist/components/DiscoverView.d.ts.map +1 -0
- package/dist/components/DiscoverView.js +25 -0
- package/dist/components/FileSelector.js +1 -1
- package/dist/components/HistorySearch.d.ts +8 -0
- package/dist/components/HistorySearch.d.ts.map +1 -0
- package/dist/components/HistorySearch.js +67 -0
- package/dist/components/InputBox.d.ts +1 -1
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +29 -19
- package/dist/components/InstalledView.d.ts +3 -0
- package/dist/components/InstalledView.d.ts.map +1 -0
- package/dist/components/InstalledView.js +30 -0
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown.js +24 -10
- package/dist/components/MarketplaceAddForm.d.ts +3 -0
- package/dist/components/MarketplaceAddForm.d.ts.map +1 -0
- package/dist/components/MarketplaceAddForm.js +26 -0
- package/dist/components/MarketplaceDetail.d.ts +3 -0
- package/dist/components/MarketplaceDetail.d.ts.map +1 -0
- package/dist/components/MarketplaceDetail.js +38 -0
- package/dist/components/MarketplaceList.d.ts +9 -0
- package/dist/components/MarketplaceList.d.ts.map +1 -0
- package/dist/components/MarketplaceList.js +16 -0
- package/dist/components/MarketplaceView.d.ts +3 -0
- package/dist/components/MarketplaceView.d.ts.map +1 -0
- package/dist/components/MarketplaceView.js +28 -0
- package/dist/components/PlanDisplay.d.ts.map +1 -1
- package/dist/components/PlanDisplay.js +2 -2
- package/dist/components/PluginDetail.d.ts +3 -0
- package/dist/components/PluginDetail.d.ts.map +1 -0
- package/dist/components/PluginDetail.js +63 -0
- package/dist/components/PluginList.d.ts +14 -0
- package/dist/components/PluginList.d.ts.map +1 -0
- package/dist/components/PluginList.js +12 -0
- package/dist/components/PluginManagerShell.d.ts +5 -0
- package/dist/components/PluginManagerShell.d.ts.map +1 -0
- package/dist/components/PluginManagerShell.js +89 -0
- package/dist/components/PluginManagerTypes.d.ts +33 -0
- package/dist/components/PluginManagerTypes.d.ts.map +1 -0
- package/dist/components/PluginManagerTypes.js +1 -0
- package/dist/components/RewindCommand.d.ts +9 -0
- package/dist/components/RewindCommand.d.ts.map +1 -0
- package/dist/components/RewindCommand.js +42 -0
- package/dist/components/SessionSelector.d.ts +11 -0
- package/dist/components/SessionSelector.d.ts.map +1 -0
- package/dist/components/SessionSelector.js +38 -0
- package/dist/components/SubagentBlock.d.ts.map +1 -1
- package/dist/components/SubagentBlock.js +24 -1
- package/dist/components/TaskManager.d.ts +6 -0
- package/dist/components/TaskManager.d.ts.map +1 -0
- package/dist/components/TaskManager.js +114 -0
- package/dist/components/ToolResultDisplay.d.ts.map +1 -1
- package/dist/components/ToolResultDisplay.js +2 -1
- package/dist/contexts/PluginManagerContext.d.ts +4 -0
- package/dist/contexts/PluginManagerContext.d.ts.map +1 -0
- package/dist/contexts/PluginManagerContext.js +9 -0
- package/dist/contexts/useChat.d.ts +7 -4
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +37 -12
- package/dist/hooks/useInputManager.d.ts +8 -16
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +39 -55
- package/dist/hooks/usePluginManager.d.ts +3 -0
- package/dist/hooks/usePluginManager.d.ts.map +1 -0
- package/dist/hooks/usePluginManager.js +227 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +150 -177
- package/dist/managers/InputManager.d.ts +18 -26
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +93 -119
- package/dist/plugin-manager-cli.d.ts +6 -0
- package/dist/plugin-manager-cli.d.ts.map +1 -0
- package/dist/plugin-manager-cli.js +12 -0
- package/dist/session-selector-cli.d.ts +2 -0
- package/dist/session-selector-cli.d.ts.map +1 -0
- package/dist/session-selector-cli.js +25 -0
- package/package.json +9 -5
- package/src/commands/plugin/disable.ts +7 -3
- package/src/commands/plugin/enable.ts +7 -3
- package/src/commands/plugin/install.ts +2 -1
- package/src/commands/plugin/list.ts +21 -3
- package/src/commands/plugin/marketplace.ts +17 -1
- package/src/commands/plugin/uninstall.ts +39 -0
- package/src/commands/plugin/update.ts +19 -0
- package/src/components/ChatInterface.tsx +2 -1
- package/src/components/CommandSelector.tsx +10 -3
- package/src/components/Confirmation.tsx +115 -25
- package/src/components/DiffDisplay.tsx +60 -106
- package/src/components/DiscoverView.tsx +31 -0
- package/src/components/FileSelector.tsx +1 -1
- package/src/components/HistorySearch.tsx +148 -0
- package/src/components/InputBox.tsx +51 -34
- package/src/components/InstalledView.tsx +61 -0
- package/src/components/Markdown.tsx +44 -28
- package/src/components/MarketplaceAddForm.tsx +39 -0
- package/src/components/MarketplaceDetail.tsx +79 -0
- package/src/components/MarketplaceList.tsx +52 -0
- package/src/components/MarketplaceView.tsx +43 -0
- package/src/components/PlanDisplay.tsx +14 -19
- package/src/components/PluginDetail.tsx +147 -0
- package/src/components/PluginList.tsx +51 -0
- package/src/components/PluginManagerShell.tsx +189 -0
- package/src/components/PluginManagerTypes.ts +47 -0
- package/src/components/RewindCommand.tsx +114 -0
- package/src/components/SessionSelector.tsx +127 -0
- package/src/components/SubagentBlock.tsx +34 -1
- package/src/components/{BashShellManager.tsx → TaskManager.tsx} +79 -75
- package/src/components/ToolResultDisplay.tsx +6 -2
- package/src/contexts/PluginManagerContext.ts +15 -0
- package/src/contexts/useChat.tsx +51 -20
- package/src/hooks/useInputManager.ts +39 -71
- package/src/hooks/usePluginManager.ts +302 -0
- package/src/index.ts +241 -280
- package/src/managers/InputManager.ts +113 -162
- package/src/plugin-manager-cli.tsx +13 -0
- package/src/session-selector-cli.tsx +37 -0
- package/dist/components/BashHistorySelector.d.ts +0 -11
- package/dist/components/BashHistorySelector.d.ts.map +0 -1
- package/dist/components/BashHistorySelector.js +0 -93
- package/dist/components/BashShellManager.d.ts +0 -6
- package/dist/components/BashShellManager.d.ts.map +0 -1
- package/dist/components/BashShellManager.js +0 -116
- package/dist/hooks/usePagination.d.ts +0 -20
- package/dist/hooks/usePagination.d.ts.map +0 -1
- package/dist/hooks/usePagination.js +0 -168
- package/src/components/BashHistorySelector.tsx +0 -181
- package/src/hooks/usePagination.ts +0 -203
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Box, useInput } from "ink";
|
|
3
|
+
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
|
+
import { PluginList } from "./PluginList.js";
|
|
5
|
+
|
|
6
|
+
export const DiscoverView: React.FC = () => {
|
|
7
|
+
const { discoverablePlugins, actions } = usePluginManagerContext();
|
|
8
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
9
|
+
|
|
10
|
+
useInput((input, key) => {
|
|
11
|
+
if (key.upArrow) {
|
|
12
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
13
|
+
} else if (key.downArrow) {
|
|
14
|
+
setSelectedIndex(
|
|
15
|
+
Math.min(discoverablePlugins.length - 1, selectedIndex + 1),
|
|
16
|
+
);
|
|
17
|
+
} else if (key.return) {
|
|
18
|
+
const plugin = discoverablePlugins[selectedIndex];
|
|
19
|
+
if (plugin) {
|
|
20
|
+
actions.setSelectedId(`${plugin.name}@${plugin.marketplace}`);
|
|
21
|
+
actions.setView("PLUGIN_DETAIL");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Box flexDirection="column">
|
|
28
|
+
<PluginList plugins={discoverablePlugins} selectedIndex={selectedIndex} />
|
|
29
|
+
</Box>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
@@ -131,7 +131,7 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
|
|
|
131
131
|
Use ↑↓ to navigate, Enter/Tab to select, Escape to cancel
|
|
132
132
|
</Text>
|
|
133
133
|
<Text dimColor>
|
|
134
|
-
File {selectedIndex + 1} of {files.length}
|
|
134
|
+
, File {selectedIndex + 1} of {files.length}
|
|
135
135
|
</Text>
|
|
136
136
|
</Box>
|
|
137
137
|
</Box>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import React, { useState, useEffect } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { PromptHistoryManager, type PromptEntry } from "wave-agent-sdk";
|
|
4
|
+
|
|
5
|
+
export interface HistorySearchProps {
|
|
6
|
+
searchQuery: string;
|
|
7
|
+
onSelect: (prompt: string) => void;
|
|
8
|
+
onCancel: () => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const HistorySearch: React.FC<HistorySearchProps> = ({
|
|
12
|
+
searchQuery,
|
|
13
|
+
onSelect,
|
|
14
|
+
onCancel,
|
|
15
|
+
}) => {
|
|
16
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
17
|
+
const [entries, setEntries] = useState<PromptEntry[]>([]);
|
|
18
|
+
|
|
19
|
+
const entriesRef = React.useRef<PromptEntry[]>([]);
|
|
20
|
+
const selectedIndexRef = React.useRef(0);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
entriesRef.current = entries;
|
|
24
|
+
}, [entries]);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
selectedIndexRef.current = selectedIndex;
|
|
28
|
+
}, [selectedIndex]);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const fetchHistory = async () => {
|
|
32
|
+
const results = await PromptHistoryManager.searchHistory(searchQuery);
|
|
33
|
+
const limitedResults = results.slice(0, 10);
|
|
34
|
+
setEntries(limitedResults); // Limit to 10 results
|
|
35
|
+
setSelectedIndex(0);
|
|
36
|
+
};
|
|
37
|
+
fetchHistory();
|
|
38
|
+
}, [searchQuery]);
|
|
39
|
+
|
|
40
|
+
useInput((input, key) => {
|
|
41
|
+
if (key.return) {
|
|
42
|
+
if (
|
|
43
|
+
entriesRef.current.length > 0 &&
|
|
44
|
+
selectedIndexRef.current < entriesRef.current.length
|
|
45
|
+
) {
|
|
46
|
+
onSelect(entriesRef.current[selectedIndexRef.current].prompt);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (key.escape) {
|
|
52
|
+
onCancel();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (key.upArrow) {
|
|
57
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (key.downArrow) {
|
|
62
|
+
setSelectedIndex((prev) =>
|
|
63
|
+
Math.min(entriesRef.current.length - 1, prev + 1),
|
|
64
|
+
);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (entries.length === 0) {
|
|
70
|
+
return (
|
|
71
|
+
<Box
|
|
72
|
+
flexDirection="column"
|
|
73
|
+
borderStyle="single"
|
|
74
|
+
borderColor="yellow"
|
|
75
|
+
borderBottom={false}
|
|
76
|
+
borderLeft={false}
|
|
77
|
+
borderRight={false}
|
|
78
|
+
paddingTop={1}
|
|
79
|
+
>
|
|
80
|
+
<Text color="yellow">
|
|
81
|
+
No history found {searchQuery && `for "${searchQuery}"`}
|
|
82
|
+
</Text>
|
|
83
|
+
<Text dimColor>Press Escape to cancel</Text>
|
|
84
|
+
</Box>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const formatTimestamp = (timestamp: number): string => {
|
|
89
|
+
const date = new Date(timestamp);
|
|
90
|
+
const now = new Date();
|
|
91
|
+
const diffMs = now.getTime() - date.getTime();
|
|
92
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
93
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
94
|
+
|
|
95
|
+
if (diffDays > 0) {
|
|
96
|
+
return `${diffDays}d ago`;
|
|
97
|
+
} else if (diffHours > 0) {
|
|
98
|
+
return `${diffHours}h ago`;
|
|
99
|
+
} else {
|
|
100
|
+
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
|
101
|
+
return diffMinutes > 0 ? `${diffMinutes}m ago` : "just now";
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Box
|
|
107
|
+
flexDirection="column"
|
|
108
|
+
borderStyle="single"
|
|
109
|
+
borderColor="blue"
|
|
110
|
+
borderBottom={false}
|
|
111
|
+
borderLeft={false}
|
|
112
|
+
borderRight={false}
|
|
113
|
+
paddingTop={1}
|
|
114
|
+
gap={1}
|
|
115
|
+
>
|
|
116
|
+
<Box>
|
|
117
|
+
<Text color="blue" bold>
|
|
118
|
+
Prompt History {searchQuery && `(filtering: "${searchQuery}")`}
|
|
119
|
+
</Text>
|
|
120
|
+
</Box>
|
|
121
|
+
|
|
122
|
+
{entries.map((entry, index) => (
|
|
123
|
+
<Box key={index} flexDirection="column">
|
|
124
|
+
<Text
|
|
125
|
+
color={index === selectedIndex ? "black" : "white"}
|
|
126
|
+
backgroundColor={index === selectedIndex ? "blue" : undefined}
|
|
127
|
+
wrap="truncate-end"
|
|
128
|
+
>
|
|
129
|
+
{entry.prompt.replace(/\n/g, " ")}
|
|
130
|
+
</Text>
|
|
131
|
+
{index === selectedIndex && (
|
|
132
|
+
<Box marginLeft={4}>
|
|
133
|
+
<Text color="gray" dimColor>
|
|
134
|
+
{formatTimestamp(entry.timestamp)}
|
|
135
|
+
</Text>
|
|
136
|
+
</Box>
|
|
137
|
+
)}
|
|
138
|
+
</Box>
|
|
139
|
+
))}
|
|
140
|
+
|
|
141
|
+
<Box>
|
|
142
|
+
<Text dimColor>
|
|
143
|
+
Use ↑↓ to navigate, Enter to select, Escape to cancel
|
|
144
|
+
</Text>
|
|
145
|
+
</Box>
|
|
146
|
+
</Box>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import React, { useEffect
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import { useInput } from "ink";
|
|
4
4
|
import { FileSelector } from "./FileSelector.js";
|
|
5
5
|
import { CommandSelector } from "./CommandSelector.js";
|
|
6
|
-
import {
|
|
6
|
+
import { HistorySearch } from "./HistorySearch.js";
|
|
7
7
|
import { MemoryTypeSelector } from "./MemoryTypeSelector.js";
|
|
8
|
-
import {
|
|
8
|
+
import { TaskManager } from "./TaskManager.js";
|
|
9
9
|
import { McpManager } from "./McpManager.js";
|
|
10
|
+
import { RewindCommand } from "./RewindCommand.js";
|
|
10
11
|
import { useInputManager } from "../hooks/useInputManager.js";
|
|
11
12
|
import { useChat } from "../contexts/useChat.js";
|
|
12
13
|
|
|
13
14
|
import type { McpServerStatus, SlashCommand } from "wave-agent-sdk";
|
|
14
15
|
|
|
15
16
|
export const INPUT_PLACEHOLDER_TEXT =
|
|
16
|
-
"Type your message (use @ to reference files, / for commands,
|
|
17
|
+
"Type your message (use @ to reference files, / for commands, # to add memory, Ctrl+R to search history)...";
|
|
17
18
|
|
|
18
19
|
export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(
|
|
19
20
|
0,
|
|
@@ -43,7 +44,6 @@ export interface InputBoxProps {
|
|
|
43
44
|
export const InputBox: React.FC<InputBoxProps> = ({
|
|
44
45
|
isLoading = false,
|
|
45
46
|
isCommandRunning = false,
|
|
46
|
-
workdir,
|
|
47
47
|
userInputHistory = [],
|
|
48
48
|
sendMessage = () => {},
|
|
49
49
|
abortMessage = () => {},
|
|
@@ -54,12 +54,12 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
54
54
|
slashCommands = [],
|
|
55
55
|
hasSlashCommand = () => false,
|
|
56
56
|
}) => {
|
|
57
|
-
// Get current working directory - memoized to avoid repeated process.cwd() calls
|
|
58
|
-
const currentWorkdir = useMemo(() => workdir || process.cwd(), [workdir]);
|
|
59
|
-
|
|
60
57
|
const {
|
|
61
58
|
permissionMode: chatPermissionMode,
|
|
62
59
|
setPermissionMode: setChatPermissionMode,
|
|
60
|
+
handleRewindSelect,
|
|
61
|
+
backgroundCurrentTask,
|
|
62
|
+
messages,
|
|
63
63
|
} = useChat();
|
|
64
64
|
|
|
65
65
|
// Input manager with all input state and functionality (including images)
|
|
@@ -81,29 +81,28 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
81
81
|
handleCommandSelect,
|
|
82
82
|
handleCommandInsert,
|
|
83
83
|
handleCancelCommandSelect,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
bashHistorySearchQuery,
|
|
87
|
-
handleBashHistorySelect,
|
|
88
|
-
handleCancelBashHistorySelect,
|
|
84
|
+
handleHistorySearchSelect,
|
|
85
|
+
handleCancelHistorySearch,
|
|
89
86
|
// Memory type selector
|
|
90
87
|
showMemoryTypeSelector,
|
|
91
88
|
memoryMessage,
|
|
92
89
|
handleMemoryTypeSelect,
|
|
93
90
|
handleCancelMemoryTypeSelect,
|
|
94
|
-
//
|
|
95
|
-
|
|
91
|
+
// History search
|
|
92
|
+
showHistorySearch,
|
|
93
|
+
historySearchQuery,
|
|
94
|
+
// Task/MCP Manager
|
|
95
|
+
showTaskManager,
|
|
96
96
|
showMcpManager,
|
|
97
|
-
|
|
97
|
+
showRewindManager,
|
|
98
|
+
setShowTaskManager,
|
|
98
99
|
setShowMcpManager,
|
|
100
|
+
setShowRewindManager,
|
|
99
101
|
// Permission mode
|
|
100
102
|
permissionMode,
|
|
101
103
|
setPermissionMode,
|
|
102
104
|
// Input history
|
|
103
105
|
setUserInputHistory,
|
|
104
|
-
// Complex handlers combining multiple operations
|
|
105
|
-
handleBashHistoryExecuteAndSend,
|
|
106
|
-
handleBashHistoryDelete,
|
|
107
106
|
// Main handler
|
|
108
107
|
handleInput,
|
|
109
108
|
// Manager ready state
|
|
@@ -113,6 +112,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
113
112
|
onHasSlashCommand: hasSlashCommand,
|
|
114
113
|
onSaveMemory: saveMemory,
|
|
115
114
|
onAbortMessage: abortMessage,
|
|
115
|
+
onBackgroundCurrentTask: backgroundCurrentTask,
|
|
116
116
|
onPermissionModeChange: setChatPermissionMode,
|
|
117
117
|
});
|
|
118
118
|
|
|
@@ -138,9 +138,11 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
138
138
|
);
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
const handleRewindCancel = () => {
|
|
142
|
+
if (setShowRewindManager) {
|
|
143
|
+
setShowRewindManager(false);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
144
146
|
|
|
145
147
|
const isPlaceholder = !inputText;
|
|
146
148
|
const placeholderText = INPUT_PLACEHOLDER_TEXT;
|
|
@@ -154,7 +156,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
154
156
|
cursorPosition < displayText.length ? displayText[cursorPosition] : " ";
|
|
155
157
|
const afterCursor = displayText.substring(cursorPosition + 1);
|
|
156
158
|
|
|
157
|
-
// Always show cursor, allow user to continue input during
|
|
159
|
+
// Always show cursor, allow user to continue input during memory mode
|
|
158
160
|
const shouldShowCursor = true;
|
|
159
161
|
|
|
160
162
|
// Only show the Box after InputManager is created on first mount
|
|
@@ -162,6 +164,23 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
162
164
|
return null;
|
|
163
165
|
}
|
|
164
166
|
|
|
167
|
+
const handleRewindSelectWithClose = async (index: number) => {
|
|
168
|
+
if (setShowRewindManager) {
|
|
169
|
+
setShowRewindManager(false);
|
|
170
|
+
}
|
|
171
|
+
await handleRewindSelect(index);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (showRewindManager) {
|
|
175
|
+
return (
|
|
176
|
+
<RewindCommand
|
|
177
|
+
messages={messages}
|
|
178
|
+
onSelect={handleRewindSelectWithClose}
|
|
179
|
+
onCancel={handleRewindCancel}
|
|
180
|
+
/>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
165
184
|
return (
|
|
166
185
|
<Box flexDirection="column">
|
|
167
186
|
{showFileSelector && (
|
|
@@ -183,14 +202,11 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
183
202
|
/>
|
|
184
203
|
)}
|
|
185
204
|
|
|
186
|
-
{
|
|
187
|
-
<
|
|
188
|
-
searchQuery={
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
onExecute={handleBashHistoryExecuteAndSend}
|
|
192
|
-
onDelete={handleBashHistoryDelete}
|
|
193
|
-
onCancel={handleCancelBashHistorySelect}
|
|
205
|
+
{showHistorySearch && (
|
|
206
|
+
<HistorySearch
|
|
207
|
+
searchQuery={historySearchQuery}
|
|
208
|
+
onSelect={handleHistorySearchSelect}
|
|
209
|
+
onCancel={handleCancelHistorySearch}
|
|
194
210
|
/>
|
|
195
211
|
)}
|
|
196
212
|
|
|
@@ -202,8 +218,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
202
218
|
/>
|
|
203
219
|
)}
|
|
204
220
|
|
|
205
|
-
{
|
|
206
|
-
<
|
|
221
|
+
{showTaskManager && (
|
|
222
|
+
<TaskManager onCancel={() => setShowTaskManager(false)} />
|
|
207
223
|
)}
|
|
208
224
|
|
|
209
225
|
{showMcpManager && (
|
|
@@ -214,7 +230,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
|
|
|
214
230
|
onDisconnectServer={disconnectMcpServer}
|
|
215
231
|
/>
|
|
216
232
|
)}
|
|
217
|
-
|
|
233
|
+
|
|
234
|
+
{showTaskManager || showMcpManager || showRewindManager || (
|
|
218
235
|
<Box flexDirection="column">
|
|
219
236
|
<Box
|
|
220
237
|
borderStyle="single"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
|
+
|
|
5
|
+
export const InstalledView: React.FC = () => {
|
|
6
|
+
const { installedPlugins, actions } = usePluginManagerContext();
|
|
7
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
8
|
+
|
|
9
|
+
useInput((input, key) => {
|
|
10
|
+
if (key.upArrow) {
|
|
11
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
12
|
+
} else if (key.downArrow) {
|
|
13
|
+
setSelectedIndex(
|
|
14
|
+
Math.min(installedPlugins.length - 1, selectedIndex + 1),
|
|
15
|
+
);
|
|
16
|
+
} else if (key.return) {
|
|
17
|
+
const plugin = installedPlugins[selectedIndex];
|
|
18
|
+
if (plugin) {
|
|
19
|
+
actions.setSelectedId(`${plugin.name}@${plugin.marketplace}`);
|
|
20
|
+
actions.setView("PLUGIN_DETAIL");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (installedPlugins.length === 0) {
|
|
26
|
+
return (
|
|
27
|
+
<Box padding={1}>
|
|
28
|
+
<Text dimColor>No plugins installed.</Text>
|
|
29
|
+
</Box>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Box flexDirection="column">
|
|
35
|
+
{installedPlugins.map((plugin, index) => {
|
|
36
|
+
const isSelected = index === selectedIndex;
|
|
37
|
+
return (
|
|
38
|
+
<Box
|
|
39
|
+
key={`${plugin.name}@${plugin.marketplace}`}
|
|
40
|
+
flexDirection="column"
|
|
41
|
+
marginBottom={1}
|
|
42
|
+
>
|
|
43
|
+
<Box>
|
|
44
|
+
<Text color={isSelected ? "cyan" : undefined}>
|
|
45
|
+
{isSelected ? "> " : " "}
|
|
46
|
+
<Text bold>{plugin.name}</Text>
|
|
47
|
+
<Text dimColor> @{plugin.marketplace}</Text>
|
|
48
|
+
{plugin.scope && <Text color="gray"> ({plugin.scope})</Text>}
|
|
49
|
+
</Text>
|
|
50
|
+
</Box>
|
|
51
|
+
{isSelected && (
|
|
52
|
+
<Box marginLeft={4}>
|
|
53
|
+
<Text dimColor>Press Enter for actions</Text>
|
|
54
|
+
</Box>
|
|
55
|
+
)}
|
|
56
|
+
</Box>
|
|
57
|
+
);
|
|
58
|
+
})}
|
|
59
|
+
</Box>
|
|
60
|
+
);
|
|
61
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { Box, Text, useStdout } from "ink";
|
|
3
3
|
import { marked, type Token, type Tokens } from "marked";
|
|
4
|
+
import { highlight } from "cli-highlight";
|
|
4
5
|
|
|
5
6
|
export interface MarkdownProps {
|
|
6
7
|
children: string;
|
|
@@ -12,7 +13,8 @@ const unescapeHtml = (html: string) => {
|
|
|
12
13
|
.replace(/</g, "<")
|
|
13
14
|
.replace(/>/g, ">")
|
|
14
15
|
.replace(/"/g, '"')
|
|
15
|
-
.replace(/'/g, "'")
|
|
16
|
+
.replace(/'/g, "'")
|
|
17
|
+
.replace(/'/g, "'");
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
const InlineRenderer = ({ tokens }: { tokens: Token[] }) => {
|
|
@@ -53,16 +55,21 @@ const InlineRenderer = ({ tokens }: { tokens: Token[] }) => {
|
|
|
53
55
|
{unescapeHtml((token as Tokens.Codespan).text)}
|
|
54
56
|
</Text>
|
|
55
57
|
);
|
|
56
|
-
case "link":
|
|
58
|
+
case "link": {
|
|
59
|
+
const t = token as Tokens.Link;
|
|
57
60
|
return (
|
|
58
|
-
<Text key={index}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
<Text key={index}>
|
|
62
|
+
<Text color="blue" underline>
|
|
63
|
+
{t.tokens ? (
|
|
64
|
+
<InlineRenderer tokens={t.tokens} />
|
|
65
|
+
) : (
|
|
66
|
+
unescapeHtml(t.text)
|
|
67
|
+
)}
|
|
68
|
+
</Text>
|
|
69
|
+
<Text color="gray"> ({t.href})</Text>
|
|
64
70
|
</Text>
|
|
65
71
|
);
|
|
72
|
+
}
|
|
66
73
|
case "br":
|
|
67
74
|
return <Text key={index}>{"\n"}</Text>;
|
|
68
75
|
case "del":
|
|
@@ -199,23 +206,29 @@ const BlockRenderer = ({ tokens }: { tokens: Token[] }) => {
|
|
|
199
206
|
case "paragraph": {
|
|
200
207
|
const t = token as Tokens.Paragraph;
|
|
201
208
|
return (
|
|
202
|
-
<Box
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
flexWrap="wrap"
|
|
207
|
-
>
|
|
208
|
-
<InlineRenderer tokens={t.tokens} />
|
|
209
|
+
<Box key={index} marginBottom={1} flexDirection="row">
|
|
210
|
+
<Text>
|
|
211
|
+
<InlineRenderer tokens={t.tokens} />
|
|
212
|
+
</Text>
|
|
209
213
|
</Box>
|
|
210
214
|
);
|
|
211
215
|
}
|
|
212
216
|
case "code": {
|
|
213
217
|
const t = token as Tokens.Code;
|
|
214
218
|
if (t.lang !== undefined) {
|
|
215
|
-
const
|
|
219
|
+
const raw = token.raw.endsWith("\n")
|
|
220
|
+
? token.raw.slice(0, -1)
|
|
221
|
+
: token.raw;
|
|
222
|
+
const lines = raw.split("\n");
|
|
216
223
|
const opening = lines[0];
|
|
217
224
|
const closing = lines[lines.length - 1];
|
|
218
225
|
const content = lines.slice(1, -1).join("\n");
|
|
226
|
+
const highlighted = content
|
|
227
|
+
? highlight(unescapeHtml(content), {
|
|
228
|
+
language: t.lang,
|
|
229
|
+
ignoreIllegals: true,
|
|
230
|
+
})
|
|
231
|
+
: "";
|
|
219
232
|
return (
|
|
220
233
|
<Box
|
|
221
234
|
key={index}
|
|
@@ -224,7 +237,7 @@ const BlockRenderer = ({ tokens }: { tokens: Token[] }) => {
|
|
|
224
237
|
marginBottom={1}
|
|
225
238
|
>
|
|
226
239
|
<Text color="gray">{opening}</Text>
|
|
227
|
-
{
|
|
240
|
+
{highlighted && <Text>{highlighted}</Text>}
|
|
228
241
|
<Text color="gray">{closing}</Text>
|
|
229
242
|
</Box>
|
|
230
243
|
);
|
|
@@ -236,7 +249,7 @@ const BlockRenderer = ({ tokens }: { tokens: Token[] }) => {
|
|
|
236
249
|
paddingX={1}
|
|
237
250
|
marginBottom={1}
|
|
238
251
|
>
|
|
239
|
-
<Text>{t.text}</Text>
|
|
252
|
+
<Text>{unescapeHtml(t.text)}</Text>
|
|
240
253
|
</Box>
|
|
241
254
|
);
|
|
242
255
|
}
|
|
@@ -258,17 +271,20 @@ const BlockRenderer = ({ tokens }: { tokens: Token[] }) => {
|
|
|
258
271
|
</Text>
|
|
259
272
|
<Box flexDirection="column" flexGrow={1}>
|
|
260
273
|
{item.tokens.map((itemToken, itemIndex) => {
|
|
261
|
-
if (
|
|
262
|
-
|
|
274
|
+
if (
|
|
275
|
+
itemToken.type === "text" ||
|
|
276
|
+
itemToken.type === "paragraph"
|
|
277
|
+
) {
|
|
278
|
+
const it = itemToken as
|
|
279
|
+
| Tokens.Text
|
|
280
|
+
| Tokens.Paragraph;
|
|
263
281
|
return (
|
|
264
|
-
<Box
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
tokens={it.tokens || [itemToken]}
|
|
271
|
-
/>
|
|
282
|
+
<Box key={itemIndex} flexDirection="row">
|
|
283
|
+
<Text>
|
|
284
|
+
<InlineRenderer
|
|
285
|
+
tokens={it.tokens || [itemToken]}
|
|
286
|
+
/>
|
|
287
|
+
</Text>
|
|
272
288
|
</Box>
|
|
273
289
|
);
|
|
274
290
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
|
+
|
|
5
|
+
export const MarketplaceAddForm: React.FC = () => {
|
|
6
|
+
const { actions } = usePluginManagerContext();
|
|
7
|
+
const [source, setSource] = useState("");
|
|
8
|
+
|
|
9
|
+
useInput((input, key) => {
|
|
10
|
+
if (key.escape) {
|
|
11
|
+
actions.setView("MARKETPLACES");
|
|
12
|
+
} else if (key.return) {
|
|
13
|
+
if (source.trim()) {
|
|
14
|
+
actions.addMarketplace(source.trim());
|
|
15
|
+
actions.setView("MARKETPLACES");
|
|
16
|
+
}
|
|
17
|
+
} else if (key.backspace || key.delete) {
|
|
18
|
+
setSource((prev) => prev.slice(0, -1));
|
|
19
|
+
} else if (input.length === 1) {
|
|
20
|
+
setSource((prev) => prev + input);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Box flexDirection="column" padding={1}>
|
|
26
|
+
<Text bold color="cyan">
|
|
27
|
+
Add Marketplace
|
|
28
|
+
</Text>
|
|
29
|
+
<Box marginTop={1}>
|
|
30
|
+
<Text>Source (URL or Path): </Text>
|
|
31
|
+
<Text color="yellow">{source}</Text>
|
|
32
|
+
<Text color="yellow">_</Text>
|
|
33
|
+
</Box>
|
|
34
|
+
<Box marginTop={1}>
|
|
35
|
+
<Text dimColor>Press Enter to add, Esc to cancel</Text>
|
|
36
|
+
</Box>
|
|
37
|
+
</Box>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import { Box, Text, useInput } from "ink";
|
|
3
|
+
import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
|
|
4
|
+
|
|
5
|
+
export const MarketplaceDetail: React.FC = () => {
|
|
6
|
+
const { state, marketplaces, actions } = usePluginManagerContext();
|
|
7
|
+
const [selectedActionIndex, setSelectedActionIndex] = useState(0);
|
|
8
|
+
|
|
9
|
+
const marketplace = marketplaces.find((m) => m.name === state.selectedId);
|
|
10
|
+
|
|
11
|
+
const ACTIONS = [
|
|
12
|
+
{ id: "update", label: "Update marketplace" },
|
|
13
|
+
{ id: "remove", label: "Remove marketplace" },
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
useInput((input, key) => {
|
|
17
|
+
if (key.escape) {
|
|
18
|
+
actions.setView("MARKETPLACES");
|
|
19
|
+
} else if (key.upArrow) {
|
|
20
|
+
setSelectedActionIndex((prev) =>
|
|
21
|
+
prev > 0 ? prev - 1 : ACTIONS.length - 1,
|
|
22
|
+
);
|
|
23
|
+
} else if (key.downArrow) {
|
|
24
|
+
setSelectedActionIndex((prev) =>
|
|
25
|
+
prev < ACTIONS.length - 1 ? prev + 1 : 0,
|
|
26
|
+
);
|
|
27
|
+
} else if (key.return && marketplace) {
|
|
28
|
+
const action = ACTIONS[selectedActionIndex].id;
|
|
29
|
+
if (action === "update") {
|
|
30
|
+
actions.updateMarketplace(marketplace.name);
|
|
31
|
+
} else {
|
|
32
|
+
actions.removeMarketplace(marketplace.name);
|
|
33
|
+
}
|
|
34
|
+
actions.setView("MARKETPLACES");
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!marketplace) {
|
|
39
|
+
return (
|
|
40
|
+
<Box>
|
|
41
|
+
<Text color="red">Marketplace not found.</Text>
|
|
42
|
+
</Box>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<Box flexDirection="column" padding={1}>
|
|
48
|
+
<Box marginBottom={1}>
|
|
49
|
+
<Text bold color="cyan">
|
|
50
|
+
{marketplace.name}
|
|
51
|
+
</Text>
|
|
52
|
+
{marketplace.isBuiltin && <Text dimColor> (Built-in)</Text>}
|
|
53
|
+
</Box>
|
|
54
|
+
|
|
55
|
+
<Box marginBottom={1}>
|
|
56
|
+
<Text>Source: {JSON.stringify(marketplace.source)}</Text>
|
|
57
|
+
</Box>
|
|
58
|
+
|
|
59
|
+
<Box marginTop={1} flexDirection="column">
|
|
60
|
+
<Text bold>Marketplace Actions:</Text>
|
|
61
|
+
{ACTIONS.map((action, index) => (
|
|
62
|
+
<Text
|
|
63
|
+
key={action.id}
|
|
64
|
+
color={index === selectedActionIndex ? "yellow" : undefined}
|
|
65
|
+
>
|
|
66
|
+
{index === selectedActionIndex ? "> " : " "}
|
|
67
|
+
{action.label}
|
|
68
|
+
</Text>
|
|
69
|
+
))}
|
|
70
|
+
<Box marginTop={1}>
|
|
71
|
+
<Text dimColor>Use ↑/↓ to select, Enter to confirm</Text>
|
|
72
|
+
</Box>
|
|
73
|
+
<Box marginTop={1}>
|
|
74
|
+
<Text dimColor>Press Esc to go back</Text>
|
|
75
|
+
</Box>
|
|
76
|
+
</Box>
|
|
77
|
+
</Box>
|
|
78
|
+
);
|
|
79
|
+
};
|