@navios/commander-tui 1.0.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 (348) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +275 -0
  3. package/coverage/__tests__/utils/factories.ts.html +1147 -0
  4. package/coverage/__tests__/utils/index.html +131 -0
  5. package/coverage/__tests__/utils/render-utils.tsx.html +202 -0
  6. package/coverage/base.css +224 -0
  7. package/coverage/block-navigation.js +87 -0
  8. package/coverage/clover.xml +959 -0
  9. package/coverage/components/filter/filter_bar.tsx.html +322 -0
  10. package/coverage/components/filter/index.html +116 -0
  11. package/coverage/components/log/index.html +131 -0
  12. package/coverage/components/log/index.ts.html +88 -0
  13. package/coverage/components/log/log_message.tsx.html +391 -0
  14. package/coverage/components/prompt/index.html +116 -0
  15. package/coverage/components/prompt/prompt_renderer.tsx.html +1123 -0
  16. package/coverage/components/screen/index.html +131 -0
  17. package/coverage/components/screen/loading_message.tsx.html +217 -0
  18. package/coverage/components/screen/progress_message.tsx.html +265 -0
  19. package/coverage/components/sidebar/index.html +146 -0
  20. package/coverage/components/sidebar/sidebar.tsx.html +391 -0
  21. package/coverage/components/sidebar/sidebar_item.tsx.html +235 -0
  22. package/coverage/components/sidebar/sidebar_separator.tsx.html +124 -0
  23. package/coverage/context/index.html +131 -0
  24. package/coverage/context/index.ts.html +88 -0
  25. package/coverage/context/logger_context.tsx.html +412 -0
  26. package/coverage/coverage-final.json +55 -0
  27. package/coverage/favicon.png +0 -0
  28. package/coverage/filter/filter_engine.ts.html +424 -0
  29. package/coverage/filter/index.html +116 -0
  30. package/coverage/hooks/index.html +131 -0
  31. package/coverage/hooks/index.ts.html +88 -0
  32. package/coverage/hooks/use_theme.ts.html +121 -0
  33. package/coverage/index.html +356 -0
  34. package/coverage/keyboard/index.html +116 -0
  35. package/coverage/keyboard/keyboard_manager.ts.html +784 -0
  36. package/coverage/prettify.css +1 -0
  37. package/coverage/prettify.js +2 -0
  38. package/coverage/schemas/index.html +161 -0
  39. package/coverage/schemas/index.ts.html +94 -0
  40. package/coverage/schemas/logger-options.ts.html +124 -0
  41. package/coverage/schemas/prompt-options.ts.html +112 -0
  42. package/coverage/schemas/screen-options.ts.html +127 -0
  43. package/coverage/services/index.html +146 -0
  44. package/coverage/services/logger.ts.html +1192 -0
  45. package/coverage/services/prompt.ts.html +568 -0
  46. package/coverage/services/screen.ts.html +1804 -0
  47. package/coverage/sort-arrow-sprite.png +0 -0
  48. package/coverage/sorter.js +210 -0
  49. package/coverage/themes/dark.ts.html +604 -0
  50. package/coverage/themes/high-contrast.ts.html +619 -0
  51. package/coverage/themes/index.html +176 -0
  52. package/coverage/themes/index.ts.html +97 -0
  53. package/coverage/themes/light.ts.html +601 -0
  54. package/coverage/themes/utils.ts.html +334 -0
  55. package/coverage/tokens/index.html +161 -0
  56. package/coverage/tokens/index.ts.html +94 -0
  57. package/coverage/tokens/logger.ts.html +115 -0
  58. package/coverage/tokens/prompt.ts.html +115 -0
  59. package/coverage/tokens/screen.ts.html +115 -0
  60. package/coverage/types/file.types.ts.html +265 -0
  61. package/coverage/types/filter.types.ts.html +238 -0
  62. package/coverage/types/index.html +236 -0
  63. package/coverage/types/index.ts.html +151 -0
  64. package/coverage/types/keyboard.types.ts.html +364 -0
  65. package/coverage/types/log.types.ts.html +268 -0
  66. package/coverage/types/message.types.ts.html +445 -0
  67. package/coverage/types/prompt.types.ts.html +403 -0
  68. package/coverage/types/screen.types.ts.html +451 -0
  69. package/coverage/types/theme.types.ts.html +841 -0
  70. package/coverage/utils/colors/file-colors.ts.html +112 -0
  71. package/coverage/utils/colors/helpers.ts.html +235 -0
  72. package/coverage/utils/colors/index.html +221 -0
  73. package/coverage/utils/colors/index.ts.html +145 -0
  74. package/coverage/utils/colors/log-colors.ts.html +253 -0
  75. package/coverage/utils/colors/progress-colors.ts.html +160 -0
  76. package/coverage/utils/colors/prompt-colors.ts.html +175 -0
  77. package/coverage/utils/colors/sidebar-colors.ts.html +241 -0
  78. package/coverage/utils/colors/table-colors.ts.html +118 -0
  79. package/coverage/utils/filetype.ts.html +277 -0
  80. package/coverage/utils/format.ts.html +241 -0
  81. package/coverage/utils/index.html +161 -0
  82. package/coverage/utils/index.ts.html +118 -0
  83. package/coverage/utils/stdout-printer.ts.html +523 -0
  84. package/dist/src/components/file/file_log.d.ts +35 -0
  85. package/dist/src/components/file/file_log.d.ts.map +1 -0
  86. package/dist/src/components/file/index.d.ts +2 -0
  87. package/dist/src/components/file/index.d.ts.map +1 -0
  88. package/dist/src/components/filter/filter_bar.d.ts +10 -0
  89. package/dist/src/components/filter/filter_bar.d.ts.map +1 -0
  90. package/dist/src/components/filter/index.d.ts +2 -0
  91. package/dist/src/components/filter/index.d.ts.map +1 -0
  92. package/dist/src/components/help/help_overlay.d.ts +10 -0
  93. package/dist/src/components/help/help_overlay.d.ts.map +1 -0
  94. package/dist/src/components/help/index.d.ts +2 -0
  95. package/dist/src/components/help/index.d.ts.map +1 -0
  96. package/dist/src/components/index.d.ts +27 -0
  97. package/dist/src/components/index.d.ts.map +1 -0
  98. package/dist/src/components/log/debug_log.d.ts +3 -0
  99. package/dist/src/components/log/debug_log.d.ts.map +1 -0
  100. package/dist/src/components/log/error_log.d.ts +3 -0
  101. package/dist/src/components/log/error_log.d.ts.map +1 -0
  102. package/dist/src/components/log/fatal_log.d.ts +3 -0
  103. package/dist/src/components/log/fatal_log.d.ts.map +1 -0
  104. package/dist/src/components/log/index.d.ts +9 -0
  105. package/dist/src/components/log/index.d.ts.map +1 -0
  106. package/dist/src/components/log/info_log.d.ts +3 -0
  107. package/dist/src/components/log/info_log.d.ts.map +1 -0
  108. package/dist/src/components/log/log_message.d.ts +33 -0
  109. package/dist/src/components/log/log_message.d.ts.map +1 -0
  110. package/dist/src/components/log/success_log.d.ts +3 -0
  111. package/dist/src/components/log/success_log.d.ts.map +1 -0
  112. package/dist/src/components/log/trace_log.d.ts +3 -0
  113. package/dist/src/components/log/trace_log.d.ts.map +1 -0
  114. package/dist/src/components/log/warning_log.d.ts +3 -0
  115. package/dist/src/components/log/warning_log.d.ts.map +1 -0
  116. package/dist/src/components/prompt/index.d.ts +3 -0
  117. package/dist/src/components/prompt/index.d.ts.map +1 -0
  118. package/dist/src/components/prompt/prompt_renderer.d.ts +6 -0
  119. package/dist/src/components/prompt/prompt_renderer.d.ts.map +1 -0
  120. package/dist/src/components/screen/group_renderer.d.ts +19 -0
  121. package/dist/src/components/screen/group_renderer.d.ts.map +1 -0
  122. package/dist/src/components/screen/index.d.ts +13 -0
  123. package/dist/src/components/screen/index.d.ts.map +1 -0
  124. package/dist/src/components/screen/loading_message.d.ts +6 -0
  125. package/dist/src/components/screen/loading_message.d.ts.map +1 -0
  126. package/dist/src/components/screen/message_renderer.d.ts +8 -0
  127. package/dist/src/components/screen/message_renderer.d.ts.map +1 -0
  128. package/dist/src/components/screen/progress_message.d.ts +8 -0
  129. package/dist/src/components/screen/progress_message.d.ts.map +1 -0
  130. package/dist/src/components/screen/screen_bridge.d.ts +20 -0
  131. package/dist/src/components/screen/screen_bridge.d.ts.map +1 -0
  132. package/dist/src/components/screen/table_message.d.ts +6 -0
  133. package/dist/src/components/screen/table_message.d.ts.map +1 -0
  134. package/dist/src/components/screen_manager_bridge.d.ts +11 -0
  135. package/dist/src/components/screen_manager_bridge.d.ts.map +1 -0
  136. package/dist/src/components/sidebar/index.d.ts +6 -0
  137. package/dist/src/components/sidebar/index.d.ts.map +1 -0
  138. package/dist/src/components/sidebar/sidebar.d.ts +18 -0
  139. package/dist/src/components/sidebar/sidebar.d.ts.map +1 -0
  140. package/dist/src/components/sidebar/sidebar_item.d.ts +14 -0
  141. package/dist/src/components/sidebar/sidebar_item.d.ts.map +1 -0
  142. package/dist/src/components/sidebar/sidebar_separator.d.ts +2 -0
  143. package/dist/src/components/sidebar/sidebar_separator.d.ts.map +1 -0
  144. package/dist/src/context/logger_context.d.ts +60 -0
  145. package/dist/src/context/logger_context.d.ts.map +1 -0
  146. package/dist/src/filter/filter_engine.d.ts +20 -0
  147. package/dist/src/filter/filter_engine.d.ts.map +1 -0
  148. package/dist/src/filter/index.d.ts +4 -0
  149. package/dist/src/filter/index.d.ts.map +1 -0
  150. package/dist/src/hooks/index.d.ts +2 -0
  151. package/dist/src/hooks/index.d.ts.map +1 -0
  152. package/dist/src/hooks/use_theme.d.ts +7 -0
  153. package/dist/src/hooks/use_theme.d.ts.map +1 -0
  154. package/dist/src/index.d.ts +88 -0
  155. package/dist/src/index.d.ts.map +1 -0
  156. package/dist/src/keyboard/create_bindings.d.ts +31 -0
  157. package/dist/src/keyboard/create_bindings.d.ts.map +1 -0
  158. package/dist/src/keyboard/index.d.ts +12 -0
  159. package/dist/src/keyboard/index.d.ts.map +1 -0
  160. package/dist/src/keyboard/keyboard_manager.d.ts +69 -0
  161. package/dist/src/keyboard/keyboard_manager.d.ts.map +1 -0
  162. package/dist/src/services/index.d.ts +5 -0
  163. package/dist/src/services/index.d.ts.map +1 -0
  164. package/dist/src/services/logger.d.ts +61 -0
  165. package/dist/src/services/logger.d.ts.map +1 -0
  166. package/dist/src/services/prompt.d.ts +37 -0
  167. package/dist/src/services/prompt.d.ts.map +1 -0
  168. package/dist/src/services/screen.d.ts +165 -0
  169. package/dist/src/services/screen.d.ts.map +1 -0
  170. package/dist/src/services/screen_manager.d.ts +124 -0
  171. package/dist/src/services/screen_manager.d.ts.map +1 -0
  172. package/dist/src/themes/dark.d.ts +7 -0
  173. package/dist/src/themes/dark.d.ts.map +1 -0
  174. package/dist/src/themes/high-contrast.d.ts +7 -0
  175. package/dist/src/themes/high-contrast.d.ts.map +1 -0
  176. package/dist/src/themes/index.d.ts +18 -0
  177. package/dist/src/themes/index.d.ts.map +1 -0
  178. package/dist/src/themes/light.d.ts +6 -0
  179. package/dist/src/themes/light.d.ts.map +1 -0
  180. package/dist/src/themes/utils.d.ts +22 -0
  181. package/dist/src/themes/utils.d.ts.map +1 -0
  182. package/dist/src/types/file.types.d.ts +40 -0
  183. package/dist/src/types/file.types.d.ts.map +1 -0
  184. package/dist/src/types/filter.types.d.ts +31 -0
  185. package/dist/src/types/filter.types.d.ts.map +1 -0
  186. package/dist/src/types/index.d.ts +81 -0
  187. package/dist/src/types/index.d.ts.map +1 -0
  188. package/dist/src/types/keyboard.types.d.ts +83 -0
  189. package/dist/src/types/keyboard.types.d.ts.map +1 -0
  190. package/dist/src/types/log.types.d.ts +32 -0
  191. package/dist/src/types/log.types.d.ts.map +1 -0
  192. package/dist/src/types/message.types.d.ts +91 -0
  193. package/dist/src/types/message.types.d.ts.map +1 -0
  194. package/dist/src/types/prompt.types.d.ts +89 -0
  195. package/dist/src/types/prompt.types.d.ts.map +1 -0
  196. package/dist/src/types/screen.types.d.ts +85 -0
  197. package/dist/src/types/screen.types.d.ts.map +1 -0
  198. package/dist/src/types/theme.types.d.ts +216 -0
  199. package/dist/src/types/theme.types.d.ts.map +1 -0
  200. package/dist/src/utils/colors/file-colors.d.ts +10 -0
  201. package/dist/src/utils/colors/file-colors.d.ts.map +1 -0
  202. package/dist/src/utils/colors/helpers.d.ts +29 -0
  203. package/dist/src/utils/colors/helpers.d.ts.map +1 -0
  204. package/dist/src/utils/colors/index.d.ts +13 -0
  205. package/dist/src/utils/colors/index.d.ts.map +1 -0
  206. package/dist/src/utils/colors/log-colors.d.ts +15 -0
  207. package/dist/src/utils/colors/log-colors.d.ts.map +1 -0
  208. package/dist/src/utils/colors/progress-colors.d.ts +25 -0
  209. package/dist/src/utils/colors/progress-colors.d.ts.map +1 -0
  210. package/dist/src/utils/colors/prompt-colors.d.ts +22 -0
  211. package/dist/src/utils/colors/prompt-colors.d.ts.map +1 -0
  212. package/dist/src/utils/colors/sidebar-colors.d.ts +50 -0
  213. package/dist/src/utils/colors/sidebar-colors.d.ts.map +1 -0
  214. package/dist/src/utils/colors/table-colors.d.ts +12 -0
  215. package/dist/src/utils/colors/table-colors.d.ts.map +1 -0
  216. package/dist/src/utils/filetype.d.ts +19 -0
  217. package/dist/src/utils/filetype.d.ts.map +1 -0
  218. package/dist/src/utils/format.d.ts +9 -0
  219. package/dist/src/utils/format.d.ts.map +1 -0
  220. package/dist/src/utils/index.d.ts +20 -0
  221. package/dist/src/utils/index.d.ts.map +1 -0
  222. package/dist/src/utils/stdout-printer.d.ts +10 -0
  223. package/dist/src/utils/stdout-printer.d.ts.map +1 -0
  224. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  225. package/dist/tsconfig.tsbuildinfo +1 -0
  226. package/dist/tsdown.config.d.mts +5 -0
  227. package/dist/tsdown.config.d.mts.map +1 -0
  228. package/dist/vitest.config.d.mts +3 -0
  229. package/dist/vitest.config.d.mts.map +1 -0
  230. package/lib/index.cjs +6349 -0
  231. package/lib/index.cjs.map +1 -0
  232. package/lib/index.d.cts +1720 -0
  233. package/lib/index.d.cts.map +1 -0
  234. package/lib/index.d.mts +1720 -0
  235. package/lib/index.d.mts.map +1 -0
  236. package/lib/index.mjs +6264 -0
  237. package/lib/index.mjs.map +1 -0
  238. package/lib/screen_manager_bridge-BpDgVu3e.cjs +3357 -0
  239. package/lib/screen_manager_bridge-BpDgVu3e.cjs.map +1 -0
  240. package/lib/screen_manager_bridge-CkV7637i.cjs +3 -0
  241. package/lib/screen_manager_bridge-DN2J6_k1.mjs +3 -0
  242. package/lib/screen_manager_bridge-Dfg4QUrl.mjs +3034 -0
  243. package/lib/screen_manager_bridge-Dfg4QUrl.mjs.map +1 -0
  244. package/package.json +43 -0
  245. package/project.json +60 -0
  246. package/src/__tests__/components/__snapshots__/filter_bar.spec.tsx.snap +2245 -0
  247. package/src/__tests__/components/__snapshots__/loading_message.spec.tsx.snap +1382 -0
  248. package/src/__tests__/components/__snapshots__/log_message.spec.tsx.snap +3169 -0
  249. package/src/__tests__/components/__snapshots__/progress_message.spec.tsx.snap +1743 -0
  250. package/src/__tests__/components/__snapshots__/prompt_renderer.spec.tsx.snap +3135 -0
  251. package/src/__tests__/components/__snapshots__/sidebar.spec.tsx.snap +2617 -0
  252. package/src/__tests__/components/filter_bar.spec.tsx +190 -0
  253. package/src/__tests__/components/loading_message.spec.tsx +110 -0
  254. package/src/__tests__/components/log_message.spec.tsx +166 -0
  255. package/src/__tests__/components/progress_message.spec.tsx +147 -0
  256. package/src/__tests__/components/prompt_renderer.spec.tsx +274 -0
  257. package/src/__tests__/components/sidebar.spec.tsx +305 -0
  258. package/src/__tests__/filter/filter_engine.spec.ts +325 -0
  259. package/src/__tests__/keyboard/keyboard_manager.spec.ts +557 -0
  260. package/src/__tests__/mocks/scm-mock.ts +5 -0
  261. package/src/__tests__/services/logger.spec.ts +630 -0
  262. package/src/__tests__/services/prompt.spec.ts +411 -0
  263. package/src/__tests__/services/screen.spec.ts +721 -0
  264. package/src/__tests__/setup.ts +43 -0
  265. package/src/__tests__/utils/factories.ts +354 -0
  266. package/src/__tests__/utils/filetype.spec.ts +195 -0
  267. package/src/__tests__/utils/format.spec.ts +178 -0
  268. package/src/__tests__/utils/render-utils.tsx +39 -0
  269. package/src/__tests__/utils/test-container.ts +48 -0
  270. package/src/components/file/file_log.tsx +241 -0
  271. package/src/components/file/index.ts +1 -0
  272. package/src/components/filter/filter_bar.tsx +79 -0
  273. package/src/components/filter/index.ts +1 -0
  274. package/src/components/help/help_overlay.tsx +100 -0
  275. package/src/components/help/index.ts +1 -0
  276. package/src/components/index.ts +15 -0
  277. package/src/components/log/index.ts +1 -0
  278. package/src/components/log/log_message.tsx +102 -0
  279. package/src/components/prompt/index.ts +2 -0
  280. package/src/components/prompt/prompt_renderer.tsx +346 -0
  281. package/src/components/screen/group_renderer.tsx +64 -0
  282. package/src/components/screen/index.ts +6 -0
  283. package/src/components/screen/loading_message.tsx +44 -0
  284. package/src/components/screen/message_renderer.tsx +108 -0
  285. package/src/components/screen/progress_message.tsx +60 -0
  286. package/src/components/screen/screen_bridge.tsx +149 -0
  287. package/src/components/screen/table_message.tsx +57 -0
  288. package/src/components/screen_manager_bridge.tsx +245 -0
  289. package/src/components/sidebar/index.ts +3 -0
  290. package/src/components/sidebar/sidebar.tsx +102 -0
  291. package/src/components/sidebar/sidebar_item.tsx +50 -0
  292. package/src/components/sidebar/sidebar_separator.tsx +13 -0
  293. package/src/context/index.ts +1 -0
  294. package/src/context/logger_context.tsx +109 -0
  295. package/src/factories/index.ts +1 -0
  296. package/src/factories/screen.factory.ts +22 -0
  297. package/src/filter/filter_engine.ts +113 -0
  298. package/src/filter/index.ts +1 -0
  299. package/src/hooks/index.ts +1 -0
  300. package/src/hooks/use_theme.ts +12 -0
  301. package/src/index.ts +64 -0
  302. package/src/keyboard/create_bindings.ts +457 -0
  303. package/src/keyboard/index.ts +2 -0
  304. package/src/keyboard/keyboard_manager.ts +233 -0
  305. package/src/overrides/console.logger.override.ts +61 -0
  306. package/src/overrides/index.ts +1 -0
  307. package/src/schemas/index.ts +3 -0
  308. package/src/schemas/logger-options.ts +13 -0
  309. package/src/schemas/prompt-options.ts +9 -0
  310. package/src/schemas/screen-options.ts +14 -0
  311. package/src/services/index.ts +4 -0
  312. package/src/services/logger.ts +369 -0
  313. package/src/services/prompt.ts +169 -0
  314. package/src/services/screen.ts +590 -0
  315. package/src/services/screen_manager.tsx +390 -0
  316. package/src/themes/dark.ts +173 -0
  317. package/src/themes/high-contrast.ts +178 -0
  318. package/src/themes/index.ts +4 -0
  319. package/src/themes/light.ts +172 -0
  320. package/src/themes/utils.ts +83 -0
  321. package/src/tokens/index.ts +3 -0
  322. package/src/tokens/logger.ts +10 -0
  323. package/src/tokens/prompt.ts +10 -0
  324. package/src/tokens/screen.ts +10 -0
  325. package/src/types/file.types.ts +60 -0
  326. package/src/types/filter.types.ts +51 -0
  327. package/src/types/index.ts +22 -0
  328. package/src/types/keyboard.types.ts +93 -0
  329. package/src/types/log.types.ts +61 -0
  330. package/src/types/message.types.ts +120 -0
  331. package/src/types/prompt.types.ts +106 -0
  332. package/src/types/screen.types.ts +124 -0
  333. package/src/types/theme.types.ts +252 -0
  334. package/src/utils/colors/file-colors.ts +9 -0
  335. package/src/utils/colors/helpers.ts +50 -0
  336. package/src/utils/colors/index.ts +20 -0
  337. package/src/utils/colors/log-colors.ts +56 -0
  338. package/src/utils/colors/progress-colors.ts +25 -0
  339. package/src/utils/colors/prompt-colors.ts +30 -0
  340. package/src/utils/colors/sidebar-colors.ts +52 -0
  341. package/src/utils/colors/table-colors.ts +11 -0
  342. package/src/utils/filetype.ts +64 -0
  343. package/src/utils/format.ts +52 -0
  344. package/src/utils/index.ts +11 -0
  345. package/src/utils/stdout-printer.ts +255 -0
  346. package/tsconfig.json +14 -0
  347. package/tsdown.config.mts +34 -0
  348. package/vitest.config.mts +10 -0
@@ -0,0 +1,590 @@
1
+ import type { ScreenOptions } from '../schemas/index.ts'
2
+ import type {
3
+ ChoicePromptData,
4
+ ConfirmPromptData,
5
+ InputPromptData,
6
+ LoadingMessageData,
7
+ MessageData,
8
+ MultiChoicePromptData,
9
+ ProgressMessageData,
10
+ PromptData,
11
+ ScreenStatus,
12
+ } from '../types/index.ts'
13
+
14
+ import type { ScreenManager } from './screen_manager.tsx'
15
+
16
+ interface PendingPrompt {
17
+ data: PromptData
18
+ resolve: (value: string | boolean | string[]) => void
19
+ timeoutId?: ReturnType<typeof setTimeout>
20
+ }
21
+
22
+ export class ScreenInstance {
23
+ private id: string
24
+ private name: string
25
+ private icon?: string
26
+ private badgeCount: number = 0
27
+ private status: ScreenStatus = 'waiting'
28
+ private hidden: boolean = false
29
+ private messages: MessageData[] = []
30
+ private manager: ScreenManager | null = null
31
+ private changeListeners: Set<() => void> = new Set()
32
+ private printFn: ((messages: MessageData[], name: string, isError: boolean) => void) | null = null
33
+ private hasPrinted: boolean = false
34
+ private version: number = 0
35
+
36
+ // Prompt queue system
37
+ private promptQueue: PendingPrompt[] = []
38
+ private activePrompt: PendingPrompt | null = null
39
+
40
+ constructor(id: string, options: ScreenOptions) {
41
+ this.id = id
42
+ this.name = options.name
43
+ this.icon = options.icon
44
+ this.badgeCount = options.badgeCount ?? 0
45
+ this.hidden = options.hidden ?? false
46
+ }
47
+
48
+ incrementVersion(): void {
49
+ this.version++
50
+ }
51
+
52
+ getVersion(): number {
53
+ return this.version
54
+ }
55
+
56
+ /**
57
+ * Internal: Set the manager reference
58
+ */
59
+ _setManager(manager: ScreenManager): void {
60
+ this.manager = manager
61
+ }
62
+
63
+ /**
64
+ * Internal: Set the print function for stdout output
65
+ */
66
+ _setPrintFn(fn: (messages: MessageData[], name: string, isError: boolean) => void): void {
67
+ this.printFn = fn
68
+ }
69
+
70
+ getId(): string {
71
+ return this.id
72
+ }
73
+
74
+ getName(): string {
75
+ return this.name
76
+ }
77
+
78
+ getIcon(): string | undefined {
79
+ return this.icon
80
+ }
81
+
82
+ getBadgeCount(): number {
83
+ return this.badgeCount
84
+ }
85
+
86
+ setBadgeCount(count: number): this {
87
+ this.badgeCount = count
88
+ this.notifyChange()
89
+ return this
90
+ }
91
+
92
+ isHidden(): boolean {
93
+ return this.hidden
94
+ }
95
+
96
+ setHidden(hidden: boolean): this {
97
+ this.hidden = hidden
98
+ this.manager?.onScreenVisibilityChanged(this)
99
+ this.notifyChange()
100
+ return this
101
+ }
102
+
103
+ show(): this {
104
+ return this.setHidden(false)
105
+ }
106
+
107
+ hide(): this {
108
+ return this.setHidden(true)
109
+ }
110
+
111
+ getStatus(): ScreenStatus {
112
+ return this.status
113
+ }
114
+
115
+ /**
116
+ * Set screen status. When success/fail and TUI is not bound, prints to stdout/stderr
117
+ */
118
+ setStatus(status: ScreenStatus): this {
119
+ const wasComplete = this.status === 'success' || this.status === 'fail'
120
+ this.status = status
121
+
122
+ // When transitioning to complete state
123
+ if (!wasComplete && (status === 'success' || status === 'fail')) {
124
+ // Only print if TUI is NOT bound (non-interactive mode)
125
+ if (!this.manager?.isTuiBound()) {
126
+ this.printToConsole()
127
+ }
128
+ this.manager?.onScreenCompleted(this)
129
+ }
130
+
131
+ this.notifyChange()
132
+ return this
133
+ }
134
+
135
+ /**
136
+ * Check if this screen is complete (success or fail)
137
+ */
138
+ isComplete(): boolean {
139
+ return this.status === 'success' || this.status === 'fail'
140
+ }
141
+
142
+ /**
143
+ * Check if this screen has been printed to console
144
+ */
145
+ hasPrintedToConsole(): boolean {
146
+ return this.hasPrinted
147
+ }
148
+
149
+ /**
150
+ * Force print to console (called when TUI unbinds)
151
+ */
152
+ _flushToConsole(force: boolean = false): void {
153
+ if (!this.hasPrinted && (this.isComplete() || force)) {
154
+ this.printToConsole()
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Get all messages for rendering
160
+ */
161
+ getMessages(): MessageData[] {
162
+ return [...this.messages]
163
+ }
164
+
165
+ /**
166
+ * Add a message to the screen (internal use by Logger)
167
+ */
168
+ addMessage(message: MessageData): void {
169
+ this.messages.push(message)
170
+ this.incrementVersion()
171
+ this.notifyChange()
172
+ }
173
+
174
+ /**
175
+ * Update a loading message (internal use by Logger)
176
+ */
177
+ updateMessage(id: string, updates: Partial<LoadingMessageData>): void {
178
+ const index = this.messages.findIndex((m) => m.id === id)
179
+ if (index !== -1) {
180
+ this.messages[index] = {
181
+ ...this.messages[index],
182
+ ...updates,
183
+ } as MessageData
184
+ this.incrementVersion()
185
+ this.notifyChange()
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Update a progress message (internal use by Logger)
191
+ */
192
+ updateProgressMessage(id: string, updates: Partial<ProgressMessageData>): void {
193
+ const index = this.messages.findIndex((m) => m.id === id)
194
+ if (index !== -1) {
195
+ this.messages[index] = {
196
+ ...this.messages[index],
197
+ ...updates,
198
+ } as MessageData
199
+ this.incrementVersion()
200
+ this.notifyChange()
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Clear all messages
206
+ */
207
+ clear(): this {
208
+ this.messages = []
209
+ this.incrementVersion()
210
+ this.notifyChange()
211
+ return this
212
+ }
213
+
214
+ // ============================================
215
+ // Prompt Methods
216
+ // ============================================
217
+
218
+ /**
219
+ * Add a prompt to the queue (internal use by Prompt class)
220
+ * Returns a promise that resolves when the user responds
221
+ */
222
+ _addPrompt(prompt: PromptData): Promise<string | boolean | string[]> {
223
+ return new Promise((resolve) => {
224
+ const pending: PendingPrompt = { data: prompt, resolve }
225
+
226
+ // If TUI is not bound (non-interactive mode), return default immediately
227
+ if (!this.manager?.isTuiBound()) {
228
+ this.resolvePromptWithDefault(prompt, resolve)
229
+ return
230
+ }
231
+
232
+ // Set up timeout if specified
233
+ if (prompt.timeout && prompt.timeoutStarted) {
234
+ pending.timeoutId = setTimeout(() => {
235
+ if (this.activePrompt === pending) {
236
+ this.resolvePromptWithDefault(prompt, resolve)
237
+ this.activePrompt = null
238
+ this.activateNextPrompt()
239
+ this.incrementVersion()
240
+ this.notifyChange()
241
+ } else {
242
+ // Remove from queue if not yet active
243
+ const idx = this.promptQueue.indexOf(pending)
244
+ if (idx !== -1) {
245
+ this.promptQueue.splice(idx, 1)
246
+ this.resolvePromptWithDefault(prompt, resolve)
247
+ this.incrementVersion()
248
+ }
249
+ }
250
+ }, prompt.timeout)
251
+ }
252
+
253
+ // Add to queue
254
+ this.promptQueue.push(pending)
255
+
256
+ // If no active prompt, activate this one
257
+ if (!this.activePrompt) {
258
+ this.activateNextPrompt()
259
+ }
260
+ })
261
+ }
262
+
263
+ /**
264
+ * Resolve a prompt with its default value
265
+ */
266
+ private resolvePromptWithDefault(
267
+ prompt: PromptData,
268
+ resolve: (value: string | boolean | string[]) => void,
269
+ ): void {
270
+ switch (prompt.type) {
271
+ case 'choice':
272
+ resolve(prompt.defaultChoice)
273
+ break
274
+ case 'confirm':
275
+ resolve(prompt.defaultValue)
276
+ break
277
+ case 'input':
278
+ resolve(prompt.defaultValue)
279
+ break
280
+ case 'multiChoice':
281
+ resolve(prompt.choices.filter((_, i) => prompt.selectedIndices.has(i)).map((c) => c.value))
282
+ break
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Get the currently active prompt (for rendering)
288
+ */
289
+ getActivePrompt(): PromptData | null {
290
+ return this.activePrompt?.data ?? null
291
+ }
292
+
293
+ /**
294
+ * Check if this screen has an active prompt
295
+ */
296
+ hasActivePrompt(): boolean {
297
+ return this.activePrompt !== null
298
+ }
299
+
300
+ /**
301
+ * Update prompt selection (for keyboard navigation)
302
+ */
303
+ updatePromptSelection(index: number): void {
304
+ if (!this.activePrompt) return
305
+
306
+ const prompt = this.activePrompt.data
307
+ if (prompt.type === 'choice') {
308
+ const maxIndex = prompt.choices.length - 1
309
+ ;(prompt as ChoicePromptData).selectedIndex = Math.max(0, Math.min(index, maxIndex))
310
+ } else if (prompt.type === 'multiChoice') {
311
+ const maxIndex = prompt.choices.length - 1
312
+ ;(prompt as MultiChoicePromptData).focusedIndex = Math.max(0, Math.min(index, maxIndex))
313
+ } else if (prompt.type === 'confirm') {
314
+ ;(prompt as ConfirmPromptData).selectedValue = index === 0
315
+ }
316
+ this.notifyChange()
317
+ }
318
+
319
+ /**
320
+ * Navigate prompt selection up
321
+ */
322
+ promptNavigateUp(): void {
323
+ if (!this.activePrompt) return
324
+
325
+ const prompt = this.activePrompt.data
326
+ if (prompt.type === 'choice') {
327
+ this.updatePromptSelection(prompt.selectedIndex - 1)
328
+ } else if (prompt.type === 'multiChoice') {
329
+ this.updatePromptSelection(prompt.focusedIndex - 1)
330
+ } else if (prompt.type === 'confirm') {
331
+ this.updatePromptSelection(prompt.selectedValue ? 0 : 1)
332
+ }
333
+ }
334
+
335
+ /**
336
+ * Navigate prompt selection down
337
+ */
338
+ promptNavigateDown(): void {
339
+ if (!this.activePrompt) return
340
+
341
+ const prompt = this.activePrompt.data
342
+ if (prompt.type === 'choice') {
343
+ this.updatePromptSelection(prompt.selectedIndex + 1)
344
+ } else if (prompt.type === 'multiChoice') {
345
+ this.updatePromptSelection(prompt.focusedIndex + 1)
346
+ } else if (prompt.type === 'confirm') {
347
+ this.updatePromptSelection(prompt.selectedValue ? 0 : 1)
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Toggle left/right for confirm prompts
353
+ */
354
+ promptNavigateLeft(): void {
355
+ if (!this.activePrompt) return
356
+ const prompt = this.activePrompt.data
357
+ if (prompt.type === 'confirm') {
358
+ ;(prompt as ConfirmPromptData).selectedValue = true
359
+ this.notifyChange()
360
+ }
361
+ }
362
+
363
+ promptNavigateRight(): void {
364
+ if (!this.activePrompt) return
365
+ const prompt = this.activePrompt.data
366
+ if (prompt.type === 'confirm') {
367
+ ;(prompt as ConfirmPromptData).selectedValue = false
368
+ this.notifyChange()
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Toggle selection for multiChoice prompts
374
+ */
375
+ promptToggleSelection(): void {
376
+ if (!this.activePrompt) return
377
+ const prompt = this.activePrompt.data
378
+ if (prompt.type === 'multiChoice') {
379
+ const p = prompt as MultiChoicePromptData
380
+ if (p.selectedIndices.has(p.focusedIndex)) {
381
+ p.selectedIndices.delete(p.focusedIndex)
382
+ } else if (p.selectedIndices.size < p.maxSelect) {
383
+ p.selectedIndices.add(p.focusedIndex)
384
+ }
385
+ this.notifyChange()
386
+ }
387
+ }
388
+
389
+ /**
390
+ * Enter input mode for choice prompts (if current selection allows input)
391
+ * or for input prompts (always)
392
+ */
393
+ promptEnterInputMode(): boolean {
394
+ if (!this.activePrompt) return false
395
+
396
+ const prompt = this.activePrompt.data
397
+ if (prompt.type === 'choice') {
398
+ const choice = prompt.choices[prompt.selectedIndex]
399
+ if (choice?.input) {
400
+ ;(prompt as ChoicePromptData).inputMode = true
401
+ this.notifyChange()
402
+ return true
403
+ }
404
+ } else if (prompt.type === 'input') {
405
+ // Input prompts are always in input mode
406
+ return true
407
+ }
408
+ return false
409
+ }
410
+
411
+ /**
412
+ * Exit input mode for choice prompts
413
+ */
414
+ promptExitInputMode(): void {
415
+ if (!this.activePrompt) return
416
+
417
+ const prompt = this.activePrompt.data
418
+ if (prompt.type === 'choice' && prompt.inputMode) {
419
+ ;(prompt as ChoicePromptData).inputMode = false
420
+ this.notifyChange()
421
+ }
422
+ // Input prompts cannot exit input mode
423
+ }
424
+
425
+ /**
426
+ * Check if prompt is in input mode
427
+ */
428
+ isPromptInInputMode(): boolean {
429
+ if (!this.activePrompt) return false
430
+ const prompt = this.activePrompt.data
431
+ if (prompt.type === 'choice') {
432
+ return prompt.inputMode
433
+ }
434
+ if (prompt.type === 'input') {
435
+ return true // Input prompts are always in input mode
436
+ }
437
+ return false
438
+ }
439
+
440
+ /**
441
+ * Update input value for choice prompts
442
+ */
443
+ promptUpdateInput(value: string): void {
444
+ if (!this.activePrompt) return
445
+
446
+ const prompt = this.activePrompt.data
447
+ if (prompt.type === 'choice' && prompt.inputMode) {
448
+ ;(prompt as ChoicePromptData).inputValue = value
449
+ this.notifyChange()
450
+ } else if (prompt.type === 'input') {
451
+ ;(prompt as InputPromptData).value = value
452
+ this.notifyChange()
453
+ }
454
+ }
455
+
456
+ /**
457
+ * Append character to input
458
+ */
459
+ promptAppendInput(char: string): void {
460
+ if (!this.activePrompt) return
461
+
462
+ const prompt = this.activePrompt.data
463
+ if (prompt.type === 'choice' && prompt.inputMode) {
464
+ ;(prompt as ChoicePromptData).inputValue += char
465
+ this.notifyChange()
466
+ } else if (prompt.type === 'input') {
467
+ ;(prompt as InputPromptData).value += char
468
+ this.notifyChange()
469
+ }
470
+ }
471
+
472
+ /**
473
+ * Delete last character from input
474
+ */
475
+ promptDeleteLastChar(): void {
476
+ if (!this.activePrompt) return
477
+
478
+ const prompt = this.activePrompt.data
479
+ if (prompt.type === 'choice' && prompt.inputMode) {
480
+ ;(prompt as ChoicePromptData).inputValue = prompt.inputValue.slice(0, -1)
481
+ this.notifyChange()
482
+ } else if (prompt.type === 'input') {
483
+ ;(prompt as InputPromptData).value = prompt.value.slice(0, -1)
484
+ this.notifyChange()
485
+ }
486
+ }
487
+
488
+ /**
489
+ * Check if multiChoice prompt can be submitted (meets minSelect requirement)
490
+ */
491
+ canSubmitPrompt(): boolean {
492
+ if (!this.activePrompt) return false
493
+ const prompt = this.activePrompt.data
494
+ if (prompt.type === 'multiChoice') {
495
+ return prompt.selectedIndices.size >= prompt.minSelect
496
+ }
497
+ return true
498
+ }
499
+
500
+ /**
501
+ * Submit the current prompt selection
502
+ */
503
+ promptSubmit(): void {
504
+ if (!this.activePrompt) return
505
+
506
+ const prompt = this.activePrompt.data
507
+
508
+ // Check if can submit (for multiChoice min selection)
509
+ if (!this.canSubmitPrompt()) return
510
+
511
+ prompt.resolved = true
512
+
513
+ // Clear timeout if any
514
+ if (this.activePrompt.timeoutId) {
515
+ clearTimeout(this.activePrompt.timeoutId)
516
+ }
517
+
518
+ switch (prompt.type) {
519
+ case 'choice': {
520
+ const choice = prompt.choices[prompt.selectedIndex]
521
+ const value = choice?.input && prompt.inputValue ? prompt.inputValue : (choice?.value ?? '')
522
+ ;(prompt as ChoicePromptData).resolvedValue = value
523
+ this.activePrompt.resolve(value)
524
+ break
525
+ }
526
+ case 'confirm': {
527
+ ;(prompt as ConfirmPromptData).resolvedValue = prompt.selectedValue
528
+ this.activePrompt.resolve(prompt.selectedValue)
529
+ break
530
+ }
531
+ case 'input': {
532
+ ;(prompt as InputPromptData).resolvedValue = prompt.value
533
+ this.activePrompt.resolve(prompt.value)
534
+ break
535
+ }
536
+ case 'multiChoice': {
537
+ const values = prompt.choices
538
+ .filter((_, i) => prompt.selectedIndices.has(i))
539
+ .map((c) => c.value)
540
+ ;(prompt as MultiChoicePromptData).resolvedValues = values
541
+ this.activePrompt.resolve(values)
542
+ break
543
+ }
544
+ }
545
+
546
+ // Move to next prompt
547
+ this.activePrompt = null
548
+ this.activateNextPrompt()
549
+ this.incrementVersion()
550
+ this.notifyChange()
551
+ }
552
+
553
+ /**
554
+ * Activate the next prompt in the queue
555
+ */
556
+ private activateNextPrompt(): void {
557
+ if (this.promptQueue.length > 0) {
558
+ this.activePrompt = this.promptQueue.shift()!
559
+ // Notify manager to focus this screen
560
+ this.manager?.onScreenPromptActivated(this)
561
+ this.incrementVersion()
562
+ this.notifyChange()
563
+ }
564
+ }
565
+
566
+ /**
567
+ * Register a change listener for re-renders
568
+ */
569
+ onChange(listener: () => void): () => void {
570
+ this.changeListeners.add(listener)
571
+ return () => this.changeListeners.delete(listener)
572
+ }
573
+
574
+ private notifyChange(): void {
575
+ this.changeListeners.forEach((listener) => listener())
576
+ }
577
+
578
+ /**
579
+ * Print all messages to stdout/stderr with ANSI colors
580
+ */
581
+ private printToConsole(): void {
582
+ if (this.hasPrinted) return
583
+
584
+ const isError = this.status === 'fail'
585
+ if (this.printFn) {
586
+ this.printFn(this.messages, this.name, isError)
587
+ this.hasPrinted = true
588
+ }
589
+ }
590
+ }