@navios/commander-tui 1.3.0 → 1.5.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 +36 -0
- package/{src/__tests__/mocks/scm-mock.ts → dist/base/src/__tests__/mocks/scm-mock.d.ts} +3 -1
- package/dist/base/src/__tests__/mocks/scm-mock.d.ts.map +1 -0
- package/dist/base/src/__tests__/setup.d.ts +2 -0
- package/dist/base/src/__tests__/setup.d.ts.map +1 -0
- package/dist/base/src/__tests__/utils/factories.d.ts +67 -0
- package/dist/base/src/__tests__/utils/factories.d.ts.map +1 -0
- package/dist/base/src/__tests__/utils/render-utils.d.ts +21 -0
- package/dist/base/src/__tests__/utils/render-utils.d.ts.map +1 -0
- package/dist/base/src/__tests__/utils/test-container.d.ts +20 -0
- package/dist/base/src/__tests__/utils/test-container.d.ts.map +1 -0
- package/dist/base/src/adapters/interface.d.ts +9 -2
- package/dist/base/src/adapters/interface.d.ts.map +1 -1
- package/dist/base/src/overrides/missing-adapter.override.d.ts +10 -0
- package/dist/base/src/overrides/missing-adapter.override.d.ts.map +1 -1
- package/dist/base/src/services/index.d.ts +1 -0
- package/dist/base/src/services/index.d.ts.map +1 -1
- package/dist/base/src/services/logger.d.ts.map +1 -1
- package/dist/base/src/services/readline_prompt.d.ts +27 -0
- package/dist/base/src/services/readline_prompt.d.ts.map +1 -0
- package/dist/base/src/services/screen.d.ts +14 -8
- package/dist/base/src/services/screen.d.ts.map +1 -1
- package/dist/base/src/services/screen_manager.d.ts +62 -12
- package/dist/base/src/services/screen_manager.d.ts.map +1 -1
- package/dist/base/src/tokens/logger.d.ts +2 -2
- package/dist/base/src/types/events.types.d.ts +40 -0
- package/dist/base/src/types/events.types.d.ts.map +1 -0
- package/dist/base/src/types/index.d.ts +1 -0
- package/dist/base/src/types/index.d.ts.map +1 -1
- package/dist/base/src/types/screen.types.d.ts +28 -0
- package/dist/base/src/types/screen.types.d.ts.map +1 -1
- package/dist/base/src/utils/colors/helpers.d.ts +0 -16
- package/dist/base/src/utils/colors/helpers.d.ts.map +1 -1
- package/dist/base/src/utils/index.d.ts +2 -0
- package/dist/base/src/utils/index.d.ts.map +1 -1
- package/dist/base/src/utils/prompt.d.ts +7 -0
- package/dist/base/src/utils/prompt.d.ts.map +1 -0
- package/dist/base/src/utils/runtime.d.ts +10 -0
- package/dist/base/src/utils/runtime.d.ts.map +1 -0
- package/dist/base/src/utils/stdout-printer.d.ts +5 -0
- package/dist/base/src/utils/stdout-printer.d.ts.map +1 -1
- package/dist/base/tsconfig.base.tsbuildinfo +1 -1
- package/dist/base/tsconfig.tsbuildinfo +1 -0
- package/dist/ink/src/adapters/ink/components/file/file_log.d.ts +28 -0
- package/dist/ink/src/adapters/ink/components/file/file_log.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/file/index.d.ts +2 -0
- package/dist/ink/src/adapters/ink/components/file/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/filter/filter_bar.d.ts +7 -0
- package/dist/ink/src/adapters/ink/components/filter/filter_bar.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/filter/index.d.ts +2 -0
- package/dist/ink/src/adapters/ink/components/filter/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/help/help_overlay.d.ts +6 -0
- package/dist/ink/src/adapters/ink/components/help/help_overlay.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/help/index.d.ts +2 -0
- package/dist/ink/src/adapters/ink/components/help/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/index.d.ts +9 -0
- package/dist/ink/src/adapters/ink/components/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/log/index.d.ts +2 -0
- package/dist/ink/src/adapters/ink/components/log/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/log/log_message.d.ts +15 -0
- package/dist/ink/src/adapters/ink/components/log/log_message.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/prompt/index.d.ts +2 -0
- package/dist/ink/src/adapters/ink/components/prompt/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/prompt/prompt_renderer.d.ts +6 -0
- package/dist/ink/src/adapters/ink/components/prompt/prompt_renderer.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/screen/group_renderer.d.ts +14 -0
- package/dist/ink/src/adapters/ink/components/screen/group_renderer.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/screen/index.d.ts +7 -0
- package/dist/ink/src/adapters/ink/components/screen/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/screen/loading_message.d.ts +6 -0
- package/dist/ink/src/adapters/ink/components/screen/loading_message.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/screen/message_renderer.d.ts +6 -0
- package/dist/ink/src/adapters/ink/components/screen/message_renderer.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/screen/progress_message.d.ts +6 -0
- package/dist/ink/src/adapters/ink/components/screen/progress_message.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/screen/screen_bridge.d.ts +14 -0
- package/dist/ink/src/adapters/ink/components/screen/screen_bridge.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/screen/table_message.d.ts +6 -0
- package/dist/ink/src/adapters/ink/components/screen/table_message.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/screen_manager_bridge.d.ts +8 -0
- package/dist/ink/src/adapters/ink/components/screen_manager_bridge.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/sidebar/index.d.ts +4 -0
- package/dist/ink/src/adapters/ink/components/sidebar/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/sidebar/sidebar.d.ts +11 -0
- package/dist/ink/src/adapters/ink/components/sidebar/sidebar.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/sidebar/sidebar_item.d.ts +9 -0
- package/dist/ink/src/adapters/ink/components/sidebar/sidebar_item.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/components/sidebar/sidebar_separator.d.ts +2 -0
- package/dist/ink/src/adapters/ink/components/sidebar/sidebar_separator.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/context/index.d.ts +2 -0
- package/dist/ink/src/adapters/ink/context/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/context/logger_context.d.ts +33 -0
- package/dist/ink/src/adapters/ink/context/logger_context.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/hooks/index.d.ts +2 -0
- package/dist/ink/src/adapters/ink/hooks/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/hooks/use_theme.d.ts +7 -0
- package/dist/ink/src/adapters/ink/hooks/use_theme.d.ts.map +1 -0
- package/dist/ink/src/adapters/ink/index.d.ts +18 -0
- package/dist/ink/src/adapters/ink/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/react-shared/hooks/index.d.ts +4 -0
- package/dist/ink/src/adapters/react-shared/hooks/index.d.ts.map +1 -0
- package/dist/ink/src/adapters/react-shared/hooks/use_filter_state.d.ts +18 -0
- package/dist/ink/src/adapters/react-shared/hooks/use_filter_state.d.ts.map +1 -0
- package/dist/ink/src/adapters/react-shared/hooks/use_keyboard_manager.d.ts +26 -0
- package/dist/ink/src/adapters/react-shared/hooks/use_keyboard_manager.d.ts.map +1 -0
- package/dist/ink/src/adapters/react-shared/hooks/use_manager_subscription.d.ts +7 -0
- package/dist/ink/src/adapters/react-shared/hooks/use_manager_subscription.d.ts.map +1 -0
- package/dist/ink/src/adapters/react-shared/index.d.ts +2 -0
- package/dist/ink/src/adapters/react-shared/index.d.ts.map +1 -0
- package/dist/ink/tsconfig.ink.tsbuildinfo +1 -0
- package/dist/react/src/adapters/react-shared/hooks/index.d.ts +4 -0
- package/dist/react/src/adapters/react-shared/hooks/index.d.ts.map +1 -0
- package/dist/react/src/adapters/react-shared/hooks/use_filter_state.d.ts +18 -0
- package/dist/react/src/adapters/react-shared/hooks/use_filter_state.d.ts.map +1 -0
- package/dist/react/src/adapters/react-shared/hooks/use_keyboard_manager.d.ts +26 -0
- package/dist/react/src/adapters/react-shared/hooks/use_keyboard_manager.d.ts.map +1 -0
- package/dist/react/src/adapters/react-shared/hooks/use_manager_subscription.d.ts +7 -0
- package/dist/react/src/adapters/react-shared/hooks/use_manager_subscription.d.ts.map +1 -0
- package/dist/react/src/adapters/react-shared/index.d.ts +2 -0
- package/dist/react/src/adapters/react-shared/index.d.ts.map +1 -0
- package/dist/react/tsconfig.react.tsbuildinfo +1 -1
- package/dist/solid/tsconfig.solid.tsbuildinfo +1 -1
- package/lib/index.cjs +2180 -181
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +3199 -277
- package/lib/index.d.cts.map +1 -0
- package/lib/index.d.mts +3199 -277
- package/lib/index.d.mts.map +1 -0
- package/lib/index.mjs +2101 -138
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -31
- package/src/__tests__/services/logger.spec.ts +32 -0
- package/src/__tests__/services/screen.spec.ts +114 -101
- package/src/__tests__/utils/factories.ts +2 -0
- package/src/adapters/interface.ts +10 -2
- package/src/overrides/missing-adapter.override.ts +16 -6
- package/src/services/index.ts +1 -0
- package/src/services/logger.ts +7 -1
- package/src/services/readline_prompt.ts +194 -0
- package/src/services/screen.ts +108 -57
- package/src/services/screen_manager.ts +287 -74
- package/src/types/events.types.ts +84 -0
- package/src/types/index.ts +3 -0
- package/src/types/screen.types.ts +34 -0
- package/src/utils/colors/helpers.ts +0 -25
- package/src/utils/index.ts +6 -0
- package/src/utils/prompt.ts +18 -0
- package/src/utils/runtime.ts +14 -0
- package/src/utils/stdout-printer.ts +16 -5
- package/tsconfig.base.json +1 -17
- package/tsconfig.json +1 -6
- package/tsdown.config.mts +13 -70
- package/lib/adapters/react/index.cjs +0 -1768
- package/lib/adapters/react/index.cjs.map +0 -1
- package/lib/adapters/react/index.d.cts +0 -80
- package/lib/adapters/react/index.d.mts +0 -80
- package/lib/adapters/react/index.mjs +0 -1760
- package/lib/adapters/react/index.mjs.map +0 -1
- package/lib/adapters/solid/index.cjs +0 -3855
- package/lib/adapters/solid/index.cjs.map +0 -1
- package/lib/adapters/solid/index.d.cts +0 -74
- package/lib/adapters/solid/index.d.mts +0 -74
- package/lib/adapters/solid/index.mjs +0 -3847
- package/lib/adapters/solid/index.mjs.map +0 -1
- package/lib/filter_engine-DXqu9Vaq.cjs +0 -1836
- package/lib/filter_engine-DXqu9Vaq.cjs.map +0 -1
- package/lib/filter_engine-DmqhEhpA.mjs +0 -1609
- package/lib/filter_engine-DmqhEhpA.mjs.map +0 -1
- package/lib/interface-CTHQ08aY.d.mts +0 -699
- package/lib/interface-DQEIz9Mb.d.cts +0 -699
- package/src/__tests__/components/__snapshots__/filter_bar.spec.tsx.snap +0 -2293
- package/src/__tests__/components/__snapshots__/loading_message.spec.tsx.snap +0 -1414
- package/src/__tests__/components/__snapshots__/log_message.spec.tsx.snap +0 -3245
- package/src/__tests__/components/__snapshots__/progress_message.spec.tsx.snap +0 -1783
- package/src/__tests__/components/__snapshots__/prompt_renderer.spec.tsx.snap +0 -3203
- package/src/__tests__/components/__snapshots__/sidebar.spec.tsx.snap +0 -2857
- package/src/__tests__/components/filter_bar.spec.tsx +0 -190
- package/src/__tests__/components/loading_message.spec.tsx +0 -110
- package/src/__tests__/components/log_message.spec.tsx +0 -166
- package/src/__tests__/components/progress_message.spec.tsx +0 -147
- package/src/__tests__/components/prompt_renderer.spec.tsx +0 -274
- package/src/__tests__/components/sidebar.spec.tsx +0 -322
- package/src/__tests__/utils/render-utils.tsx +0 -39
- package/src/adapters/react/components/file/file_log.tsx +0 -241
- package/src/adapters/react/components/file/index.ts +0 -1
- package/src/adapters/react/components/filter/filter_bar.tsx +0 -79
- package/src/adapters/react/components/filter/index.ts +0 -1
- package/src/adapters/react/components/help/help_overlay.tsx +0 -100
- package/src/adapters/react/components/help/index.ts +0 -1
- package/src/adapters/react/components/log/index.ts +0 -1
- package/src/adapters/react/components/log/log_message.tsx +0 -102
- package/src/adapters/react/components/prompt/index.ts +0 -2
- package/src/adapters/react/components/prompt/prompt_renderer.tsx +0 -346
- package/src/adapters/react/components/screen/group_renderer.tsx +0 -64
- package/src/adapters/react/components/screen/index.ts +0 -6
- package/src/adapters/react/components/screen/loading_message.tsx +0 -43
- package/src/adapters/react/components/screen/message_renderer.tsx +0 -108
- package/src/adapters/react/components/screen/progress_message.tsx +0 -60
- package/src/adapters/react/components/screen/screen_bridge.tsx +0 -149
- package/src/adapters/react/components/screen/table_message.tsx +0 -57
- package/src/adapters/react/components/screen_manager_bridge.tsx +0 -245
- package/src/adapters/react/components/sidebar/index.ts +0 -3
- package/src/adapters/react/components/sidebar/sidebar.tsx +0 -102
- package/src/adapters/react/components/sidebar/sidebar_item.tsx +0 -50
- package/src/adapters/react/components/sidebar/sidebar_separator.tsx +0 -13
- package/src/adapters/react/context/index.ts +0 -1
- package/src/adapters/react/context/logger_context.tsx +0 -109
- package/src/adapters/react/hooks/index.ts +0 -1
- package/src/adapters/react/hooks/use_theme.ts +0 -12
- package/src/adapters/react/index.ts +0 -39
- package/src/adapters/solid/components/file/file_log.tsx +0 -221
- package/src/adapters/solid/components/file/index.ts +0 -1
- package/src/adapters/solid/components/filter/filter_bar.tsx +0 -84
- package/src/adapters/solid/components/filter/index.ts +0 -1
- package/src/adapters/solid/components/help/help_overlay.tsx +0 -106
- package/src/adapters/solid/components/help/index.ts +0 -1
- package/src/adapters/solid/components/log/index.ts +0 -1
- package/src/adapters/solid/components/log/log_message.tsx +0 -92
- package/src/adapters/solid/components/prompt/index.ts +0 -2
- package/src/adapters/solid/components/prompt/prompt_renderer.tsx +0 -350
- package/src/adapters/solid/components/screen/group_renderer.tsx +0 -61
- package/src/adapters/solid/components/screen/index.ts +0 -6
- package/src/adapters/solid/components/screen/loading_message.tsx +0 -39
- package/src/adapters/solid/components/screen/message_renderer.tsx +0 -122
- package/src/adapters/solid/components/screen/progress_message.tsx +0 -61
- package/src/adapters/solid/components/screen/screen_bridge.tsx +0 -155
- package/src/adapters/solid/components/screen/table_message.tsx +0 -58
- package/src/adapters/solid/components/screen_manager_bridge.tsx +0 -243
- package/src/adapters/solid/components/sidebar/index.ts +0 -3
- package/src/adapters/solid/components/sidebar/sidebar.tsx +0 -108
- package/src/adapters/solid/components/sidebar/sidebar_item.tsx +0 -55
- package/src/adapters/solid/components/sidebar/sidebar_separator.tsx +0 -13
- package/src/adapters/solid/context/index.ts +0 -2
- package/src/adapters/solid/context/logger_context.tsx +0 -95
- package/src/adapters/solid/hooks/index.ts +0 -1
- package/src/adapters/solid/hooks/use_theme.ts +0 -12
- package/src/adapters/solid/index.tsx +0 -43
- package/src/adapters/solid/jsx.d.ts +0 -98
- package/tsconfig.react.json +0 -16
- package/tsconfig.solid.json +0 -16
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import * as readline from 'node:readline'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ChoicePromptData,
|
|
5
|
+
ConfirmPromptData,
|
|
6
|
+
InputPromptData,
|
|
7
|
+
MultiChoicePromptData,
|
|
8
|
+
PromptData,
|
|
9
|
+
} from '../types/index.ts'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Readline-based prompt service for stdout mode.
|
|
13
|
+
* Provides basic interactive prompts when TUI adapter is not available.
|
|
14
|
+
*/
|
|
15
|
+
export class ReadlinePromptService {
|
|
16
|
+
private rl: readline.Interface | null = null
|
|
17
|
+
private promptQueue: Array<{
|
|
18
|
+
prompt: PromptData
|
|
19
|
+
resolve: (value: string | boolean | string[]) => void
|
|
20
|
+
}> = []
|
|
21
|
+
private isProcessing = false
|
|
22
|
+
|
|
23
|
+
private ensureInterface(): readline.Interface {
|
|
24
|
+
if (!this.rl) {
|
|
25
|
+
this.rl = readline.createInterface({
|
|
26
|
+
input: process.stdin,
|
|
27
|
+
output: process.stdout,
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
return this.rl
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Handle a prompt interactively via readline.
|
|
35
|
+
* Prompts are queued and processed sequentially.
|
|
36
|
+
*/
|
|
37
|
+
async handlePrompt(prompt: PromptData): Promise<string | boolean | string[]> {
|
|
38
|
+
return new Promise((resolve) => {
|
|
39
|
+
this.promptQueue.push({ prompt, resolve })
|
|
40
|
+
this.processQueue()
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private async processQueue(): Promise<void> {
|
|
45
|
+
if (this.isProcessing || this.promptQueue.length === 0) return
|
|
46
|
+
|
|
47
|
+
this.isProcessing = true
|
|
48
|
+
|
|
49
|
+
while (this.promptQueue.length > 0) {
|
|
50
|
+
const { prompt, resolve } = this.promptQueue.shift()!
|
|
51
|
+
const result = await this.processPrompt(prompt)
|
|
52
|
+
resolve(result)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.isProcessing = false
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private async processPrompt(prompt: PromptData): Promise<string | boolean | string[]> {
|
|
59
|
+
switch (prompt.type) {
|
|
60
|
+
case 'choice':
|
|
61
|
+
return this.handleChoice(prompt)
|
|
62
|
+
case 'confirm':
|
|
63
|
+
return this.handleConfirm(prompt)
|
|
64
|
+
case 'input':
|
|
65
|
+
return this.handleInput(prompt)
|
|
66
|
+
case 'multiChoice':
|
|
67
|
+
return this.handleMultiChoice(prompt)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private async handleChoice(prompt: ChoicePromptData): Promise<string> {
|
|
72
|
+
const rl = this.ensureInterface()
|
|
73
|
+
|
|
74
|
+
// Display question and choices
|
|
75
|
+
console.log(`\n${prompt.question}`)
|
|
76
|
+
prompt.choices.forEach((choice, index) => {
|
|
77
|
+
const isDefault = choice.value === prompt.defaultChoice
|
|
78
|
+
const marker = isDefault ? '*' : ' '
|
|
79
|
+
const inputHint = choice.input ? ' (allows custom input)' : ''
|
|
80
|
+
console.log(` ${marker} ${index + 1}. ${choice.label}${inputHint}`)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
rl.question('Enter number (or press Enter for default): ', (answer) => {
|
|
85
|
+
const trimmed = answer.trim()
|
|
86
|
+
if (!trimmed) {
|
|
87
|
+
resolve(prompt.defaultChoice)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const num = parseInt(trimmed, 10)
|
|
92
|
+
if (num >= 1 && num <= prompt.choices.length) {
|
|
93
|
+
const choice = prompt.choices[num - 1]
|
|
94
|
+
if (choice.input) {
|
|
95
|
+
// If choice allows input, ask for it
|
|
96
|
+
rl.question(`Enter value for "${choice.label}": `, (inputValue) => {
|
|
97
|
+
resolve(inputValue.trim() || choice.value)
|
|
98
|
+
})
|
|
99
|
+
} else {
|
|
100
|
+
resolve(choice.value)
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
resolve(prompt.defaultChoice)
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private async handleConfirm(prompt: ConfirmPromptData): Promise<boolean> {
|
|
110
|
+
const rl = this.ensureInterface()
|
|
111
|
+
const defaultText = prompt.defaultValue ? 'Y/n' : 'y/N'
|
|
112
|
+
|
|
113
|
+
return new Promise((resolve) => {
|
|
114
|
+
rl.question(`${prompt.question} [${defaultText}]: `, (answer) => {
|
|
115
|
+
const trimmed = answer.trim().toLowerCase()
|
|
116
|
+
if (!trimmed) {
|
|
117
|
+
resolve(prompt.defaultValue)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
resolve(trimmed === 'y' || trimmed === 'yes')
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private async handleInput(prompt: InputPromptData): Promise<string> {
|
|
126
|
+
const rl = this.ensureInterface()
|
|
127
|
+
const defaultHint = prompt.defaultValue ? ` (default: ${prompt.defaultValue})` : ''
|
|
128
|
+
const placeholderHint = prompt.placeholder ? ` [${prompt.placeholder}]` : ''
|
|
129
|
+
|
|
130
|
+
return new Promise((resolve) => {
|
|
131
|
+
rl.question(`${prompt.question}${placeholderHint}${defaultHint}: `, (answer) => {
|
|
132
|
+
resolve(answer.trim() || prompt.defaultValue)
|
|
133
|
+
})
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private async handleMultiChoice(prompt: MultiChoicePromptData): Promise<string[]> {
|
|
138
|
+
const rl = this.ensureInterface()
|
|
139
|
+
|
|
140
|
+
// Display question and choices
|
|
141
|
+
console.log(`\n${prompt.question}`)
|
|
142
|
+
console.log(`(Select ${prompt.minSelect}-${prompt.maxSelect} options, enter comma-separated numbers)`)
|
|
143
|
+
prompt.choices.forEach((choice, index) => {
|
|
144
|
+
const selected = prompt.selectedIndices.has(index) ? '[x]' : '[ ]'
|
|
145
|
+
console.log(` ${selected} ${index + 1}. ${choice.label}`)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
return new Promise((resolve) => {
|
|
149
|
+
rl.question('Enter numbers (e.g., 1,3,5): ', (answer) => {
|
|
150
|
+
const trimmed = answer.trim()
|
|
151
|
+
if (!trimmed) {
|
|
152
|
+
// Return default selections
|
|
153
|
+
resolve(
|
|
154
|
+
prompt.choices.filter((_, i) => prompt.selectedIndices.has(i)).map((c) => c.value),
|
|
155
|
+
)
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const indices = trimmed
|
|
160
|
+
.split(',')
|
|
161
|
+
.map((s) => parseInt(s.trim(), 10) - 1)
|
|
162
|
+
.filter((n) => n >= 0 && n < prompt.choices.length)
|
|
163
|
+
|
|
164
|
+
// Validate selection count
|
|
165
|
+
if (indices.length < prompt.minSelect) {
|
|
166
|
+
console.log(`Minimum ${prompt.minSelect} selections required. Using defaults.`)
|
|
167
|
+
resolve(
|
|
168
|
+
prompt.choices.filter((_, i) => prompt.selectedIndices.has(i)).map((c) => c.value),
|
|
169
|
+
)
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (indices.length > prompt.maxSelect) {
|
|
174
|
+
console.log(`Maximum ${prompt.maxSelect} selections allowed. Taking first ${prompt.maxSelect}.`)
|
|
175
|
+
indices.splice(prompt.maxSelect)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
resolve(indices.map((i) => prompt.choices[i].value))
|
|
179
|
+
})
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Cleanup readline interface
|
|
185
|
+
*/
|
|
186
|
+
destroy(): void {
|
|
187
|
+
if (this.rl) {
|
|
188
|
+
this.rl.close()
|
|
189
|
+
this.rl = null
|
|
190
|
+
}
|
|
191
|
+
this.promptQueue = []
|
|
192
|
+
this.isProcessing = false
|
|
193
|
+
}
|
|
194
|
+
}
|
package/src/services/screen.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events'
|
|
2
|
+
|
|
3
|
+
import type { LogLevel } from '@navios/core'
|
|
4
|
+
|
|
5
|
+
import { RenderMode } from '../types/index.ts'
|
|
6
|
+
import { getPromptDefaultValue, printSingleMessage } from '../utils/index.ts'
|
|
7
|
+
|
|
1
8
|
import type { ScreenOptions } from '../schemas/index.ts'
|
|
2
9
|
import type {
|
|
3
10
|
ChoicePromptData,
|
|
@@ -8,6 +15,7 @@ import type {
|
|
|
8
15
|
MultiChoicePromptData,
|
|
9
16
|
ProgressMessageData,
|
|
10
17
|
PromptData,
|
|
18
|
+
ScreenEventMap,
|
|
11
19
|
ScreenStatus,
|
|
12
20
|
} from '../types/index.ts'
|
|
13
21
|
|
|
@@ -19,7 +27,7 @@ interface PendingPrompt {
|
|
|
19
27
|
timeoutId?: ReturnType<typeof setTimeout>
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
export class ScreenInstance {
|
|
30
|
+
export class ScreenInstance extends EventEmitter<ScreenEventMap> {
|
|
23
31
|
private id: string
|
|
24
32
|
private name: string
|
|
25
33
|
private icon?: string
|
|
@@ -28,7 +36,6 @@ export class ScreenInstance {
|
|
|
28
36
|
private hidden: boolean = false
|
|
29
37
|
private messages: MessageData[] = []
|
|
30
38
|
private manager: ScreenManagerInstance | null = null
|
|
31
|
-
private changeListeners: Set<() => void> = new Set()
|
|
32
39
|
private printFn: ((messages: MessageData[], name: string, isError: boolean) => void) | null = null
|
|
33
40
|
private hasPrinted: boolean = false
|
|
34
41
|
private version: number = 0
|
|
@@ -38,14 +45,14 @@ export class ScreenInstance {
|
|
|
38
45
|
private activePrompt: PendingPrompt | null = null
|
|
39
46
|
|
|
40
47
|
constructor(id: string, options: ScreenOptions) {
|
|
48
|
+
super()
|
|
41
49
|
this.id = id
|
|
42
50
|
this.name = options.name
|
|
43
51
|
this.icon = options.icon
|
|
44
52
|
this.badgeCount = options.badgeCount ?? 0
|
|
45
53
|
this.hidden = options.hidden ?? false
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
54
|
+
|
|
55
|
+
this.status = (options.static ?? true) ? 'static' : 'waiting'
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
incrementVersion(): void {
|
|
@@ -88,7 +95,7 @@ export class ScreenInstance {
|
|
|
88
95
|
|
|
89
96
|
setBadgeCount(count: number): this {
|
|
90
97
|
this.badgeCount = count
|
|
91
|
-
this.
|
|
98
|
+
this.emit('badge:changed', count)
|
|
92
99
|
return this
|
|
93
100
|
}
|
|
94
101
|
|
|
@@ -99,7 +106,7 @@ export class ScreenInstance {
|
|
|
99
106
|
setHidden(hidden: boolean): this {
|
|
100
107
|
this.hidden = hidden
|
|
101
108
|
this.manager?.onScreenVisibilityChanged(this)
|
|
102
|
-
this.
|
|
109
|
+
this.emit('visibility:changed', hidden)
|
|
103
110
|
return this
|
|
104
111
|
}
|
|
105
112
|
|
|
@@ -116,7 +123,18 @@ export class ScreenInstance {
|
|
|
116
123
|
}
|
|
117
124
|
|
|
118
125
|
/**
|
|
119
|
-
*
|
|
126
|
+
* Check if a log level is enabled globally via the ScreenManager.
|
|
127
|
+
* Returns true if no manager is set or if the level is allowed.
|
|
128
|
+
*/
|
|
129
|
+
isLogLevelEnabled(level: LogLevel): boolean {
|
|
130
|
+
if (!this.manager) {
|
|
131
|
+
return true
|
|
132
|
+
}
|
|
133
|
+
return this.manager.isLogLevelEnabled(level)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Set screen status. When success/fail and not in TUI mode, prints to stdout/stderr
|
|
120
138
|
*/
|
|
121
139
|
setStatus(status: ScreenStatus): this {
|
|
122
140
|
const wasComplete = this.status === 'success' || this.status === 'fail'
|
|
@@ -124,14 +142,15 @@ export class ScreenInstance {
|
|
|
124
142
|
|
|
125
143
|
// When transitioning to complete state
|
|
126
144
|
if (!wasComplete && (status === 'success' || status === 'fail')) {
|
|
127
|
-
// Only print if
|
|
128
|
-
|
|
145
|
+
// Only print immediately if NOT in TUI mode
|
|
146
|
+
// In TUI mode, printing happens on unbind()
|
|
147
|
+
if (!this.manager?.hasTuiRenderer()) {
|
|
129
148
|
this.printToConsole()
|
|
130
149
|
}
|
|
131
150
|
this.manager?.onScreenCompleted(this)
|
|
132
151
|
}
|
|
133
152
|
|
|
134
|
-
this.
|
|
153
|
+
this.emit('status:changed', status)
|
|
135
154
|
return this
|
|
136
155
|
}
|
|
137
156
|
|
|
@@ -143,10 +162,20 @@ export class ScreenInstance {
|
|
|
143
162
|
}
|
|
144
163
|
|
|
145
164
|
/**
|
|
146
|
-
* Check if this screen has been printed to console
|
|
165
|
+
* Check if this screen has been printed to console.
|
|
166
|
+
* Static screens in non-TUI modes are considered printed as they print incrementally.
|
|
147
167
|
*/
|
|
148
168
|
hasPrintedToConsole(): boolean {
|
|
149
|
-
return
|
|
169
|
+
// If already fully printed, return true
|
|
170
|
+
if (this.hasPrinted) return true
|
|
171
|
+
|
|
172
|
+
// Static screens in non-TUI modes print incrementally, so they're considered "printed"
|
|
173
|
+
// as their messages are output immediately when added
|
|
174
|
+
if (this.status === 'static' && !this.manager?.hasTuiRenderer()) {
|
|
175
|
+
return true
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return false
|
|
150
179
|
}
|
|
151
180
|
|
|
152
181
|
/**
|
|
@@ -171,7 +200,13 @@ export class ScreenInstance {
|
|
|
171
200
|
addMessage(message: MessageData): void {
|
|
172
201
|
this.messages.push(message)
|
|
173
202
|
this.incrementVersion()
|
|
174
|
-
|
|
203
|
+
|
|
204
|
+
// In non-TUI modes, static screens print messages immediately
|
|
205
|
+
if (this.status === 'static' && !this.manager?.hasTuiRenderer()) {
|
|
206
|
+
this.printSingleMessageToConsole(message)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
this.emit('message:added', message.id)
|
|
175
210
|
}
|
|
176
211
|
|
|
177
212
|
/**
|
|
@@ -180,12 +215,23 @@ export class ScreenInstance {
|
|
|
180
215
|
updateMessage(id: string, updates: Partial<LoadingMessageData>): void {
|
|
181
216
|
const index = this.messages.findIndex((m) => m.id === id)
|
|
182
217
|
if (index !== -1) {
|
|
218
|
+
const oldMessage = this.messages[index] as LoadingMessageData
|
|
183
219
|
this.messages[index] = {
|
|
184
220
|
...this.messages[index],
|
|
185
221
|
...updates,
|
|
186
222
|
} as MessageData
|
|
187
223
|
this.incrementVersion()
|
|
188
|
-
|
|
224
|
+
|
|
225
|
+
// In non-TUI modes for static screens, print when loading completes
|
|
226
|
+
if (this.status === 'static' && !this.manager?.hasTuiRenderer()) {
|
|
227
|
+
const newMessage = this.messages[index] as LoadingMessageData
|
|
228
|
+
// Only print when transitioning from 'loading' to 'success'/'fail'
|
|
229
|
+
if (newMessage.status !== 'loading' && oldMessage.status === 'loading') {
|
|
230
|
+
this.printSingleMessageToConsole(newMessage)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.emit('message:updated', id)
|
|
189
235
|
}
|
|
190
236
|
}
|
|
191
237
|
|
|
@@ -195,12 +241,26 @@ export class ScreenInstance {
|
|
|
195
241
|
updateProgressMessage(id: string, updates: Partial<ProgressMessageData>): void {
|
|
196
242
|
const index = this.messages.findIndex((m) => m.id === id)
|
|
197
243
|
if (index !== -1) {
|
|
244
|
+
const oldMessage = this.messages[index] as ProgressMessageData
|
|
198
245
|
this.messages[index] = {
|
|
199
246
|
...this.messages[index],
|
|
200
247
|
...updates,
|
|
201
248
|
} as MessageData
|
|
202
249
|
this.incrementVersion()
|
|
203
|
-
|
|
250
|
+
|
|
251
|
+
// In non-TUI modes for static screens, print when progress completes
|
|
252
|
+
if (this.status === 'static' && !this.manager?.hasTuiRenderer()) {
|
|
253
|
+
const newMessage = this.messages[index] as ProgressMessageData
|
|
254
|
+
if (
|
|
255
|
+
(newMessage.status === 'complete' || newMessage.status === 'failed') &&
|
|
256
|
+
oldMessage.status !== 'complete' &&
|
|
257
|
+
oldMessage.status !== 'failed'
|
|
258
|
+
) {
|
|
259
|
+
this.printSingleMessageToConsole(newMessage)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.emit('message:updated', id)
|
|
204
264
|
}
|
|
205
265
|
}
|
|
206
266
|
|
|
@@ -210,7 +270,7 @@ export class ScreenInstance {
|
|
|
210
270
|
clear(): this {
|
|
211
271
|
this.messages = []
|
|
212
272
|
this.incrementVersion()
|
|
213
|
-
this.
|
|
273
|
+
this.emit('messages:cleared')
|
|
214
274
|
return this
|
|
215
275
|
}
|
|
216
276
|
|
|
@@ -224,14 +284,23 @@ export class ScreenInstance {
|
|
|
224
284
|
*/
|
|
225
285
|
_addPrompt(prompt: PromptData): Promise<string | boolean | string[]> {
|
|
226
286
|
return new Promise((resolve) => {
|
|
227
|
-
const
|
|
287
|
+
const mode = this.manager?.getRenderMode() ?? RenderMode.UNBOUND
|
|
228
288
|
|
|
229
|
-
//
|
|
230
|
-
if (
|
|
289
|
+
// UNBOUND mode: return defaults immediately (no interaction possible)
|
|
290
|
+
if (mode === RenderMode.UNBOUND) {
|
|
231
291
|
this.resolvePromptWithDefault(prompt, resolve)
|
|
232
292
|
return
|
|
233
293
|
}
|
|
234
294
|
|
|
295
|
+
// STDOUT modes (STDOUT_INTERACTIVE, STDOUT_FALLBACK): use readline for interactive prompts
|
|
296
|
+
if (mode === RenderMode.STDOUT_INTERACTIVE || mode === RenderMode.STDOUT_FALLBACK) {
|
|
297
|
+
this.manager?.handleReadlinePrompt(prompt).then(resolve)
|
|
298
|
+
return
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// TUI_ACTIVE mode: use TUI prompt queue system
|
|
302
|
+
const pending: PendingPrompt = { data: prompt, resolve }
|
|
303
|
+
|
|
235
304
|
// Set up timeout if specified
|
|
236
305
|
if (prompt.timeout && prompt.timeoutStarted) {
|
|
237
306
|
pending.timeoutId = setTimeout(() => {
|
|
@@ -240,7 +309,7 @@ export class ScreenInstance {
|
|
|
240
309
|
this.activePrompt = null
|
|
241
310
|
this.activateNextPrompt()
|
|
242
311
|
this.incrementVersion()
|
|
243
|
-
this.
|
|
312
|
+
this.emit('prompt:resolved')
|
|
244
313
|
} else {
|
|
245
314
|
// Remove from queue if not yet active
|
|
246
315
|
const idx = this.promptQueue.indexOf(pending)
|
|
@@ -270,20 +339,7 @@ export class ScreenInstance {
|
|
|
270
339
|
prompt: PromptData,
|
|
271
340
|
resolve: (value: string | boolean | string[]) => void,
|
|
272
341
|
): void {
|
|
273
|
-
|
|
274
|
-
case 'choice':
|
|
275
|
-
resolve(prompt.defaultChoice)
|
|
276
|
-
break
|
|
277
|
-
case 'confirm':
|
|
278
|
-
resolve(prompt.defaultValue)
|
|
279
|
-
break
|
|
280
|
-
case 'input':
|
|
281
|
-
resolve(prompt.defaultValue)
|
|
282
|
-
break
|
|
283
|
-
case 'multiChoice':
|
|
284
|
-
resolve(prompt.choices.filter((_, i) => prompt.selectedIndices.has(i)).map((c) => c.value))
|
|
285
|
-
break
|
|
286
|
-
}
|
|
342
|
+
resolve(getPromptDefaultValue(prompt))
|
|
287
343
|
}
|
|
288
344
|
|
|
289
345
|
/**
|
|
@@ -316,7 +372,7 @@ export class ScreenInstance {
|
|
|
316
372
|
} else if (prompt.type === 'confirm') {
|
|
317
373
|
;(prompt as ConfirmPromptData).selectedValue = index === 0
|
|
318
374
|
}
|
|
319
|
-
this.
|
|
375
|
+
this.emit('prompt:updated')
|
|
320
376
|
}
|
|
321
377
|
|
|
322
378
|
/**
|
|
@@ -359,7 +415,7 @@ export class ScreenInstance {
|
|
|
359
415
|
const prompt = this.activePrompt.data
|
|
360
416
|
if (prompt.type === 'confirm') {
|
|
361
417
|
;(prompt as ConfirmPromptData).selectedValue = true
|
|
362
|
-
this.
|
|
418
|
+
this.emit('prompt:updated')
|
|
363
419
|
}
|
|
364
420
|
}
|
|
365
421
|
|
|
@@ -368,7 +424,7 @@ export class ScreenInstance {
|
|
|
368
424
|
const prompt = this.activePrompt.data
|
|
369
425
|
if (prompt.type === 'confirm') {
|
|
370
426
|
;(prompt as ConfirmPromptData).selectedValue = false
|
|
371
|
-
this.
|
|
427
|
+
this.emit('prompt:updated')
|
|
372
428
|
}
|
|
373
429
|
}
|
|
374
430
|
|
|
@@ -385,7 +441,7 @@ export class ScreenInstance {
|
|
|
385
441
|
} else if (p.selectedIndices.size < p.maxSelect) {
|
|
386
442
|
p.selectedIndices.add(p.focusedIndex)
|
|
387
443
|
}
|
|
388
|
-
this.
|
|
444
|
+
this.emit('prompt:updated')
|
|
389
445
|
}
|
|
390
446
|
}
|
|
391
447
|
|
|
@@ -401,7 +457,7 @@ export class ScreenInstance {
|
|
|
401
457
|
const choice = prompt.choices[prompt.selectedIndex]
|
|
402
458
|
if (choice?.input) {
|
|
403
459
|
;(prompt as ChoicePromptData).inputMode = true
|
|
404
|
-
this.
|
|
460
|
+
this.emit('prompt:updated')
|
|
405
461
|
return true
|
|
406
462
|
}
|
|
407
463
|
} else if (prompt.type === 'input') {
|
|
@@ -420,7 +476,7 @@ export class ScreenInstance {
|
|
|
420
476
|
const prompt = this.activePrompt.data
|
|
421
477
|
if (prompt.type === 'choice' && prompt.inputMode) {
|
|
422
478
|
;(prompt as ChoicePromptData).inputMode = false
|
|
423
|
-
this.
|
|
479
|
+
this.emit('prompt:updated')
|
|
424
480
|
}
|
|
425
481
|
// Input prompts cannot exit input mode
|
|
426
482
|
}
|
|
@@ -449,10 +505,10 @@ export class ScreenInstance {
|
|
|
449
505
|
const prompt = this.activePrompt.data
|
|
450
506
|
if (prompt.type === 'choice' && prompt.inputMode) {
|
|
451
507
|
;(prompt as ChoicePromptData).inputValue = value
|
|
452
|
-
this.
|
|
508
|
+
this.emit('prompt:updated')
|
|
453
509
|
} else if (prompt.type === 'input') {
|
|
454
510
|
;(prompt as InputPromptData).value = value
|
|
455
|
-
this.
|
|
511
|
+
this.emit('prompt:updated')
|
|
456
512
|
}
|
|
457
513
|
}
|
|
458
514
|
|
|
@@ -465,10 +521,10 @@ export class ScreenInstance {
|
|
|
465
521
|
const prompt = this.activePrompt.data
|
|
466
522
|
if (prompt.type === 'choice' && prompt.inputMode) {
|
|
467
523
|
;(prompt as ChoicePromptData).inputValue += char
|
|
468
|
-
this.
|
|
524
|
+
this.emit('prompt:updated')
|
|
469
525
|
} else if (prompt.type === 'input') {
|
|
470
526
|
;(prompt as InputPromptData).value += char
|
|
471
|
-
this.
|
|
527
|
+
this.emit('prompt:updated')
|
|
472
528
|
}
|
|
473
529
|
}
|
|
474
530
|
|
|
@@ -481,10 +537,10 @@ export class ScreenInstance {
|
|
|
481
537
|
const prompt = this.activePrompt.data
|
|
482
538
|
if (prompt.type === 'choice' && prompt.inputMode) {
|
|
483
539
|
;(prompt as ChoicePromptData).inputValue = prompt.inputValue.slice(0, -1)
|
|
484
|
-
this.
|
|
540
|
+
this.emit('prompt:updated')
|
|
485
541
|
} else if (prompt.type === 'input') {
|
|
486
542
|
;(prompt as InputPromptData).value = prompt.value.slice(0, -1)
|
|
487
|
-
this.
|
|
543
|
+
this.emit('prompt:updated')
|
|
488
544
|
}
|
|
489
545
|
}
|
|
490
546
|
|
|
@@ -550,7 +606,7 @@ export class ScreenInstance {
|
|
|
550
606
|
this.activePrompt = null
|
|
551
607
|
this.activateNextPrompt()
|
|
552
608
|
this.incrementVersion()
|
|
553
|
-
this.
|
|
609
|
+
this.emit('prompt:resolved')
|
|
554
610
|
}
|
|
555
611
|
|
|
556
612
|
/**
|
|
@@ -562,20 +618,15 @@ export class ScreenInstance {
|
|
|
562
618
|
// Notify manager to focus this screen
|
|
563
619
|
this.manager?.onScreenPromptActivated(this)
|
|
564
620
|
this.incrementVersion()
|
|
565
|
-
this.
|
|
621
|
+
this.emit('prompt:activated')
|
|
566
622
|
}
|
|
567
623
|
}
|
|
568
624
|
|
|
569
625
|
/**
|
|
570
|
-
*
|
|
626
|
+
* Print a single message immediately to stdout (for static screens in stdout mode)
|
|
571
627
|
*/
|
|
572
|
-
|
|
573
|
-
this.
|
|
574
|
-
return () => this.changeListeners.delete(listener)
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
private notifyChange(): void {
|
|
578
|
-
this.changeListeners.forEach((listener) => listener())
|
|
628
|
+
private printSingleMessageToConsole(message: MessageData): void {
|
|
629
|
+
printSingleMessage(message, this.name, false)
|
|
579
630
|
}
|
|
580
631
|
|
|
581
632
|
/**
|