claudeup 1.7.0 → 3.3.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 (302) hide show
  1. package/bin/claudeup.js +20 -2
  2. package/package.json +10 -19
  3. package/src/data/cli-tools.js +123 -0
  4. package/src/data/cli-tools.ts +140 -0
  5. package/{dist → src}/data/marketplaces.js +23 -24
  6. package/src/data/marketplaces.ts +95 -0
  7. package/src/data/mcp-servers.js +509 -0
  8. package/src/data/mcp-servers.ts +526 -0
  9. package/src/data/statuslines.js +159 -0
  10. package/src/data/statuslines.ts +188 -0
  11. package/src/index.js +4 -0
  12. package/src/index.ts +5 -0
  13. package/{dist → src}/main.js +46 -47
  14. package/src/main.tsx +145 -0
  15. package/src/opentui.d.ts +191 -0
  16. package/src/prerunner/index.js +87 -0
  17. package/src/prerunner/index.ts +124 -0
  18. package/{dist → src}/services/claude-runner.js +9 -10
  19. package/src/services/claude-runner.ts +31 -0
  20. package/{dist → src}/services/claude-settings.js +72 -33
  21. package/src/services/claude-settings.ts +934 -0
  22. package/src/services/local-marketplace.js +339 -0
  23. package/src/services/local-marketplace.ts +489 -0
  24. package/{dist → src}/services/mcp-registry.js +13 -14
  25. package/src/services/mcp-registry.ts +105 -0
  26. package/{dist → src}/services/plugin-manager.js +61 -13
  27. package/src/services/plugin-manager.ts +693 -0
  28. package/{dist → src}/services/plugin-mcp-config.js +19 -18
  29. package/src/services/plugin-mcp-config.ts +242 -0
  30. package/{dist → src}/services/update-cache.js +7 -8
  31. package/src/services/update-cache.ts +78 -0
  32. package/{dist → src}/services/version-check.js +15 -14
  33. package/src/services/version-check.ts +122 -0
  34. package/src/types/index.js +1 -0
  35. package/src/types/index.ts +141 -0
  36. package/src/ui/App.js +213 -0
  37. package/src/ui/App.tsx +359 -0
  38. package/src/ui/components/CategoryHeader.js +9 -0
  39. package/src/ui/components/CategoryHeader.tsx +41 -0
  40. package/{dist → src}/ui/components/ScrollableList.js +19 -6
  41. package/src/ui/components/ScrollableList.tsx +98 -0
  42. package/src/ui/components/SearchInput.js +19 -0
  43. package/src/ui/components/SearchInput.tsx +56 -0
  44. package/src/ui/components/StyledText.js +39 -0
  45. package/src/ui/components/StyledText.tsx +70 -0
  46. package/src/ui/components/TabBar.js +38 -0
  47. package/src/ui/components/TabBar.tsx +88 -0
  48. package/src/ui/components/layout/Panel.js +6 -0
  49. package/src/ui/components/layout/Panel.tsx +62 -0
  50. package/src/ui/components/layout/ProgressBar.js +14 -0
  51. package/src/ui/components/layout/ProgressBar.tsx +47 -0
  52. package/src/ui/components/layout/ScopeTabs.js +6 -0
  53. package/src/ui/components/layout/ScopeTabs.tsx +53 -0
  54. package/src/ui/components/layout/ScreenLayout.js +21 -0
  55. package/src/ui/components/layout/ScreenLayout.tsx +147 -0
  56. package/src/ui/components/layout/index.js +4 -0
  57. package/src/ui/components/layout/index.ts +4 -0
  58. package/src/ui/components/modals/ConfirmModal.js +14 -0
  59. package/src/ui/components/modals/ConfirmModal.tsx +59 -0
  60. package/src/ui/components/modals/InputModal.js +16 -0
  61. package/src/ui/components/modals/InputModal.tsx +68 -0
  62. package/src/ui/components/modals/LoadingModal.js +14 -0
  63. package/src/ui/components/modals/LoadingModal.tsx +40 -0
  64. package/src/ui/components/modals/MessageModal.js +16 -0
  65. package/src/ui/components/modals/MessageModal.tsx +64 -0
  66. package/src/ui/components/modals/ModalContainer.js +56 -0
  67. package/src/ui/components/modals/ModalContainer.tsx +104 -0
  68. package/src/ui/components/modals/SelectModal.js +26 -0
  69. package/src/ui/components/modals/SelectModal.tsx +82 -0
  70. package/src/ui/components/modals/index.js +6 -0
  71. package/src/ui/components/modals/index.ts +6 -0
  72. package/src/ui/hooks/index.js +3 -0
  73. package/src/ui/hooks/index.ts +3 -0
  74. package/{dist → src}/ui/hooks/useAsyncData.js +21 -22
  75. package/src/ui/hooks/useAsyncData.ts +127 -0
  76. package/src/ui/hooks/useKeyboard.js +13 -0
  77. package/src/ui/hooks/useKeyboard.ts +26 -0
  78. package/src/ui/hooks/useKeyboardHandler.js +39 -0
  79. package/src/ui/hooks/useKeyboardHandler.ts +63 -0
  80. package/{dist → src}/ui/screens/CliToolsScreen.js +60 -54
  81. package/src/ui/screens/CliToolsScreen.tsx +468 -0
  82. package/src/ui/screens/EnvVarsScreen.js +154 -0
  83. package/src/ui/screens/EnvVarsScreen.tsx +269 -0
  84. package/{dist → src}/ui/screens/McpRegistryScreen.js +56 -55
  85. package/src/ui/screens/McpRegistryScreen.tsx +331 -0
  86. package/{dist → src}/ui/screens/McpScreen.js +46 -47
  87. package/src/ui/screens/McpScreen.tsx +392 -0
  88. package/src/ui/screens/ModelSelectorScreen.js +292 -0
  89. package/src/ui/screens/ModelSelectorScreen.tsx +441 -0
  90. package/{dist → src}/ui/screens/PluginsScreen.js +305 -293
  91. package/src/ui/screens/PluginsScreen.tsx +1231 -0
  92. package/src/ui/screens/StatusLineScreen.js +200 -0
  93. package/src/ui/screens/StatusLineScreen.tsx +411 -0
  94. package/src/ui/screens/index.js +7 -0
  95. package/src/ui/screens/index.ts +7 -0
  96. package/src/ui/state/AnimationContext.js +34 -0
  97. package/src/ui/state/AnimationContext.tsx +76 -0
  98. package/{dist → src}/ui/state/AppContext.js +31 -32
  99. package/src/ui/state/AppContext.tsx +235 -0
  100. package/{dist → src}/ui/state/DimensionsContext.js +16 -17
  101. package/src/ui/state/DimensionsContext.tsx +144 -0
  102. package/{dist → src}/ui/state/reducer.js +89 -90
  103. package/src/ui/state/reducer.ts +467 -0
  104. package/src/ui/state/types.js +1 -0
  105. package/src/ui/state/types.ts +273 -0
  106. package/{dist → src}/utils/command-utils.js +3 -4
  107. package/src/utils/command-utils.ts +20 -0
  108. package/{dist → src}/utils/fuzzy-search.js +2 -3
  109. package/src/utils/fuzzy-search.ts +138 -0
  110. package/{dist → src}/utils/string-utils.js +6 -6
  111. package/src/utils/string-utils.ts +88 -0
  112. package/dist/data/cli-tools.d.ts +0 -13
  113. package/dist/data/cli-tools.d.ts.map +0 -1
  114. package/dist/data/cli-tools.js +0 -124
  115. package/dist/data/cli-tools.js.map +0 -1
  116. package/dist/data/marketplaces.d.ts +0 -6
  117. package/dist/data/marketplaces.d.ts.map +0 -1
  118. package/dist/data/marketplaces.js.map +0 -1
  119. package/dist/data/mcp-servers.d.ts +0 -8
  120. package/dist/data/mcp-servers.d.ts.map +0 -1
  121. package/dist/data/mcp-servers.js +0 -503
  122. package/dist/data/mcp-servers.js.map +0 -1
  123. package/dist/data/statuslines.d.ts +0 -10
  124. package/dist/data/statuslines.d.ts.map +0 -1
  125. package/dist/data/statuslines.js +0 -160
  126. package/dist/data/statuslines.js.map +0 -1
  127. package/dist/index.d.ts +0 -3
  128. package/dist/index.d.ts.map +0 -1
  129. package/dist/index.js +0 -90
  130. package/dist/index.js.map +0 -1
  131. package/dist/main.d.ts +0 -3
  132. package/dist/main.d.ts.map +0 -1
  133. package/dist/main.js.map +0 -1
  134. package/dist/prerunner/index.d.ts +0 -7
  135. package/dist/prerunner/index.d.ts.map +0 -1
  136. package/dist/prerunner/index.js +0 -64
  137. package/dist/prerunner/index.js.map +0 -1
  138. package/dist/services/claude-runner.d.ts +0 -7
  139. package/dist/services/claude-runner.d.ts.map +0 -1
  140. package/dist/services/claude-runner.js.map +0 -1
  141. package/dist/services/claude-settings.d.ts +0 -73
  142. package/dist/services/claude-settings.d.ts.map +0 -1
  143. package/dist/services/claude-settings.js.map +0 -1
  144. package/dist/services/local-marketplace.d.ts +0 -111
  145. package/dist/services/local-marketplace.d.ts.map +0 -1
  146. package/dist/services/local-marketplace.js +0 -599
  147. package/dist/services/local-marketplace.js.map +0 -1
  148. package/dist/services/mcp-registry.d.ts +0 -10
  149. package/dist/services/mcp-registry.d.ts.map +0 -1
  150. package/dist/services/mcp-registry.js.map +0 -1
  151. package/dist/services/plugin-manager.d.ts +0 -65
  152. package/dist/services/plugin-manager.d.ts.map +0 -1
  153. package/dist/services/plugin-manager.js.map +0 -1
  154. package/dist/services/plugin-mcp-config.d.ts +0 -52
  155. package/dist/services/plugin-mcp-config.d.ts.map +0 -1
  156. package/dist/services/plugin-mcp-config.js.map +0 -1
  157. package/dist/services/update-cache.d.ts +0 -16
  158. package/dist/services/update-cache.d.ts.map +0 -1
  159. package/dist/services/update-cache.js.map +0 -1
  160. package/dist/services/version-check.d.ts +0 -20
  161. package/dist/services/version-check.d.ts.map +0 -1
  162. package/dist/services/version-check.js.map +0 -1
  163. package/dist/types/index.d.ts +0 -105
  164. package/dist/types/index.d.ts.map +0 -1
  165. package/dist/types/index.js +0 -2
  166. package/dist/types/index.js.map +0 -1
  167. package/dist/ui/InkApp.d.ts +0 -5
  168. package/dist/ui/InkApp.d.ts.map +0 -1
  169. package/dist/ui/InkApp.js +0 -188
  170. package/dist/ui/InkApp.js.map +0 -1
  171. package/dist/ui/components/CategoryHeader.d.ts +0 -16
  172. package/dist/ui/components/CategoryHeader.d.ts.map +0 -1
  173. package/dist/ui/components/CategoryHeader.js +0 -11
  174. package/dist/ui/components/CategoryHeader.js.map +0 -1
  175. package/dist/ui/components/ScrollableList.d.ts +0 -16
  176. package/dist/ui/components/ScrollableList.d.ts.map +0 -1
  177. package/dist/ui/components/ScrollableList.js.map +0 -1
  178. package/dist/ui/components/SearchInput.d.ts +0 -18
  179. package/dist/ui/components/SearchInput.d.ts.map +0 -1
  180. package/dist/ui/components/SearchInput.js +0 -30
  181. package/dist/ui/components/SearchInput.js.map +0 -1
  182. package/dist/ui/components/TabBar.d.ts +0 -8
  183. package/dist/ui/components/TabBar.d.ts.map +0 -1
  184. package/dist/ui/components/TabBar.js +0 -18
  185. package/dist/ui/components/TabBar.js.map +0 -1
  186. package/dist/ui/components/layout/Footer.d.ts +0 -14
  187. package/dist/ui/components/layout/Footer.d.ts.map +0 -1
  188. package/dist/ui/components/layout/Footer.js +0 -23
  189. package/dist/ui/components/layout/Footer.js.map +0 -1
  190. package/dist/ui/components/layout/Header.d.ts +0 -4
  191. package/dist/ui/components/layout/Header.d.ts.map +0 -1
  192. package/dist/ui/components/layout/Header.js +0 -25
  193. package/dist/ui/components/layout/Header.js.map +0 -1
  194. package/dist/ui/components/layout/Panel.d.ts +0 -22
  195. package/dist/ui/components/layout/Panel.d.ts.map +0 -1
  196. package/dist/ui/components/layout/Panel.js +0 -8
  197. package/dist/ui/components/layout/Panel.js.map +0 -1
  198. package/dist/ui/components/layout/ProgressBar.d.ts +0 -12
  199. package/dist/ui/components/layout/ProgressBar.d.ts.map +0 -1
  200. package/dist/ui/components/layout/ProgressBar.js +0 -16
  201. package/dist/ui/components/layout/ProgressBar.js.map +0 -1
  202. package/dist/ui/components/layout/ScopeTabs.d.ts +0 -12
  203. package/dist/ui/components/layout/ScopeTabs.d.ts.map +0 -1
  204. package/dist/ui/components/layout/ScopeTabs.js +0 -8
  205. package/dist/ui/components/layout/ScopeTabs.js.map +0 -1
  206. package/dist/ui/components/layout/ScreenLayout.d.ts +0 -30
  207. package/dist/ui/components/layout/ScreenLayout.d.ts.map +0 -1
  208. package/dist/ui/components/layout/ScreenLayout.js +0 -23
  209. package/dist/ui/components/layout/ScreenLayout.js.map +0 -1
  210. package/dist/ui/components/layout/index.d.ts +0 -7
  211. package/dist/ui/components/layout/index.d.ts.map +0 -1
  212. package/dist/ui/components/layout/index.js +0 -7
  213. package/dist/ui/components/layout/index.js.map +0 -1
  214. package/dist/ui/components/modals/ConfirmModal.d.ts +0 -14
  215. package/dist/ui/components/modals/ConfirmModal.d.ts.map +0 -1
  216. package/dist/ui/components/modals/ConfirmModal.js +0 -15
  217. package/dist/ui/components/modals/ConfirmModal.js.map +0 -1
  218. package/dist/ui/components/modals/InputModal.d.ts +0 -16
  219. package/dist/ui/components/modals/InputModal.d.ts.map +0 -1
  220. package/dist/ui/components/modals/InputModal.js +0 -23
  221. package/dist/ui/components/modals/InputModal.js.map +0 -1
  222. package/dist/ui/components/modals/LoadingModal.d.ts +0 -8
  223. package/dist/ui/components/modals/LoadingModal.d.ts.map +0 -1
  224. package/dist/ui/components/modals/LoadingModal.js +0 -8
  225. package/dist/ui/components/modals/LoadingModal.js.map +0 -1
  226. package/dist/ui/components/modals/MessageModal.d.ts +0 -14
  227. package/dist/ui/components/modals/MessageModal.d.ts.map +0 -1
  228. package/dist/ui/components/modals/MessageModal.js +0 -17
  229. package/dist/ui/components/modals/MessageModal.js.map +0 -1
  230. package/dist/ui/components/modals/ModalContainer.d.ts +0 -7
  231. package/dist/ui/components/modals/ModalContainer.d.ts.map +0 -1
  232. package/dist/ui/components/modals/ModalContainer.js +0 -38
  233. package/dist/ui/components/modals/ModalContainer.js.map +0 -1
  234. package/dist/ui/components/modals/SelectModal.d.ts +0 -17
  235. package/dist/ui/components/modals/SelectModal.d.ts.map +0 -1
  236. package/dist/ui/components/modals/SelectModal.js +0 -33
  237. package/dist/ui/components/modals/SelectModal.js.map +0 -1
  238. package/dist/ui/components/modals/index.d.ts +0 -7
  239. package/dist/ui/components/modals/index.d.ts.map +0 -1
  240. package/dist/ui/components/modals/index.js +0 -7
  241. package/dist/ui/components/modals/index.js.map +0 -1
  242. package/dist/ui/hooks/index.d.ts +0 -3
  243. package/dist/ui/hooks/index.d.ts.map +0 -1
  244. package/dist/ui/hooks/index.js +0 -3
  245. package/dist/ui/hooks/index.js.map +0 -1
  246. package/dist/ui/hooks/useAsyncData.d.ts +0 -40
  247. package/dist/ui/hooks/useAsyncData.d.ts.map +0 -1
  248. package/dist/ui/hooks/useAsyncData.js.map +0 -1
  249. package/dist/ui/hooks/useKeyboardNavigation.d.ts +0 -27
  250. package/dist/ui/hooks/useKeyboardNavigation.d.ts.map +0 -1
  251. package/dist/ui/hooks/useKeyboardNavigation.js +0 -82
  252. package/dist/ui/hooks/useKeyboardNavigation.js.map +0 -1
  253. package/dist/ui/screens/CliToolsScreen.d.ts +0 -4
  254. package/dist/ui/screens/CliToolsScreen.d.ts.map +0 -1
  255. package/dist/ui/screens/CliToolsScreen.js.map +0 -1
  256. package/dist/ui/screens/EnvVarsScreen.d.ts +0 -4
  257. package/dist/ui/screens/EnvVarsScreen.d.ts.map +0 -1
  258. package/dist/ui/screens/EnvVarsScreen.js +0 -145
  259. package/dist/ui/screens/EnvVarsScreen.js.map +0 -1
  260. package/dist/ui/screens/McpRegistryScreen.d.ts +0 -4
  261. package/dist/ui/screens/McpRegistryScreen.d.ts.map +0 -1
  262. package/dist/ui/screens/McpRegistryScreen.js.map +0 -1
  263. package/dist/ui/screens/McpScreen.d.ts +0 -4
  264. package/dist/ui/screens/McpScreen.d.ts.map +0 -1
  265. package/dist/ui/screens/McpScreen.js.map +0 -1
  266. package/dist/ui/screens/ModelSelectorScreen.d.ts +0 -4
  267. package/dist/ui/screens/ModelSelectorScreen.d.ts.map +0 -1
  268. package/dist/ui/screens/ModelSelectorScreen.js +0 -143
  269. package/dist/ui/screens/ModelSelectorScreen.js.map +0 -1
  270. package/dist/ui/screens/PluginsScreen.d.ts +0 -4
  271. package/dist/ui/screens/PluginsScreen.d.ts.map +0 -1
  272. package/dist/ui/screens/PluginsScreen.js.map +0 -1
  273. package/dist/ui/screens/StatusLineScreen.d.ts +0 -4
  274. package/dist/ui/screens/StatusLineScreen.d.ts.map +0 -1
  275. package/dist/ui/screens/StatusLineScreen.js +0 -197
  276. package/dist/ui/screens/StatusLineScreen.js.map +0 -1
  277. package/dist/ui/screens/index.d.ts +0 -8
  278. package/dist/ui/screens/index.d.ts.map +0 -1
  279. package/dist/ui/screens/index.js +0 -8
  280. package/dist/ui/screens/index.js.map +0 -1
  281. package/dist/ui/state/AppContext.d.ts +0 -40
  282. package/dist/ui/state/AppContext.d.ts.map +0 -1
  283. package/dist/ui/state/AppContext.js.map +0 -1
  284. package/dist/ui/state/DimensionsContext.d.ts +0 -27
  285. package/dist/ui/state/DimensionsContext.d.ts.map +0 -1
  286. package/dist/ui/state/DimensionsContext.js.map +0 -1
  287. package/dist/ui/state/reducer.d.ts +0 -4
  288. package/dist/ui/state/reducer.d.ts.map +0 -1
  289. package/dist/ui/state/reducer.js.map +0 -1
  290. package/dist/ui/state/types.d.ts +0 -266
  291. package/dist/ui/state/types.d.ts.map +0 -1
  292. package/dist/ui/state/types.js +0 -2
  293. package/dist/ui/state/types.js.map +0 -1
  294. package/dist/utils/command-utils.d.ts +0 -8
  295. package/dist/utils/command-utils.d.ts.map +0 -1
  296. package/dist/utils/command-utils.js.map +0 -1
  297. package/dist/utils/fuzzy-search.d.ts +0 -33
  298. package/dist/utils/fuzzy-search.d.ts.map +0 -1
  299. package/dist/utils/fuzzy-search.js.map +0 -1
  300. package/dist/utils/string-utils.d.ts +0 -24
  301. package/dist/utils/string-utils.d.ts.map +0 -1
  302. package/dist/utils/string-utils.js.map +0 -1
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ export function CategoryHeader({ title, status, statusColor = "green", expanded = true, count, }) {
3
+ const expandIcon = expanded ? "▼" : "▶";
4
+ const countBadge = count !== undefined ? ` (${count})` : "";
5
+ const statusText = status ? ` ${status}` : "";
6
+ // Simple format without dynamic line calculation
7
+ return (_jsxs("text", { children: [_jsx("span", { fg: "#666666", children: expandIcon }), _jsx("span", { fg: "white", children: _jsxs("strong", { children: [" ", title] }) }), _jsx("span", { fg: "#666666", children: countBadge }), _jsx("span", { fg: "#666666", children: " \u2500\u2500\u2500\u2500" }), _jsx("span", { fg: statusColor, children: statusText })] }));
8
+ }
9
+ export default CategoryHeader;
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+
3
+ interface CategoryHeaderProps {
4
+ /** Category title */
5
+ title: string;
6
+ /** Status badge (e.g., "✓ Configured", "3 plugins") */
7
+ status?: string;
8
+ /** Status badge color */
9
+ statusColor?: string;
10
+ /** Whether category is expanded */
11
+ expanded?: boolean;
12
+ /** Number of items in category */
13
+ count?: number;
14
+ }
15
+
16
+ export function CategoryHeader({
17
+ title,
18
+ status,
19
+ statusColor = "green",
20
+ expanded = true,
21
+ count,
22
+ }: CategoryHeaderProps) {
23
+ const expandIcon = expanded ? "▼" : "▶";
24
+ const countBadge = count !== undefined ? ` (${count})` : "";
25
+ const statusText = status ? ` ${status}` : "";
26
+
27
+ // Simple format without dynamic line calculation
28
+ return (
29
+ <text>
30
+ <span fg="#666666">{expandIcon}</span>
31
+ <span fg="white">
32
+ <strong> {title}</strong>
33
+ </span>
34
+ <span fg="#666666">{countBadge}</span>
35
+ <span fg="#666666"> ────</span>
36
+ <span fg={statusColor}>{statusText}</span>
37
+ </text>
38
+ );
39
+ }
40
+
41
+ export default CategoryHeader;
@@ -1,12 +1,26 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
2
2
  import { useState, useEffect, useMemo } from 'react';
3
- import { Box, Text } from 'ink';
4
- export function ScrollableList({ items, selectedIndex, renderItem, maxHeight, showScrollIndicators = true, }) {
3
+ import { useKeyboardHandler } from '../hooks/useKeyboardHandler';
4
+ export function ScrollableList({ items, selectedIndex, renderItem, maxHeight, showScrollIndicators = true, onSelect, focused = false, }) {
5
5
  const [scrollOffset, setScrollOffset] = useState(0);
6
+ // Handle keyboard navigation
7
+ useKeyboardHandler((input, key) => {
8
+ if (!focused || !onSelect)
9
+ return;
10
+ if (key.upArrow || input === 'k') {
11
+ const newIndex = Math.max(0, selectedIndex - 1);
12
+ onSelect(newIndex);
13
+ }
14
+ else if (key.downArrow || input === 'j') {
15
+ const newIndex = Math.min(items.length - 1, selectedIndex + 1);
16
+ onSelect(newIndex);
17
+ }
18
+ });
6
19
  // Account for scroll indicators in available space
7
20
  const hasItemsAbove = scrollOffset > 0;
8
21
  const hasItemsBelow = scrollOffset + maxHeight < items.length;
9
- const indicatorLines = (showScrollIndicators && hasItemsAbove ? 1 : 0) + (showScrollIndicators && hasItemsBelow ? 1 : 0);
22
+ const indicatorLines = (showScrollIndicators && hasItemsAbove ? 1 : 0) +
23
+ (showScrollIndicators && hasItemsBelow ? 1 : 0);
10
24
  const effectiveMaxHeight = Math.max(1, maxHeight - indicatorLines);
11
25
  // Adjust scroll offset to keep selected item visible
12
26
  useEffect(() => {
@@ -29,7 +43,6 @@ export function ScrollableList({ items, selectedIndex, renderItem, maxHeight, sh
29
43
  }));
30
44
  }, [items, scrollOffset, effectiveMaxHeight]);
31
45
  const itemsBelow = items.length - scrollOffset - effectiveMaxHeight;
32
- return (_jsxs(Box, { flexDirection: "column", children: [showScrollIndicators && hasItemsAbove && (_jsxs(Text, { color: "cyan", children: ["\u2191 ", scrollOffset, " more"] })), visibleItems.map(({ item, originalIndex }) => (_jsx(Box, { width: "100%", overflow: "hidden", children: renderItem(item, originalIndex, originalIndex === selectedIndex) }, originalIndex))), showScrollIndicators && hasItemsBelow && (_jsxs(Text, { color: "cyan", children: ["\u2193 ", itemsBelow, " more"] }))] }));
46
+ return (_jsxs("box", { flexDirection: "column", children: [showScrollIndicators && hasItemsAbove && (_jsxs("text", { fg: "cyan", children: ["\u2191 ", scrollOffset, " more"] })), visibleItems.map(({ item, originalIndex }) => (_jsx("box", { width: "100%", overflow: "hidden", children: renderItem(item, originalIndex, originalIndex === selectedIndex) }, originalIndex))), showScrollIndicators && hasItemsBelow && (_jsxs("text", { fg: "cyan", children: ["\u2193 ", itemsBelow, " more"] }))] }));
33
47
  }
34
48
  export default ScrollableList;
35
- //# sourceMappingURL=ScrollableList.js.map
@@ -0,0 +1,98 @@
1
+ import React, { useState, useEffect, useMemo } from 'react';
2
+ import { useKeyboardHandler } from '../hooks/useKeyboardHandler';
3
+
4
+ interface ScrollableListProps<T> {
5
+ /** Array of items to display */
6
+ items: T[];
7
+ /** Currently selected index */
8
+ selectedIndex: number;
9
+ /** Render function for each item */
10
+ renderItem: (item: T, index: number, isSelected: boolean) => React.ReactNode;
11
+ /** Maximum visible height (number of lines) - REQUIRED for proper rendering */
12
+ maxHeight: number;
13
+ /** Show scroll indicators */
14
+ showScrollIndicators?: boolean;
15
+ /** Called when selection changes (arrow keys) */
16
+ onSelect?: (index: number) => void;
17
+ /** Whether this list should receive keyboard input */
18
+ focused?: boolean;
19
+ }
20
+
21
+ export function ScrollableList<T>({
22
+ items,
23
+ selectedIndex,
24
+ renderItem,
25
+ maxHeight,
26
+ showScrollIndicators = true,
27
+ onSelect,
28
+ focused = false,
29
+ }: ScrollableListProps<T>) {
30
+ const [scrollOffset, setScrollOffset] = useState(0);
31
+
32
+ // Handle keyboard navigation
33
+ useKeyboardHandler((input, key) => {
34
+ if (!focused || !onSelect) return;
35
+
36
+ if (key.upArrow || input === 'k') {
37
+ const newIndex = Math.max(0, selectedIndex - 1);
38
+ onSelect(newIndex);
39
+ } else if (key.downArrow || input === 'j') {
40
+ const newIndex = Math.min(items.length - 1, selectedIndex + 1);
41
+ onSelect(newIndex);
42
+ }
43
+ });
44
+
45
+ // Account for scroll indicators in available space
46
+ const hasItemsAbove = scrollOffset > 0;
47
+ const hasItemsBelow = scrollOffset + maxHeight < items.length;
48
+ const indicatorLines =
49
+ (showScrollIndicators && hasItemsAbove ? 1 : 0) +
50
+ (showScrollIndicators && hasItemsBelow ? 1 : 0);
51
+ const effectiveMaxHeight = Math.max(1, maxHeight - indicatorLines);
52
+
53
+ // Adjust scroll offset to keep selected item visible
54
+ useEffect(() => {
55
+ if (selectedIndex < scrollOffset) {
56
+ // Selected is above viewport - scroll up
57
+ setScrollOffset(selectedIndex);
58
+ } else if (selectedIndex >= scrollOffset + effectiveMaxHeight) {
59
+ // Selected is below viewport - scroll down
60
+ setScrollOffset(selectedIndex - effectiveMaxHeight + 1);
61
+ }
62
+ }, [selectedIndex, effectiveMaxHeight, scrollOffset]);
63
+
64
+ // Calculate visible items - strictly limited to effectiveMaxHeight
65
+ const visibleItems = useMemo(() => {
66
+ const start = scrollOffset;
67
+ const end = Math.min(scrollOffset + effectiveMaxHeight, items.length);
68
+ return items.slice(start, end).map((item, idx) => ({
69
+ item,
70
+ originalIndex: start + idx,
71
+ }));
72
+ }, [items, scrollOffset, effectiveMaxHeight]);
73
+
74
+ const itemsBelow = items.length - scrollOffset - effectiveMaxHeight;
75
+
76
+ return (
77
+ <box flexDirection="column">
78
+ {/* Scroll up indicator */}
79
+ {showScrollIndicators && hasItemsAbove && (
80
+ <text fg="cyan">↑ {scrollOffset} more</text>
81
+ )}
82
+
83
+ {/* Visible items - strictly limited */}
84
+ {visibleItems.map(({ item, originalIndex }) => (
85
+ <box key={originalIndex} width="100%" overflow="hidden">
86
+ {renderItem(item, originalIndex, originalIndex === selectedIndex)}
87
+ </box>
88
+ ))}
89
+
90
+ {/* Scroll down indicator */}
91
+ {showScrollIndicators && hasItemsBelow && (
92
+ <text fg="cyan">↓ {itemsBelow} more</text>
93
+ )}
94
+ </box>
95
+ );
96
+ }
97
+
98
+ export default ScrollableList;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { useKeyboardHandler } from '../hooks/useKeyboardHandler.js';
3
+ export function SearchInput({ value, onChange, placeholder = 'Search...', isActive, onExit, onSubmit, }) {
4
+ // Handle keyboard shortcuts when active
5
+ useKeyboardHandler((_input, key) => {
6
+ if (!isActive)
7
+ return;
8
+ if (key.escape) {
9
+ onExit?.();
10
+ return;
11
+ }
12
+ if (key.return) {
13
+ onSubmit?.();
14
+ return;
15
+ }
16
+ });
17
+ return (_jsxs("box", { flexDirection: "row", children: [_jsx("text", { fg: "cyan", children: "\u276F " }), _jsx("input", { value: value, onChange: onChange, placeholder: placeholder, focused: isActive, width: 40 })] }));
18
+ }
19
+ export default SearchInput;
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import { useKeyboardHandler } from '../hooks/useKeyboardHandler.js';
3
+
4
+ interface SearchInputProps {
5
+ /** Current search value */
6
+ value: string;
7
+ /** Called when value changes */
8
+ onChange: (value: string) => void;
9
+ /** Placeholder text when empty */
10
+ placeholder?: string;
11
+ /** Whether the input is focused/active */
12
+ isActive: boolean;
13
+ /** Called when user presses escape to exit search */
14
+ onExit?: () => void;
15
+ /** Called when user presses enter */
16
+ onSubmit?: () => void;
17
+ }
18
+
19
+ export function SearchInput({
20
+ value,
21
+ onChange,
22
+ placeholder = 'Search...',
23
+ isActive,
24
+ onExit,
25
+ onSubmit,
26
+ }: SearchInputProps) {
27
+ // Handle keyboard shortcuts when active
28
+ useKeyboardHandler((_input, key) => {
29
+ if (!isActive) return;
30
+
31
+ if (key.escape) {
32
+ onExit?.();
33
+ return;
34
+ }
35
+
36
+ if (key.return) {
37
+ onSubmit?.();
38
+ return;
39
+ }
40
+ });
41
+
42
+ return (
43
+ <box flexDirection="row">
44
+ <text fg="cyan">❯ </text>
45
+ <input
46
+ value={value}
47
+ onChange={onChange}
48
+ placeholder={placeholder}
49
+ focused={isActive}
50
+ width={40}
51
+ />
52
+ </box>
53
+ );
54
+ }
55
+
56
+ export default SearchInput;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "@opentui/react/jsx-runtime";
2
+ /**
3
+ * StyledText - Utility component to simplify text styling with OpenTUI
4
+ *
5
+ * Converts props to nested tag pattern required by OpenTUI.
6
+ *
7
+ * Example:
8
+ * <StyledText bold color="green">Success</StyledText>
9
+ * becomes: <text fg="green"><strong>Success</strong></text>
10
+ */
11
+ export function StyledText({ children, bold = false, color, bgColor, dim = false, italic = false, underline = false, inverse = false, }) {
12
+ let content = children;
13
+ // Apply text modifiers (innermost to outermost)
14
+ if (bold) {
15
+ content = _jsx("strong", { children: content });
16
+ }
17
+ if (italic) {
18
+ content = _jsx("em", { children: content });
19
+ }
20
+ if (underline) {
21
+ content = _jsx("u", { children: content });
22
+ }
23
+ // Note: dim and inverse use span with colors since OpenTUI doesn't have these as React elements
24
+ if (dim) {
25
+ content = _jsx("span", { fg: "#666666", children: content });
26
+ }
27
+ if (inverse) {
28
+ // Inverse effect approximated with contrasting colors
29
+ content = _jsx("span", { fg: "black", bg: "white", children: content });
30
+ }
31
+ // Apply colors (outer layer)
32
+ const textProps = {};
33
+ if (color)
34
+ textProps.fg = color;
35
+ if (bgColor)
36
+ textProps.bg = bgColor;
37
+ return _jsx("text", { ...textProps, children: content });
38
+ }
39
+ export default StyledText;
@@ -0,0 +1,70 @@
1
+ import React from "react";
2
+
3
+ interface StyledTextProps {
4
+ /** Text content */
5
+ children: React.ReactNode;
6
+ /** Bold text */
7
+ bold?: boolean;
8
+ /** Foreground color */
9
+ color?: string;
10
+ /** Background color */
11
+ bgColor?: string;
12
+ /** Dimmed text */
13
+ dim?: boolean;
14
+ /** Italic text */
15
+ italic?: boolean;
16
+ /** Underlined text */
17
+ underline?: boolean;
18
+ /** Inverse colors */
19
+ inverse?: boolean;
20
+ }
21
+
22
+ /**
23
+ * StyledText - Utility component to simplify text styling with OpenTUI
24
+ *
25
+ * Converts props to nested tag pattern required by OpenTUI.
26
+ *
27
+ * Example:
28
+ * <StyledText bold color="green">Success</StyledText>
29
+ * becomes: <text fg="green"><strong>Success</strong></text>
30
+ */
31
+ export function StyledText({
32
+ children,
33
+ bold = false,
34
+ color,
35
+ bgColor,
36
+ dim = false,
37
+ italic = false,
38
+ underline = false,
39
+ inverse = false,
40
+ }: StyledTextProps) {
41
+ let content: React.ReactNode = children;
42
+
43
+ // Apply text modifiers (innermost to outermost)
44
+ if (bold) {
45
+ content = <strong>{content}</strong>;
46
+ }
47
+ if (italic) {
48
+ content = <em>{content}</em>;
49
+ }
50
+ if (underline) {
51
+ content = <u>{content}</u>;
52
+ }
53
+ // Note: dim and inverse use span with colors since OpenTUI doesn't have these as React elements
54
+ if (dim) {
55
+ content = <span fg="#666666">{content}</span>;
56
+ }
57
+ if (inverse) {
58
+ // Inverse effect approximated with contrasting colors
59
+ content = <span fg="black" bg="white">{content}</span>;
60
+ }
61
+
62
+ // Apply colors (outer layer)
63
+ const textProps: { fg?: string; bg?: string } = {};
64
+ if (color) textProps.fg = color;
65
+ if (bgColor) textProps.bg = bgColor;
66
+
67
+ return <text {...textProps}>{content}</text>;
68
+ }
69
+
70
+ export default StyledText;
@@ -0,0 +1,38 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "@opentui/react/jsx-runtime";
2
+ import { useKeyboardHandler } from '../hooks/useKeyboardHandler';
3
+ const TABS = [
4
+ { key: '1', label: 'Plugins', screen: 'plugins' },
5
+ { key: '2', label: 'MCP', screen: 'mcp' },
6
+ { key: '3', label: 'Status', screen: 'statusline' },
7
+ { key: '4', label: 'Env', screen: 'env-vars' },
8
+ { key: '5', label: 'CLI', screen: 'cli-tools' },
9
+ ];
10
+ export function TabBar({ currentScreen, onTabChange, }) {
11
+ // Handle number key shortcuts (1-5)
12
+ useKeyboardHandler((input, key) => {
13
+ if (!onTabChange)
14
+ return;
15
+ // Number keys 1-5
16
+ const tabIndex = Number.parseInt(input, 10);
17
+ if (tabIndex >= 1 && tabIndex <= TABS.length) {
18
+ const tab = TABS[tabIndex - 1];
19
+ if (tab && tab.screen !== currentScreen) {
20
+ onTabChange(tab.screen);
21
+ }
22
+ }
23
+ // Tab key to cycle through tabs
24
+ if (key.tab) {
25
+ const currentIndex = TABS.findIndex((t) => t.screen === currentScreen);
26
+ const nextIndex = key.shift
27
+ ? (currentIndex - 1 + TABS.length) % TABS.length
28
+ : (currentIndex + 1) % TABS.length;
29
+ onTabChange(TABS[nextIndex].screen);
30
+ }
31
+ });
32
+ return (_jsx("box", { flexDirection: "row", gap: 0, children: TABS.map((tab, index) => {
33
+ const isSelected = tab.screen === currentScreen;
34
+ const isLast = index === TABS.length - 1;
35
+ return (_jsxs("box", { flexDirection: "row", children: [isSelected ? (_jsx("box", { children: _jsx("text", { bg: "#7e57c2", fg: "white", children: _jsxs("strong", { children: [' ', tab.key, ":", tab.label, ' '] }) }) })) : (_jsx("box", { children: _jsxs("text", { fg: "gray", children: [' ', tab.key, ":", tab.label, ' '] }) })), !isLast && (_jsx("text", { fg: "#666666", children: "\u2502" }))] }, tab.key));
36
+ }) }));
37
+ }
38
+ export default TabBar;
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import { useKeyboardHandler } from '../hooks/useKeyboardHandler';
3
+ import type { Screen } from '../state/types.js';
4
+
5
+ interface Tab {
6
+ key: string;
7
+ label: string;
8
+ screen: Screen;
9
+ }
10
+
11
+ const TABS: Tab[] = [
12
+ { key: '1', label: 'Plugins', screen: 'plugins' },
13
+ { key: '2', label: 'MCP', screen: 'mcp' },
14
+ { key: '3', label: 'Status', screen: 'statusline' },
15
+ { key: '4', label: 'Env', screen: 'env-vars' },
16
+ { key: '5', label: 'CLI', screen: 'cli-tools' },
17
+ ];
18
+
19
+ interface TabBarProps {
20
+ currentScreen: Screen;
21
+ onTabChange?: (screen: Screen) => void;
22
+ }
23
+
24
+ export function TabBar({
25
+ currentScreen,
26
+ onTabChange,
27
+ }: TabBarProps) {
28
+ // Handle number key shortcuts (1-5)
29
+ useKeyboardHandler((input, key) => {
30
+ if (!onTabChange) return;
31
+
32
+ // Number keys 1-5
33
+ const tabIndex = Number.parseInt(input, 10);
34
+ if (tabIndex >= 1 && tabIndex <= TABS.length) {
35
+ const tab = TABS[tabIndex - 1];
36
+ if (tab && tab.screen !== currentScreen) {
37
+ onTabChange(tab.screen);
38
+ }
39
+ }
40
+
41
+ // Tab key to cycle through tabs
42
+ if (key.tab) {
43
+ const currentIndex = TABS.findIndex((t) => t.screen === currentScreen);
44
+ const nextIndex = key.shift
45
+ ? (currentIndex - 1 + TABS.length) % TABS.length
46
+ : (currentIndex + 1) % TABS.length;
47
+ onTabChange(TABS[nextIndex].screen);
48
+ }
49
+ });
50
+
51
+ return (
52
+ <box flexDirection="row" gap={0}>
53
+ {TABS.map((tab, index) => {
54
+ const isSelected = tab.screen === currentScreen;
55
+ const isLast = index === TABS.length - 1;
56
+
57
+ return (
58
+ <box key={tab.key} flexDirection="row">
59
+ {/* Tab content */}
60
+ {isSelected ? (
61
+ <box>
62
+ <text bg="#7e57c2" fg="white">
63
+ <strong>
64
+ {' '}
65
+ {tab.key}:{tab.label}{' '}
66
+ </strong>
67
+ </text>
68
+ </box>
69
+ ) : (
70
+ <box>
71
+ <text fg="gray">
72
+ {' '}
73
+ {tab.key}:{tab.label}{' '}
74
+ </text>
75
+ </box>
76
+ )}
77
+ {/* Separator */}
78
+ {!isLast && (
79
+ <text fg="#666666">│</text>
80
+ )}
81
+ </box>
82
+ );
83
+ })}
84
+ </box>
85
+ );
86
+ }
87
+
88
+ export default TabBar;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ export function Panel({ title, children, borderColor = "#7e57c2", titleColor = "#7e57c2", width, height, flexGrow = 1, focused = false, }) {
3
+ const activeColor = focused ? "#7e57c2" : borderColor;
4
+ return (_jsxs("box", { flexDirection: "column", width: width, height: height, flexGrow: flexGrow, border: true, borderStyle: "single", borderColor: activeColor, padding: 1, children: [title && (_jsx("box", { marginBottom: 0, children: _jsx("text", { fg: titleColor, children: _jsx("strong", { children: title }) }) })), _jsx("box", { flexDirection: "column", flexGrow: 1, children: children })] }));
5
+ }
6
+ export default Panel;
@@ -0,0 +1,62 @@
1
+ import React from "react";
2
+
3
+ interface PanelProps {
4
+ /** Panel title */
5
+ title?: string;
6
+ /** Panel content */
7
+ children: React.ReactNode;
8
+ /** Border color */
9
+ borderColor?: string;
10
+ /** Title color */
11
+ titleColor?: string;
12
+ /** Panel width - number or percentage like "50%" */
13
+ width?: number | `${number}%` | "auto";
14
+ /** Panel height - number or percentage like "50%" */
15
+ height?: number | `${number}%` | "auto";
16
+ /** Whether to use flexGrow */
17
+ flexGrow?: number;
18
+ /** Whether panel is focused/active */
19
+ focused?: boolean;
20
+ }
21
+
22
+ export function Panel({
23
+ title,
24
+ children,
25
+ borderColor = "#7e57c2",
26
+ titleColor = "#7e57c2",
27
+ width,
28
+ height,
29
+ flexGrow = 1,
30
+ focused = false,
31
+ }: PanelProps) {
32
+ const activeColor = focused ? "#7e57c2" : borderColor;
33
+
34
+ return (
35
+ <box
36
+ flexDirection="column"
37
+ width={width}
38
+ height={height}
39
+ flexGrow={flexGrow}
40
+ border
41
+ borderStyle="single"
42
+ borderColor={activeColor}
43
+ padding={1}
44
+ >
45
+ {/* Title row */}
46
+ {title && (
47
+ <box marginBottom={0}>
48
+ <text fg={titleColor}>
49
+ <strong>{title}</strong>
50
+ </text>
51
+ </box>
52
+ )}
53
+
54
+ {/* Content */}
55
+ <box flexDirection="column" flexGrow={1}>
56
+ {children}
57
+ </box>
58
+ </box>
59
+ );
60
+ }
61
+
62
+ export default Panel;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ export function ProgressBar({ message, current, total, }) {
3
+ const isDeterminate = current !== undefined && total !== undefined && total > 0;
4
+ if (isDeterminate) {
5
+ const barWidth = 20;
6
+ const filled = Math.round((current / total) * barWidth);
7
+ const empty = barWidth - filled;
8
+ const bar = "█".repeat(filled) + "░".repeat(empty);
9
+ return (_jsxs("box", { children: [_jsx("text", { fg: "cyan", children: "\u27F3" }), _jsxs("text", { children: [" ", message, " "] }), _jsxs("text", { fg: "cyan", children: ["[", bar, "] ", current, "/", total] })] }));
10
+ }
11
+ // Indeterminate progress
12
+ return (_jsxs("box", { children: [_jsx("text", { fg: "cyan", children: "\u27F3" }), _jsxs("text", { children: [" ", message] }), _jsx("text", { fg: "gray", children: " ..." })] }));
13
+ }
14
+ export default ProgressBar;
@@ -0,0 +1,47 @@
1
+ import React from "react";
2
+
3
+ interface ProgressBarProps {
4
+ /** Progress message */
5
+ message: string;
6
+ /** Current progress (if determinate) */
7
+ current?: number;
8
+ /** Total items (if determinate) */
9
+ total?: number;
10
+ }
11
+
12
+ export function ProgressBar({
13
+ message,
14
+ current,
15
+ total,
16
+ }: ProgressBarProps) {
17
+ const isDeterminate =
18
+ current !== undefined && total !== undefined && total > 0;
19
+
20
+ if (isDeterminate) {
21
+ const barWidth = 20;
22
+ const filled = Math.round((current / total) * barWidth);
23
+ const empty = barWidth - filled;
24
+ const bar = "█".repeat(filled) + "░".repeat(empty);
25
+
26
+ return (
27
+ <box>
28
+ <text fg="cyan">⟳</text>
29
+ <text> {message} </text>
30
+ <text fg="cyan">
31
+ [{bar}] {current}/{total}
32
+ </text>
33
+ </box>
34
+ );
35
+ }
36
+
37
+ // Indeterminate progress
38
+ return (
39
+ <box>
40
+ <text fg="cyan">⟳</text>
41
+ <text> {message}</text>
42
+ <text fg="gray"> ...</text>
43
+ </box>
44
+ );
45
+ }
46
+
47
+ export default ProgressBar;
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ export function ScopeTabs({ scope, onToggle: _onToggle, toggleHint, }) {
3
+ const isProject = scope === "project";
4
+ return (_jsxs("box", { marginBottom: 1, flexDirection: "row", gap: 1, children: [_jsx("box", { children: isProject ? (_jsx("text", { bg: "cyan", fg: "black", children: _jsx("strong", { children: " \u25C6 Project " }) })) : (_jsx("text", { fg: "gray", children: " \u25CB Project " })) }), _jsx("box", { children: !isProject ? (_jsx("text", { bg: "magenta", fg: "white", children: _jsx("strong", { children: " \u25C6 Global " }) })) : (_jsx("text", { fg: "gray", children: " \u25CB Global " })) }), toggleHint && (_jsx("box", { marginLeft: 2, children: _jsxs("text", { fg: "#666666", children: ["(", toggleHint, ")"] }) }))] }));
5
+ }
6
+ export default ScopeTabs;