@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,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
|