@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,178 @@
1
+ import { describe, expect, it } from 'vitest'
2
+
3
+ import { captureTrace, formatObject } from '../../utils/format.ts'
4
+
5
+ describe('formatObject', () => {
6
+ describe('primitives', () => {
7
+ it('should format null', () => {
8
+ expect(formatObject(null)).toBe('null')
9
+ })
10
+
11
+ it('should format undefined', () => {
12
+ expect(formatObject(undefined)).toBe('undefined')
13
+ })
14
+
15
+ it('should format strings with quotes', () => {
16
+ expect(formatObject('hello')).toBe('"hello"')
17
+ })
18
+
19
+ it('should format numbers', () => {
20
+ expect(formatObject(42)).toBe('42')
21
+ expect(formatObject(3.14)).toBe('3.14')
22
+ expect(formatObject(-10)).toBe('-10')
23
+ })
24
+
25
+ it('should format booleans', () => {
26
+ expect(formatObject(true)).toBe('true')
27
+ expect(formatObject(false)).toBe('false')
28
+ })
29
+ })
30
+
31
+ describe('special types', () => {
32
+ it('should format Date objects as ISO string', () => {
33
+ const date = new Date('2024-01-15T10:30:00.000Z')
34
+ expect(formatObject(date)).toBe('2024-01-15T10:30:00.000Z')
35
+ })
36
+
37
+ it('should format Error objects as name: message', () => {
38
+ const error = new Error('Something went wrong')
39
+ expect(formatObject(error)).toBe('Error: Something went wrong')
40
+ })
41
+
42
+ it('should format custom error types', () => {
43
+ class CustomError extends Error {
44
+ name = 'CustomError'
45
+ }
46
+ const error = new CustomError('Custom message')
47
+ expect(formatObject(error)).toBe('CustomError: Custom message')
48
+ })
49
+
50
+ it('should format functions as [Function]', () => {
51
+ const fn = () => {}
52
+ expect(formatObject(fn)).toBe('[Function]')
53
+
54
+ function namedFn() {}
55
+ expect(formatObject(namedFn)).toBe('[Function]')
56
+ })
57
+ })
58
+
59
+ describe('arrays', () => {
60
+ it('should format empty arrays', () => {
61
+ expect(formatObject([])).toBe('[]')
62
+ })
63
+
64
+ it('should format arrays with primitives', () => {
65
+ const result = formatObject([1, 2, 3])
66
+ expect(result).toContain('1')
67
+ expect(result).toContain('2')
68
+ expect(result).toContain('3')
69
+ })
70
+
71
+ it('should format nested arrays with indentation', () => {
72
+ const result = formatObject([1, [2, 3]])
73
+ expect(result).toContain('[')
74
+ expect(result).toContain(']')
75
+ // Should have nested structure
76
+ expect(result.split('\n').length).toBeGreaterThan(1)
77
+ })
78
+ })
79
+
80
+ describe('objects', () => {
81
+ it('should format empty objects', () => {
82
+ expect(formatObject({})).toBe('{}')
83
+ })
84
+
85
+ it('should format objects with properties', () => {
86
+ const result = formatObject({ name: 'test', value: 42 })
87
+ expect(result).toContain('name:')
88
+ expect(result).toContain('"test"')
89
+ expect(result).toContain('value:')
90
+ expect(result).toContain('42')
91
+ })
92
+
93
+ it('should format nested objects with indentation', () => {
94
+ const result = formatObject({
95
+ outer: {
96
+ inner: 'value',
97
+ },
98
+ })
99
+ expect(result).toContain('outer:')
100
+ expect(result).toContain('inner:')
101
+ // Should have nested structure
102
+ expect(result.split('\n').length).toBeGreaterThan(1)
103
+ })
104
+ })
105
+
106
+ describe('depth limit', () => {
107
+ it('should return [Array] for arrays at depth limit', () => {
108
+ const deep = { level1: { level2: [1, 2, 3] } }
109
+ const result = formatObject(deep, 2)
110
+ expect(result).toContain('[Array]')
111
+ })
112
+
113
+ it('should return [Object] for objects at depth limit', () => {
114
+ const deep = { level1: { level2: { level3: 'value' } } }
115
+ const result = formatObject(deep, 2)
116
+ expect(result).toContain('[Object]')
117
+ })
118
+
119
+ it('should use constructor name for custom objects at depth limit', () => {
120
+ class CustomClass {
121
+ value = 1
122
+ }
123
+ const deep = { level1: { level2: new CustomClass() } }
124
+ const result = formatObject(deep, 2)
125
+ expect(result).toContain('[CustomClass]')
126
+ })
127
+
128
+ it('should respect custom depth parameter', () => {
129
+ const deep = { a: { b: { c: { d: 'value' } } } }
130
+
131
+ // Depth 1: only see first level
132
+ const depth1 = formatObject(deep, 1)
133
+ expect(depth1).toContain('[Object]')
134
+
135
+ // Depth 3: should see deeper
136
+ const depth3 = formatObject(deep, 3)
137
+ expect(depth3).toContain('c:')
138
+ })
139
+ })
140
+
141
+ describe('mixed types', () => {
142
+ it('should handle complex nested structures', () => {
143
+ const complex = {
144
+ users: [
145
+ { name: 'Alice', age: 30 },
146
+ { name: 'Bob', age: 25 },
147
+ ],
148
+ metadata: {
149
+ count: 2,
150
+ timestamp: new Date('2024-01-01T00:00:00.000Z'),
151
+ },
152
+ }
153
+
154
+ const result = formatObject(complex, 3)
155
+ expect(result).toContain('users:')
156
+ expect(result).toContain('Alice')
157
+ expect(result).toContain('metadata:')
158
+ expect(result).toContain('count:')
159
+ })
160
+ })
161
+ })
162
+
163
+ describe('captureTrace', () => {
164
+ it('should return a stack trace string', () => {
165
+ const trace = captureTrace()
166
+ expect(typeof trace).toBe('string')
167
+ expect(trace.length).toBeGreaterThan(0)
168
+ })
169
+
170
+ it('should filter internal frames (skip first 4 lines)', () => {
171
+ const trace = captureTrace()
172
+ // The trace should not contain "captureTrace" in the first visible line
173
+ // since it filters out internal frames
174
+ const lines = trace.split('\n')
175
+ // First line should be the caller, not captureTrace itself
176
+ expect(lines[0]).not.toContain('captureTrace')
177
+ })
178
+ })
@@ -0,0 +1,39 @@
1
+ import { LoggerProvider } from '../../context/index.ts'
2
+ import { darkTheme } from '../../themes/index.ts'
3
+
4
+ import type { ReactNode } from 'react'
5
+ import type { Theme, LogLevelColorMap } from '../../types/index.ts'
6
+
7
+ export interface RenderContextOptions {
8
+ theme?: Theme
9
+ levelColors?: Partial<LogLevelColorMap>
10
+ }
11
+
12
+ /**
13
+ * Wrap a component with LoggerProvider for testing.
14
+ * Returns the wrapped component tree.
15
+ */
16
+ export function wrapWithContext(
17
+ component: ReactNode,
18
+ options: RenderContextOptions = {},
19
+ ): ReactNode {
20
+ const { theme = darkTheme, levelColors } = options
21
+
22
+ return (
23
+ <LoggerProvider theme={theme} levelColors={levelColors}>
24
+ {component}
25
+ </LoggerProvider>
26
+ )
27
+ }
28
+
29
+ /**
30
+ * Create a default context value for testing components outside LoggerProvider.
31
+ */
32
+ export function createMockContextValue(overrides: Partial<RenderContextOptions> = {}) {
33
+ return {
34
+ syntaxStyle: undefined,
35
+ treeSitterClient: undefined,
36
+ levelColors: darkTheme.logLevels,
37
+ theme: overrides.theme ?? darkTheme,
38
+ }
39
+ }
@@ -0,0 +1,48 @@
1
+ import { TestContainer } from '@navios/core/testing'
2
+
3
+ import { Screen, ScreenLogger, Prompt } from '../../tokens/index.ts'
4
+
5
+ import { createMockScreenInstance } from './factories.ts'
6
+
7
+ import type { ScreenInstance } from '../../services/index.ts'
8
+ import type { MockScreenInstance } from './factories.ts'
9
+
10
+ /**
11
+ * Create a pre-configured TestContainer for commander-tui tests.
12
+ * Includes common bindings for Screen token with a mock instance.
13
+ */
14
+ export function createTestContainer(): TestContainer {
15
+ return new TestContainer()
16
+ }
17
+
18
+ /**
19
+ * Bind a mock ScreenInstance to the Screen token.
20
+ * Returns the mock for assertions.
21
+ */
22
+ export function bindMockScreen(
23
+ container: TestContainer,
24
+ overrides?: Partial<MockScreenInstance>,
25
+ ): MockScreenInstance {
26
+ const mockScreen = createMockScreenInstance(overrides)
27
+ // Cast through unknown since MockScreenInstance has mock functions instead of real implementations
28
+ container.bind(Screen).toValue(mockScreen as unknown as ScreenInstance)
29
+ return mockScreen
30
+ }
31
+
32
+ /**
33
+ * Rebind a mock ScreenInstance to the Screen token.
34
+ * Must invalidate the previous instance first.
35
+ */
36
+ export function rebindMockScreen(
37
+ container: TestContainer,
38
+ previousInstance: MockScreenInstance,
39
+ overrides?: Partial<MockScreenInstance>,
40
+ ): MockScreenInstance {
41
+ const mockScreen = createMockScreenInstance(overrides)
42
+ container.invalidate(previousInstance as unknown as ScreenInstance)
43
+ // Cast through unknown since MockScreenInstance has mock functions instead of real implementations
44
+ container.bind(Screen).toValue(mockScreen as unknown as ScreenInstance)
45
+ return mockScreen
46
+ }
47
+
48
+ export { TestContainer, Screen, ScreenLogger, Prompt }
@@ -0,0 +1,241 @@
1
+ import { useMemo } from 'react'
2
+
3
+ import { useLoggerContext } from '../../context/index.ts'
4
+ import { ERROR_HIGHLIGHT_COLORS, HEADER_COLORS, resolveFiletype } from '../../utils/index.ts'
5
+
6
+ import type {
7
+ FileLogProps,
8
+ FileLogFullProps,
9
+ FileLogDiffProps,
10
+ FileLogPartialProps,
11
+ } from '../../types/index.ts'
12
+
13
+ /**
14
+ * FileLog - Displays file content, diffs, or partial files with error highlighting.
15
+ *
16
+ * Three modes:
17
+ * 1. "full" - Display complete file with syntax highlighting
18
+ * 2. "diff" - Display unified diff using <diff> component
19
+ * 3. "partial" - Display file excerpt with optional error line highlighting
20
+ *
21
+ * @example Full file
22
+ * <FileLog
23
+ * mode="full"
24
+ * filePath="src/index.ts"
25
+ * content={fileContent}
26
+ * />
27
+ *
28
+ * @example Diff
29
+ * <FileLog
30
+ * mode="diff"
31
+ * filePath="src/api.ts"
32
+ * diff={unifiedDiff}
33
+ * view="unified"
34
+ * />
35
+ *
36
+ * @example Partial with error
37
+ * <FileLog
38
+ * mode="partial"
39
+ * filePath="src/utils.ts"
40
+ * content={excerpt}
41
+ * startLine={42}
42
+ * errorLines={[45, 46]}
43
+ * />
44
+ */
45
+ export function FileLog(props: FileLogProps) {
46
+ const { syntaxStyle, treeSitterClient } = useLoggerContext()
47
+
48
+ // Auto-detect filetype from path
49
+ const filetype = props.filetype ?? resolveFiletype(props.filePath)
50
+ const showHeader = props.showHeader ?? true
51
+ const showLineNumbers = props.showLineNumbers ?? true
52
+
53
+ return (
54
+ <box flexDirection="column" marginBottom={1}>
55
+ {/* File header */}
56
+ {showHeader && (
57
+ <box
58
+ backgroundColor={props.headerBackgroundColor ?? HEADER_COLORS.background}
59
+ paddingLeft={1}
60
+ paddingRight={1}
61
+ border={['bottom']}
62
+ borderColor={HEADER_COLORS.border}
63
+ >
64
+ <text fg={HEADER_COLORS.text}>{props.filePath}</text>
65
+ </box>
66
+ )}
67
+
68
+ {/* Content based on mode */}
69
+ {props.mode === 'full' && (
70
+ <FileLogFull
71
+ {...props}
72
+ filetype={filetype}
73
+ syntaxStyle={syntaxStyle}
74
+ treeSitterClient={treeSitterClient}
75
+ showLineNumbers={showLineNumbers}
76
+ />
77
+ )}
78
+
79
+ {props.mode === 'diff' && (
80
+ <FileLogDiff
81
+ {...props}
82
+ filetype={filetype}
83
+ syntaxStyle={syntaxStyle}
84
+ treeSitterClient={treeSitterClient}
85
+ />
86
+ )}
87
+
88
+ {props.mode === 'partial' && (
89
+ <FileLogPartial
90
+ {...props}
91
+ filetype={filetype}
92
+ syntaxStyle={syntaxStyle}
93
+ treeSitterClient={treeSitterClient}
94
+ showLineNumbers={showLineNumbers}
95
+ />
96
+ )}
97
+ </box>
98
+ )
99
+ }
100
+
101
+ interface FileLogInternalProps {
102
+ filetype?: string
103
+ syntaxStyle?: ReturnType<typeof useLoggerContext>['syntaxStyle']
104
+ treeSitterClient?: ReturnType<typeof useLoggerContext>['treeSitterClient']
105
+ }
106
+
107
+ /**
108
+ * Full file display with syntax highlighting.
109
+ */
110
+ function FileLogFull({
111
+ content,
112
+ filetype,
113
+ syntaxStyle,
114
+ treeSitterClient,
115
+ }: FileLogFullProps & FileLogInternalProps) {
116
+ if (!syntaxStyle) {
117
+ // Fallback to plain text display if no syntax style available
118
+ return (
119
+ <box paddingLeft={1} paddingRight={1}>
120
+ <text>{content}</text>
121
+ </box>
122
+ )
123
+ }
124
+
125
+ return (
126
+ <code
127
+ content={content}
128
+ filetype={filetype}
129
+ syntaxStyle={syntaxStyle}
130
+ treeSitterClient={treeSitterClient}
131
+ />
132
+ )
133
+ }
134
+
135
+ /**
136
+ * Diff display using the built-in <diff> component.
137
+ */
138
+ function FileLogDiff({
139
+ diff,
140
+ view = 'unified',
141
+ filetype,
142
+ syntaxStyle,
143
+ treeSitterClient,
144
+ }: FileLogDiffProps & FileLogInternalProps) {
145
+ if (!syntaxStyle) {
146
+ // Fallback to plain text display if no syntax style available
147
+ return (
148
+ <box paddingLeft={1} paddingRight={1}>
149
+ <text>{diff}</text>
150
+ </box>
151
+ )
152
+ }
153
+
154
+ return (
155
+ <diff
156
+ diff={diff}
157
+ view={view}
158
+ filetype={filetype}
159
+ syntaxStyle={syntaxStyle}
160
+ treeSitterClient={treeSitterClient}
161
+ />
162
+ )
163
+ }
164
+
165
+ /**
166
+ * Partial file display with error line highlighting.
167
+ *
168
+ * This component renders a file excerpt and highlights specific lines
169
+ * as errors using box overlays with colored backgrounds.
170
+ */
171
+ function FileLogPartial({
172
+ content,
173
+ startLine,
174
+ errorLines = [],
175
+ errorLineBackground,
176
+ errorLineBorderColor,
177
+ filetype,
178
+ syntaxStyle,
179
+ treeSitterClient,
180
+ showLineNumbers,
181
+ }: FileLogPartialProps & FileLogInternalProps) {
182
+ // Parse content into lines for error highlighting
183
+ const lines = useMemo(() => content.split('\n'), [content])
184
+
185
+ // Calculate which lines are errors (relative to startLine)
186
+ const errorLineSet = useMemo(() => new Set(errorLines), [errorLines])
187
+
188
+ // Error colors
189
+ const errorBg = errorLineBackground ?? ERROR_HIGHLIGHT_COLORS.background
190
+ const errorBorder = errorLineBorderColor ?? ERROR_HIGHLIGHT_COLORS.border
191
+
192
+ // Line number width based on max line number
193
+ const maxLineNum = startLine + lines.length - 1
194
+ const lineNumWidth = Math.max(4, String(maxLineNum).length + 1)
195
+
196
+ return (
197
+ <box flexDirection="column">
198
+ {lines.map((line, index) => {
199
+ const lineNumber = startLine + index
200
+ const isError = errorLineSet.has(lineNumber)
201
+
202
+ return (
203
+ <box
204
+ key={lineNumber}
205
+ flexDirection="row"
206
+ backgroundColor={isError ? errorBg : undefined}
207
+ border={isError ? ['left'] : undefined}
208
+ borderColor={isError ? errorBorder : undefined}
209
+ >
210
+ {/* Line number gutter */}
211
+ {showLineNumbers && (
212
+ <box
213
+ width={lineNumWidth}
214
+ backgroundColor={
215
+ isError ? ERROR_HIGHLIGHT_COLORS.gutterBackground : HEADER_COLORS.background
216
+ }
217
+ paddingRight={1}
218
+ >
219
+ <text fg="#6B7280">{String(lineNumber).padStart(lineNumWidth - 1)}</text>
220
+ </box>
221
+ )}
222
+
223
+ {/* Code line */}
224
+ <box flexGrow={1} paddingLeft={1}>
225
+ {syntaxStyle ? (
226
+ <code
227
+ content={line}
228
+ filetype={filetype}
229
+ syntaxStyle={syntaxStyle}
230
+ treeSitterClient={treeSitterClient}
231
+ />
232
+ ) : (
233
+ <text>{line}</text>
234
+ )}
235
+ </box>
236
+ </box>
237
+ )
238
+ })}
239
+ </box>
240
+ )
241
+ }
@@ -0,0 +1 @@
1
+ export * from './file_log.tsx'
@@ -0,0 +1,79 @@
1
+ import { TextAttributes } from '@opentui/core'
2
+
3
+ import type { LogLevel } from '@navios/core'
4
+
5
+ import { useTheme } from '../../hooks/index.ts'
6
+ import { ALL_LOG_LEVELS } from '../../types/index.ts'
7
+
8
+ import type { FilterState, LevelCounts } from '../../types/index.ts'
9
+
10
+ export interface FilterBarProps {
11
+ filter: FilterState
12
+ levelCounts: LevelCounts
13
+ }
14
+
15
+ const LEVEL_LABELS: Record<LogLevel, string> = {
16
+ verbose: 'V',
17
+ debug: 'D',
18
+ log: 'L',
19
+ warn: 'W',
20
+ error: 'E',
21
+ fatal: 'F',
22
+ }
23
+
24
+ export function FilterBar({ filter, levelCounts }: FilterBarProps) {
25
+ const theme = useTheme()
26
+ const isSearchFocused = filter.focusedField === 'search'
27
+
28
+ return (
29
+ <box
30
+ flexDirection="column"
31
+ backgroundColor={theme.filter.background}
32
+ borderColor={theme.filter.border}
33
+ border={['bottom']}
34
+ paddingLeft={1}
35
+ paddingRight={1}
36
+ >
37
+ {/* Search input row */}
38
+ <box flexDirection="row" gap={1}>
39
+ <text fg={isSearchFocused ? theme.colors.primary : theme.filter.textDim}>/</text>
40
+ <text fg={filter.searchQuery ? theme.filter.inputText : theme.filter.inputPlaceholder}>
41
+ {filter.searchQuery || 'Search logs...'}
42
+ </text>
43
+ {isSearchFocused && (
44
+ <text fg={theme.filter.cursor} attributes={TextAttributes.BLINK}>
45
+ _
46
+ </text>
47
+ )}
48
+ </box>
49
+
50
+ {/* Level filters row */}
51
+ <box flexDirection="row" gap={1}>
52
+ <text fg={theme.filter.textDim}>Levels:</text>
53
+ {ALL_LOG_LEVELS.map((level, index) => {
54
+ const isEnabled = filter.enabledLevels.has(level)
55
+ const count = levelCounts[level]
56
+ const levelColor = theme.logLevels[level].border
57
+ const isLevelsFocused = filter.focusedField === 'levels'
58
+
59
+ return (
60
+ <box key={level} flexDirection="row">
61
+ <text
62
+ fg={isEnabled ? levelColor : theme.filter.inactiveLevel}
63
+ attributes={isLevelsFocused ? TextAttributes.BOLD : undefined}
64
+ >
65
+ {index + 1}:{LEVEL_LABELS[level]}
66
+ </text>
67
+ {count > 0 && <text fg={theme.filter.textDim}>({count > 99 ? '99+' : count})</text>}
68
+ </box>
69
+ )
70
+ })}
71
+ </box>
72
+
73
+ {/* Hints */}
74
+ <box flexDirection="row">
75
+ <text fg={theme.filter.textDim}>Tab: switch fields | 1-7: toggle levels | Esc: close</text>
76
+ </box>
77
+ </box>
78
+ )
79
+ }
@@ -0,0 +1 @@
1
+ export { FilterBar } from './filter_bar.tsx'
@@ -0,0 +1,100 @@
1
+ import { TextAttributes } from '@opentui/core'
2
+
3
+ import { useTheme } from '../../hooks/index.ts'
4
+ import { formatKeyBinding } from '../../keyboard/index.ts'
5
+
6
+ import type { KeyBinding, KeyBindingCategory } from '../../types/index.ts'
7
+
8
+ export interface HelpOverlayProps {
9
+ bindings: KeyBinding[]
10
+ }
11
+
12
+ const CATEGORY_ORDER: KeyBindingCategory[] = ['general', 'navigation', 'screen', 'filter', 'prompt']
13
+
14
+ const CATEGORY_LABELS: Record<KeyBindingCategory, string> = {
15
+ general: 'General',
16
+ navigation: 'Navigation',
17
+ screen: 'Screen',
18
+ filter: 'Filter',
19
+ prompt: 'Prompts',
20
+ }
21
+
22
+ function groupByCategory(bindings: KeyBinding[]): Record<KeyBindingCategory, KeyBinding[]> {
23
+ const grouped: Record<KeyBindingCategory, KeyBinding[]> = {
24
+ general: [],
25
+ navigation: [],
26
+ screen: [],
27
+ filter: [],
28
+ prompt: [],
29
+ }
30
+
31
+ for (const binding of bindings) {
32
+ if (binding.description) {
33
+ grouped[binding.category].push(binding)
34
+ }
35
+ }
36
+
37
+ return grouped
38
+ }
39
+
40
+ export function HelpOverlay({ bindings }: HelpOverlayProps) {
41
+ const theme = useTheme()
42
+ const grouped = groupByCategory(bindings)
43
+
44
+ return (
45
+ <box
46
+ position="absolute"
47
+ top={1}
48
+ left={2}
49
+ right={2}
50
+ bottom={1}
51
+ backgroundColor={theme.help.background}
52
+ borderColor={theme.help.border}
53
+ border={['top', 'bottom', 'left', 'right']}
54
+ flexDirection="column"
55
+ paddingLeft={2}
56
+ paddingRight={2}
57
+ paddingTop={1}
58
+ paddingBottom={1}
59
+ >
60
+ {/* Header */}
61
+ <box marginBottom={1}>
62
+ <text fg={theme.help.title} attributes={TextAttributes.BOLD}>
63
+ Keyboard Shortcuts
64
+ </text>
65
+ </box>
66
+
67
+ {/* Categories */}
68
+ <scrollbox scrollY flexGrow={1}>
69
+ <box flexDirection="column" gap={1}>
70
+ {CATEGORY_ORDER.map((category) => {
71
+ const categoryBindings = grouped[category]
72
+ if (categoryBindings.length === 0) return null
73
+
74
+ return (
75
+ <box key={category} flexDirection="column">
76
+ <text fg={theme.help.category} attributes={TextAttributes.BOLD}>
77
+ {CATEGORY_LABELS[category]}
78
+ </text>
79
+
80
+ {categoryBindings.map((binding, index) => (
81
+ <box key={`${binding.key}-${index}`} flexDirection="row" paddingLeft={2}>
82
+ <text fg={theme.help.key} width={14}>
83
+ {formatKeyBinding(binding)}
84
+ </text>
85
+ <text fg={theme.help.description}>{binding.description}</text>
86
+ </box>
87
+ ))}
88
+ </box>
89
+ )
90
+ })}
91
+ </box>
92
+ </scrollbox>
93
+
94
+ {/* Footer */}
95
+ <box marginTop={1} borderColor={theme.separator.line} border={['top']} paddingTop={1}>
96
+ <text fg={theme.help.hint}>Press ? or Esc to close</text>
97
+ </box>
98
+ </box>
99
+ )
100
+ }
@@ -0,0 +1 @@
1
+ export { HelpOverlay } from './help_overlay.tsx'