wave-code 0.2.0 → 0.4.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 +6 -0
- package/dist/components/Confirmation.js +1 -1
- 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 +26 -17
- 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 +22 -9
- 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/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 +20 -1
- package/dist/components/ToolResultDisplay.js +1 -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 +2 -0
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +21 -0
- package/dist/hooks/useInputManager.d.ts +6 -14
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +29 -45
- package/dist/hooks/usePluginManager.d.ts +3 -0
- package/dist/hooks/usePluginManager.d.ts.map +1 -0
- package/dist/hooks/usePluginManager.js +223 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +150 -177
- package/dist/managers/InputManager.d.ts +12 -21
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +77 -108
- 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 +7 -3
- 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 +7 -0
- package/src/components/Confirmation.tsx +1 -1
- 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 +43 -28
- package/src/components/InstalledView.tsx +61 -0
- package/src/components/Markdown.tsx +37 -26
- 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/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 +29 -1
- package/src/components/ToolResultDisplay.tsx +2 -2
- package/src/contexts/PluginManagerContext.ts +15 -0
- package/src/contexts/useChat.tsx +26 -0
- package/src/hooks/useInputManager.ts +29 -61
- package/src/hooks/usePluginManager.ts +296 -0
- package/src/index.ts +241 -280
- package/src/managers/InputManager.ts +93 -149
- 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/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
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useMemo } from "react";
|
|
2
|
-
import { useInput } from "ink";
|
|
3
|
-
import { MESSAGES_PER_PAGE } from "../utils/constants.js";
|
|
4
|
-
export const usePagination = (messages) => {
|
|
5
|
-
const messagesPerPage = MESSAGES_PER_PAGE;
|
|
6
|
-
// Calculate pagination info, ensuring first page can be incomplete while subsequent pages are complete
|
|
7
|
-
const paginationInfo = useMemo(() => {
|
|
8
|
-
if (messages.length <= messagesPerPage) {
|
|
9
|
-
// If total messages don't exceed one page, display all messages directly
|
|
10
|
-
return {
|
|
11
|
-
currentPage: 1,
|
|
12
|
-
totalPages: 1,
|
|
13
|
-
startIndex: 0,
|
|
14
|
-
endIndex: messages.length,
|
|
15
|
-
messagesPerPage,
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
// Calculate remaining messages (messages other than the first page)
|
|
19
|
-
const remainingMessages = messages.length % messagesPerPage;
|
|
20
|
-
if (remainingMessages === 0) {
|
|
21
|
-
// Message count fits perfectly into complete pages, use standard pagination
|
|
22
|
-
const totalPages = Math.ceil(messages.length / messagesPerPage);
|
|
23
|
-
const currentPage = totalPages;
|
|
24
|
-
const startIndex = (currentPage - 1) * messagesPerPage;
|
|
25
|
-
const endIndex = messages.length;
|
|
26
|
-
return {
|
|
27
|
-
currentPage,
|
|
28
|
-
totalPages,
|
|
29
|
-
startIndex,
|
|
30
|
-
endIndex,
|
|
31
|
-
messagesPerPage,
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
// Has remaining messages, let first page display remaining messages, subsequent pages are complete
|
|
36
|
-
const firstPageMessageCount = remainingMessages;
|
|
37
|
-
const totalPages = Math.floor(messages.length / messagesPerPage) + 1;
|
|
38
|
-
// Default to showing the last page (complete page)
|
|
39
|
-
const currentPage = totalPages;
|
|
40
|
-
const startIndex = firstPageMessageCount + (currentPage - 2) * messagesPerPage;
|
|
41
|
-
const endIndex = messages.length;
|
|
42
|
-
return {
|
|
43
|
-
currentPage,
|
|
44
|
-
totalPages,
|
|
45
|
-
startIndex,
|
|
46
|
-
endIndex,
|
|
47
|
-
messagesPerPage,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
}, [messages.length, messagesPerPage]);
|
|
51
|
-
// Manually controlled current page (for keyboard navigation)
|
|
52
|
-
const [manualPage, setManualPage] = useState(null);
|
|
53
|
-
// Calculate actual display page info
|
|
54
|
-
const displayInfo = useMemo(() => {
|
|
55
|
-
if (manualPage === null) {
|
|
56
|
-
return paginationInfo; // Auto mode: display last page
|
|
57
|
-
}
|
|
58
|
-
// Manual mode: display user-selected page
|
|
59
|
-
const totalPages = paginationInfo.totalPages;
|
|
60
|
-
const currentPage = Math.min(Math.max(1, manualPage), totalPages);
|
|
61
|
-
if (messages.length <= messagesPerPage) {
|
|
62
|
-
// Only one page case
|
|
63
|
-
return {
|
|
64
|
-
currentPage,
|
|
65
|
-
totalPages,
|
|
66
|
-
startIndex: 0,
|
|
67
|
-
endIndex: messages.length,
|
|
68
|
-
messagesPerPage,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
const remainingMessages = messages.length % messagesPerPage;
|
|
72
|
-
if (remainingMessages === 0) {
|
|
73
|
-
// Message count fits perfectly into complete pages
|
|
74
|
-
const startIndex = (currentPage - 1) * messagesPerPage;
|
|
75
|
-
const endIndex = Math.min(startIndex + messagesPerPage, messages.length);
|
|
76
|
-
return {
|
|
77
|
-
currentPage,
|
|
78
|
-
totalPages,
|
|
79
|
-
startIndex,
|
|
80
|
-
endIndex,
|
|
81
|
-
messagesPerPage,
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
// First page incomplete, subsequent pages complete
|
|
86
|
-
if (currentPage === 1) {
|
|
87
|
-
// First page: display remaining message count
|
|
88
|
-
return {
|
|
89
|
-
currentPage,
|
|
90
|
-
totalPages,
|
|
91
|
-
startIndex: 0,
|
|
92
|
-
endIndex: remainingMessages,
|
|
93
|
-
messagesPerPage,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
// Other pages: display complete message count per page
|
|
98
|
-
const firstPageMessageCount = remainingMessages;
|
|
99
|
-
const startIndex = firstPageMessageCount + (currentPage - 2) * messagesPerPage;
|
|
100
|
-
const endIndex = Math.min(startIndex + messagesPerPage, messages.length);
|
|
101
|
-
return {
|
|
102
|
-
currentPage,
|
|
103
|
-
totalPages,
|
|
104
|
-
startIndex,
|
|
105
|
-
endIndex,
|
|
106
|
-
messagesPerPage,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}, [messages.length, messagesPerPage, manualPage, paginationInfo]);
|
|
111
|
-
// When message count changes, if user hasn't manually navigated, reset to auto mode
|
|
112
|
-
useEffect(() => {
|
|
113
|
-
if (manualPage !== null) {
|
|
114
|
-
// If user is currently on the last page, keep auto mode
|
|
115
|
-
const totalPages = Math.ceil(messages.length / messagesPerPage);
|
|
116
|
-
if (manualPage >= totalPages) {
|
|
117
|
-
setManualPage(null);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}, [messages.length, messagesPerPage, manualPage]);
|
|
121
|
-
// Pagination functionality
|
|
122
|
-
const goToPage = (page) => {
|
|
123
|
-
setManualPage(page);
|
|
124
|
-
};
|
|
125
|
-
const goToPrevPage = () => {
|
|
126
|
-
const currentPage = manualPage ?? displayInfo.currentPage;
|
|
127
|
-
setManualPage(Math.max(1, currentPage - 1));
|
|
128
|
-
};
|
|
129
|
-
const goToNextPage = () => {
|
|
130
|
-
const currentPage = manualPage ?? displayInfo.currentPage;
|
|
131
|
-
setManualPage(Math.min(displayInfo.totalPages, currentPage + 1));
|
|
132
|
-
};
|
|
133
|
-
const goToFirstPage = () => {
|
|
134
|
-
setManualPage(1);
|
|
135
|
-
};
|
|
136
|
-
const goToLastPage = () => {
|
|
137
|
-
setManualPage(null); // Return to auto mode (last page)
|
|
138
|
-
};
|
|
139
|
-
// Integrate keyboard shortcut handling
|
|
140
|
-
useInput((input, key) => {
|
|
141
|
-
// Ctrl+U/D shortcuts (Vim/Less style)
|
|
142
|
-
if (key.ctrl) {
|
|
143
|
-
if (input === "u") {
|
|
144
|
-
goToPrevPage();
|
|
145
|
-
}
|
|
146
|
-
else if (input === "d") {
|
|
147
|
-
goToNextPage();
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
// Page Up/Down support
|
|
151
|
-
if (key.pageUp) {
|
|
152
|
-
goToPrevPage();
|
|
153
|
-
}
|
|
154
|
-
if (key.pageDown) {
|
|
155
|
-
goToNextPage();
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
return {
|
|
159
|
-
displayInfo,
|
|
160
|
-
manualPage,
|
|
161
|
-
setManualPage,
|
|
162
|
-
goToPage,
|
|
163
|
-
goToPrevPage,
|
|
164
|
-
goToNextPage,
|
|
165
|
-
goToFirstPage,
|
|
166
|
-
goToLastPage,
|
|
167
|
-
};
|
|
168
|
-
};
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { Box, Text, useInput } from "ink";
|
|
3
|
-
import { searchBashHistory, type BashHistoryEntry } from "wave-agent-sdk";
|
|
4
|
-
import { logger } from "../utils/logger.js";
|
|
5
|
-
|
|
6
|
-
export interface BashHistorySelectorProps {
|
|
7
|
-
searchQuery: string;
|
|
8
|
-
workdir: string;
|
|
9
|
-
onSelect: (command: string) => void;
|
|
10
|
-
onExecute: (command: string) => void;
|
|
11
|
-
onDelete: (command: string, workdir?: string) => void;
|
|
12
|
-
onCancel: () => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export const BashHistorySelector: React.FC<BashHistorySelectorProps> = ({
|
|
16
|
-
searchQuery,
|
|
17
|
-
workdir,
|
|
18
|
-
onSelect,
|
|
19
|
-
onExecute,
|
|
20
|
-
onDelete,
|
|
21
|
-
onCancel,
|
|
22
|
-
}) => {
|
|
23
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
24
|
-
const [commands, setCommands] = useState<BashHistoryEntry[]>([]);
|
|
25
|
-
const [refreshCounter, setRefreshCounter] = useState(0);
|
|
26
|
-
|
|
27
|
-
// Search bash history
|
|
28
|
-
useEffect(() => {
|
|
29
|
-
const results = searchBashHistory(searchQuery, 10);
|
|
30
|
-
setCommands(results);
|
|
31
|
-
setSelectedIndex(0);
|
|
32
|
-
logger.debug("Bash history search:", {
|
|
33
|
-
searchQuery,
|
|
34
|
-
workdir,
|
|
35
|
-
resultCount: results.length,
|
|
36
|
-
refreshCounter,
|
|
37
|
-
});
|
|
38
|
-
}, [searchQuery, workdir, refreshCounter]);
|
|
39
|
-
|
|
40
|
-
useInput((input, key) => {
|
|
41
|
-
logger.debug("BashHistorySelector useInput:", {
|
|
42
|
-
input,
|
|
43
|
-
key,
|
|
44
|
-
commandsLength: commands.length,
|
|
45
|
-
selectedIndex,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
if (key.return) {
|
|
49
|
-
if (commands.length > 0 && selectedIndex < commands.length) {
|
|
50
|
-
const selectedCommand = commands[selectedIndex];
|
|
51
|
-
onExecute(selectedCommand.command);
|
|
52
|
-
} else if (commands.length === 0 && searchQuery.trim()) {
|
|
53
|
-
// When no history records match, execute the search query as a new command
|
|
54
|
-
onExecute(searchQuery.trim());
|
|
55
|
-
}
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (key.tab) {
|
|
60
|
-
if (commands.length > 0 && selectedIndex < commands.length) {
|
|
61
|
-
const selectedCommand = commands[selectedIndex];
|
|
62
|
-
onSelect(selectedCommand.command);
|
|
63
|
-
} else if (commands.length === 0 && searchQuery.trim()) {
|
|
64
|
-
// When no history records match, insert the search query
|
|
65
|
-
onSelect(searchQuery.trim());
|
|
66
|
-
}
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (key.escape) {
|
|
71
|
-
onCancel();
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (key.ctrl && input === "d") {
|
|
76
|
-
if (commands.length > 0 && selectedIndex < commands.length) {
|
|
77
|
-
const selectedCommand = commands[selectedIndex];
|
|
78
|
-
onDelete(selectedCommand.command, selectedCommand.workdir);
|
|
79
|
-
setRefreshCounter((prev) => prev + 1);
|
|
80
|
-
}
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (key.upArrow) {
|
|
85
|
-
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (key.downArrow) {
|
|
90
|
-
setSelectedIndex(Math.min(commands.length - 1, selectedIndex + 1));
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
if (commands.length === 0) {
|
|
96
|
-
return (
|
|
97
|
-
<Box
|
|
98
|
-
flexDirection="column"
|
|
99
|
-
borderStyle="single"
|
|
100
|
-
borderColor="yellow"
|
|
101
|
-
borderBottom={false}
|
|
102
|
-
borderLeft={false}
|
|
103
|
-
borderRight={false}
|
|
104
|
-
paddingTop={1}
|
|
105
|
-
>
|
|
106
|
-
<Text color="yellow">
|
|
107
|
-
No bash history found {searchQuery && `for "${searchQuery}"`}
|
|
108
|
-
</Text>
|
|
109
|
-
{searchQuery.trim() && (
|
|
110
|
-
<Text color="green">Press Enter to execute: {searchQuery}</Text>
|
|
111
|
-
)}
|
|
112
|
-
{searchQuery.trim() && (
|
|
113
|
-
<Text color="blue">Press Tab to insert: {searchQuery}</Text>
|
|
114
|
-
)}
|
|
115
|
-
<Text dimColor>Press Escape to cancel</Text>
|
|
116
|
-
</Box>
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const formatTimestamp = (timestamp: number): string => {
|
|
121
|
-
const date = new Date(timestamp);
|
|
122
|
-
const now = new Date();
|
|
123
|
-
const diffMs = now.getTime() - date.getTime();
|
|
124
|
-
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
125
|
-
const diffDays = Math.floor(diffHours / 24);
|
|
126
|
-
|
|
127
|
-
if (diffDays > 0) {
|
|
128
|
-
return `${diffDays}d ago`;
|
|
129
|
-
} else if (diffHours > 0) {
|
|
130
|
-
return `${diffHours}h ago`;
|
|
131
|
-
} else {
|
|
132
|
-
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
|
133
|
-
return diffMinutes > 0 ? `${diffMinutes}m ago` : "just now";
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
return (
|
|
138
|
-
<Box
|
|
139
|
-
flexDirection="column"
|
|
140
|
-
borderStyle="single"
|
|
141
|
-
borderColor="blue"
|
|
142
|
-
borderBottom={false}
|
|
143
|
-
borderLeft={false}
|
|
144
|
-
borderRight={false}
|
|
145
|
-
paddingTop={1}
|
|
146
|
-
gap={1}
|
|
147
|
-
>
|
|
148
|
-
<Box>
|
|
149
|
-
<Text color="blue" bold>
|
|
150
|
-
Bash History {searchQuery && `(filtering: "${searchQuery}")`}
|
|
151
|
-
</Text>
|
|
152
|
-
</Box>
|
|
153
|
-
|
|
154
|
-
{commands.map((cmd, index) => (
|
|
155
|
-
<Box key={index} flexDirection="column">
|
|
156
|
-
<Text
|
|
157
|
-
color={index === selectedIndex ? "black" : "white"}
|
|
158
|
-
backgroundColor={index === selectedIndex ? "blue" : undefined}
|
|
159
|
-
>
|
|
160
|
-
{cmd.command}
|
|
161
|
-
</Text>
|
|
162
|
-
{index === selectedIndex && (
|
|
163
|
-
<Box marginLeft={4} flexDirection="column">
|
|
164
|
-
<Text color="gray" dimColor>
|
|
165
|
-
{formatTimestamp(cmd.timestamp)}
|
|
166
|
-
{cmd.workdir !== workdir && ` • in ${cmd.workdir}`}
|
|
167
|
-
</Text>
|
|
168
|
-
</Box>
|
|
169
|
-
)}
|
|
170
|
-
</Box>
|
|
171
|
-
))}
|
|
172
|
-
|
|
173
|
-
<Box>
|
|
174
|
-
<Text dimColor>
|
|
175
|
-
Use ↑↓ to navigate, Enter to execute, Tab to insert, Ctrl+d to remove,
|
|
176
|
-
Escape to cancel
|
|
177
|
-
</Text>
|
|
178
|
-
</Box>
|
|
179
|
-
</Box>
|
|
180
|
-
);
|
|
181
|
-
};
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useMemo } from "react";
|
|
2
|
-
import { useInput } from "ink";
|
|
3
|
-
import type { Message } from "wave-agent-sdk";
|
|
4
|
-
import { MESSAGES_PER_PAGE } from "../utils/constants.js";
|
|
5
|
-
|
|
6
|
-
interface PaginationInfo {
|
|
7
|
-
currentPage: number;
|
|
8
|
-
totalPages: number;
|
|
9
|
-
startIndex: number;
|
|
10
|
-
endIndex: number;
|
|
11
|
-
messagesPerPage: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const usePagination = (messages: Message[]) => {
|
|
15
|
-
const messagesPerPage = MESSAGES_PER_PAGE;
|
|
16
|
-
|
|
17
|
-
// Calculate pagination info, ensuring first page can be incomplete while subsequent pages are complete
|
|
18
|
-
const paginationInfo = useMemo((): PaginationInfo => {
|
|
19
|
-
if (messages.length <= messagesPerPage) {
|
|
20
|
-
// If total messages don't exceed one page, display all messages directly
|
|
21
|
-
return {
|
|
22
|
-
currentPage: 1,
|
|
23
|
-
totalPages: 1,
|
|
24
|
-
startIndex: 0,
|
|
25
|
-
endIndex: messages.length,
|
|
26
|
-
messagesPerPage,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Calculate remaining messages (messages other than the first page)
|
|
31
|
-
const remainingMessages = messages.length % messagesPerPage;
|
|
32
|
-
|
|
33
|
-
if (remainingMessages === 0) {
|
|
34
|
-
// Message count fits perfectly into complete pages, use standard pagination
|
|
35
|
-
const totalPages = Math.ceil(messages.length / messagesPerPage);
|
|
36
|
-
const currentPage = totalPages;
|
|
37
|
-
const startIndex = (currentPage - 1) * messagesPerPage;
|
|
38
|
-
const endIndex = messages.length;
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
currentPage,
|
|
42
|
-
totalPages,
|
|
43
|
-
startIndex,
|
|
44
|
-
endIndex,
|
|
45
|
-
messagesPerPage,
|
|
46
|
-
};
|
|
47
|
-
} else {
|
|
48
|
-
// Has remaining messages, let first page display remaining messages, subsequent pages are complete
|
|
49
|
-
const firstPageMessageCount = remainingMessages;
|
|
50
|
-
const totalPages = Math.floor(messages.length / messagesPerPage) + 1;
|
|
51
|
-
|
|
52
|
-
// Default to showing the last page (complete page)
|
|
53
|
-
const currentPage = totalPages;
|
|
54
|
-
const startIndex =
|
|
55
|
-
firstPageMessageCount + (currentPage - 2) * messagesPerPage;
|
|
56
|
-
const endIndex = messages.length;
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
currentPage,
|
|
60
|
-
totalPages,
|
|
61
|
-
startIndex,
|
|
62
|
-
endIndex,
|
|
63
|
-
messagesPerPage,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}, [messages.length, messagesPerPage]);
|
|
67
|
-
|
|
68
|
-
// Manually controlled current page (for keyboard navigation)
|
|
69
|
-
const [manualPage, setManualPage] = useState<number | null>(null);
|
|
70
|
-
|
|
71
|
-
// Calculate actual display page info
|
|
72
|
-
const displayInfo = useMemo((): PaginationInfo => {
|
|
73
|
-
if (manualPage === null) {
|
|
74
|
-
return paginationInfo; // Auto mode: display last page
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Manual mode: display user-selected page
|
|
78
|
-
const totalPages = paginationInfo.totalPages;
|
|
79
|
-
const currentPage = Math.min(Math.max(1, manualPage), totalPages);
|
|
80
|
-
|
|
81
|
-
if (messages.length <= messagesPerPage) {
|
|
82
|
-
// Only one page case
|
|
83
|
-
return {
|
|
84
|
-
currentPage,
|
|
85
|
-
totalPages,
|
|
86
|
-
startIndex: 0,
|
|
87
|
-
endIndex: messages.length,
|
|
88
|
-
messagesPerPage,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const remainingMessages = messages.length % messagesPerPage;
|
|
93
|
-
|
|
94
|
-
if (remainingMessages === 0) {
|
|
95
|
-
// Message count fits perfectly into complete pages
|
|
96
|
-
const startIndex = (currentPage - 1) * messagesPerPage;
|
|
97
|
-
const endIndex = Math.min(startIndex + messagesPerPage, messages.length);
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
currentPage,
|
|
101
|
-
totalPages,
|
|
102
|
-
startIndex,
|
|
103
|
-
endIndex,
|
|
104
|
-
messagesPerPage,
|
|
105
|
-
};
|
|
106
|
-
} else {
|
|
107
|
-
// First page incomplete, subsequent pages complete
|
|
108
|
-
if (currentPage === 1) {
|
|
109
|
-
// First page: display remaining message count
|
|
110
|
-
return {
|
|
111
|
-
currentPage,
|
|
112
|
-
totalPages,
|
|
113
|
-
startIndex: 0,
|
|
114
|
-
endIndex: remainingMessages,
|
|
115
|
-
messagesPerPage,
|
|
116
|
-
};
|
|
117
|
-
} else {
|
|
118
|
-
// Other pages: display complete message count per page
|
|
119
|
-
const firstPageMessageCount = remainingMessages;
|
|
120
|
-
const startIndex =
|
|
121
|
-
firstPageMessageCount + (currentPage - 2) * messagesPerPage;
|
|
122
|
-
const endIndex = Math.min(
|
|
123
|
-
startIndex + messagesPerPage,
|
|
124
|
-
messages.length,
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
currentPage,
|
|
129
|
-
totalPages,
|
|
130
|
-
startIndex,
|
|
131
|
-
endIndex,
|
|
132
|
-
messagesPerPage,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}, [messages.length, messagesPerPage, manualPage, paginationInfo]);
|
|
137
|
-
|
|
138
|
-
// When message count changes, if user hasn't manually navigated, reset to auto mode
|
|
139
|
-
useEffect(() => {
|
|
140
|
-
if (manualPage !== null) {
|
|
141
|
-
// If user is currently on the last page, keep auto mode
|
|
142
|
-
const totalPages = Math.ceil(messages.length / messagesPerPage);
|
|
143
|
-
if (manualPage >= totalPages) {
|
|
144
|
-
setManualPage(null);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}, [messages.length, messagesPerPage, manualPage]);
|
|
148
|
-
|
|
149
|
-
// Pagination functionality
|
|
150
|
-
const goToPage = (page: number | null) => {
|
|
151
|
-
setManualPage(page);
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const goToPrevPage = () => {
|
|
155
|
-
const currentPage = manualPage ?? displayInfo.currentPage;
|
|
156
|
-
setManualPage(Math.max(1, currentPage - 1));
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const goToNextPage = () => {
|
|
160
|
-
const currentPage = manualPage ?? displayInfo.currentPage;
|
|
161
|
-
setManualPage(Math.min(displayInfo.totalPages, currentPage + 1));
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
const goToFirstPage = () => {
|
|
165
|
-
setManualPage(1);
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const goToLastPage = () => {
|
|
169
|
-
setManualPage(null); // Return to auto mode (last page)
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
// Integrate keyboard shortcut handling
|
|
173
|
-
useInput((input, key) => {
|
|
174
|
-
// Ctrl+U/D shortcuts (Vim/Less style)
|
|
175
|
-
if (key.ctrl) {
|
|
176
|
-
if (input === "u") {
|
|
177
|
-
goToPrevPage();
|
|
178
|
-
} else if (input === "d") {
|
|
179
|
-
goToNextPage();
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Page Up/Down support
|
|
184
|
-
if (key.pageUp) {
|
|
185
|
-
goToPrevPage();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (key.pageDown) {
|
|
189
|
-
goToNextPage();
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
displayInfo,
|
|
195
|
-
manualPage,
|
|
196
|
-
setManualPage,
|
|
197
|
-
goToPage,
|
|
198
|
-
goToPrevPage,
|
|
199
|
-
goToNextPage,
|
|
200
|
-
goToFirstPage,
|
|
201
|
-
goToLastPage,
|
|
202
|
-
};
|
|
203
|
-
};
|