@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,3034 @@
1
+ import { jsx, jsxs } from "@opentui/react/jsx-runtime";
2
+ import { RGBA, SyntaxStyle, TextAttributes } from "@opentui/core";
3
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
+ import { useKeyboard } from "@opentui/react";
5
+
6
+ //#region src/types/filter.types.ts
7
+ /**
8
+ * All log levels in order.
9
+ */ const ALL_LOG_LEVELS = [
10
+ "verbose",
11
+ "debug",
12
+ "log",
13
+ "warn",
14
+ "error",
15
+ "fatal"
16
+ ];
17
+ /**
18
+ * Create a default filter state.
19
+ */ function createDefaultFilterState() {
20
+ return {
21
+ enabledLevels: new Set(ALL_LOG_LEVELS),
22
+ searchQuery: "",
23
+ isVisible: false,
24
+ focusedField: "search"
25
+ };
26
+ }
27
+ /**
28
+ * Check if any filtering is active.
29
+ */ function hasActiveFilter(filter) {
30
+ return filter.searchQuery !== "" || filter.enabledLevels.size < ALL_LOG_LEVELS.length;
31
+ }
32
+
33
+ //#endregion
34
+ //#region src/themes/dark.ts
35
+ /**
36
+ * Default dark theme.
37
+ * Migrated from the scattered color files in utils/colors/.
38
+ */ const darkTheme = {
39
+ name: "dark",
40
+ logLevels: {
41
+ verbose: {
42
+ border: "#6B7280",
43
+ background: "#6B728015"
44
+ },
45
+ debug: {
46
+ border: "#8B5CF6",
47
+ background: "#8B5CF615"
48
+ },
49
+ log: {
50
+ border: "#3B82F6",
51
+ background: "#3B82F615"
52
+ },
53
+ warn: {
54
+ border: "#F59E0B",
55
+ background: "#F59E0B15"
56
+ },
57
+ error: {
58
+ border: "#EF4444",
59
+ background: "#EF444415"
60
+ },
61
+ fatal: {
62
+ border: "#DC2626",
63
+ background: "#DC262625",
64
+ text: "#FCA5A5"
65
+ }
66
+ },
67
+ sidebar: {
68
+ background: void 0,
69
+ selectedBackground: "#1F293780",
70
+ hoverBackground: "#374151",
71
+ text: "#E5E7EB",
72
+ textDim: "#6B7280",
73
+ border: "#374151",
74
+ badge: "#3B82F6",
75
+ focusBorder: "#3B82F6"
76
+ },
77
+ header: {
78
+ background: void 0,
79
+ text: "#F9FAFB",
80
+ border: "#374151"
81
+ },
82
+ statusIndicators: {
83
+ waiting: {
84
+ icon: "○",
85
+ color: "#6B7280"
86
+ },
87
+ pending: {
88
+ icon: "◐",
89
+ color: "#F59E0B"
90
+ },
91
+ success: {
92
+ icon: "✓",
93
+ color: "#22C55E"
94
+ },
95
+ fail: {
96
+ icon: "✗",
97
+ color: "#EF4444"
98
+ }
99
+ },
100
+ separator: {
101
+ line: "#374151",
102
+ text: "#6B7280"
103
+ },
104
+ progress: {
105
+ border: "#3B82F6",
106
+ background: "#3B82F615",
107
+ barFilled: "#3B82F6",
108
+ barEmpty: "#374151",
109
+ text: "#E5E7EB",
110
+ textDim: "#9CA3AF",
111
+ complete: "#22C55E",
112
+ completeBackground: "#22C55E15",
113
+ failed: "#EF4444",
114
+ failedBackground: "#EF444415"
115
+ },
116
+ group: {
117
+ border: "#6B7280",
118
+ background: "#6B728010",
119
+ headerText: "#E5E7EB",
120
+ icon: "#9CA3AF"
121
+ },
122
+ table: {
123
+ border: "#3B82F6",
124
+ background: "#3B82F615",
125
+ headerText: "#F9FAFB",
126
+ cellText: "#E5E7EB",
127
+ title: "#F9FAFB",
128
+ separator: "#3B82F650"
129
+ },
130
+ file: {
131
+ border: "#3B82F6",
132
+ background: "#3B82F615",
133
+ headerText: "#F9FAFB",
134
+ headerBackground: "#3B82F625"
135
+ },
136
+ prompt: {
137
+ question: "#F9FAFB",
138
+ optionText: "#E5E7EB",
139
+ optionTextDim: "#9CA3AF",
140
+ optionSelected: "#3B82F6",
141
+ optionSelectedBackground: "#1E3A5F",
142
+ confirmButton: "#22C55E",
143
+ cancelButton: "#EF4444",
144
+ buttonBackground: "#374151",
145
+ buttonSelectedBackground: "#1F2937",
146
+ inputBorder: "#3B82F6",
147
+ inputBackground: "#1F2937",
148
+ inputText: "#F9FAFB",
149
+ inputPlaceholder: "#6B7280",
150
+ inputCursor: "#3B82F6",
151
+ border: "#374151",
152
+ focusBorder: "#3B82F6"
153
+ },
154
+ errorHighlight: {
155
+ background: "#EF444425",
156
+ border: "#EF4444",
157
+ gutterBackground: "#EF444440"
158
+ },
159
+ filter: {
160
+ background: "#1F293780",
161
+ border: "#3B82F6",
162
+ text: "#E5E7EB",
163
+ textDim: "#6B7280",
164
+ inputBackground: "#1F2937",
165
+ inputText: "#F9FAFB",
166
+ inputPlaceholder: "#6B7280",
167
+ cursor: "#3B82F6",
168
+ activeLevel: "#3B82F6",
169
+ inactiveLevel: "#4B5563"
170
+ },
171
+ help: {
172
+ background: "#1F2937",
173
+ border: "#3B82F6",
174
+ title: "#F9FAFB",
175
+ category: "#3B82F6",
176
+ key: "#F59E0B",
177
+ description: "#E5E7EB",
178
+ hint: "#6B7280"
179
+ },
180
+ colors: {
181
+ primary: "#3B82F6",
182
+ secondary: "#8B5CF6",
183
+ success: "#22C55E",
184
+ warning: "#F59E0B",
185
+ error: "#EF4444",
186
+ muted: "#6B7280",
187
+ background: "#111827",
188
+ foreground: "#F9FAFB"
189
+ }
190
+ };
191
+
192
+ //#endregion
193
+ //#region src/themes/light.ts
194
+ /**
195
+ * Light theme for terminals with light backgrounds.
196
+ */ const lightTheme = {
197
+ name: "light",
198
+ logLevels: {
199
+ verbose: {
200
+ border: "#9CA3AF",
201
+ background: "#F3F4F6"
202
+ },
203
+ debug: {
204
+ border: "#7C3AED",
205
+ background: "#EDE9FE"
206
+ },
207
+ log: {
208
+ border: "#2563EB",
209
+ background: "#DBEAFE"
210
+ },
211
+ warn: {
212
+ border: "#D97706",
213
+ background: "#FEF3C7"
214
+ },
215
+ error: {
216
+ border: "#DC2626",
217
+ background: "#FEE2E2"
218
+ },
219
+ fatal: {
220
+ border: "#991B1B",
221
+ background: "#FECACA",
222
+ text: "#7F1D1D"
223
+ }
224
+ },
225
+ sidebar: {
226
+ background: "#F9FAFB",
227
+ selectedBackground: "#E5E7EB",
228
+ hoverBackground: "#F3F4F6",
229
+ text: "#1F2937",
230
+ textDim: "#6B7280",
231
+ border: "#D1D5DB",
232
+ badge: "#2563EB",
233
+ focusBorder: "#2563EB"
234
+ },
235
+ header: {
236
+ background: "#F9FAFB",
237
+ text: "#111827",
238
+ border: "#D1D5DB"
239
+ },
240
+ statusIndicators: {
241
+ waiting: {
242
+ icon: "○",
243
+ color: "#9CA3AF"
244
+ },
245
+ pending: {
246
+ icon: "◐",
247
+ color: "#D97706"
248
+ },
249
+ success: {
250
+ icon: "✓",
251
+ color: "#16A34A"
252
+ },
253
+ fail: {
254
+ icon: "✗",
255
+ color: "#DC2626"
256
+ }
257
+ },
258
+ separator: {
259
+ line: "#D1D5DB",
260
+ text: "#6B7280"
261
+ },
262
+ progress: {
263
+ border: "#2563EB",
264
+ background: "#DBEAFE",
265
+ barFilled: "#2563EB",
266
+ barEmpty: "#E5E7EB",
267
+ text: "#1F2937",
268
+ textDim: "#6B7280",
269
+ complete: "#16A34A",
270
+ completeBackground: "#DCFCE7",
271
+ failed: "#DC2626",
272
+ failedBackground: "#FEE2E2"
273
+ },
274
+ group: {
275
+ border: "#9CA3AF",
276
+ background: "#F3F4F6",
277
+ headerText: "#1F2937",
278
+ icon: "#6B7280"
279
+ },
280
+ table: {
281
+ border: "#2563EB",
282
+ background: "#DBEAFE",
283
+ headerText: "#111827",
284
+ cellText: "#1F2937",
285
+ title: "#111827",
286
+ separator: "#93C5FD"
287
+ },
288
+ file: {
289
+ border: "#2563EB",
290
+ background: "#DBEAFE",
291
+ headerText: "#111827",
292
+ headerBackground: "#BFDBFE"
293
+ },
294
+ prompt: {
295
+ question: "#111827",
296
+ optionText: "#1F2937",
297
+ optionTextDim: "#6B7280",
298
+ optionSelected: "#2563EB",
299
+ optionSelectedBackground: "#DBEAFE",
300
+ confirmButton: "#16A34A",
301
+ cancelButton: "#DC2626",
302
+ buttonBackground: "#E5E7EB",
303
+ buttonSelectedBackground: "#D1D5DB",
304
+ inputBorder: "#2563EB",
305
+ inputBackground: "#FFFFFF",
306
+ inputText: "#111827",
307
+ inputPlaceholder: "#9CA3AF",
308
+ inputCursor: "#2563EB",
309
+ border: "#D1D5DB",
310
+ focusBorder: "#2563EB"
311
+ },
312
+ errorHighlight: {
313
+ background: "#FEE2E2",
314
+ border: "#DC2626",
315
+ gutterBackground: "#FECACA"
316
+ },
317
+ filter: {
318
+ background: "#F3F4F6",
319
+ border: "#2563EB",
320
+ text: "#1F2937",
321
+ textDim: "#6B7280",
322
+ inputBackground: "#FFFFFF",
323
+ inputText: "#111827",
324
+ inputPlaceholder: "#9CA3AF",
325
+ cursor: "#2563EB",
326
+ activeLevel: "#2563EB",
327
+ inactiveLevel: "#D1D5DB"
328
+ },
329
+ help: {
330
+ background: "#FFFFFF",
331
+ border: "#2563EB",
332
+ title: "#111827",
333
+ category: "#2563EB",
334
+ key: "#D97706",
335
+ description: "#1F2937",
336
+ hint: "#6B7280"
337
+ },
338
+ colors: {
339
+ primary: "#2563EB",
340
+ secondary: "#7C3AED",
341
+ success: "#16A34A",
342
+ warning: "#D97706",
343
+ error: "#DC2626",
344
+ muted: "#6B7280",
345
+ background: "#FFFFFF",
346
+ foreground: "#111827"
347
+ }
348
+ };
349
+
350
+ //#endregion
351
+ //#region src/themes/high-contrast.ts
352
+ /**
353
+ * High contrast theme for accessibility.
354
+ * Uses pure black/white with saturated colors for maximum visibility.
355
+ */ const highContrastTheme = {
356
+ name: "high-contrast",
357
+ logLevels: {
358
+ verbose: {
359
+ border: "#FFFFFF",
360
+ background: "#1A1A1A",
361
+ text: "#FFFFFF"
362
+ },
363
+ debug: {
364
+ border: "#A855F7",
365
+ background: "#1A1A1A",
366
+ text: "#E9D5FF"
367
+ },
368
+ log: {
369
+ border: "#3B82F6",
370
+ background: "#1A1A1A",
371
+ text: "#BFDBFE"
372
+ },
373
+ warn: {
374
+ border: "#FBBF24",
375
+ background: "#1A1A1A",
376
+ text: "#FEF08A"
377
+ },
378
+ error: {
379
+ border: "#EF4444",
380
+ background: "#1A1A1A",
381
+ text: "#FECACA"
382
+ },
383
+ fatal: {
384
+ border: "#FF0000",
385
+ background: "#330000",
386
+ text: "#FFFFFF"
387
+ }
388
+ },
389
+ sidebar: {
390
+ background: "#000000",
391
+ selectedBackground: "#333333",
392
+ hoverBackground: "#1A1A1A",
393
+ text: "#FFFFFF",
394
+ textDim: "#AAAAAA",
395
+ border: "#FFFFFF",
396
+ badge: "#FFFF00",
397
+ focusBorder: "#FFFF00"
398
+ },
399
+ header: {
400
+ background: "#000000",
401
+ text: "#FFFFFF",
402
+ border: "#FFFFFF"
403
+ },
404
+ statusIndicators: {
405
+ waiting: {
406
+ icon: "○",
407
+ color: "#AAAAAA"
408
+ },
409
+ pending: {
410
+ icon: "◐",
411
+ color: "#FBBF24"
412
+ },
413
+ success: {
414
+ icon: "✓",
415
+ color: "#22C55E"
416
+ },
417
+ fail: {
418
+ icon: "✗",
419
+ color: "#EF4444"
420
+ }
421
+ },
422
+ separator: {
423
+ line: "#FFFFFF",
424
+ text: "#AAAAAA"
425
+ },
426
+ progress: {
427
+ border: "#3B82F6",
428
+ background: "#1A1A1A",
429
+ barFilled: "#3B82F6",
430
+ barEmpty: "#333333",
431
+ text: "#FFFFFF",
432
+ textDim: "#AAAAAA",
433
+ complete: "#22C55E",
434
+ completeBackground: "#1A1A1A",
435
+ failed: "#EF4444",
436
+ failedBackground: "#1A1A1A"
437
+ },
438
+ group: {
439
+ border: "#FFFFFF",
440
+ background: "#1A1A1A",
441
+ headerText: "#FFFFFF",
442
+ icon: "#AAAAAA"
443
+ },
444
+ table: {
445
+ border: "#FFFFFF",
446
+ background: "#1A1A1A",
447
+ headerText: "#FFFFFF",
448
+ cellText: "#FFFFFF",
449
+ title: "#FFFFFF",
450
+ separator: "#666666"
451
+ },
452
+ file: {
453
+ border: "#FFFFFF",
454
+ background: "#1A1A1A",
455
+ headerText: "#FFFFFF",
456
+ headerBackground: "#333333"
457
+ },
458
+ prompt: {
459
+ question: "#FFFFFF",
460
+ optionText: "#FFFFFF",
461
+ optionTextDim: "#AAAAAA",
462
+ optionSelected: "#FFFF00",
463
+ optionSelectedBackground: "#333333",
464
+ confirmButton: "#22C55E",
465
+ cancelButton: "#EF4444",
466
+ buttonBackground: "#333333",
467
+ buttonSelectedBackground: "#1A1A1A",
468
+ inputBorder: "#FFFF00",
469
+ inputBackground: "#1A1A1A",
470
+ inputText: "#FFFFFF",
471
+ inputPlaceholder: "#666666",
472
+ inputCursor: "#FFFF00",
473
+ border: "#FFFFFF",
474
+ focusBorder: "#FFFF00"
475
+ },
476
+ errorHighlight: {
477
+ background: "#330000",
478
+ border: "#FF0000",
479
+ gutterBackground: "#660000"
480
+ },
481
+ filter: {
482
+ background: "#1A1A1A",
483
+ border: "#FFFF00",
484
+ text: "#FFFFFF",
485
+ textDim: "#AAAAAA",
486
+ inputBackground: "#000000",
487
+ inputText: "#FFFFFF",
488
+ inputPlaceholder: "#666666",
489
+ cursor: "#FFFF00",
490
+ activeLevel: "#FFFF00",
491
+ inactiveLevel: "#666666"
492
+ },
493
+ help: {
494
+ background: "#000000",
495
+ border: "#FFFFFF",
496
+ title: "#FFFFFF",
497
+ category: "#FFFF00",
498
+ key: "#00FFFF",
499
+ description: "#FFFFFF",
500
+ hint: "#AAAAAA"
501
+ },
502
+ colors: {
503
+ primary: "#3B82F6",
504
+ secondary: "#A855F7",
505
+ success: "#22C55E",
506
+ warning: "#FBBF24",
507
+ error: "#EF4444",
508
+ muted: "#AAAAAA",
509
+ background: "#000000",
510
+ foreground: "#FFFFFF"
511
+ }
512
+ };
513
+
514
+ //#endregion
515
+ //#region src/themes/utils.ts
516
+ /**
517
+ * Deep merge two objects, with source taking precedence.
518
+ */ function deepMerge(target, source) {
519
+ const result = { ...target };
520
+ for (const key in source) if (Object.prototype.hasOwnProperty.call(source, key)) {
521
+ const sourceValue = source[key];
522
+ const targetValue = target[key];
523
+ if (sourceValue !== void 0 && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue)) result[key] = deepMerge(targetValue, sourceValue);
524
+ else if (sourceValue !== void 0) result[key] = sourceValue;
525
+ }
526
+ return result;
527
+ }
528
+ /**
529
+ * Get a theme by preset name.
530
+ */ function getThemePreset(preset) {
531
+ switch (preset) {
532
+ case "dark": return darkTheme;
533
+ case "light": return lightTheme;
534
+ case "high-contrast": return highContrastTheme;
535
+ default: return darkTheme;
536
+ }
537
+ }
538
+ /**
539
+ * Merge a base theme with partial overrides.
540
+ */ function mergeThemes(base, overrides) {
541
+ return deepMerge(base, overrides);
542
+ }
543
+ /**
544
+ * Create a custom theme by extending the dark theme with overrides.
545
+ */ function createTheme(overrides) {
546
+ return mergeThemes(darkTheme, overrides);
547
+ }
548
+ /**
549
+ * Create a custom theme by extending a specific base theme.
550
+ */ function createThemeFrom(base, overrides) {
551
+ return mergeThemes(typeof base === "string" ? getThemePreset(base) : base, overrides);
552
+ }
553
+ /**
554
+ * Resolve a theme value which can be a Theme object or a preset name.
555
+ */ function resolveTheme(theme) {
556
+ return typeof theme === "string" ? getThemePreset(theme) : theme;
557
+ }
558
+
559
+ //#endregion
560
+ //#region src/context/logger_context.tsx
561
+ const LoggerContext = /* @__PURE__ */ createContext(null);
562
+ /**
563
+ * LoggerProvider - Provides shared context for logger components.
564
+ *
565
+ * This context provides:
566
+ * - Shared SyntaxStyle instance for consistent code highlighting
567
+ * - Optional TreeSitterClient for advanced parsing
568
+ * - Theme configuration for consistent styling
569
+ * - Log level color configuration (for backwards compatibility)
570
+ *
571
+ * @example
572
+ * // Using a preset theme
573
+ * <LoggerProvider theme="dark">
574
+ * <ScreenManager screens={screens} activeScreenId={activeId}>
575
+ * <Screen name="Logs">
576
+ * <LogMessage level="info">Hello</LogMessage>
577
+ * </Screen>
578
+ * </ScreenManager>
579
+ * </LoggerProvider>
580
+ *
581
+ * @example
582
+ * // Using a custom theme
583
+ * <LoggerProvider theme={myCustomTheme}>
584
+ * ...
585
+ * </LoggerProvider>
586
+ */ function LoggerProvider({ children, syntaxStyle: customSyntaxStyle, treeSitterClient, theme: themeProp = "dark", levelColors: customLevelColors }) {
587
+ const syntaxStyle = useMemo(() => customSyntaxStyle ?? SyntaxStyle.create(), [customSyntaxStyle]);
588
+ const theme = useMemo(() => resolveTheme(themeProp), [themeProp]);
589
+ const levelColors = useMemo(() => ({
590
+ ...theme.logLevels,
591
+ ...customLevelColors
592
+ }), [theme.logLevels, customLevelColors]);
593
+ const value = useMemo(() => ({
594
+ syntaxStyle,
595
+ treeSitterClient,
596
+ levelColors,
597
+ theme
598
+ }), [
599
+ syntaxStyle,
600
+ treeSitterClient,
601
+ levelColors,
602
+ theme
603
+ ]);
604
+ return /* @__PURE__ */ jsx(LoggerContext.Provider, {
605
+ value,
606
+ children
607
+ });
608
+ }
609
+ /**
610
+ * Hook to access logger context.
611
+ * Returns default values if used outside LoggerProvider.
612
+ */ function useLoggerContext() {
613
+ const context = useContext(LoggerContext);
614
+ if (!context) return {
615
+ syntaxStyle: void 0,
616
+ treeSitterClient: void 0,
617
+ levelColors: darkTheme.logLevels,
618
+ theme: darkTheme
619
+ };
620
+ return context;
621
+ }
622
+
623
+ //#endregion
624
+ //#region src/hooks/use_theme.ts
625
+ /**
626
+ * Hook to access the current theme.
627
+ * Returns the theme from LoggerContext.
628
+ */ function useTheme() {
629
+ const { theme } = useLoggerContext();
630
+ return theme;
631
+ }
632
+
633
+ //#endregion
634
+ //#region src/utils/colors/log-colors.ts
635
+ /**
636
+ * Default color scheme for all log levels.
637
+ * Each level has a prominent border color and a subtle background tint.
638
+ */ const DEFAULT_LOG_LEVEL_COLORS = {
639
+ verbose: {
640
+ border: "#6B7280",
641
+ background: "#6B728015"
642
+ },
643
+ debug: {
644
+ border: "#8B5CF6",
645
+ background: "#8B5CF615"
646
+ },
647
+ log: {
648
+ border: "#3B82F6",
649
+ background: "#3B82F615"
650
+ },
651
+ warn: {
652
+ border: "#F59E0B",
653
+ background: "#F59E0B15"
654
+ },
655
+ error: {
656
+ border: "#EF4444",
657
+ background: "#EF444415"
658
+ },
659
+ fatal: {
660
+ border: "#DC2626",
661
+ background: "#DC262625",
662
+ text: "#FCA5A5"
663
+ }
664
+ };
665
+ /**
666
+ * Colors for semantic variants (override level colors when variant is set).
667
+ */ const VARIANT_COLORS = {
668
+ success: {
669
+ border: "#22C55E",
670
+ background: "#22C55E15"
671
+ },
672
+ trace: {
673
+ border: "#6B7280",
674
+ background: "#6B728015"
675
+ }
676
+ };
677
+ /**
678
+ * Error highlighting colors.
679
+ */ const ERROR_HIGHLIGHT_COLORS = {
680
+ background: "#EF444425",
681
+ border: "#EF4444",
682
+ gutterBackground: "#EF444440"
683
+ };
684
+
685
+ //#endregion
686
+ //#region src/utils/colors/sidebar-colors.ts
687
+ /**
688
+ * Sidebar colors.
689
+ */ const SIDEBAR_COLORS = {
690
+ background: void 0,
691
+ selectedBackground: "#1F293780",
692
+ hoverBackground: "#374151",
693
+ text: "#E5E7EB",
694
+ textDim: "#6B7280",
695
+ border: "#374151",
696
+ badge: "#3B82F6",
697
+ focusBorder: "#3B82F6"
698
+ };
699
+ /**
700
+ * Screen header colors.
701
+ */ const HEADER_COLORS = {
702
+ background: void 0,
703
+ text: "#F9FAFB",
704
+ border: "#374151"
705
+ };
706
+ /**
707
+ * Screen status indicator colors and icons.
708
+ */ const STATUS_INDICATORS = {
709
+ waiting: {
710
+ icon: "○",
711
+ color: "#6B7280"
712
+ },
713
+ pending: {
714
+ icon: "◐",
715
+ color: "#F59E0B"
716
+ },
717
+ success: {
718
+ icon: "✓",
719
+ color: "#22C55E"
720
+ },
721
+ fail: {
722
+ icon: "✗",
723
+ color: "#EF4444"
724
+ }
725
+ };
726
+ /**
727
+ * Separator colors for sidebar sections.
728
+ */ const SEPARATOR_COLORS = {
729
+ line: "#374151",
730
+ text: "#6B7280"
731
+ };
732
+
733
+ //#endregion
734
+ //#region src/utils/colors/progress-colors.ts
735
+ /**
736
+ * Progress bar colors.
737
+ */ const PROGRESS_COLORS = {
738
+ border: "#3B82F6",
739
+ background: "#3B82F615",
740
+ barFilled: "#3B82F6",
741
+ barEmpty: "#374151",
742
+ text: "#E5E7EB",
743
+ textDim: "#9CA3AF",
744
+ complete: "#22C55E",
745
+ completeBackground: "#22C55E15",
746
+ failed: "#EF4444",
747
+ failedBackground: "#EF444415"
748
+ };
749
+ /**
750
+ * Group colors for collapsible log groups.
751
+ */ const GROUP_COLORS = {
752
+ border: "#6B7280",
753
+ background: "#6B728010",
754
+ headerText: "#E5E7EB",
755
+ icon: "#9CA3AF"
756
+ };
757
+
758
+ //#endregion
759
+ //#region src/utils/colors/table-colors.ts
760
+ /**
761
+ * Table colors (uses info/blue scheme).
762
+ */ const TABLE_COLORS = {
763
+ border: "#3B82F6",
764
+ background: "#3B82F615",
765
+ headerText: "#F9FAFB",
766
+ cellText: "#E5E7EB",
767
+ title: "#F9FAFB",
768
+ separator: "#3B82F650"
769
+ };
770
+
771
+ //#endregion
772
+ //#region src/utils/colors/file-colors.ts
773
+ /**
774
+ * File display colors (uses info/blue scheme).
775
+ */ const FILE_COLORS = {
776
+ border: "#3B82F6",
777
+ background: "#3B82F615",
778
+ headerText: "#F9FAFB",
779
+ headerBackground: "#3B82F625"
780
+ };
781
+
782
+ //#endregion
783
+ //#region src/utils/colors/prompt-colors.ts
784
+ /**
785
+ * Prompt colors for interactive prompts.
786
+ */ const PROMPT_COLORS = {
787
+ question: "#F9FAFB",
788
+ optionText: "#E5E7EB",
789
+ optionTextDim: "#9CA3AF",
790
+ optionSelected: "#3B82F6",
791
+ optionSelectedBackground: "#1E3A5F",
792
+ confirmButton: "#22C55E",
793
+ cancelButton: "#EF4444",
794
+ buttonBackground: "#374151",
795
+ buttonSelectedBackground: "#1F2937",
796
+ inputBorder: "#3B82F6",
797
+ inputBackground: "#1F2937",
798
+ inputText: "#F9FAFB",
799
+ inputPlaceholder: "#6B7280",
800
+ inputCursor: "#3B82F6",
801
+ border: "#374151",
802
+ focusBorder: "#3B82F6"
803
+ };
804
+
805
+ //#endregion
806
+ //#region src/utils/colors/helpers.ts
807
+ /**
808
+ * Creates a tinted (subtle) version of a color by setting alpha.
809
+ *
810
+ * @param color - The base color (hex string or RGBA)
811
+ * @param alpha - Target alpha value (0-1), default 0.08 for subtle tinting
812
+ * @returns New RGBA with adjusted alpha
813
+ */ function createTintedColor(color, alpha = .08) {
814
+ const rgba = typeof color === "string" ? RGBA.fromHex(color) : color;
815
+ return RGBA.fromValues(rgba.r, rgba.g, rgba.b, alpha);
816
+ }
817
+ /**
818
+ * Creates a more prominent version of a color for borders.
819
+ *
820
+ * @param color - The base color
821
+ * @param alpha - Target alpha, default 1.0 for solid borders
822
+ */ function createBorderColor(color, alpha = 1) {
823
+ const rgba = typeof color === "string" ? RGBA.fromHex(color) : color;
824
+ return RGBA.fromValues(rgba.r, rgba.g, rgba.b, alpha);
825
+ }
826
+ /**
827
+ * Gets colors for a specific log level.
828
+ *
829
+ * @param level - The log level
830
+ * @param customColors - Optional custom color map
831
+ */ function getLogLevelColors(level, customColors) {
832
+ const defaultColors = DEFAULT_LOG_LEVEL_COLORS[level];
833
+ const custom = customColors?.[level];
834
+ return {
835
+ border: custom?.border ?? defaultColors.border,
836
+ background: custom?.background ?? defaultColors.background,
837
+ text: custom?.text ?? defaultColors.text
838
+ };
839
+ }
840
+
841
+ //#endregion
842
+ //#region src/utils/filetype.ts
843
+ /**
844
+ * Common file extension to filetype mapping for syntax highlighting.
845
+ */ const COMMON_FILETYPES = {
846
+ ts: "typescript",
847
+ tsx: "tsx",
848
+ js: "javascript",
849
+ jsx: "javascript",
850
+ json: "json",
851
+ md: "markdown",
852
+ py: "python",
853
+ rs: "rust",
854
+ go: "go",
855
+ java: "java",
856
+ c: "c",
857
+ cpp: "cpp",
858
+ h: "c",
859
+ hpp: "cpp",
860
+ css: "css",
861
+ html: "html",
862
+ yaml: "yaml",
863
+ yml: "yaml",
864
+ sh: "bash",
865
+ bash: "bash",
866
+ zsh: "bash",
867
+ toml: "toml",
868
+ xml: "xml",
869
+ sql: "sql",
870
+ rb: "ruby",
871
+ php: "php",
872
+ swift: "swift",
873
+ kt: "kotlin",
874
+ scala: "scala",
875
+ hs: "haskell",
876
+ elm: "elm",
877
+ vue: "vue",
878
+ svelte: "svelte"
879
+ };
880
+ /**
881
+ * Resolves filetype from file path for syntax highlighting.
882
+ *
883
+ * @param filePath - Full file path or just filename
884
+ * @returns Filetype string for tree-sitter, or undefined
885
+ */ function resolveFiletype(filePath) {
886
+ const lastDot = filePath.lastIndexOf(".");
887
+ if (lastDot === -1) return void 0;
888
+ return COMMON_FILETYPES[filePath.slice(lastDot + 1).toLowerCase()];
889
+ }
890
+ /**
891
+ * Gets the filename from a full path.
892
+ *
893
+ * @param filePath - Full file path
894
+ * @returns Just the filename
895
+ */ function getFileName(filePath) {
896
+ const lastSlash = Math.max(filePath.lastIndexOf("/"), filePath.lastIndexOf("\\"));
897
+ return lastSlash === -1 ? filePath : filePath.slice(lastSlash + 1);
898
+ }
899
+
900
+ //#endregion
901
+ //#region src/utils/stdout-printer.ts
902
+ const RESET = "\x1B[0m";
903
+ const BOLD = "\x1B[1m";
904
+ const DIM = "\x1B[2m";
905
+ function hexToAnsi(hex, isForeground = true) {
906
+ const cleanHex = hex.replace("#", "").slice(0, 6);
907
+ if (!/^[0-9A-Fa-f]{6}$/.test(cleanHex)) return "";
908
+ const r = parseInt(cleanHex.slice(0, 2), 16);
909
+ const g = parseInt(cleanHex.slice(2, 4), 16);
910
+ const b = parseInt(cleanHex.slice(4, 6), 16);
911
+ return `\x1b[${isForeground ? 38 : 48};2;${r};${g};${b}m`;
912
+ }
913
+ function getLogLevelAnsiColor(level, variant) {
914
+ return hexToAnsi((variant ? VARIANT_COLORS[variant] : DEFAULT_LOG_LEVEL_COLORS[level]).border, true);
915
+ }
916
+ function formatLevel(level, variant) {
917
+ return `${getLogLevelAnsiColor(level, variant)}${BOLD}[${(variant ?? level).toUpperCase().padEnd(7)}]${RESET}`;
918
+ }
919
+ function formatTimestamp(date) {
920
+ return `${DIM}${date.toLocaleTimeString()}${RESET}`;
921
+ }
922
+ function printMessage(message, stream) {
923
+ switch (message.type) {
924
+ case "log": {
925
+ const logMessage = message;
926
+ const timestamp = formatTimestamp(message.timestamp);
927
+ const level = formatLevel(logMessage.level, logMessage.variant);
928
+ const label = logMessage.label ? ` ${DIM}[${logMessage.label}]${RESET}` : "";
929
+ stream.write(`${timestamp} ${level}${label} ${logMessage.content}\n`);
930
+ break;
931
+ }
932
+ case "file": {
933
+ const header = `${DIM}─── ${message.filePath} ───${RESET}`;
934
+ stream.write(`${header}\n${message.content}\n`);
935
+ break;
936
+ }
937
+ case "diff": {
938
+ const header = `${DIM}─── ${message.filePath} (diff) ───${RESET}`;
939
+ const coloredDiff = message.diff.split("\n").map((line) => {
940
+ if (line.startsWith("+") && !line.startsWith("+++")) return `\x1b[32m${line}${RESET}`;
941
+ else if (line.startsWith("-") && !line.startsWith("---")) return `\x1b[31m${line}${RESET}`;
942
+ else if (line.startsWith("@@")) return `\x1b[36m${line}${RESET}`;
943
+ return line;
944
+ }).join("\n");
945
+ stream.write(`${header}\n${coloredDiff}\n`);
946
+ break;
947
+ }
948
+ case "fileError": {
949
+ const header = `${DIM}─── ${message.filePath} ───${RESET}`;
950
+ const errorLineSet = new Set(message.errorLines);
951
+ const startLine = message.startLine ?? 1;
952
+ const numberedLines = message.content.split("\n").map((line, index) => {
953
+ const lineNum = startLine + index;
954
+ const numStr = String(lineNum).padStart(4);
955
+ if (errorLineSet.has(lineNum)) return `\x1b[31m${numStr} │ ${line}${RESET}`;
956
+ return `${DIM}${numStr}${RESET} │ ${line}`;
957
+ });
958
+ stream.write(`${header}\n${numberedLines.join("\n")}\n`);
959
+ break;
960
+ }
961
+ case "loading": {
962
+ const loadingMessage = message;
963
+ const status = loadingMessage.status;
964
+ const content = loadingMessage.resolvedContent ?? loadingMessage.content;
965
+ let level = "log";
966
+ let variant;
967
+ if (status === "success") {
968
+ level = "log";
969
+ variant = "success";
970
+ } else if (status === "fail") level = "error";
971
+ const timestamp = formatTimestamp(message.timestamp);
972
+ const levelStr = formatLevel(level, variant);
973
+ const prefix = status === "loading" ? "... " : "";
974
+ stream.write(`${timestamp} ${levelStr} ${prefix}${content}\n`);
975
+ break;
976
+ }
977
+ case "progress": {
978
+ const { label, current, total, status, resolvedContent } = message;
979
+ const percentage = total > 0 ? Math.round(current / total * 100) : 0;
980
+ const barWidth = 20;
981
+ const filledWidth = Math.round(percentage / 100 * barWidth);
982
+ const emptyWidth = barWidth - filledWidth;
983
+ const progressColor = hexToAnsi(PROGRESS_COLORS.barFilled);
984
+ const emptyColor = hexToAnsi(PROGRESS_COLORS.barEmpty);
985
+ const filledBar = "█".repeat(filledWidth);
986
+ const emptyBar = "░".repeat(emptyWidth);
987
+ let statusIcon = "";
988
+ let statusColor = "";
989
+ if (status === "complete") {
990
+ statusIcon = "✓";
991
+ statusColor = hexToAnsi(PROGRESS_COLORS.complete);
992
+ } else if (status === "failed") {
993
+ statusIcon = "✗";
994
+ statusColor = hexToAnsi(PROGRESS_COLORS.failed);
995
+ }
996
+ const displayLabel = resolvedContent ?? label;
997
+ const progressBar = `${progressColor}${filledBar}${RESET}${emptyColor}${emptyBar}${RESET}`;
998
+ const percentStr = `${String(percentage).padStart(3)}%`;
999
+ if (statusIcon) stream.write(`${statusColor}${statusIcon}${RESET} ${displayLabel} [${progressBar}] ${percentStr}\n`);
1000
+ else stream.write(` ${displayLabel} [${progressBar}] ${percentStr} (${current}/${total})\n`);
1001
+ break;
1002
+ }
1003
+ case "group": {
1004
+ const { label, collapsed, isEnd } = message;
1005
+ const groupColor = hexToAnsi(GROUP_COLORS.border);
1006
+ const iconColor = hexToAnsi(GROUP_COLORS.icon);
1007
+ if (isEnd) stream.write(`${groupColor}└─${RESET} ${DIM}end ${label}${RESET}\n`);
1008
+ else {
1009
+ const icon = collapsed ? "▶" : "▼";
1010
+ stream.write(`${groupColor}┌─${RESET} ${iconColor}${icon}${RESET} ${BOLD}${label}${RESET}\n`);
1011
+ }
1012
+ break;
1013
+ }
1014
+ case "table": {
1015
+ const { headers, rows, title } = message;
1016
+ const tableColor = hexToAnsi(TABLE_COLORS.border);
1017
+ const headerColor = hexToAnsi(TABLE_COLORS.headerText);
1018
+ const cellColor = hexToAnsi(TABLE_COLORS.cellText);
1019
+ const colWidths = headers.map((h, i) => {
1020
+ const maxRowWidth = rows.reduce((max, row) => Math.max(max, (row[i] ?? "").length), 0);
1021
+ return Math.max(h.length, maxRowWidth);
1022
+ });
1023
+ const separator = `${tableColor}├${colWidths.map((w) => "─".repeat(w + 2)).join("┼")}┤${RESET}`;
1024
+ const topBorder = `${tableColor}┌${colWidths.map((w) => "─".repeat(w + 2)).join("┬")}┐${RESET}`;
1025
+ const bottomBorder = `${tableColor}└${colWidths.map((w) => "─".repeat(w + 2)).join("┴")}┘${RESET}`;
1026
+ if (title) stream.write(`${BOLD}${title}${RESET}\n`);
1027
+ stream.write(`${topBorder}\n`);
1028
+ const headerRow = headers.map((h, i) => ` ${h.padEnd(colWidths[i])} `).join(`${tableColor}│${RESET}`);
1029
+ stream.write(`${tableColor}│${RESET}${headerColor}${headerRow}${RESET}${tableColor}│${RESET}\n`);
1030
+ stream.write(`${separator}\n`);
1031
+ for (const row of rows) {
1032
+ const rowStr = headers.map((_, i) => ` ${(row[i] ?? "").padEnd(colWidths[i])} `).join(`${tableColor}│${RESET}`);
1033
+ stream.write(`${tableColor}│${RESET}${cellColor}${rowStr}${RESET}${tableColor}│${RESET}\n`);
1034
+ }
1035
+ stream.write(`${bottomBorder}\n`);
1036
+ break;
1037
+ }
1038
+ }
1039
+ }
1040
+ /**
1041
+ * Print all messages to stdout (or stderr if isError)
1042
+ */ function printMessagesToStdout(messages, screenName, isError = false) {
1043
+ const stream = isError ? process.stderr : process.stdout;
1044
+ const headerColor = isError ? "\x1B[31m" : "\x1B[32m";
1045
+ const status = isError ? "FAILED" : "COMPLETED";
1046
+ stream.write(`\n${headerColor}${BOLD}═══ ${screenName} [${status}] ═══${RESET}\n\n`);
1047
+ for (const message of messages) printMessage(message, stream);
1048
+ stream.write("\n");
1049
+ }
1050
+
1051
+ //#endregion
1052
+ //#region src/utils/format.ts
1053
+ /**
1054
+ * Format an object for display with configurable depth
1055
+ */ function formatObject(obj, depth = 2, currentDepth = 0) {
1056
+ if (currentDepth >= depth) {
1057
+ if (Array.isArray(obj)) return "[Array]";
1058
+ if (typeof obj === "object" && obj !== null) return `[${obj.constructor?.name ?? "Object"}]`;
1059
+ return String(obj);
1060
+ }
1061
+ if (obj === null) return "null";
1062
+ if (obj === void 0) return "undefined";
1063
+ if (typeof obj === "string") return `"${obj}"`;
1064
+ if (typeof obj === "number" || typeof obj === "boolean") return String(obj);
1065
+ if (typeof obj === "function") return "[Function]";
1066
+ if (obj instanceof Date) return obj.toISOString();
1067
+ if (obj instanceof Error) return `${obj.name}: ${obj.message}`;
1068
+ const indent = " ".repeat(currentDepth);
1069
+ const childIndent = " ".repeat(currentDepth + 1);
1070
+ if (Array.isArray(obj)) {
1071
+ if (obj.length === 0) return "[]";
1072
+ return `[\n${childIndent}${obj.map((item) => formatObject(item, depth, currentDepth + 1)).join(`,\n${childIndent}`)}\n${indent}]`;
1073
+ }
1074
+ if (typeof obj === "object") {
1075
+ const entries = Object.entries(obj);
1076
+ if (entries.length === 0) return "{}";
1077
+ return `{\n${childIndent}${entries.map(([key, value]) => `${key}: ${formatObject(value, depth, currentDepth + 1)}`).join(`,\n${childIndent}`)}\n${indent}}`;
1078
+ }
1079
+ return String(obj);
1080
+ }
1081
+ /**
1082
+ * Capture a stack trace, filtering out internal frames
1083
+ */ function captureTrace() {
1084
+ return ((/* @__PURE__ */ new Error()).stack ?? "").split("\n").slice(4).join("\n");
1085
+ }
1086
+
1087
+ //#endregion
1088
+ //#region src/components/log/log_message.tsx
1089
+ /**
1090
+ * LogMessage - A chat-like styled log message with level-based coloring.
1091
+ *
1092
+ * Features:
1093
+ * - Left border with prominent color based on log level
1094
+ * - Subtle tinted background matching the log level
1095
+ * - Optional timestamp and label display
1096
+ * - Supports both simple text and complex React children
1097
+ * - Optional variant for semantic styling (e.g., 'success', 'trace')
1098
+ *
1099
+ * @example
1100
+ * <LogMessage level="error">
1101
+ * Connection failed: timeout after 30s
1102
+ * </LogMessage>
1103
+ *
1104
+ * @example
1105
+ * <LogMessage level="log" timestamp={new Date()} label="API">
1106
+ * Request completed successfully
1107
+ * </LogMessage>
1108
+ *
1109
+ * @example
1110
+ * <LogMessage level="log" variant="success">
1111
+ * Operation completed successfully
1112
+ * </LogMessage>
1113
+ */ function LogMessage({ level, variant, children, timestamp, label, trace, borderColor: customBorderColor, backgroundColor: customBackgroundColor, borderStyle = "thin", padding = 1, margin = 0 }) {
1114
+ const theme = useTheme();
1115
+ const levelColors = variant ? VARIANT_COLORS[variant] : theme.logLevels[level];
1116
+ const borderColor = customBorderColor ?? levelColors.border;
1117
+ const backgroundColor = customBackgroundColor ?? levelColors.background;
1118
+ const textColor = levelColors.text ?? theme.colors.foreground;
1119
+ const borderSides = borderStyle === "thin" ? ["left"] : [
1120
+ "left",
1121
+ "top",
1122
+ "bottom"
1123
+ ];
1124
+ const formattedTimestamp = timestamp ? timestamp instanceof Date ? timestamp.toLocaleTimeString() : timestamp : null;
1125
+ return /* @__PURE__ */ jsxs("box", {
1126
+ flexDirection: "column",
1127
+ border: borderSides,
1128
+ borderColor,
1129
+ backgroundColor,
1130
+ paddingLeft: padding,
1131
+ paddingRight: padding,
1132
+ marginBottom: margin,
1133
+ children: [
1134
+ (formattedTimestamp || label) && /* @__PURE__ */ jsxs("box", {
1135
+ flexDirection: "row",
1136
+ gap: 1,
1137
+ children: [formattedTimestamp && /* @__PURE__ */ jsx("text", {
1138
+ fg: theme.colors.muted,
1139
+ children: formattedTimestamp
1140
+ }), label && /* @__PURE__ */ jsxs("text", {
1141
+ fg: levelColors.border,
1142
+ attributes: TextAttributes.BOLD,
1143
+ children: [
1144
+ "[",
1145
+ label,
1146
+ "]"
1147
+ ]
1148
+ })]
1149
+ }),
1150
+ /* @__PURE__ */ jsx("text", {
1151
+ fg: textColor,
1152
+ children
1153
+ }),
1154
+ trace && /* @__PURE__ */ jsx("box", {
1155
+ marginTop: 1,
1156
+ children: /* @__PURE__ */ jsx("text", {
1157
+ fg: theme.colors.muted,
1158
+ children: trace
1159
+ })
1160
+ })
1161
+ ]
1162
+ });
1163
+ }
1164
+
1165
+ //#endregion
1166
+ //#region src/components/file/file_log.tsx
1167
+ /**
1168
+ * FileLog - Displays file content, diffs, or partial files with error highlighting.
1169
+ *
1170
+ * Three modes:
1171
+ * 1. "full" - Display complete file with syntax highlighting
1172
+ * 2. "diff" - Display unified diff using <diff> component
1173
+ * 3. "partial" - Display file excerpt with optional error line highlighting
1174
+ *
1175
+ * @example Full file
1176
+ * <FileLog
1177
+ * mode="full"
1178
+ * filePath="src/index.ts"
1179
+ * content={fileContent}
1180
+ * />
1181
+ *
1182
+ * @example Diff
1183
+ * <FileLog
1184
+ * mode="diff"
1185
+ * filePath="src/api.ts"
1186
+ * diff={unifiedDiff}
1187
+ * view="unified"
1188
+ * />
1189
+ *
1190
+ * @example Partial with error
1191
+ * <FileLog
1192
+ * mode="partial"
1193
+ * filePath="src/utils.ts"
1194
+ * content={excerpt}
1195
+ * startLine={42}
1196
+ * errorLines={[45, 46]}
1197
+ * />
1198
+ */ function FileLog(props) {
1199
+ const { syntaxStyle, treeSitterClient } = useLoggerContext();
1200
+ const filetype = props.filetype ?? resolveFiletype(props.filePath);
1201
+ const showHeader = props.showHeader ?? true;
1202
+ const showLineNumbers = props.showLineNumbers ?? true;
1203
+ return /* @__PURE__ */ jsxs("box", {
1204
+ flexDirection: "column",
1205
+ marginBottom: 1,
1206
+ children: [
1207
+ showHeader && /* @__PURE__ */ jsx("box", {
1208
+ backgroundColor: props.headerBackgroundColor ?? HEADER_COLORS.background,
1209
+ paddingLeft: 1,
1210
+ paddingRight: 1,
1211
+ border: ["bottom"],
1212
+ borderColor: HEADER_COLORS.border,
1213
+ children: /* @__PURE__ */ jsx("text", {
1214
+ fg: HEADER_COLORS.text,
1215
+ children: props.filePath
1216
+ })
1217
+ }),
1218
+ props.mode === "full" && /* @__PURE__ */ jsx(FileLogFull, {
1219
+ ...props,
1220
+ filetype,
1221
+ syntaxStyle,
1222
+ treeSitterClient,
1223
+ showLineNumbers
1224
+ }),
1225
+ props.mode === "diff" && /* @__PURE__ */ jsx(FileLogDiff, {
1226
+ ...props,
1227
+ filetype,
1228
+ syntaxStyle,
1229
+ treeSitterClient
1230
+ }),
1231
+ props.mode === "partial" && /* @__PURE__ */ jsx(FileLogPartial, {
1232
+ ...props,
1233
+ filetype,
1234
+ syntaxStyle,
1235
+ treeSitterClient,
1236
+ showLineNumbers
1237
+ })
1238
+ ]
1239
+ });
1240
+ }
1241
+ /**
1242
+ * Full file display with syntax highlighting.
1243
+ */ function FileLogFull({ content, filetype, syntaxStyle, treeSitterClient }) {
1244
+ if (!syntaxStyle) return /* @__PURE__ */ jsx("box", {
1245
+ paddingLeft: 1,
1246
+ paddingRight: 1,
1247
+ children: /* @__PURE__ */ jsx("text", { children: content })
1248
+ });
1249
+ return /* @__PURE__ */ jsx("code", {
1250
+ content,
1251
+ filetype,
1252
+ syntaxStyle,
1253
+ treeSitterClient
1254
+ });
1255
+ }
1256
+ /**
1257
+ * Diff display using the built-in <diff> component.
1258
+ */ function FileLogDiff({ diff, view = "unified", filetype, syntaxStyle, treeSitterClient }) {
1259
+ if (!syntaxStyle) return /* @__PURE__ */ jsx("box", {
1260
+ paddingLeft: 1,
1261
+ paddingRight: 1,
1262
+ children: /* @__PURE__ */ jsx("text", { children: diff })
1263
+ });
1264
+ return /* @__PURE__ */ jsx("diff", {
1265
+ diff,
1266
+ view,
1267
+ filetype,
1268
+ syntaxStyle,
1269
+ treeSitterClient
1270
+ });
1271
+ }
1272
+ /**
1273
+ * Partial file display with error line highlighting.
1274
+ *
1275
+ * This component renders a file excerpt and highlights specific lines
1276
+ * as errors using box overlays with colored backgrounds.
1277
+ */ function FileLogPartial({ content, startLine, errorLines = [], errorLineBackground, errorLineBorderColor, filetype, syntaxStyle, treeSitterClient, showLineNumbers }) {
1278
+ const lines = useMemo(() => content.split("\n"), [content]);
1279
+ const errorLineSet = useMemo(() => new Set(errorLines), [errorLines]);
1280
+ const errorBg = errorLineBackground ?? ERROR_HIGHLIGHT_COLORS.background;
1281
+ const errorBorder = errorLineBorderColor ?? ERROR_HIGHLIGHT_COLORS.border;
1282
+ const maxLineNum = startLine + lines.length - 1;
1283
+ const lineNumWidth = Math.max(4, String(maxLineNum).length + 1);
1284
+ return /* @__PURE__ */ jsx("box", {
1285
+ flexDirection: "column",
1286
+ children: lines.map((line, index) => {
1287
+ const lineNumber = startLine + index;
1288
+ const isError = errorLineSet.has(lineNumber);
1289
+ return /* @__PURE__ */ jsxs("box", {
1290
+ flexDirection: "row",
1291
+ backgroundColor: isError ? errorBg : void 0,
1292
+ border: isError ? ["left"] : void 0,
1293
+ borderColor: isError ? errorBorder : void 0,
1294
+ children: [showLineNumbers && /* @__PURE__ */ jsx("box", {
1295
+ width: lineNumWidth,
1296
+ backgroundColor: isError ? ERROR_HIGHLIGHT_COLORS.gutterBackground : HEADER_COLORS.background,
1297
+ paddingRight: 1,
1298
+ children: /* @__PURE__ */ jsx("text", {
1299
+ fg: "#6B7280",
1300
+ children: String(lineNumber).padStart(lineNumWidth - 1)
1301
+ })
1302
+ }), /* @__PURE__ */ jsx("box", {
1303
+ flexGrow: 1,
1304
+ paddingLeft: 1,
1305
+ children: syntaxStyle ? /* @__PURE__ */ jsx("code", {
1306
+ content: line,
1307
+ filetype,
1308
+ syntaxStyle,
1309
+ treeSitterClient
1310
+ }) : /* @__PURE__ */ jsx("text", { children: line })
1311
+ })]
1312
+ }, lineNumber);
1313
+ })
1314
+ });
1315
+ }
1316
+
1317
+ //#endregion
1318
+ //#region src/components/prompt/prompt_renderer.tsx
1319
+ function PromptRenderer({ prompt }) {
1320
+ const theme = useTheme();
1321
+ const timeoutRemaining = getTimeoutRemaining(prompt);
1322
+ switch (prompt.type) {
1323
+ case "choice": return /* @__PURE__ */ jsx(ChoicePromptRenderer, {
1324
+ prompt,
1325
+ timeoutRemaining,
1326
+ colors: theme.prompt
1327
+ });
1328
+ case "confirm": return /* @__PURE__ */ jsx(ConfirmPromptRenderer, {
1329
+ prompt,
1330
+ timeoutRemaining,
1331
+ colors: theme.prompt
1332
+ });
1333
+ case "input": return /* @__PURE__ */ jsx(InputPromptRenderer, {
1334
+ prompt,
1335
+ timeoutRemaining,
1336
+ colors: theme.prompt
1337
+ });
1338
+ case "multiChoice": return /* @__PURE__ */ jsx(MultiChoicePromptRenderer, {
1339
+ prompt,
1340
+ timeoutRemaining,
1341
+ colors: theme.prompt
1342
+ });
1343
+ default: return null;
1344
+ }
1345
+ }
1346
+ function getTimeoutRemaining(prompt) {
1347
+ if (!prompt.timeout || !prompt.timeoutStarted) return null;
1348
+ const elapsed = Date.now() - prompt.timeoutStarted;
1349
+ const remaining = Math.max(0, prompt.timeout - elapsed);
1350
+ return Math.ceil(remaining / 1e3);
1351
+ }
1352
+ function TimeoutIndicator({ seconds, colors }) {
1353
+ if (seconds === null) return null;
1354
+ return /* @__PURE__ */ jsxs("text", {
1355
+ fg: colors.optionTextDim,
1356
+ attributes: TextAttributes.DIM,
1357
+ children: [
1358
+ " ",
1359
+ "(auto-select in ",
1360
+ seconds,
1361
+ "s)"
1362
+ ]
1363
+ });
1364
+ }
1365
+ function ChoicePromptRenderer({ prompt, timeoutRemaining, colors }) {
1366
+ return /* @__PURE__ */ jsxs("box", {
1367
+ flexDirection: "column",
1368
+ borderColor: colors.focusBorder,
1369
+ border: ["left"],
1370
+ paddingLeft: 1,
1371
+ paddingRight: 1,
1372
+ gap: 1,
1373
+ children: [
1374
+ /* @__PURE__ */ jsxs("box", {
1375
+ flexDirection: "row",
1376
+ children: [/* @__PURE__ */ jsxs("text", {
1377
+ fg: colors.question,
1378
+ attributes: TextAttributes.BOLD,
1379
+ children: ["? ", prompt.question]
1380
+ }), /* @__PURE__ */ jsx(TimeoutIndicator, {
1381
+ seconds: timeoutRemaining,
1382
+ colors
1383
+ })]
1384
+ }),
1385
+ /* @__PURE__ */ jsx("box", {
1386
+ flexDirection: "column",
1387
+ children: prompt.choices.map((choice, index) => {
1388
+ const isSelected = index === prompt.selectedIndex;
1389
+ const showInput = isSelected && choice.input && prompt.inputMode;
1390
+ return /* @__PURE__ */ jsxs("box", {
1391
+ flexDirection: "row",
1392
+ children: [
1393
+ /* @__PURE__ */ jsxs("text", {
1394
+ fg: isSelected ? colors.optionSelected : "transparent",
1395
+ children: [">", " "]
1396
+ }),
1397
+ /* @__PURE__ */ jsx("text", {
1398
+ fg: isSelected ? colors.optionText : colors.optionTextDim,
1399
+ attributes: isSelected ? TextAttributes.BOLD : void 0,
1400
+ children: choice.label
1401
+ }),
1402
+ showInput && /* @__PURE__ */ jsxs("box", {
1403
+ flexDirection: "row",
1404
+ marginLeft: 1,
1405
+ children: [
1406
+ /* @__PURE__ */ jsx("text", {
1407
+ fg: colors.inputText,
1408
+ children: ": "
1409
+ }),
1410
+ /* @__PURE__ */ jsx("text", {
1411
+ fg: colors.inputText,
1412
+ children: prompt.inputValue
1413
+ }),
1414
+ /* @__PURE__ */ jsx("text", {
1415
+ fg: colors.inputCursor,
1416
+ attributes: TextAttributes.BLINK,
1417
+ children: "_"
1418
+ })
1419
+ ]
1420
+ }),
1421
+ isSelected && choice.input && !prompt.inputMode && /* @__PURE__ */ jsx("text", {
1422
+ fg: colors.optionTextDim,
1423
+ children: " (press Enter to type)"
1424
+ })
1425
+ ]
1426
+ }, choice.value);
1427
+ })
1428
+ }),
1429
+ prompt.inputMode ? /* @__PURE__ */ jsx("text", {
1430
+ fg: colors.optionTextDim,
1431
+ attributes: TextAttributes.DIM,
1432
+ children: "Type your answer, Enter to submit, Esc to cancel"
1433
+ }) : /* @__PURE__ */ jsx("text", {
1434
+ fg: colors.optionTextDim,
1435
+ attributes: TextAttributes.DIM,
1436
+ children: "↑/↓ to navigate, Enter to select"
1437
+ })
1438
+ ]
1439
+ });
1440
+ }
1441
+ function ConfirmPromptRenderer({ prompt, timeoutRemaining, colors }) {
1442
+ const confirmSelected = prompt.selectedValue === true;
1443
+ const cancelSelected = prompt.selectedValue === false;
1444
+ return /* @__PURE__ */ jsxs("box", {
1445
+ flexDirection: "column",
1446
+ borderColor: colors.focusBorder,
1447
+ border: ["left"],
1448
+ paddingLeft: 1,
1449
+ paddingRight: 1,
1450
+ gap: 1,
1451
+ children: [
1452
+ /* @__PURE__ */ jsxs("box", {
1453
+ flexDirection: "row",
1454
+ children: [/* @__PURE__ */ jsxs("text", {
1455
+ fg: colors.question,
1456
+ attributes: TextAttributes.BOLD,
1457
+ children: ["? ", prompt.question]
1458
+ }), /* @__PURE__ */ jsx(TimeoutIndicator, {
1459
+ seconds: timeoutRemaining,
1460
+ colors
1461
+ })]
1462
+ }),
1463
+ /* @__PURE__ */ jsxs("box", {
1464
+ flexDirection: "row",
1465
+ gap: 2,
1466
+ children: [/* @__PURE__ */ jsx("box", {
1467
+ backgroundColor: confirmSelected ? colors.buttonSelectedBackground : void 0,
1468
+ paddingLeft: 1,
1469
+ paddingRight: 1,
1470
+ children: /* @__PURE__ */ jsxs("text", {
1471
+ fg: confirmSelected ? colors.confirmButton : colors.optionTextDim,
1472
+ attributes: confirmSelected ? TextAttributes.BOLD : void 0,
1473
+ children: [
1474
+ confirmSelected ? ">" : " ",
1475
+ " ",
1476
+ prompt.confirmText
1477
+ ]
1478
+ })
1479
+ }), /* @__PURE__ */ jsx("box", {
1480
+ backgroundColor: cancelSelected ? colors.buttonSelectedBackground : void 0,
1481
+ paddingLeft: 1,
1482
+ paddingRight: 1,
1483
+ children: /* @__PURE__ */ jsxs("text", {
1484
+ fg: cancelSelected ? colors.cancelButton : colors.optionTextDim,
1485
+ attributes: cancelSelected ? TextAttributes.BOLD : void 0,
1486
+ children: [
1487
+ cancelSelected ? ">" : " ",
1488
+ " ",
1489
+ prompt.cancelText
1490
+ ]
1491
+ })
1492
+ })]
1493
+ }),
1494
+ /* @__PURE__ */ jsx("text", {
1495
+ fg: colors.optionTextDim,
1496
+ attributes: TextAttributes.DIM,
1497
+ children: "←/→ to select, Enter to confirm"
1498
+ })
1499
+ ]
1500
+ });
1501
+ }
1502
+ function InputPromptRenderer({ prompt, timeoutRemaining, colors }) {
1503
+ const hasValue = prompt.value.length > 0;
1504
+ return /* @__PURE__ */ jsxs("box", {
1505
+ flexDirection: "column",
1506
+ borderColor: colors.focusBorder,
1507
+ border: ["left"],
1508
+ paddingLeft: 1,
1509
+ paddingRight: 1,
1510
+ gap: 1,
1511
+ children: [
1512
+ /* @__PURE__ */ jsxs("box", {
1513
+ flexDirection: "row",
1514
+ children: [/* @__PURE__ */ jsxs("text", {
1515
+ fg: colors.question,
1516
+ attributes: TextAttributes.BOLD,
1517
+ children: ["? ", prompt.question]
1518
+ }), /* @__PURE__ */ jsx(TimeoutIndicator, {
1519
+ seconds: timeoutRemaining,
1520
+ colors
1521
+ })]
1522
+ }),
1523
+ /* @__PURE__ */ jsxs("box", {
1524
+ flexDirection: "row",
1525
+ borderColor: colors.inputBorder,
1526
+ border: ["left"],
1527
+ paddingLeft: 1,
1528
+ children: [/* @__PURE__ */ jsx("text", {
1529
+ fg: hasValue ? colors.inputText : colors.inputPlaceholder,
1530
+ children: hasValue ? prompt.value : prompt.placeholder
1531
+ }), /* @__PURE__ */ jsx("text", {
1532
+ fg: colors.inputCursor,
1533
+ attributes: TextAttributes.BLINK,
1534
+ children: "_"
1535
+ })]
1536
+ }),
1537
+ /* @__PURE__ */ jsx("text", {
1538
+ fg: colors.optionTextDim,
1539
+ attributes: TextAttributes.DIM,
1540
+ children: "Type your answer, Enter to submit"
1541
+ })
1542
+ ]
1543
+ });
1544
+ }
1545
+ function MultiChoicePromptRenderer({ prompt, timeoutRemaining, colors }) {
1546
+ const selectedCount = prompt.selectedIndices.size;
1547
+ const canSubmit = selectedCount >= prompt.minSelect;
1548
+ return /* @__PURE__ */ jsxs("box", {
1549
+ flexDirection: "column",
1550
+ borderColor: colors.focusBorder,
1551
+ border: ["left"],
1552
+ paddingLeft: 1,
1553
+ paddingRight: 1,
1554
+ gap: 1,
1555
+ children: [
1556
+ /* @__PURE__ */ jsxs("box", {
1557
+ flexDirection: "row",
1558
+ children: [
1559
+ /* @__PURE__ */ jsxs("text", {
1560
+ fg: colors.question,
1561
+ attributes: TextAttributes.BOLD,
1562
+ children: ["? ", prompt.question]
1563
+ }),
1564
+ /* @__PURE__ */ jsxs("text", {
1565
+ fg: colors.optionTextDim,
1566
+ children: [
1567
+ " ",
1568
+ "(",
1569
+ selectedCount,
1570
+ "/",
1571
+ prompt.maxSelect,
1572
+ " selected)"
1573
+ ]
1574
+ }),
1575
+ /* @__PURE__ */ jsx(TimeoutIndicator, {
1576
+ seconds: timeoutRemaining,
1577
+ colors
1578
+ })
1579
+ ]
1580
+ }),
1581
+ /* @__PURE__ */ jsx("box", {
1582
+ flexDirection: "column",
1583
+ children: prompt.choices.map((choice, index) => {
1584
+ const isFocused = index === prompt.focusedIndex;
1585
+ const isChecked = prompt.selectedIndices.has(index);
1586
+ return /* @__PURE__ */ jsxs("box", {
1587
+ flexDirection: "row",
1588
+ children: [
1589
+ /* @__PURE__ */ jsxs("text", {
1590
+ fg: isFocused ? colors.optionSelected : "transparent",
1591
+ children: [">", " "]
1592
+ }),
1593
+ /* @__PURE__ */ jsxs("text", {
1594
+ fg: isChecked ? colors.optionSelected : colors.optionTextDim,
1595
+ children: [isChecked ? "[✓]" : "[ ]", " "]
1596
+ }),
1597
+ /* @__PURE__ */ jsx("text", {
1598
+ fg: isFocused ? colors.optionText : colors.optionTextDim,
1599
+ attributes: isFocused ? TextAttributes.BOLD : void 0,
1600
+ children: choice.label
1601
+ })
1602
+ ]
1603
+ }, choice.value);
1604
+ })
1605
+ }),
1606
+ /* @__PURE__ */ jsxs("box", {
1607
+ flexDirection: "column",
1608
+ children: [/* @__PURE__ */ jsx("text", {
1609
+ fg: colors.optionTextDim,
1610
+ attributes: TextAttributes.DIM,
1611
+ children: "↑/↓ to navigate, Space to toggle"
1612
+ }), canSubmit ? /* @__PURE__ */ jsx("text", {
1613
+ fg: colors.optionTextDim,
1614
+ attributes: TextAttributes.DIM,
1615
+ children: "Enter to confirm"
1616
+ }) : /* @__PURE__ */ jsxs("text", {
1617
+ fg: colors.cancelButton,
1618
+ attributes: TextAttributes.DIM,
1619
+ children: [
1620
+ "Select at least ",
1621
+ prompt.minSelect,
1622
+ " option",
1623
+ prompt.minSelect > 1 ? "s" : ""
1624
+ ]
1625
+ })]
1626
+ })
1627
+ ]
1628
+ });
1629
+ }
1630
+
1631
+ //#endregion
1632
+ //#region src/components/screen/loading_message.tsx
1633
+ const SPINNER_FRAMES = [
1634
+ "⠋",
1635
+ "⠙",
1636
+ "⠹",
1637
+ "⠸",
1638
+ "⠼",
1639
+ "⠴",
1640
+ "⠦",
1641
+ "⠧",
1642
+ "⠇",
1643
+ "⠏"
1644
+ ];
1645
+ function LoadingMessage({ message }) {
1646
+ const [frameIndex, setFrameIndex] = useState(0);
1647
+ const intervalRef = useRef(null);
1648
+ useEffect(() => {
1649
+ if (message.status === "loading") intervalRef.current = setInterval(() => {
1650
+ setFrameIndex((prev) => (prev + 1) % SPINNER_FRAMES.length);
1651
+ }, 80);
1652
+ return () => {
1653
+ if (intervalRef.current) clearInterval(intervalRef.current);
1654
+ };
1655
+ }, [message.status]);
1656
+ const level = message.status === "fail" ? "error" : "log";
1657
+ const variant = message.status === "success" ? "success" : void 0;
1658
+ const displayContent = message.resolvedContent ?? message.content;
1659
+ const spinner = message.status === "loading" ? SPINNER_FRAMES[frameIndex] + " " : "";
1660
+ return /* @__PURE__ */ jsxs(LogMessage, {
1661
+ level,
1662
+ variant,
1663
+ timestamp: message.timestamp,
1664
+ children: [spinner, displayContent]
1665
+ });
1666
+ }
1667
+
1668
+ //#endregion
1669
+ //#region src/components/screen/progress_message.tsx
1670
+ function ProgressMessage({ message }) {
1671
+ const theme = useTheme();
1672
+ const percent = Math.round(message.current / message.total * 100);
1673
+ const barWidth = 20;
1674
+ const filled = Math.round(percent / 100 * barWidth);
1675
+ const empty = barWidth - filled;
1676
+ const barFilled = "█".repeat(filled);
1677
+ const barEmpty = "░".repeat(empty);
1678
+ const borderColor = message.status === "complete" ? theme.progress.complete : message.status === "failed" ? theme.progress.failed : theme.progress.border;
1679
+ const backgroundColor = message.status === "complete" ? theme.progress.completeBackground : message.status === "failed" ? theme.progress.failedBackground : theme.progress.background;
1680
+ const barColor = message.status === "complete" ? theme.progress.complete : message.status === "failed" ? theme.progress.failed : theme.progress.barFilled;
1681
+ const displayLabel = message.resolvedContent ?? message.label;
1682
+ return /* @__PURE__ */ jsx("box", {
1683
+ flexDirection: "column",
1684
+ border: ["left"],
1685
+ borderColor,
1686
+ backgroundColor,
1687
+ paddingLeft: 1,
1688
+ paddingRight: 1,
1689
+ children: /* @__PURE__ */ jsxs("box", {
1690
+ flexDirection: "row",
1691
+ gap: 1,
1692
+ children: [
1693
+ /* @__PURE__ */ jsxs("text", {
1694
+ fg: barColor,
1695
+ children: ["[", barFilled]
1696
+ }),
1697
+ /* @__PURE__ */ jsxs("text", {
1698
+ fg: theme.progress.barEmpty,
1699
+ children: [barEmpty, "]"]
1700
+ }),
1701
+ /* @__PURE__ */ jsxs("text", {
1702
+ fg: theme.progress.textDim,
1703
+ children: [percent, "%"]
1704
+ }),
1705
+ /* @__PURE__ */ jsx("text", {
1706
+ fg: theme.progress.text,
1707
+ children: displayLabel
1708
+ })
1709
+ ]
1710
+ })
1711
+ });
1712
+ }
1713
+
1714
+ //#endregion
1715
+ //#region src/components/screen/table_message.tsx
1716
+ function TableMessage({ message }) {
1717
+ const colWidths = message.headers.map((h, i) => {
1718
+ const headerLen = h.length;
1719
+ const maxRowLen = message.rows.length > 0 ? Math.max(...message.rows.map((r) => (r[i] ?? "").length)) : 0;
1720
+ return Math.max(headerLen, maxRowLen);
1721
+ });
1722
+ const pad = (str, width) => str.padEnd(width);
1723
+ const headerRow = message.headers.map((h, i) => pad(h, colWidths[i] ?? 0)).join(" │ ");
1724
+ const separator = colWidths.map((w) => "─".repeat(w)).join("─┼─");
1725
+ const dataRows = message.rows.map((row) => row.map((cell, i) => pad(cell, colWidths[i] ?? 0)).join(" │ "));
1726
+ return /* @__PURE__ */ jsxs("box", {
1727
+ flexDirection: "column",
1728
+ border: ["left"],
1729
+ borderColor: TABLE_COLORS.border,
1730
+ backgroundColor: TABLE_COLORS.background,
1731
+ paddingLeft: 1,
1732
+ paddingRight: 1,
1733
+ children: [
1734
+ message.title && /* @__PURE__ */ jsx("text", {
1735
+ fg: TABLE_COLORS.title,
1736
+ attributes: TextAttributes.BOLD,
1737
+ children: message.title
1738
+ }),
1739
+ /* @__PURE__ */ jsx("text", {
1740
+ fg: TABLE_COLORS.headerText,
1741
+ attributes: TextAttributes.BOLD,
1742
+ children: headerRow
1743
+ }),
1744
+ /* @__PURE__ */ jsx("text", {
1745
+ fg: TABLE_COLORS.separator,
1746
+ children: separator
1747
+ }),
1748
+ dataRows.map((row, i) => /* @__PURE__ */ jsx("text", {
1749
+ fg: TABLE_COLORS.cellText,
1750
+ children: row
1751
+ }, i))
1752
+ ]
1753
+ });
1754
+ }
1755
+
1756
+ //#endregion
1757
+ //#region src/components/screen/message_renderer.tsx
1758
+ function MessageRenderer({ message }) {
1759
+ const theme = useTheme();
1760
+ switch (message.type) {
1761
+ case "log": return /* @__PURE__ */ jsx(LogMessage, {
1762
+ level: message.level,
1763
+ timestamp: message.timestamp,
1764
+ variant: message.variant,
1765
+ label: message.label,
1766
+ trace: message.trace,
1767
+ children: message.content
1768
+ });
1769
+ case "file": return /* @__PURE__ */ jsx("box", {
1770
+ flexDirection: "column",
1771
+ border: ["left"],
1772
+ borderColor: theme.file.border,
1773
+ backgroundColor: theme.file.background,
1774
+ children: /* @__PURE__ */ jsx(FileLog, {
1775
+ mode: "full",
1776
+ filePath: message.filePath,
1777
+ content: message.content,
1778
+ headerBackgroundColor: theme.file.headerBackground
1779
+ })
1780
+ });
1781
+ case "diff": return /* @__PURE__ */ jsx("box", {
1782
+ flexDirection: "column",
1783
+ border: ["left"],
1784
+ borderColor: theme.file.border,
1785
+ backgroundColor: theme.file.background,
1786
+ children: /* @__PURE__ */ jsx(FileLog, {
1787
+ mode: "diff",
1788
+ filePath: message.filePath,
1789
+ diff: message.diff,
1790
+ view: message.view,
1791
+ headerBackgroundColor: theme.file.headerBackground
1792
+ })
1793
+ });
1794
+ case "fileError": return /* @__PURE__ */ jsx("box", {
1795
+ flexDirection: "column",
1796
+ border: ["left"],
1797
+ borderColor: theme.file.border,
1798
+ backgroundColor: theme.file.background,
1799
+ children: /* @__PURE__ */ jsx(FileLog, {
1800
+ mode: "partial",
1801
+ filePath: message.filePath,
1802
+ content: message.content,
1803
+ startLine: message.startLine,
1804
+ errorLines: message.errorLines,
1805
+ headerBackgroundColor: theme.file.headerBackground
1806
+ })
1807
+ });
1808
+ case "loading": return /* @__PURE__ */ jsx(LoadingMessage, { message });
1809
+ case "progress": return /* @__PURE__ */ jsx(ProgressMessage, { message });
1810
+ case "group": return /* @__PURE__ */ jsx(GroupMessageRenderer, { message });
1811
+ case "table": return /* @__PURE__ */ jsx(TableMessage, { message });
1812
+ default: return null;
1813
+ }
1814
+ }
1815
+
1816
+ //#endregion
1817
+ //#region src/components/screen/group_renderer.tsx
1818
+ function GroupRenderer({ label, messages }) {
1819
+ const theme = useTheme();
1820
+ return /* @__PURE__ */ jsxs("box", {
1821
+ flexDirection: "column",
1822
+ border: ["left"],
1823
+ borderColor: theme.group.border,
1824
+ backgroundColor: theme.group.background,
1825
+ paddingLeft: 1,
1826
+ children: [/* @__PURE__ */ jsxs("box", {
1827
+ flexDirection: "row",
1828
+ marginBottom: 1,
1829
+ children: [/* @__PURE__ */ jsx("text", {
1830
+ fg: theme.group.icon,
1831
+ children: "▼ "
1832
+ }), /* @__PURE__ */ jsx("text", {
1833
+ fg: theme.group.headerText,
1834
+ attributes: TextAttributes.BOLD,
1835
+ children: label
1836
+ })]
1837
+ }), /* @__PURE__ */ jsx("box", {
1838
+ flexDirection: "column",
1839
+ gap: 1,
1840
+ children: messages.map((msg) => /* @__PURE__ */ jsx(MessageRenderer, { message: msg }, msg.id))
1841
+ })]
1842
+ });
1843
+ }
1844
+ /**
1845
+ * Fallback renderer for group markers (when not processed at higher level)
1846
+ */ function GroupMessageRenderer({ message }) {
1847
+ const theme = useTheme();
1848
+ if (message.isEnd) return null;
1849
+ return /* @__PURE__ */ jsx("box", {
1850
+ flexDirection: "row",
1851
+ borderColor: theme.group.border,
1852
+ border: ["left"],
1853
+ paddingLeft: 1,
1854
+ children: /* @__PURE__ */ jsxs("text", {
1855
+ fg: theme.group.headerText,
1856
+ attributes: TextAttributes.BOLD,
1857
+ children: ["▼ ", message.label]
1858
+ })
1859
+ });
1860
+ }
1861
+
1862
+ //#endregion
1863
+ //#region src/components/screen/screen_bridge.tsx
1864
+ function processMessagesIntoGroups(messages) {
1865
+ const result = [];
1866
+ let i = 0;
1867
+ while (i < messages.length) {
1868
+ const msg = messages[i];
1869
+ if (msg.type === "group" && !msg.isEnd) {
1870
+ const groupLabel = msg.label;
1871
+ const groupMessages = [];
1872
+ i++;
1873
+ while (i < messages.length) {
1874
+ const innerMsg = messages[i];
1875
+ if (innerMsg.type === "group" && innerMsg.isEnd) {
1876
+ i++;
1877
+ break;
1878
+ }
1879
+ groupMessages.push(innerMsg);
1880
+ i++;
1881
+ }
1882
+ result.push({
1883
+ type: "group",
1884
+ label: groupLabel,
1885
+ messages: groupMessages
1886
+ });
1887
+ } else if (msg.type === "group" && msg.isEnd) i++;
1888
+ else {
1889
+ result.push({
1890
+ type: "single",
1891
+ message: msg
1892
+ });
1893
+ i++;
1894
+ }
1895
+ }
1896
+ return result;
1897
+ }
1898
+ function ScreenBridge({ screen, focused, filteredMessages, isFiltering, totalMessages }) {
1899
+ const theme = useTheme();
1900
+ const [, forceUpdate] = useState({});
1901
+ useEffect(() => {
1902
+ return screen.onChange(() => forceUpdate({}));
1903
+ }, [screen]);
1904
+ const messages = filteredMessages ?? screen.getMessages();
1905
+ const activePrompt = screen.getActivePrompt();
1906
+ const processedMessages = processMessagesIntoGroups(messages);
1907
+ const filteredCount = messages.length;
1908
+ const total = totalMessages ?? messages.length;
1909
+ const showFilterStatus = isFiltering && filteredCount !== total;
1910
+ return /* @__PURE__ */ jsxs("box", {
1911
+ flexDirection: "column",
1912
+ flexGrow: 1,
1913
+ children: [/* @__PURE__ */ jsxs("box", {
1914
+ backgroundColor: theme.header.background,
1915
+ borderColor: focused ? theme.sidebar.focusBorder : theme.header.border,
1916
+ border: ["bottom"],
1917
+ paddingLeft: 1,
1918
+ paddingRight: 1,
1919
+ flexDirection: "row",
1920
+ justifyContent: "space-between",
1921
+ children: [/* @__PURE__ */ jsx("text", {
1922
+ fg: theme.header.text,
1923
+ attributes: TextAttributes.BOLD,
1924
+ children: screen.getName()
1925
+ }), showFilterStatus && /* @__PURE__ */ jsxs("text", {
1926
+ fg: theme.sidebar.textDim,
1927
+ children: [
1928
+ filteredCount,
1929
+ "/",
1930
+ total,
1931
+ " messages"
1932
+ ]
1933
+ })]
1934
+ }), /* @__PURE__ */ jsxs("scrollbox", {
1935
+ flexGrow: 1,
1936
+ scrollY: true,
1937
+ stickyScroll: true,
1938
+ stickyStart: "bottom",
1939
+ contentOptions: {
1940
+ paddingLeft: 1,
1941
+ paddingRight: 1,
1942
+ paddingTop: 1,
1943
+ paddingBottom: 1,
1944
+ gap: 1
1945
+ },
1946
+ children: [processedMessages.map((item, index) => {
1947
+ if (item.type === "group") return /* @__PURE__ */ jsx(GroupRenderer, {
1948
+ label: item.label,
1949
+ messages: item.messages
1950
+ }, `group-${index}`);
1951
+ else return /* @__PURE__ */ jsx(MessageRenderer, { message: item.message }, item.message.id);
1952
+ }), activePrompt && /* @__PURE__ */ jsx(PromptRenderer, { prompt: activePrompt })]
1953
+ })]
1954
+ });
1955
+ }
1956
+
1957
+ //#endregion
1958
+ //#region src/components/sidebar/sidebar_item.tsx
1959
+ function SidebarItem({ screen, isSelected, isActive, focused }) {
1960
+ const theme = useTheme();
1961
+ const status = screen.getStatus();
1962
+ const statusIndicator = theme.statusIndicators[status];
1963
+ let backgroundColor = void 0;
1964
+ if (isSelected && focused) backgroundColor = theme.sidebar.selectedBackground;
1965
+ else if (isActive) backgroundColor = theme.sidebar.selectedBackground + "80";
1966
+ return /* @__PURE__ */ jsxs("box", {
1967
+ flexDirection: "row",
1968
+ paddingLeft: 1,
1969
+ paddingRight: 1,
1970
+ backgroundColor,
1971
+ children: [
1972
+ /* @__PURE__ */ jsxs("text", {
1973
+ fg: isSelected && focused ? theme.sidebar.focusBorder : "transparent",
1974
+ children: [">", " "]
1975
+ }),
1976
+ /* @__PURE__ */ jsxs("text", {
1977
+ fg: statusIndicator.color,
1978
+ children: [statusIndicator.icon, " "]
1979
+ }),
1980
+ /* @__PURE__ */ jsx("text", {
1981
+ fg: isActive ? theme.sidebar.text : theme.sidebar.textDim,
1982
+ flexGrow: 1,
1983
+ children: screen.getName()
1984
+ }),
1985
+ screen.getBadgeCount() > 0 && /* @__PURE__ */ jsx("box", {
1986
+ backgroundColor: theme.sidebar.badge,
1987
+ paddingLeft: 1,
1988
+ paddingRight: 1,
1989
+ children: /* @__PURE__ */ jsx("text", {
1990
+ fg: theme.colors.foreground,
1991
+ attributes: TextAttributes.BOLD,
1992
+ children: screen.getBadgeCount() > 99 ? "99+" : screen.getBadgeCount()
1993
+ })
1994
+ })
1995
+ ]
1996
+ });
1997
+ }
1998
+
1999
+ //#endregion
2000
+ //#region src/components/sidebar/sidebar_separator.tsx
2001
+ function SidebarSeparator() {
2002
+ const theme = useTheme();
2003
+ return /* @__PURE__ */ jsxs("box", {
2004
+ flexDirection: "row",
2005
+ paddingTop: 1,
2006
+ children: [
2007
+ /* @__PURE__ */ jsx("box", { flexGrow: 1 }),
2008
+ /* @__PURE__ */ jsx("text", {
2009
+ fg: theme.separator.line,
2010
+ children: "· · ·"
2011
+ }),
2012
+ /* @__PURE__ */ jsx("box", { flexGrow: 1 })
2013
+ ]
2014
+ });
2015
+ }
2016
+
2017
+ //#endregion
2018
+ //#region src/components/sidebar/sidebar.tsx
2019
+ function Sidebar({ screens, selectedIndex, activeScreenId, focused, width, title }) {
2020
+ const theme = useTheme();
2021
+ const pendingScreens = [];
2022
+ const otherScreens = [];
2023
+ screens.forEach((screen, index) => {
2024
+ if (screen.getStatus() === "pending") pendingScreens.push({
2025
+ screen,
2026
+ originalIndex: index
2027
+ });
2028
+ else otherScreens.push({
2029
+ screen,
2030
+ originalIndex: index
2031
+ });
2032
+ });
2033
+ const hasPending = pendingScreens.length > 0;
2034
+ const hasOther = otherScreens.length > 0;
2035
+ const showSeparator = hasPending && hasOther;
2036
+ return /* @__PURE__ */ jsxs("box", {
2037
+ flexDirection: "column",
2038
+ width,
2039
+ borderColor: focused ? theme.sidebar.focusBorder : theme.sidebar.border,
2040
+ border: ["right"],
2041
+ children: [
2042
+ /* @__PURE__ */ jsx("box", {
2043
+ backgroundColor: theme.header.background,
2044
+ paddingLeft: 1,
2045
+ paddingRight: 1,
2046
+ borderColor: theme.header.border,
2047
+ border: ["bottom"],
2048
+ children: /* @__PURE__ */ jsx("text", {
2049
+ fg: theme.header.text,
2050
+ attributes: TextAttributes.BOLD,
2051
+ children: title
2052
+ })
2053
+ }),
2054
+ /* @__PURE__ */ jsx("scrollbox", {
2055
+ scrollY: true,
2056
+ stickyScroll: false,
2057
+ flexGrow: 1,
2058
+ contentOptions: { flexGrow: 1 },
2059
+ children: /* @__PURE__ */ jsxs("box", {
2060
+ flexDirection: "column",
2061
+ children: [
2062
+ pendingScreens.map(({ screen, originalIndex }) => /* @__PURE__ */ jsx(SidebarItem, {
2063
+ screen,
2064
+ isSelected: originalIndex === selectedIndex,
2065
+ isActive: screen.getId() === activeScreenId,
2066
+ focused
2067
+ }, screen.getId())),
2068
+ showSeparator && /* @__PURE__ */ jsx(SidebarSeparator, {}),
2069
+ otherScreens.map(({ screen, originalIndex }) => /* @__PURE__ */ jsx(SidebarItem, {
2070
+ screen,
2071
+ isSelected: originalIndex === selectedIndex,
2072
+ isActive: screen.getId() === activeScreenId,
2073
+ focused
2074
+ }, screen.getId()))
2075
+ ]
2076
+ })
2077
+ }),
2078
+ /* @__PURE__ */ jsx("box", {
2079
+ paddingLeft: 1,
2080
+ paddingRight: 1,
2081
+ borderColor: theme.sidebar.border,
2082
+ border: ["top"],
2083
+ children: /* @__PURE__ */ jsx("text", {
2084
+ fg: theme.sidebar.text,
2085
+ children: "q: exit | Tab: focus | ?: help"
2086
+ })
2087
+ })
2088
+ ]
2089
+ });
2090
+ }
2091
+
2092
+ //#endregion
2093
+ //#region src/filter/filter_engine.ts
2094
+ /**
2095
+ * Filter engine for filtering log messages.
2096
+ */ var FilterEngine = class {
2097
+ /**
2098
+ * Apply filter to messages array.
2099
+ */ static filterMessages(messages, filter) {
2100
+ if (filter.searchQuery === "" && filter.enabledLevels.size === ALL_LOG_LEVELS.length) return messages;
2101
+ return messages.filter((msg) => {
2102
+ if (msg.type !== "log") {
2103
+ if (filter.searchQuery) return this.messageMatchesSearch(msg, filter.searchQuery);
2104
+ if (msg.type === "group") return true;
2105
+ return true;
2106
+ }
2107
+ const logMsg = msg;
2108
+ if (!filter.enabledLevels.has(logMsg.level)) return false;
2109
+ if (filter.searchQuery && !this.messageMatchesSearch(logMsg, filter.searchQuery)) return false;
2110
+ return true;
2111
+ });
2112
+ }
2113
+ /**
2114
+ * Check if a message matches the search query.
2115
+ */ static messageMatchesSearch(msg, query) {
2116
+ const lowerQuery = query.toLowerCase();
2117
+ switch (msg.type) {
2118
+ case "log": return msg.content.toLowerCase().includes(lowerQuery) || (msg.label?.toLowerCase().includes(lowerQuery) ?? false);
2119
+ case "file":
2120
+ case "fileError": return msg.filePath.toLowerCase().includes(lowerQuery) || msg.content.toLowerCase().includes(lowerQuery);
2121
+ case "diff": return msg.filePath.toLowerCase().includes(lowerQuery) || msg.diff.toLowerCase().includes(lowerQuery);
2122
+ case "loading": return msg.content.toLowerCase().includes(lowerQuery) || (msg.resolvedContent?.toLowerCase().includes(lowerQuery) ?? false);
2123
+ case "progress": return msg.label.toLowerCase().includes(lowerQuery);
2124
+ case "group": return msg.label.toLowerCase().includes(lowerQuery);
2125
+ case "table": return (msg.title?.toLowerCase().includes(lowerQuery) ?? false) || msg.headers.some((h) => h.toLowerCase().includes(lowerQuery)) || msg.rows.some((row) => row.some((cell) => cell.toLowerCase().includes(lowerQuery)));
2126
+ default: return false;
2127
+ }
2128
+ }
2129
+ /**
2130
+ * Count messages by log level.
2131
+ */ static countByLevel(messages) {
2132
+ const counts = {
2133
+ debug: 0,
2134
+ log: 0,
2135
+ verbose: 0,
2136
+ error: 0,
2137
+ fatal: 0,
2138
+ warn: 0
2139
+ };
2140
+ for (const msg of messages) if (msg.type === "log") counts[msg.level]++;
2141
+ return counts;
2142
+ }
2143
+ };
2144
+
2145
+ //#endregion
2146
+ //#region src/keyboard/keyboard_manager.ts
2147
+ /**
2148
+ * Manages keyboard bindings and dispatches key events to handlers.
2149
+ */ var KeyboardManager = class {
2150
+ bindings = [];
2151
+ disabled = /* @__PURE__ */ new Set();
2152
+ constructor(config) {
2153
+ if (config?.bindings) this.bindings = [...config.bindings];
2154
+ if (config?.disabled) this.disabled = new Set(config.disabled);
2155
+ this.sortBindings();
2156
+ }
2157
+ /**
2158
+ * Add bindings to the manager.
2159
+ */ addBindings(bindings) {
2160
+ this.bindings.push(...bindings);
2161
+ this.sortBindings();
2162
+ }
2163
+ /**
2164
+ * Remove a binding by key name.
2165
+ */ removeBinding(key) {
2166
+ this.bindings = this.bindings.filter((b) => {
2167
+ return !(Array.isArray(b.key) ? b.key : [b.key]).includes(key);
2168
+ });
2169
+ }
2170
+ /**
2171
+ * Disable a key (prevents it from being matched).
2172
+ */ disableKey(key) {
2173
+ this.disabled.add(key);
2174
+ }
2175
+ /**
2176
+ * Enable a previously disabled key.
2177
+ */ enableKey(key) {
2178
+ this.disabled.delete(key);
2179
+ }
2180
+ /**
2181
+ * Handle a key event, dispatching to the appropriate handler.
2182
+ * Returns true if a handler consumed the event.
2183
+ */ handleKey(key, context) {
2184
+ const binding = this.findMatchingBinding(key, context);
2185
+ if (binding) return binding.handler(key, context) !== false;
2186
+ return false;
2187
+ }
2188
+ /**
2189
+ * Get all bindings for display in help overlay.
2190
+ */ getBindingsForHelp() {
2191
+ return this.bindings.filter((b) => b.description);
2192
+ }
2193
+ /**
2194
+ * Get bindings grouped by category.
2195
+ */ getBindingsByCategory() {
2196
+ const grouped = {
2197
+ general: [],
2198
+ navigation: [],
2199
+ screen: [],
2200
+ prompt: [],
2201
+ filter: []
2202
+ };
2203
+ for (const binding of this.bindings) if (binding.description) grouped[binding.category].push(binding);
2204
+ return grouped;
2205
+ }
2206
+ /**
2207
+ * Find a matching binding for the given key and context.
2208
+ */ findMatchingBinding(key, context) {
2209
+ for (const binding of this.bindings) if (this.keyMatches(key, binding) && this.conditionMatches(binding.when, context) && !this.isDisabled(binding)) return binding;
2210
+ return null;
2211
+ }
2212
+ /**
2213
+ * Check if a key event matches a binding's key specification.
2214
+ */ keyMatches(key, binding) {
2215
+ const keys = Array.isArray(binding.key) ? binding.key : [binding.key];
2216
+ if (!(keys.includes(key.name) || key.sequence && keys.includes(key.sequence))) return false;
2217
+ const ctrlMatches = (binding.ctrl ?? false) === (key.ctrl ?? false);
2218
+ const metaMatches = (binding.meta ?? false) === (key.meta ?? false);
2219
+ const shiftMatches = (binding.shift ?? false) === (key.shift ?? false);
2220
+ return ctrlMatches && metaMatches && shiftMatches;
2221
+ }
2222
+ /**
2223
+ * Check if context matches a binding's condition.
2224
+ */ conditionMatches(condition, context) {
2225
+ if (!condition) return true;
2226
+ if (condition.hasPrompt !== void 0 && condition.hasPrompt !== context.hasPrompt) return false;
2227
+ if (condition.inInputMode !== void 0 && condition.inInputMode !== context.inInputMode) return false;
2228
+ if (condition.focusArea !== void 0 && condition.focusArea !== context.focusArea) return false;
2229
+ if (condition.isFilterActive !== void 0 && condition.isFilterActive !== context.isFilterActive) return false;
2230
+ if (condition.isHelpVisible !== void 0 && condition.isHelpVisible !== context.isHelpVisible) return false;
2231
+ if (condition.hasSidebar !== void 0 && condition.hasSidebar !== context.hasSidebar) return false;
2232
+ return true;
2233
+ }
2234
+ /**
2235
+ * Check if a binding is disabled.
2236
+ */ isDisabled(binding) {
2237
+ return (Array.isArray(binding.key) ? binding.key : [binding.key]).some((k) => this.disabled.has(k));
2238
+ }
2239
+ /**
2240
+ * Sort bindings by priority (higher first).
2241
+ */ sortBindings() {
2242
+ this.bindings.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
2243
+ }
2244
+ };
2245
+ /**
2246
+ * Format a key binding for display.
2247
+ */ function formatKeyBinding(binding) {
2248
+ const keys = Array.isArray(binding.key) ? binding.key : [binding.key];
2249
+ const parts = [];
2250
+ if (binding.ctrl) parts.push("Ctrl");
2251
+ if (binding.meta) parts.push("Cmd");
2252
+ if (binding.shift) parts.push("Shift");
2253
+ const keyDisplay = keys.map((k) => {
2254
+ switch (k) {
2255
+ case "up": return "↑";
2256
+ case "down": return "↓";
2257
+ case "left": return "←";
2258
+ case "right": return "→";
2259
+ case "return": return "Enter";
2260
+ case "escape": return "Esc";
2261
+ case "space": return "Space";
2262
+ case "tab": return "Tab";
2263
+ case "\\": return "\\";
2264
+ default: return k;
2265
+ }
2266
+ }).join("/");
2267
+ parts.push(keyDisplay);
2268
+ return parts.join("+");
2269
+ }
2270
+
2271
+ //#endregion
2272
+ //#region src/keyboard/create_bindings.ts
2273
+ /**
2274
+ * Create the default keybindings with access to manager and screens.
2275
+ */ function createDefaultBindings(handlers) {
2276
+ const { manager, getActiveScreen, toggleHelp, toggleFilter, closeFilter } = handlers;
2277
+ const bindings = [];
2278
+ bindings.push({
2279
+ key: "escape",
2280
+ handler: () => {
2281
+ const screen = getActiveScreen();
2282
+ if (screen?.isPromptInInputMode()) {
2283
+ screen.promptExitInputMode();
2284
+ return true;
2285
+ }
2286
+ },
2287
+ description: "Exit input mode",
2288
+ category: "prompt",
2289
+ when: {
2290
+ hasPrompt: true,
2291
+ inInputMode: true
2292
+ },
2293
+ priority: 100
2294
+ });
2295
+ bindings.push({
2296
+ key: "return",
2297
+ handler: () => {
2298
+ const screen = getActiveScreen();
2299
+ if (screen?.isPromptInInputMode()) {
2300
+ screen.promptSubmit();
2301
+ return true;
2302
+ }
2303
+ },
2304
+ description: "Submit input",
2305
+ category: "prompt",
2306
+ when: {
2307
+ hasPrompt: true,
2308
+ inInputMode: true
2309
+ },
2310
+ priority: 100
2311
+ });
2312
+ bindings.push({
2313
+ key: "backspace",
2314
+ handler: () => {
2315
+ const screen = getActiveScreen();
2316
+ if (screen?.isPromptInInputMode()) {
2317
+ screen.promptDeleteLastChar();
2318
+ return true;
2319
+ }
2320
+ },
2321
+ description: "Delete character",
2322
+ category: "prompt",
2323
+ when: {
2324
+ hasPrompt: true,
2325
+ inInputMode: true
2326
+ },
2327
+ priority: 100
2328
+ });
2329
+ bindings.push({
2330
+ key: ["up", "k"],
2331
+ handler: () => {
2332
+ const screen = getActiveScreen();
2333
+ const prompt = screen?.getActivePrompt();
2334
+ if (prompt?.type === "choice" || prompt?.type === "multiChoice") {
2335
+ screen?.promptNavigateUp();
2336
+ return true;
2337
+ }
2338
+ },
2339
+ description: "Navigate up",
2340
+ category: "prompt",
2341
+ when: {
2342
+ hasPrompt: true,
2343
+ inInputMode: false
2344
+ },
2345
+ priority: 90
2346
+ });
2347
+ bindings.push({
2348
+ key: ["down", "j"],
2349
+ handler: () => {
2350
+ const screen = getActiveScreen();
2351
+ const prompt = screen?.getActivePrompt();
2352
+ if (prompt?.type === "choice" || prompt?.type === "multiChoice") {
2353
+ screen?.promptNavigateDown();
2354
+ return true;
2355
+ }
2356
+ },
2357
+ description: "Navigate down",
2358
+ category: "prompt",
2359
+ when: {
2360
+ hasPrompt: true,
2361
+ inInputMode: false
2362
+ },
2363
+ priority: 90
2364
+ });
2365
+ bindings.push({
2366
+ key: ["left", "h"],
2367
+ handler: () => {
2368
+ const screen = getActiveScreen();
2369
+ if ((screen?.getActivePrompt())?.type === "confirm") {
2370
+ screen?.promptNavigateLeft();
2371
+ return true;
2372
+ }
2373
+ },
2374
+ description: "Select confirm",
2375
+ category: "prompt",
2376
+ when: {
2377
+ hasPrompt: true,
2378
+ inInputMode: false
2379
+ },
2380
+ priority: 90
2381
+ });
2382
+ bindings.push({
2383
+ key: ["right", "l"],
2384
+ handler: () => {
2385
+ const screen = getActiveScreen();
2386
+ if ((screen?.getActivePrompt())?.type === "confirm") {
2387
+ screen?.promptNavigateRight();
2388
+ return true;
2389
+ }
2390
+ },
2391
+ description: "Select cancel",
2392
+ category: "prompt",
2393
+ when: {
2394
+ hasPrompt: true,
2395
+ inInputMode: false
2396
+ },
2397
+ priority: 90
2398
+ });
2399
+ bindings.push({
2400
+ key: "space",
2401
+ handler: () => {
2402
+ const screen = getActiveScreen();
2403
+ if ((screen?.getActivePrompt())?.type === "multiChoice") {
2404
+ screen?.promptToggleSelection();
2405
+ return true;
2406
+ }
2407
+ },
2408
+ description: "Toggle selection",
2409
+ category: "prompt",
2410
+ when: {
2411
+ hasPrompt: true,
2412
+ inInputMode: false
2413
+ },
2414
+ priority: 90
2415
+ });
2416
+ bindings.push({
2417
+ key: "return",
2418
+ handler: () => {
2419
+ const screen = getActiveScreen();
2420
+ const prompt = screen?.getActivePrompt();
2421
+ if (!prompt) return false;
2422
+ if (prompt.type === "choice") {
2423
+ if (!screen?.promptEnterInputMode()) screen?.promptSubmit();
2424
+ return true;
2425
+ } else if (prompt.type === "confirm") {
2426
+ screen?.promptSubmit();
2427
+ return true;
2428
+ } else if (prompt.type === "multiChoice") {
2429
+ if (screen?.canSubmitPrompt()) screen.promptSubmit();
2430
+ return true;
2431
+ }
2432
+ },
2433
+ description: "Submit selection",
2434
+ category: "prompt",
2435
+ when: {
2436
+ hasPrompt: true,
2437
+ inInputMode: false
2438
+ },
2439
+ priority: 90
2440
+ });
2441
+ bindings.push({
2442
+ key: "?",
2443
+ handler: () => {
2444
+ toggleHelp();
2445
+ return true;
2446
+ },
2447
+ description: "Toggle help",
2448
+ category: "general",
2449
+ when: {
2450
+ hasPrompt: false,
2451
+ isFilterActive: false
2452
+ },
2453
+ priority: 50
2454
+ });
2455
+ bindings.push({
2456
+ key: "escape",
2457
+ handler: () => {
2458
+ toggleHelp();
2459
+ return true;
2460
+ },
2461
+ description: "Close help",
2462
+ category: "general",
2463
+ when: { isHelpVisible: true },
2464
+ priority: 80
2465
+ });
2466
+ bindings.push({
2467
+ key: "/",
2468
+ handler: () => {
2469
+ toggleFilter();
2470
+ return true;
2471
+ },
2472
+ description: "Toggle filter",
2473
+ category: "filter",
2474
+ when: {
2475
+ hasPrompt: false,
2476
+ isHelpVisible: false
2477
+ },
2478
+ priority: 50
2479
+ });
2480
+ bindings.push({
2481
+ key: "escape",
2482
+ handler: () => {
2483
+ closeFilter();
2484
+ return true;
2485
+ },
2486
+ description: "Close filter",
2487
+ category: "filter",
2488
+ when: { isFilterActive: true },
2489
+ priority: 70
2490
+ });
2491
+ bindings.push({
2492
+ key: "tab",
2493
+ handler: () => {
2494
+ handlers.filterCycleField();
2495
+ return true;
2496
+ },
2497
+ description: "Cycle filter fields",
2498
+ category: "filter",
2499
+ when: { isFilterActive: true },
2500
+ priority: 70
2501
+ });
2502
+ bindings.push({
2503
+ key: "backspace",
2504
+ handler: () => {
2505
+ handlers.filterDeleteChar();
2506
+ return true;
2507
+ },
2508
+ description: "Delete character",
2509
+ category: "filter",
2510
+ when: { isFilterActive: true },
2511
+ priority: 70
2512
+ });
2513
+ for (let i = 1; i <= 7; i++) bindings.push({
2514
+ key: String(i),
2515
+ handler: () => {
2516
+ handlers.filterToggleLevel(i - 1);
2517
+ return true;
2518
+ },
2519
+ description: `Toggle level ${i}`,
2520
+ category: "filter",
2521
+ when: { isFilterActive: true },
2522
+ priority: 70
2523
+ });
2524
+ bindings.push({
2525
+ key: "q",
2526
+ handler: () => {
2527
+ manager.unbind();
2528
+ return true;
2529
+ },
2530
+ description: "Exit",
2531
+ category: "general",
2532
+ when: {
2533
+ hasPrompt: false,
2534
+ isFilterActive: false,
2535
+ isHelpVisible: false
2536
+ },
2537
+ priority: 10
2538
+ });
2539
+ bindings.push({
2540
+ key: "tab",
2541
+ handler: (_, ctx) => {
2542
+ if (ctx.hasSidebar) {
2543
+ manager.toggleFocus();
2544
+ return true;
2545
+ }
2546
+ },
2547
+ description: "Toggle focus",
2548
+ category: "navigation",
2549
+ when: {
2550
+ hasPrompt: false,
2551
+ isFilterActive: false
2552
+ },
2553
+ priority: 20
2554
+ });
2555
+ bindings.push({
2556
+ key: "\\",
2557
+ handler: (_, ctx) => {
2558
+ if (ctx.hasSidebar) {
2559
+ manager.toggleFocus();
2560
+ return true;
2561
+ }
2562
+ },
2563
+ description: "Toggle focus",
2564
+ category: "navigation",
2565
+ when: {
2566
+ hasPrompt: false,
2567
+ isFilterActive: false
2568
+ },
2569
+ priority: 20
2570
+ });
2571
+ bindings.push({
2572
+ key: ["up", "k"],
2573
+ handler: () => {
2574
+ manager.navigateUp();
2575
+ return true;
2576
+ },
2577
+ description: "Navigate up",
2578
+ category: "navigation",
2579
+ when: {
2580
+ focusArea: "sidebar",
2581
+ hasPrompt: false
2582
+ },
2583
+ priority: 30
2584
+ });
2585
+ bindings.push({
2586
+ key: ["down", "j"],
2587
+ handler: () => {
2588
+ manager.navigateDown();
2589
+ return true;
2590
+ },
2591
+ description: "Navigate down",
2592
+ category: "navigation",
2593
+ when: {
2594
+ focusArea: "sidebar",
2595
+ hasPrompt: false
2596
+ },
2597
+ priority: 30
2598
+ });
2599
+ bindings.push({
2600
+ key: "return",
2601
+ handler: () => {
2602
+ manager.selectCurrent();
2603
+ return true;
2604
+ },
2605
+ description: "Select screen",
2606
+ category: "navigation",
2607
+ when: {
2608
+ focusArea: "sidebar",
2609
+ hasPrompt: false
2610
+ },
2611
+ priority: 30
2612
+ });
2613
+ for (let i = 1; i <= 9; i++) bindings.push({
2614
+ key: String(i),
2615
+ handler: () => {
2616
+ const screens = manager.getScreens();
2617
+ if (i <= screens.length) {
2618
+ manager.setActiveScreen(screens[i - 1]);
2619
+ manager.setSelectedIndex(i - 1);
2620
+ return true;
2621
+ }
2622
+ },
2623
+ description: `Jump to screen ${i}`,
2624
+ category: "screen",
2625
+ when: {
2626
+ hasPrompt: false,
2627
+ isFilterActive: false
2628
+ },
2629
+ priority: 15
2630
+ });
2631
+ bindings.push({
2632
+ key: "c",
2633
+ handler: () => {
2634
+ getActiveScreen()?.clear();
2635
+ return true;
2636
+ },
2637
+ description: "Clear screen",
2638
+ category: "screen",
2639
+ when: {
2640
+ focusArea: "content",
2641
+ hasPrompt: false,
2642
+ isFilterActive: false
2643
+ },
2644
+ priority: 15
2645
+ });
2646
+ return bindings;
2647
+ }
2648
+ /**
2649
+ * Handle printable character input for prompts and filter.
2650
+ * This should be called for any key not handled by bindings.
2651
+ */ function handlePrintableInput(key, context, handlers) {
2652
+ if (!key.sequence || key.sequence.length !== 1 || key.ctrl || key.meta) return false;
2653
+ const charCode = key.sequence.charCodeAt(0);
2654
+ if (charCode < 32 || charCode > 126) return false;
2655
+ if (context.isFilterActive) {
2656
+ handlers.filterAppendChar(key.sequence);
2657
+ return true;
2658
+ }
2659
+ if (context.hasPrompt && context.inInputMode) {
2660
+ handlers.getActiveScreen()?.promptAppendInput(key.sequence);
2661
+ return true;
2662
+ }
2663
+ return false;
2664
+ }
2665
+
2666
+ //#endregion
2667
+ //#region src/components/filter/filter_bar.tsx
2668
+ const LEVEL_LABELS = {
2669
+ verbose: "V",
2670
+ debug: "D",
2671
+ log: "L",
2672
+ warn: "W",
2673
+ error: "E",
2674
+ fatal: "F"
2675
+ };
2676
+ function FilterBar({ filter, levelCounts }) {
2677
+ const theme = useTheme();
2678
+ const isSearchFocused = filter.focusedField === "search";
2679
+ return /* @__PURE__ */ jsxs("box", {
2680
+ flexDirection: "column",
2681
+ backgroundColor: theme.filter.background,
2682
+ borderColor: theme.filter.border,
2683
+ border: ["bottom"],
2684
+ paddingLeft: 1,
2685
+ paddingRight: 1,
2686
+ children: [
2687
+ /* @__PURE__ */ jsxs("box", {
2688
+ flexDirection: "row",
2689
+ gap: 1,
2690
+ children: [
2691
+ /* @__PURE__ */ jsx("text", {
2692
+ fg: isSearchFocused ? theme.colors.primary : theme.filter.textDim,
2693
+ children: "/"
2694
+ }),
2695
+ /* @__PURE__ */ jsx("text", {
2696
+ fg: filter.searchQuery ? theme.filter.inputText : theme.filter.inputPlaceholder,
2697
+ children: filter.searchQuery || "Search logs..."
2698
+ }),
2699
+ isSearchFocused && /* @__PURE__ */ jsx("text", {
2700
+ fg: theme.filter.cursor,
2701
+ attributes: TextAttributes.BLINK,
2702
+ children: "_"
2703
+ })
2704
+ ]
2705
+ }),
2706
+ /* @__PURE__ */ jsxs("box", {
2707
+ flexDirection: "row",
2708
+ gap: 1,
2709
+ children: [/* @__PURE__ */ jsx("text", {
2710
+ fg: theme.filter.textDim,
2711
+ children: "Levels:"
2712
+ }), ALL_LOG_LEVELS.map((level, index) => {
2713
+ const isEnabled = filter.enabledLevels.has(level);
2714
+ const count = levelCounts[level];
2715
+ const levelColor = theme.logLevels[level].border;
2716
+ const isLevelsFocused = filter.focusedField === "levels";
2717
+ return /* @__PURE__ */ jsxs("box", {
2718
+ flexDirection: "row",
2719
+ children: [/* @__PURE__ */ jsxs("text", {
2720
+ fg: isEnabled ? levelColor : theme.filter.inactiveLevel,
2721
+ attributes: isLevelsFocused ? TextAttributes.BOLD : void 0,
2722
+ children: [
2723
+ index + 1,
2724
+ ":",
2725
+ LEVEL_LABELS[level]
2726
+ ]
2727
+ }), count > 0 && /* @__PURE__ */ jsxs("text", {
2728
+ fg: theme.filter.textDim,
2729
+ children: [
2730
+ "(",
2731
+ count > 99 ? "99+" : count,
2732
+ ")"
2733
+ ]
2734
+ })]
2735
+ }, level);
2736
+ })]
2737
+ }),
2738
+ /* @__PURE__ */ jsx("box", {
2739
+ flexDirection: "row",
2740
+ children: /* @__PURE__ */ jsx("text", {
2741
+ fg: theme.filter.textDim,
2742
+ children: "Tab: switch fields | 1-7: toggle levels | Esc: close"
2743
+ })
2744
+ })
2745
+ ]
2746
+ });
2747
+ }
2748
+
2749
+ //#endregion
2750
+ //#region src/components/help/help_overlay.tsx
2751
+ const CATEGORY_ORDER = [
2752
+ "general",
2753
+ "navigation",
2754
+ "screen",
2755
+ "filter",
2756
+ "prompt"
2757
+ ];
2758
+ const CATEGORY_LABELS = {
2759
+ general: "General",
2760
+ navigation: "Navigation",
2761
+ screen: "Screen",
2762
+ filter: "Filter",
2763
+ prompt: "Prompts"
2764
+ };
2765
+ function groupByCategory(bindings) {
2766
+ const grouped = {
2767
+ general: [],
2768
+ navigation: [],
2769
+ screen: [],
2770
+ filter: [],
2771
+ prompt: []
2772
+ };
2773
+ for (const binding of bindings) if (binding.description) grouped[binding.category].push(binding);
2774
+ return grouped;
2775
+ }
2776
+ function HelpOverlay({ bindings }) {
2777
+ const theme = useTheme();
2778
+ const grouped = groupByCategory(bindings);
2779
+ return /* @__PURE__ */ jsxs("box", {
2780
+ position: "absolute",
2781
+ top: 1,
2782
+ left: 2,
2783
+ right: 2,
2784
+ bottom: 1,
2785
+ backgroundColor: theme.help.background,
2786
+ borderColor: theme.help.border,
2787
+ border: [
2788
+ "top",
2789
+ "bottom",
2790
+ "left",
2791
+ "right"
2792
+ ],
2793
+ flexDirection: "column",
2794
+ paddingLeft: 2,
2795
+ paddingRight: 2,
2796
+ paddingTop: 1,
2797
+ paddingBottom: 1,
2798
+ children: [
2799
+ /* @__PURE__ */ jsx("box", {
2800
+ marginBottom: 1,
2801
+ children: /* @__PURE__ */ jsx("text", {
2802
+ fg: theme.help.title,
2803
+ attributes: TextAttributes.BOLD,
2804
+ children: "Keyboard Shortcuts"
2805
+ })
2806
+ }),
2807
+ /* @__PURE__ */ jsx("scrollbox", {
2808
+ scrollY: true,
2809
+ flexGrow: 1,
2810
+ children: /* @__PURE__ */ jsx("box", {
2811
+ flexDirection: "column",
2812
+ gap: 1,
2813
+ children: CATEGORY_ORDER.map((category) => {
2814
+ const categoryBindings = grouped[category];
2815
+ if (categoryBindings.length === 0) return null;
2816
+ return /* @__PURE__ */ jsxs("box", {
2817
+ flexDirection: "column",
2818
+ children: [/* @__PURE__ */ jsx("text", {
2819
+ fg: theme.help.category,
2820
+ attributes: TextAttributes.BOLD,
2821
+ children: CATEGORY_LABELS[category]
2822
+ }), categoryBindings.map((binding, index) => /* @__PURE__ */ jsxs("box", {
2823
+ flexDirection: "row",
2824
+ paddingLeft: 2,
2825
+ children: [/* @__PURE__ */ jsx("text", {
2826
+ fg: theme.help.key,
2827
+ width: 14,
2828
+ children: formatKeyBinding(binding)
2829
+ }), /* @__PURE__ */ jsx("text", {
2830
+ fg: theme.help.description,
2831
+ children: binding.description
2832
+ })]
2833
+ }, `${binding.key}-${index}`))]
2834
+ }, category);
2835
+ })
2836
+ })
2837
+ }),
2838
+ /* @__PURE__ */ jsx("box", {
2839
+ marginTop: 1,
2840
+ borderColor: theme.separator.line,
2841
+ border: ["top"],
2842
+ paddingTop: 1,
2843
+ children: /* @__PURE__ */ jsx("text", {
2844
+ fg: theme.help.hint,
2845
+ children: "Press ? or Esc to close"
2846
+ })
2847
+ })
2848
+ ]
2849
+ });
2850
+ }
2851
+
2852
+ //#endregion
2853
+ //#region src/components/screen_manager_bridge.tsx
2854
+ function ScreenManagerBridge({ manager, theme }) {
2855
+ const [, forceUpdate] = useState({});
2856
+ const [showHelp, setShowHelp] = useState(false);
2857
+ const [filter, setFilter] = useState(createDefaultFilterState);
2858
+ useEffect(() => {
2859
+ return manager.onChange(() => forceUpdate({}));
2860
+ }, [manager]);
2861
+ const getActiveScreen = useCallback(() => manager.getActiveScreen(), [manager]);
2862
+ const toggleHelp = useCallback(() => {
2863
+ setShowHelp((prev) => !prev);
2864
+ }, []);
2865
+ const toggleFilter = useCallback(() => {
2866
+ setFilter((prev) => ({
2867
+ ...prev,
2868
+ isVisible: !prev.isVisible,
2869
+ focusedField: "search"
2870
+ }));
2871
+ }, []);
2872
+ const closeFilter = useCallback(() => {
2873
+ setFilter((prev) => ({
2874
+ ...prev,
2875
+ isVisible: false
2876
+ }));
2877
+ }, []);
2878
+ const filterAppendChar = useCallback((char) => {
2879
+ setFilter((prev) => ({
2880
+ ...prev,
2881
+ searchQuery: prev.searchQuery + char
2882
+ }));
2883
+ }, []);
2884
+ const filterDeleteChar = useCallback(() => {
2885
+ setFilter((prev) => ({
2886
+ ...prev,
2887
+ searchQuery: prev.searchQuery.slice(0, -1)
2888
+ }));
2889
+ }, []);
2890
+ const filterToggleLevel = useCallback((index) => {
2891
+ setFilter((prev) => {
2892
+ const level = ALL_LOG_LEVELS[index];
2893
+ if (!level) return prev;
2894
+ const newLevels = new Set(prev.enabledLevels);
2895
+ if (newLevels.has(level)) newLevels.delete(level);
2896
+ else newLevels.add(level);
2897
+ return {
2898
+ ...prev,
2899
+ enabledLevels: newLevels
2900
+ };
2901
+ });
2902
+ }, []);
2903
+ const filterCycleField = useCallback(() => {
2904
+ setFilter((prev) => ({
2905
+ ...prev,
2906
+ focusedField: prev.focusedField === "search" ? "levels" : "search"
2907
+ }));
2908
+ }, []);
2909
+ const keyboardManager = useMemo(() => {
2910
+ const km = new KeyboardManager();
2911
+ const bindings = createDefaultBindings({
2912
+ manager,
2913
+ getActiveScreen,
2914
+ toggleHelp,
2915
+ toggleFilter,
2916
+ closeFilter,
2917
+ filterAppendChar,
2918
+ filterDeleteChar,
2919
+ filterToggleLevel,
2920
+ filterCycleField
2921
+ });
2922
+ km.addBindings(bindings);
2923
+ return km;
2924
+ }, [
2925
+ manager,
2926
+ getActiveScreen,
2927
+ toggleHelp,
2928
+ toggleFilter,
2929
+ closeFilter,
2930
+ filterAppendChar,
2931
+ filterDeleteChar,
2932
+ filterToggleLevel,
2933
+ filterCycleField
2934
+ ]);
2935
+ useKeyboard(useCallback((key) => {
2936
+ const screens$1 = manager.getScreens();
2937
+ const activeScreen$1 = manager.getActiveScreen();
2938
+ const context = {
2939
+ hasSidebar: screens$1.length > 1,
2940
+ focusArea: manager.focusArea,
2941
+ hasPrompt: activeScreen$1?.hasActivePrompt() ?? false,
2942
+ inInputMode: activeScreen$1?.isPromptInInputMode() ?? false,
2943
+ isFilterActive: filter.isVisible,
2944
+ isHelpVisible: showHelp
2945
+ };
2946
+ if (keyboardManager.handleKey(key, context)) return;
2947
+ handlePrintableInput(key, context, {
2948
+ manager,
2949
+ getActiveScreen,
2950
+ toggleHelp,
2951
+ toggleFilter,
2952
+ closeFilter,
2953
+ filterAppendChar,
2954
+ filterDeleteChar,
2955
+ filterToggleLevel,
2956
+ filterCycleField
2957
+ });
2958
+ }, [
2959
+ manager,
2960
+ keyboardManager,
2961
+ filter.isVisible,
2962
+ showHelp,
2963
+ getActiveScreen,
2964
+ toggleHelp,
2965
+ toggleFilter,
2966
+ closeFilter,
2967
+ filterAppendChar,
2968
+ filterDeleteChar,
2969
+ filterToggleLevel,
2970
+ filterCycleField
2971
+ ]));
2972
+ const screens = manager.getScreens();
2973
+ const activeScreen = manager.getActiveScreen();
2974
+ const activeScreenId = activeScreen?.getId() ?? screens[0]?.getId() ?? "";
2975
+ const bindOptions = manager.getBindOptions();
2976
+ const hasSidebar = screens.length > 1;
2977
+ const activeScreenVersion = useSyncExternalStore((updater) => activeScreen?.onChange(updater) ?? (() => {}), () => activeScreen?.getVersion() ?? -1);
2978
+ const levelCounts = useMemo(() => {
2979
+ if (!activeScreen) return {
2980
+ verbose: 0,
2981
+ debug: 0,
2982
+ log: 0,
2983
+ warn: 0,
2984
+ error: 0,
2985
+ fatal: 0
2986
+ };
2987
+ return FilterEngine.countByLevel(activeScreen.getMessages());
2988
+ }, [activeScreenVersion, activeScreen]);
2989
+ const filteredMessages = useMemo(() => {
2990
+ if (!activeScreen) return [];
2991
+ return FilterEngine.filterMessages(activeScreen.getMessages(), filter);
2992
+ }, [
2993
+ activeScreenVersion,
2994
+ activeScreen,
2995
+ filter
2996
+ ]);
2997
+ const isFiltering = useMemo(() => hasActiveFilter(filter), [filter]);
2998
+ return /* @__PURE__ */ jsx(LoggerProvider, {
2999
+ theme,
3000
+ children: /* @__PURE__ */ jsxs("box", {
3001
+ flexDirection: "row",
3002
+ flexGrow: 1,
3003
+ children: [
3004
+ hasSidebar && /* @__PURE__ */ jsx(Sidebar, {
3005
+ screens,
3006
+ selectedIndex: manager.selectedIndex,
3007
+ activeScreenId,
3008
+ focused: manager.focusArea === "sidebar",
3009
+ width: bindOptions.sidebarWidth ?? 25,
3010
+ title: bindOptions.sidebarTitle ?? "Screens"
3011
+ }),
3012
+ /* @__PURE__ */ jsxs("box", {
3013
+ flexDirection: "column",
3014
+ flexGrow: 1,
3015
+ children: [filter.isVisible && /* @__PURE__ */ jsx(FilterBar, {
3016
+ filter,
3017
+ levelCounts
3018
+ }), activeScreen && /* @__PURE__ */ jsx(ScreenBridge, {
3019
+ screen: activeScreen,
3020
+ focused: manager.focusArea === "content",
3021
+ filteredMessages,
3022
+ isFiltering,
3023
+ totalMessages: activeScreen.getMessages().length
3024
+ }, activeScreen.getId())]
3025
+ }),
3026
+ showHelp && /* @__PURE__ */ jsx(HelpOverlay, { bindings: keyboardManager.getBindingsForHelp() })
3027
+ ]
3028
+ })
3029
+ });
3030
+ }
3031
+
3032
+ //#endregion
3033
+ export { createDefaultFilterState as $, FILE_COLORS as A, VARIANT_COLORS as B, COMMON_FILETYPES as C, createTintedColor as D, createBorderColor as E, SEPARATOR_COLORS as F, createThemeFrom as G, LoggerProvider as H, SIDEBAR_COLORS as I, resolveTheme as J, getThemePreset as K, STATUS_INDICATORS as L, GROUP_COLORS as M, PROGRESS_COLORS as N, getLogLevelColors as O, HEADER_COLORS as P, ALL_LOG_LEVELS as Q, DEFAULT_LOG_LEVEL_COLORS as R, printMessagesToStdout as S, resolveFiletype as T, useLoggerContext as U, useTheme as V, createTheme as W, lightTheme as X, highContrastTheme as Y, darkTheme as Z, PromptRenderer as _, formatKeyBinding as a, captureTrace as b, SidebarSeparator as c, GroupMessageRenderer as d, hasActiveFilter as et, GroupRenderer as f, LoadingMessage as g, ProgressMessage as h, KeyboardManager as i, TABLE_COLORS as j, PROMPT_COLORS as k, SidebarItem as l, TableMessage as m, createDefaultBindings as n, FilterEngine as o, MessageRenderer as p, mergeThemes as q, handlePrintableInput as r, Sidebar as s, ScreenManagerBridge as t, ScreenBridge as u, FileLog as v, getFileName as w, formatObject as x, LogMessage as y, ERROR_HIGHLIGHT_COLORS as z };
3034
+ //# sourceMappingURL=screen_manager_bridge-Dfg4QUrl.mjs.map