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