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.
Files changed (140) hide show
  1. package/dist/commands/plugin/disable.d.ts +2 -1
  2. package/dist/commands/plugin/disable.d.ts.map +1 -1
  3. package/dist/commands/plugin/disable.js +3 -2
  4. package/dist/commands/plugin/enable.d.ts +2 -1
  5. package/dist/commands/plugin/enable.d.ts.map +1 -1
  6. package/dist/commands/plugin/enable.js +3 -2
  7. package/dist/commands/plugin/install.d.ts +2 -1
  8. package/dist/commands/plugin/install.d.ts.map +1 -1
  9. package/dist/commands/plugin/list.d.ts.map +1 -1
  10. package/dist/commands/plugin/list.js +15 -3
  11. package/dist/commands/plugin/marketplace.d.ts +3 -0
  12. package/dist/commands/plugin/marketplace.d.ts.map +1 -1
  13. package/dist/commands/plugin/marketplace.js +15 -1
  14. package/dist/commands/plugin/uninstall.d.ts +4 -0
  15. package/dist/commands/plugin/uninstall.d.ts.map +1 -0
  16. package/dist/commands/plugin/uninstall.js +29 -0
  17. package/dist/commands/plugin/update.d.ts +4 -0
  18. package/dist/commands/plugin/update.d.ts.map +1 -0
  19. package/dist/commands/plugin/update.js +15 -0
  20. package/dist/components/ChatInterface.d.ts.map +1 -1
  21. package/dist/components/ChatInterface.js +2 -2
  22. package/dist/components/CommandSelector.d.ts.map +1 -1
  23. package/dist/components/CommandSelector.js +6 -0
  24. package/dist/components/Confirmation.js +1 -1
  25. package/dist/components/DiscoverView.d.ts +3 -0
  26. package/dist/components/DiscoverView.d.ts.map +1 -0
  27. package/dist/components/DiscoverView.js +25 -0
  28. package/dist/components/FileSelector.js +1 -1
  29. package/dist/components/HistorySearch.d.ts +8 -0
  30. package/dist/components/HistorySearch.d.ts.map +1 -0
  31. package/dist/components/HistorySearch.js +67 -0
  32. package/dist/components/InputBox.d.ts +1 -1
  33. package/dist/components/InputBox.d.ts.map +1 -1
  34. package/dist/components/InputBox.js +26 -17
  35. package/dist/components/InstalledView.d.ts +3 -0
  36. package/dist/components/InstalledView.d.ts.map +1 -0
  37. package/dist/components/InstalledView.js +30 -0
  38. package/dist/components/Markdown.d.ts.map +1 -1
  39. package/dist/components/Markdown.js +22 -9
  40. package/dist/components/MarketplaceAddForm.d.ts +3 -0
  41. package/dist/components/MarketplaceAddForm.d.ts.map +1 -0
  42. package/dist/components/MarketplaceAddForm.js +26 -0
  43. package/dist/components/MarketplaceDetail.d.ts +3 -0
  44. package/dist/components/MarketplaceDetail.d.ts.map +1 -0
  45. package/dist/components/MarketplaceDetail.js +38 -0
  46. package/dist/components/MarketplaceList.d.ts +9 -0
  47. package/dist/components/MarketplaceList.d.ts.map +1 -0
  48. package/dist/components/MarketplaceList.js +16 -0
  49. package/dist/components/MarketplaceView.d.ts +3 -0
  50. package/dist/components/MarketplaceView.d.ts.map +1 -0
  51. package/dist/components/MarketplaceView.js +28 -0
  52. package/dist/components/PluginDetail.d.ts +3 -0
  53. package/dist/components/PluginDetail.d.ts.map +1 -0
  54. package/dist/components/PluginDetail.js +63 -0
  55. package/dist/components/PluginList.d.ts +14 -0
  56. package/dist/components/PluginList.d.ts.map +1 -0
  57. package/dist/components/PluginList.js +12 -0
  58. package/dist/components/PluginManagerShell.d.ts +5 -0
  59. package/dist/components/PluginManagerShell.d.ts.map +1 -0
  60. package/dist/components/PluginManagerShell.js +89 -0
  61. package/dist/components/PluginManagerTypes.d.ts +33 -0
  62. package/dist/components/PluginManagerTypes.d.ts.map +1 -0
  63. package/dist/components/PluginManagerTypes.js +1 -0
  64. package/dist/components/RewindCommand.d.ts +9 -0
  65. package/dist/components/RewindCommand.d.ts.map +1 -0
  66. package/dist/components/RewindCommand.js +42 -0
  67. package/dist/components/SessionSelector.d.ts +11 -0
  68. package/dist/components/SessionSelector.d.ts.map +1 -0
  69. package/dist/components/SessionSelector.js +38 -0
  70. package/dist/components/SubagentBlock.d.ts.map +1 -1
  71. package/dist/components/SubagentBlock.js +20 -1
  72. package/dist/components/ToolResultDisplay.js +1 -1
  73. package/dist/contexts/PluginManagerContext.d.ts +4 -0
  74. package/dist/contexts/PluginManagerContext.d.ts.map +1 -0
  75. package/dist/contexts/PluginManagerContext.js +9 -0
  76. package/dist/contexts/useChat.d.ts +2 -0
  77. package/dist/contexts/useChat.d.ts.map +1 -1
  78. package/dist/contexts/useChat.js +21 -0
  79. package/dist/hooks/useInputManager.d.ts +6 -14
  80. package/dist/hooks/useInputManager.d.ts.map +1 -1
  81. package/dist/hooks/useInputManager.js +29 -45
  82. package/dist/hooks/usePluginManager.d.ts +3 -0
  83. package/dist/hooks/usePluginManager.d.ts.map +1 -0
  84. package/dist/hooks/usePluginManager.js +223 -0
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.js +150 -177
  87. package/dist/managers/InputManager.d.ts +12 -21
  88. package/dist/managers/InputManager.d.ts.map +1 -1
  89. package/dist/managers/InputManager.js +77 -108
  90. package/dist/plugin-manager-cli.d.ts +6 -0
  91. package/dist/plugin-manager-cli.d.ts.map +1 -0
  92. package/dist/plugin-manager-cli.js +12 -0
  93. package/dist/session-selector-cli.d.ts +2 -0
  94. package/dist/session-selector-cli.d.ts.map +1 -0
  95. package/dist/session-selector-cli.js +25 -0
  96. package/package.json +7 -3
  97. package/src/commands/plugin/disable.ts +7 -3
  98. package/src/commands/plugin/enable.ts +7 -3
  99. package/src/commands/plugin/install.ts +2 -1
  100. package/src/commands/plugin/list.ts +21 -3
  101. package/src/commands/plugin/marketplace.ts +17 -1
  102. package/src/commands/plugin/uninstall.ts +39 -0
  103. package/src/commands/plugin/update.ts +19 -0
  104. package/src/components/ChatInterface.tsx +2 -1
  105. package/src/components/CommandSelector.tsx +7 -0
  106. package/src/components/Confirmation.tsx +1 -1
  107. package/src/components/DiscoverView.tsx +31 -0
  108. package/src/components/FileSelector.tsx +1 -1
  109. package/src/components/HistorySearch.tsx +148 -0
  110. package/src/components/InputBox.tsx +43 -28
  111. package/src/components/InstalledView.tsx +61 -0
  112. package/src/components/Markdown.tsx +37 -26
  113. package/src/components/MarketplaceAddForm.tsx +39 -0
  114. package/src/components/MarketplaceDetail.tsx +79 -0
  115. package/src/components/MarketplaceList.tsx +52 -0
  116. package/src/components/MarketplaceView.tsx +43 -0
  117. package/src/components/PluginDetail.tsx +147 -0
  118. package/src/components/PluginList.tsx +51 -0
  119. package/src/components/PluginManagerShell.tsx +189 -0
  120. package/src/components/PluginManagerTypes.ts +47 -0
  121. package/src/components/RewindCommand.tsx +114 -0
  122. package/src/components/SessionSelector.tsx +127 -0
  123. package/src/components/SubagentBlock.tsx +29 -1
  124. package/src/components/ToolResultDisplay.tsx +2 -2
  125. package/src/contexts/PluginManagerContext.ts +15 -0
  126. package/src/contexts/useChat.tsx +26 -0
  127. package/src/hooks/useInputManager.ts +29 -61
  128. package/src/hooks/usePluginManager.ts +296 -0
  129. package/src/index.ts +241 -280
  130. package/src/managers/InputManager.ts +93 -149
  131. package/src/plugin-manager-cli.tsx +13 -0
  132. package/src/session-selector-cli.tsx +37 -0
  133. package/dist/components/BashHistorySelector.d.ts +0 -11
  134. package/dist/components/BashHistorySelector.d.ts.map +0 -1
  135. package/dist/components/BashHistorySelector.js +0 -93
  136. package/dist/hooks/usePagination.d.ts +0 -20
  137. package/dist/hooks/usePagination.d.ts.map +0 -1
  138. package/dist/hooks/usePagination.js +0 -168
  139. package/src/components/BashHistorySelector.tsx +0 -181
  140. 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
- };