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,104 @@
1
+ import React from "react";
2
+ import { useApp } from "../../state/AppContext.js";
3
+ import { useKeyboard } from "../../hooks/useKeyboard.js";
4
+ import { ConfirmModal } from "./ConfirmModal.js";
5
+ import { InputModal } from "./InputModal.js";
6
+ import { SelectModal } from "./SelectModal.js";
7
+ import { MessageModal } from "./MessageModal.js";
8
+ import { LoadingModal } from "./LoadingModal.js";
9
+
10
+ /**
11
+ * Container that renders the active modal as an overlay
12
+ * Handles global Escape key to close modals
13
+ */
14
+ export function ModalContainer() {
15
+ const { state } = useApp();
16
+ const { modal } = state;
17
+
18
+ // Handle Escape key to close modal (except loading)
19
+ useKeyboard((key) => {
20
+ if (key.name === "escape" && modal && modal.type !== "loading") {
21
+ // Loading modal cannot be dismissed with Escape
22
+ if (modal.type === "confirm") {
23
+ modal.onCancel();
24
+ } else if (modal.type === "input") {
25
+ modal.onCancel();
26
+ } else if (modal.type === "select") {
27
+ modal.onCancel();
28
+ } else if (modal.type === "message") {
29
+ modal.onDismiss();
30
+ }
31
+ }
32
+ });
33
+
34
+ if (!modal) {
35
+ return null;
36
+ }
37
+
38
+ const renderModal = () => {
39
+ switch (modal.type) {
40
+ case "confirm":
41
+ return (
42
+ <ConfirmModal
43
+ title={modal.title}
44
+ message={modal.message}
45
+ onConfirm={modal.onConfirm}
46
+ onCancel={modal.onCancel}
47
+ />
48
+ );
49
+
50
+ case "input":
51
+ return (
52
+ <InputModal
53
+ title={modal.title}
54
+ label={modal.label}
55
+ defaultValue={modal.defaultValue}
56
+ onSubmit={modal.onSubmit}
57
+ onCancel={modal.onCancel}
58
+ />
59
+ );
60
+
61
+ case "select":
62
+ return (
63
+ <SelectModal
64
+ title={modal.title}
65
+ message={modal.message}
66
+ options={modal.options}
67
+ onSelect={modal.onSelect}
68
+ onCancel={modal.onCancel}
69
+ />
70
+ );
71
+
72
+ case "message":
73
+ return (
74
+ <MessageModal
75
+ title={modal.title}
76
+ message={modal.message}
77
+ variant={modal.variant}
78
+ onDismiss={modal.onDismiss}
79
+ />
80
+ );
81
+
82
+ case "loading":
83
+ return <LoadingModal message={modal.message} />;
84
+
85
+ default:
86
+ return null;
87
+ }
88
+ };
89
+
90
+ // Center the modal on screen
91
+ return (
92
+ <box
93
+ position="absolute"
94
+ width="100%"
95
+ height="100%"
96
+ justifyContent="center"
97
+ alignItems="center"
98
+ >
99
+ {renderModal()}
100
+ </box>
101
+ );
102
+ }
103
+
104
+ export default ModalContainer;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useKeyboard } from "../../hooks/useKeyboard.js";
4
+ export function SelectModal({ title, message, options, onSelect, onCancel, }) {
5
+ const [selectedIndex, setSelectedIndex] = useState(0);
6
+ useKeyboard((key) => {
7
+ if (key.name === "enter") {
8
+ onSelect(options[selectedIndex].value);
9
+ }
10
+ else if (key.name === "escape" || key.name === "q") {
11
+ onCancel();
12
+ }
13
+ else if (key.name === "up" || key.name === "k") {
14
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
15
+ }
16
+ else if (key.name === "down" || key.name === "j") {
17
+ setSelectedIndex((prev) => Math.min(options.length - 1, prev + 1));
18
+ }
19
+ });
20
+ return (_jsxs("box", { flexDirection: "column", border: true, borderStyle: "rounded", borderColor: "cyan", paddingLeft: 2, paddingRight: 2, paddingTop: 1, paddingBottom: 1, width: 50, children: [_jsx("text", { children: _jsx("strong", { children: title }) }), _jsx("box", { marginTop: 1, marginBottom: 1, children: _jsx("text", { children: message }) }), _jsx("box", { flexDirection: "column", children: options.map((option, idx) => {
21
+ const isSelected = idx === selectedIndex;
22
+ const label = isSelected ? `> ${option.label}` : ` ${option.label}`;
23
+ return (_jsxs("text", { fg: isSelected ? "cyan" : "#666666", children: [isSelected && _jsx("strong", { children: label }), !isSelected && label] }, option.value));
24
+ }) }), _jsx("box", { marginTop: 1, children: _jsx("text", { fg: "#666666", children: "\u2191\u2193 Select \u2022 Enter \u2022 Esc" }) })] }));
25
+ }
26
+ export default SelectModal;
@@ -0,0 +1,82 @@
1
+ import React, { useState } from "react";
2
+ import { useKeyboard } from "../../hooks/useKeyboard.js";
3
+ import type { SelectOption } from "../../state/types.js";
4
+
5
+ interface SelectModalProps {
6
+ /** Modal title */
7
+ title: string;
8
+ /** Modal message */
9
+ message: string;
10
+ /** Select options */
11
+ options: SelectOption[];
12
+ /** Callback when option selected */
13
+ onSelect: (value: string) => void;
14
+ /** Callback when cancelled */
15
+ onCancel: () => void;
16
+ }
17
+
18
+ export function SelectModal({
19
+ title,
20
+ message,
21
+ options,
22
+ onSelect,
23
+ onCancel,
24
+ }: SelectModalProps) {
25
+ const [selectedIndex, setSelectedIndex] = useState(0);
26
+
27
+ useKeyboard((key) => {
28
+ if (key.name === "enter") {
29
+ onSelect(options[selectedIndex].value);
30
+ } else if (key.name === "escape" || key.name === "q") {
31
+ onCancel();
32
+ } else if (key.name === "up" || key.name === "k") {
33
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
34
+ } else if (key.name === "down" || key.name === "j") {
35
+ setSelectedIndex((prev) => Math.min(options.length - 1, prev + 1));
36
+ }
37
+ });
38
+
39
+ return (
40
+ <box
41
+ flexDirection="column"
42
+ border
43
+ borderStyle="rounded"
44
+ borderColor="cyan"
45
+ paddingLeft={2}
46
+ paddingRight={2}
47
+ paddingTop={1}
48
+ paddingBottom={1}
49
+ width={50}
50
+ >
51
+ <text>
52
+ <strong>{title}</strong>
53
+ </text>
54
+
55
+ <box marginTop={1} marginBottom={1}>
56
+ <text>{message}</text>
57
+ </box>
58
+
59
+ <box flexDirection="column">
60
+ {options.map((option, idx) => {
61
+ const isSelected = idx === selectedIndex;
62
+ const label = isSelected ? `> ${option.label}` : ` ${option.label}`;
63
+ return (
64
+ <text
65
+ key={option.value}
66
+ fg={isSelected ? "cyan" : "#666666"}
67
+ >
68
+ {isSelected && <strong>{label}</strong>}
69
+ {!isSelected && label}
70
+ </text>
71
+ );
72
+ })}
73
+ </box>
74
+
75
+ <box marginTop={1}>
76
+ <text fg="#666666">↑↓ Select • Enter • Esc</text>
77
+ </box>
78
+ </box>
79
+ );
80
+ }
81
+
82
+ export default SelectModal;
@@ -0,0 +1,6 @@
1
+ export { ConfirmModal } from "./ConfirmModal.js";
2
+ export { InputModal } from "./InputModal.js";
3
+ export { SelectModal } from "./SelectModal.js";
4
+ export { MessageModal } from "./MessageModal.js";
5
+ export { LoadingModal } from "./LoadingModal.js";
6
+ export { ModalContainer } from "./ModalContainer.js";
@@ -0,0 +1,6 @@
1
+ export { ConfirmModal } from "./ConfirmModal.js";
2
+ export { InputModal } from "./InputModal.js";
3
+ export { SelectModal } from "./SelectModal.js";
4
+ export { MessageModal } from "./MessageModal.js";
5
+ export { LoadingModal } from "./LoadingModal.js";
6
+ export { ModalContainer } from "./ModalContainer.js";
@@ -0,0 +1,3 @@
1
+ export { useAsyncData, useDebouncedAsyncData } from './useAsyncData.js';
2
+ export { useKeyboardHandler } from './useKeyboardHandler.js';
3
+ export { useKeyboard } from './useKeyboard.js';
@@ -0,0 +1,3 @@
1
+ export { useAsyncData, useDebouncedAsyncData } from './useAsyncData.js';
2
+ export { useKeyboardHandler } from './useKeyboardHandler.js';
3
+ export { useKeyboard } from './useKeyboard.js';
@@ -1,18 +1,18 @@
1
- import { useState, useEffect, useCallback } from 'react';
1
+ import { useState, useEffect, useCallback } from "react";
2
2
  /**
3
3
  * Hook for fetching async data with loading/error states
4
4
  */
5
5
  export function useAsyncData({ fetcher, deps = [], immediate = true, }) {
6
- const [data, setData] = useState({ status: 'idle' });
6
+ const [data, setData] = useState({ status: "idle" });
7
7
  const refetch = useCallback(async () => {
8
- setData({ status: 'loading' });
8
+ setData({ status: "loading" });
9
9
  try {
10
10
  const result = await fetcher();
11
- setData({ status: 'success', data: result });
11
+ setData({ status: "success", data: result });
12
12
  }
13
13
  catch (err) {
14
14
  setData({
15
- status: 'error',
15
+ status: "error",
16
16
  error: err instanceof Error ? err : new Error(String(err)),
17
17
  });
18
18
  }
@@ -25,39 +25,39 @@ export function useAsyncData({ fetcher, deps = [], immediate = true, }) {
25
25
  }, deps);
26
26
  return {
27
27
  data,
28
- isLoading: data.status === 'loading',
29
- isSuccess: data.status === 'success',
30
- isError: data.status === 'error',
31
- error: data.status === 'error' ? data.error : null,
28
+ isLoading: data.status === "loading",
29
+ isSuccess: data.status === "success",
30
+ isError: data.status === "error",
31
+ error: data.status === "error" ? data.error : null,
32
32
  refetch,
33
- value: data.status === 'success' ? data.data : null,
33
+ value: data.status === "success" ? data.data : null,
34
34
  };
35
35
  }
36
36
  /**
37
37
  * Hook for fetching async data with debounced query
38
38
  */
39
39
  export function useDebouncedAsyncData({ fetcher, query, debounceMs = 300, minQueryLength = 1, }) {
40
- const [data, setData] = useState({ status: 'idle' });
40
+ const [data, setData] = useState({ status: "idle" });
41
41
  const refetch = useCallback(async () => {
42
42
  if (query.length < minQueryLength) {
43
- setData({ status: 'idle' });
43
+ setData({ status: "idle" });
44
44
  return;
45
45
  }
46
- setData({ status: 'loading' });
46
+ setData({ status: "loading" });
47
47
  try {
48
48
  const result = await fetcher(query);
49
- setData({ status: 'success', data: result });
49
+ setData({ status: "success", data: result });
50
50
  }
51
51
  catch (err) {
52
52
  setData({
53
- status: 'error',
53
+ status: "error",
54
54
  error: err instanceof Error ? err : new Error(String(err)),
55
55
  });
56
56
  }
57
57
  }, [fetcher, query, minQueryLength]);
58
58
  useEffect(() => {
59
59
  if (query.length < minQueryLength) {
60
- setData({ status: 'idle' });
60
+ setData({ status: "idle" });
61
61
  return;
62
62
  }
63
63
  const timeoutId = setTimeout(() => {
@@ -67,12 +67,11 @@ export function useDebouncedAsyncData({ fetcher, query, debounceMs = 300, minQue
67
67
  }, [query, debounceMs, minQueryLength, refetch]);
68
68
  return {
69
69
  data,
70
- isLoading: data.status === 'loading',
71
- isSuccess: data.status === 'success',
72
- isError: data.status === 'error',
73
- error: data.status === 'error' ? data.error : null,
70
+ isLoading: data.status === "loading",
71
+ isSuccess: data.status === "success",
72
+ isError: data.status === "error",
73
+ error: data.status === "error" ? data.error : null,
74
74
  refetch,
75
- value: data.status === 'success' ? data.data : null,
75
+ value: data.status === "success" ? data.data : null,
76
76
  };
77
77
  }
78
- //# sourceMappingURL=useAsyncData.js.map
@@ -0,0 +1,127 @@
1
+ import { useState, useEffect, useCallback } from "react";
2
+ import type { AsyncData } from "../state/types.js";
3
+
4
+ interface UseAsyncDataOptions<T> {
5
+ /** Function that fetches data */
6
+ fetcher: () => Promise<T>;
7
+ /** Dependencies that trigger refetch when changed */
8
+ deps?: unknown[];
9
+ /** Whether to fetch immediately on mount */
10
+ immediate?: boolean;
11
+ }
12
+
13
+ interface UseAsyncDataResult<T> {
14
+ /** Current async data state */
15
+ data: AsyncData<T>;
16
+ /** Whether data is currently loading */
17
+ isLoading: boolean;
18
+ /** Whether data has been successfully loaded */
19
+ isSuccess: boolean;
20
+ /** Whether there was an error loading data */
21
+ isError: boolean;
22
+ /** Error if any */
23
+ error: Error | null;
24
+ /** Trigger a manual refetch */
25
+ refetch: () => Promise<void>;
26
+ /** Raw data if available */
27
+ value: T | null;
28
+ }
29
+
30
+ /**
31
+ * Hook for fetching async data with loading/error states
32
+ */
33
+ export function useAsyncData<T>({
34
+ fetcher,
35
+ deps = [],
36
+ immediate = true,
37
+ }: UseAsyncDataOptions<T>): UseAsyncDataResult<T> {
38
+ const [data, setData] = useState<AsyncData<T>>({ status: "idle" });
39
+
40
+ const refetch = useCallback(async () => {
41
+ setData({ status: "loading" });
42
+ try {
43
+ const result = await fetcher();
44
+ setData({ status: "success", data: result });
45
+ } catch (err) {
46
+ setData({
47
+ status: "error",
48
+ error: err instanceof Error ? err : new Error(String(err)),
49
+ });
50
+ }
51
+ }, [fetcher]);
52
+
53
+ useEffect(() => {
54
+ if (immediate) {
55
+ refetch();
56
+ }
57
+ // eslint-disable-next-line react-hooks/exhaustive-deps
58
+ }, deps);
59
+
60
+ return {
61
+ data,
62
+ isLoading: data.status === "loading",
63
+ isSuccess: data.status === "success",
64
+ isError: data.status === "error",
65
+ error: data.status === "error" ? data.error : null,
66
+ refetch,
67
+ value: data.status === "success" ? data.data : null,
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Hook for fetching async data with debounced query
73
+ */
74
+ export function useDebouncedAsyncData<T>({
75
+ fetcher,
76
+ query,
77
+ debounceMs = 300,
78
+ minQueryLength = 1,
79
+ }: {
80
+ fetcher: (query: string) => Promise<T>;
81
+ query: string;
82
+ debounceMs?: number;
83
+ minQueryLength?: number;
84
+ }): UseAsyncDataResult<T> {
85
+ const [data, setData] = useState<AsyncData<T>>({ status: "idle" });
86
+
87
+ const refetch = useCallback(async () => {
88
+ if (query.length < minQueryLength) {
89
+ setData({ status: "idle" });
90
+ return;
91
+ }
92
+
93
+ setData({ status: "loading" });
94
+ try {
95
+ const result = await fetcher(query);
96
+ setData({ status: "success", data: result });
97
+ } catch (err) {
98
+ setData({
99
+ status: "error",
100
+ error: err instanceof Error ? err : new Error(String(err)),
101
+ });
102
+ }
103
+ }, [fetcher, query, minQueryLength]);
104
+
105
+ useEffect(() => {
106
+ if (query.length < minQueryLength) {
107
+ setData({ status: "idle" });
108
+ return;
109
+ }
110
+
111
+ const timeoutId = setTimeout(() => {
112
+ refetch();
113
+ }, debounceMs);
114
+
115
+ return () => clearTimeout(timeoutId);
116
+ }, [query, debounceMs, minQueryLength, refetch]);
117
+
118
+ return {
119
+ data,
120
+ isLoading: data.status === "loading",
121
+ isSuccess: data.status === "success",
122
+ isError: data.status === "error",
123
+ error: data.status === "error" ? data.error : null,
124
+ refetch,
125
+ value: data.status === "success" ? data.data : null,
126
+ };
127
+ }
@@ -0,0 +1,13 @@
1
+ import { useKeyboard as useOpenTUIKeyboard } from "@opentui/react";
2
+ /**
3
+ * Simple re-export of OpenTUI's useKeyboard hook
4
+ * Used by modals for direct keyboard event handling
5
+ *
6
+ * Key names in OpenTUI:
7
+ * - Single characters: "a", "1", etc.
8
+ * - Arrow keys: "up", "down", "left", "right"
9
+ * - Special: "enter", "escape", "tab", "backspace", "delete"
10
+ */
11
+ export function useKeyboard(handler) {
12
+ useOpenTUIKeyboard(handler);
13
+ }
@@ -0,0 +1,26 @@
1
+ import { useKeyboard as useOpenTUIKeyboard } from "@opentui/react";
2
+
3
+ /**
4
+ * OpenTUI keyboard event type
5
+ */
6
+ export interface KeyEvent {
7
+ name: string;
8
+ ctrl?: boolean;
9
+ shift?: boolean;
10
+ meta?: boolean;
11
+ }
12
+
13
+ /**
14
+ * Simple re-export of OpenTUI's useKeyboard hook
15
+ * Used by modals for direct keyboard event handling
16
+ *
17
+ * Key names in OpenTUI:
18
+ * - Single characters: "a", "1", etc.
19
+ * - Arrow keys: "up", "down", "left", "right"
20
+ * - Special: "enter", "escape", "tab", "backspace", "delete"
21
+ */
22
+ export function useKeyboard(
23
+ handler: (event: KeyEvent) => void,
24
+ ): void {
25
+ useOpenTUIKeyboard(handler);
26
+ }
@@ -0,0 +1,39 @@
1
+ import { useKeyboard as useOpenTUIKeyboard } from '@opentui/react';
2
+ /**
3
+ * useKeyboardHandler hook
4
+ * Provides Ink-like API for keyboard events
5
+ *
6
+ * @param handler - Callback function with Ink-compatible signature
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * useKeyboardHandler((input, key) => {
11
+ * if (key.return) handleEnter();
12
+ * if (key.upArrow) handleUp();
13
+ * if (key.escape) handleEscape();
14
+ * if (input === 'q') handleQuit();
15
+ * });
16
+ * ```
17
+ */
18
+ export function useKeyboardHandler(handler) {
19
+ useOpenTUIKeyboard((event) => {
20
+ // Map OpenTUI key names to Ink-like key names
21
+ const key = {
22
+ name: event.name,
23
+ ctrl: event.ctrl || false,
24
+ shift: event.shift || false,
25
+ meta: event.meta || false,
26
+ return: event.name === 'enter' || event.name === 'return',
27
+ upArrow: event.name === 'up',
28
+ downArrow: event.name === 'down',
29
+ leftArrow: event.name === 'left',
30
+ rightArrow: event.name === 'right',
31
+ escape: event.name === 'escape',
32
+ tab: event.name === 'tab',
33
+ };
34
+ // For printable characters, pass the character as input
35
+ // For special keys, pass empty string
36
+ const input = event.name.length === 1 ? event.name : '';
37
+ handler(input, key);
38
+ });
39
+ }
@@ -0,0 +1,63 @@
1
+ import { useKeyboard as useOpenTUIKeyboard } from '@opentui/react';
2
+
3
+ /**
4
+ * Wrapper hook for OpenTUI's useKeyboard that provides an Ink-like API
5
+ * Maps Ink key names to OpenTUI key names for easier migration
6
+ */
7
+
8
+ export interface InkLikeKey {
9
+ name: string;
10
+ ctrl: boolean;
11
+ shift: boolean;
12
+ meta: boolean;
13
+ return?: boolean;
14
+ upArrow?: boolean;
15
+ downArrow?: boolean;
16
+ leftArrow?: boolean;
17
+ rightArrow?: boolean;
18
+ escape?: boolean;
19
+ tab?: boolean;
20
+ }
21
+
22
+ type KeyHandler = (input: string, key: InkLikeKey) => void;
23
+
24
+ /**
25
+ * useKeyboardHandler hook
26
+ * Provides Ink-like API for keyboard events
27
+ *
28
+ * @param handler - Callback function with Ink-compatible signature
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * useKeyboardHandler((input, key) => {
33
+ * if (key.return) handleEnter();
34
+ * if (key.upArrow) handleUp();
35
+ * if (key.escape) handleEscape();
36
+ * if (input === 'q') handleQuit();
37
+ * });
38
+ * ```
39
+ */
40
+ export function useKeyboardHandler(handler: KeyHandler): void {
41
+ useOpenTUIKeyboard((event) => {
42
+ // Map OpenTUI key names to Ink-like key names
43
+ const key: InkLikeKey = {
44
+ name: event.name,
45
+ ctrl: event.ctrl || false,
46
+ shift: event.shift || false,
47
+ meta: event.meta || false,
48
+ return: event.name === 'enter' || event.name === 'return',
49
+ upArrow: event.name === 'up',
50
+ downArrow: event.name === 'down',
51
+ leftArrow: event.name === 'left',
52
+ rightArrow: event.name === 'right',
53
+ escape: event.name === 'escape',
54
+ tab: event.name === 'tab',
55
+ };
56
+
57
+ // For printable characters, pass the character as input
58
+ // For special keys, pass empty string
59
+ const input = event.name.length === 1 ? event.name : '';
60
+
61
+ handler(input, key);
62
+ });
63
+ }