@navios/commander-tui 1.0.0 → 1.2.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 +35 -0
- package/README.md +27 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/lib/index.cjs +338 -22
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +161 -118
- package/lib/index.d.cts.map +1 -1
- package/lib/index.d.mts +176 -133
- package/lib/index.d.mts.map +1 -1
- package/lib/index.mjs +333 -24
- package/lib/index.mjs.map +1 -1
- package/lib/{screen_manager_bridge-DN2J6_k1.mjs → screen_manager_bridge-Bk1i53h5.mjs} +1 -1
- package/lib/{screen_manager_bridge-Dfg4QUrl.mjs → screen_manager_bridge-BwPVr0bX.mjs} +17 -3
- package/lib/screen_manager_bridge-BwPVr0bX.mjs.map +1 -0
- package/lib/{screen_manager_bridge-BpDgVu3e.cjs → screen_manager_bridge-CWxLO0CK.cjs} +17 -3
- package/lib/screen_manager_bridge-CWxLO0CK.cjs.map +1 -0
- package/lib/{screen_manager_bridge-CkV7637i.cjs → screen_manager_bridge-DXc57iXW.cjs} +1 -1
- package/package.json +1 -1
- package/src/__tests__/components/__snapshots__/filter_bar.spec.tsx.snap +2604 -0
- package/src/__tests__/components/__snapshots__/loading_message.spec.tsx.snap +1621 -0
- package/src/__tests__/components/__snapshots__/log_message.spec.tsx.snap +3757 -0
- package/src/__tests__/components/__snapshots__/progress_message.spec.tsx.snap +2042 -0
- package/src/__tests__/components/__snapshots__/prompt_renderer.spec.tsx.snap +3652 -0
- package/src/__tests__/components/__snapshots__/sidebar.spec.tsx.snap +3426 -0
- package/src/__tests__/components/sidebar.spec.tsx +17 -0
- package/src/__tests__/services/logger.spec.ts +5 -3
- package/src/factories/index.ts +1 -0
- package/src/factories/isomorphic-logger.factory.ts +23 -0
- package/src/index.ts +5 -0
- package/src/interfaces/index.ts +1 -0
- package/src/interfaces/isomorphic-logger.ts +9 -0
- package/src/overrides/console.logger.override.ts +8 -1
- package/src/schemas/screen-options.ts +2 -0
- package/src/services/logger.ts +1 -1
- package/src/services/screen.ts +3 -0
- package/src/services/screen_manager.tsx +16 -12
- package/src/themes/dark.ts +4 -0
- package/src/themes/high-contrast.ts +4 -0
- package/src/themes/light.ts +4 -0
- package/src/tokens/logger.ts +9 -0
- package/src/types/screen.types.ts +2 -2
- package/src/types/theme.types.ts +1 -0
- package/src/utils/format.ts +3 -2
- package/lib/screen_manager_bridge-BpDgVu3e.cjs.map +0 -1
- package/lib/screen_manager_bridge-Dfg4QUrl.mjs.map +0 -1
|
@@ -200,6 +200,23 @@ describe('Sidebar', () => {
|
|
|
200
200
|
|
|
201
201
|
expect(component).toMatchSnapshot()
|
|
202
202
|
})
|
|
203
|
+
|
|
204
|
+
it('should render static status', () => {
|
|
205
|
+
const screens = [createMockScreen({ id: '1', name: 'Static', status: 'static' })]
|
|
206
|
+
|
|
207
|
+
const component = wrapWithContext(
|
|
208
|
+
<Sidebar
|
|
209
|
+
screens={screens}
|
|
210
|
+
selectedIndex={0}
|
|
211
|
+
activeScreenId="1"
|
|
212
|
+
focused={true}
|
|
213
|
+
width={30}
|
|
214
|
+
title="Screens"
|
|
215
|
+
/>,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
expect(component).toMatchSnapshot()
|
|
219
|
+
})
|
|
203
220
|
})
|
|
204
221
|
|
|
205
222
|
describe('badge counts', () => {
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
1
|
import { TestContainer } from '@navios/core/testing'
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import type { LogLevel } from '@navios/core'
|
|
3
5
|
|
|
4
6
|
import { ScreenLoggerInstance } from '../../services/logger.ts'
|
|
5
7
|
import { Screen } from '../../tokens/index.ts'
|
|
6
8
|
import { createMockScreenInstance } from '../utils/factories.ts'
|
|
7
9
|
|
|
10
|
+
import type { ScreenInstance } from '../../index.js'
|
|
8
11
|
import type { MockScreenInstance } from '../utils/factories.ts'
|
|
9
|
-
import type { LogLevel } from '@navios/core'
|
|
10
12
|
|
|
11
13
|
describe('ScreenLoggerInstance', () => {
|
|
12
14
|
let container: TestContainer
|
|
@@ -17,7 +19,7 @@ describe('ScreenLoggerInstance', () => {
|
|
|
17
19
|
beforeEach(() => {
|
|
18
20
|
container = new TestContainer()
|
|
19
21
|
mockScreen = createMockScreenInstance()
|
|
20
|
-
container.bind(Screen).toValue(mockScreen)
|
|
22
|
+
container.bind(Screen).toValue(mockScreen as unknown as ScreenInstance)
|
|
21
23
|
})
|
|
22
24
|
|
|
23
25
|
afterEach(async () => {
|
package/src/factories/index.ts
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Factory, inject, Logger, type FactoryContext } from '@navios/core'
|
|
2
|
+
|
|
3
|
+
import { ScreenManager } from '../services/screen_manager.js'
|
|
4
|
+
import { IsomorphicLogger, ScreenLogger } from '../tokens/logger.js'
|
|
5
|
+
|
|
6
|
+
import type { IsomorphicLoggerInterface } from '../interfaces/isomorphic-logger.js'
|
|
7
|
+
import type { LoggerOptions } from '../schemas/index.js'
|
|
8
|
+
|
|
9
|
+
@Factory({
|
|
10
|
+
token: IsomorphicLogger,
|
|
11
|
+
})
|
|
12
|
+
export class IsomorphicLoggerFactory {
|
|
13
|
+
private readonly screenManager = inject(ScreenManager)
|
|
14
|
+
|
|
15
|
+
async create(ctx: FactoryContext, options: LoggerOptions): Promise<IsomorphicLoggerInterface> {
|
|
16
|
+
if (this.screenManager.isTuiBound()) {
|
|
17
|
+
return ctx.inject(ScreenLogger, options)
|
|
18
|
+
}
|
|
19
|
+
return ctx.inject(Logger, {
|
|
20
|
+
context: options.context,
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
// ============================================
|
|
4
4
|
export * from './types/index.ts'
|
|
5
5
|
|
|
6
|
+
// ============================================
|
|
7
|
+
// Interfaces
|
|
8
|
+
// ============================================
|
|
9
|
+
export * from './interfaces/index.ts'
|
|
10
|
+
|
|
6
11
|
// ============================================
|
|
7
12
|
// Components
|
|
8
13
|
// ============================================
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './isomorphic-logger.ts'
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { LoggerInstance } from '@navios/core'
|
|
2
|
+
|
|
3
|
+
import type { ScreenLoggerInstance } from '../services/logger.ts'
|
|
4
|
+
|
|
5
|
+
type PublicLoggerMethods = Pick<LoggerInstance, keyof LoggerInstance>
|
|
6
|
+
type PublicScreenLoggerMethods = Pick<ScreenLoggerInstance, keyof ScreenLoggerInstance>
|
|
7
|
+
type ScreenOwnMethods = Partial<Omit<PublicScreenLoggerMethods, keyof PublicLoggerMethods>>
|
|
8
|
+
|
|
9
|
+
export interface IsomorphicLoggerInterface extends PublicLoggerMethods, ScreenOwnMethods {}
|
|
@@ -3,11 +3,13 @@ import {
|
|
|
3
3
|
Injectable,
|
|
4
4
|
LoggerOutput,
|
|
5
5
|
type ClassTypeWithInstance,
|
|
6
|
+
type LoggerOptions,
|
|
6
7
|
type LoggerService,
|
|
7
8
|
type LogLevel,
|
|
8
9
|
} from '@navios/core'
|
|
9
10
|
|
|
10
11
|
import { ScreenLogger } from '../tokens/index.ts'
|
|
12
|
+
import { ALL_LOG_LEVELS } from '../types/index.ts'
|
|
11
13
|
|
|
12
14
|
export function overrideConsoleLogger(
|
|
13
15
|
hidden: boolean = false,
|
|
@@ -22,12 +24,17 @@ export function overrideConsoleLogger(
|
|
|
22
24
|
class ConsoleLoggerOverride implements LoggerService {
|
|
23
25
|
protected readonly logger = inject(ScreenLogger, {
|
|
24
26
|
screen: {
|
|
25
|
-
name: '
|
|
27
|
+
name: 'internal',
|
|
26
28
|
icon: '💻',
|
|
27
29
|
hidden,
|
|
30
|
+
static: true,
|
|
28
31
|
},
|
|
29
32
|
})
|
|
30
33
|
|
|
34
|
+
setup(options: LoggerOptions & { logLevels: LogLevel[] }): void {
|
|
35
|
+
this.logger.setLogLevels(options.logLevels ?? ALL_LOG_LEVELS)
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
log(message: string): void {
|
|
32
39
|
this.logger.log(message)
|
|
33
40
|
}
|
|
@@ -9,6 +9,8 @@ export const ScreenOptionsSchema = z.object({
|
|
|
9
9
|
badgeCount: z.number().optional(),
|
|
10
10
|
/** Whether the screen is hidden */
|
|
11
11
|
hidden: z.boolean().optional().default(false),
|
|
12
|
+
/** Whether the screen is static (ignored in auto-close calculations) */
|
|
13
|
+
static: z.boolean().optional().default(false),
|
|
12
14
|
})
|
|
13
15
|
|
|
14
16
|
export type ScreenOptions = z.infer<typeof ScreenOptionsSchema>
|
package/src/services/logger.ts
CHANGED
package/src/services/screen.ts
CHANGED
|
@@ -101,7 +101,7 @@ export class ScreenManager {
|
|
|
101
101
|
this.renderer = await createCliRenderer({
|
|
102
102
|
exitOnCtrlC: options?.exitOnCtrlC ?? true,
|
|
103
103
|
useAlternateScreen: true,
|
|
104
|
-
useMouse: options?.useMouse ??
|
|
104
|
+
useMouse: options?.useMouse ?? true,
|
|
105
105
|
})
|
|
106
106
|
|
|
107
107
|
this.root = createRoot(this.renderer)
|
|
@@ -244,33 +244,34 @@ export class ScreenManager {
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
// Check for auto-close
|
|
248
|
-
this.checkAutoClose()
|
|
249
|
-
|
|
250
247
|
this.notifyChange()
|
|
251
248
|
}
|
|
252
249
|
|
|
253
250
|
/**
|
|
254
|
-
* Check if all screens are successful and start auto-close timer if enabled
|
|
251
|
+
* Check if all screens are successful (or only static) and start auto-close timer if enabled.
|
|
252
|
+
* Static screens are ignored in this calculation.
|
|
253
|
+
* If there are only static screens, the timer will trigger after the delay with no new activity.
|
|
255
254
|
*/
|
|
256
255
|
private checkAutoClose(): void {
|
|
257
256
|
const autoClose = this.bindOptions.autoClose
|
|
258
257
|
if (!autoClose || !this.isBound) return
|
|
259
258
|
|
|
260
|
-
// Clear any existing timer
|
|
259
|
+
// Clear any existing timer (will be restarted if conditions are met)
|
|
261
260
|
if (this.autoCloseTimer) {
|
|
262
261
|
clearTimeout(this.autoCloseTimer)
|
|
263
262
|
this.autoCloseTimer = null
|
|
264
263
|
}
|
|
265
264
|
|
|
266
|
-
//
|
|
267
|
-
const
|
|
268
|
-
if (screens.length === 0) return
|
|
265
|
+
// Get non-static screens
|
|
266
|
+
const nonStaticScreens = this.getScreens().filter((s) => s.getStatus() !== 'static')
|
|
269
267
|
|
|
270
|
-
|
|
271
|
-
if (
|
|
268
|
+
// If there are non-static screens, check if all are successful
|
|
269
|
+
if (nonStaticScreens.length > 0) {
|
|
270
|
+
const allSuccessful = nonStaticScreens.every((s) => s.getStatus() === 'success')
|
|
271
|
+
if (!allSuccessful) return
|
|
272
|
+
}
|
|
272
273
|
|
|
273
|
-
// Start auto-close timer
|
|
274
|
+
// Start auto-close timer (either all non-static screens succeeded, or only static screens exist)
|
|
274
275
|
const delay = typeof autoClose === 'number' ? autoClose : 5000
|
|
275
276
|
this.autoCloseTimer = setTimeout(() => {
|
|
276
277
|
this.unbind()
|
|
@@ -376,6 +377,9 @@ export class ScreenManager {
|
|
|
376
377
|
}
|
|
377
378
|
|
|
378
379
|
private notifyChange(): void {
|
|
380
|
+
// Check auto-close on every change (resets timer if activity occurs)
|
|
381
|
+
this.checkAutoClose()
|
|
382
|
+
|
|
379
383
|
// Notify listeners - React components will forceUpdate and re-render
|
|
380
384
|
this.changeListeners.forEach((listener) => listener())
|
|
381
385
|
}
|
package/src/themes/dark.ts
CHANGED
package/src/themes/light.ts
CHANGED
package/src/tokens/logger.ts
CHANGED
|
@@ -2,9 +2,18 @@ import { InjectionToken } from '@navios/core'
|
|
|
2
2
|
|
|
3
3
|
import { LoggerOptionsSchema } from '../schemas/index.ts'
|
|
4
4
|
|
|
5
|
+
import type { IsomorphicLoggerInterface } from '../interfaces/isomorphic-logger.ts'
|
|
5
6
|
import type { ScreenLoggerInstance } from '../services/index.ts'
|
|
6
7
|
|
|
7
8
|
export const ScreenLogger = InjectionToken.create<ScreenLoggerInstance, typeof LoggerOptionsSchema>(
|
|
8
9
|
'ScreenLoggerInstance',
|
|
9
10
|
LoggerOptionsSchema,
|
|
10
11
|
)
|
|
12
|
+
/**
|
|
13
|
+
* Isomorphic logger token for shared logger between TUI and Server.
|
|
14
|
+
* This token is used to inject the logger instance into the application.
|
|
15
|
+
*/
|
|
16
|
+
export const IsomorphicLogger = InjectionToken.create<
|
|
17
|
+
IsomorphicLoggerInterface,
|
|
18
|
+
typeof LoggerOptionsSchema
|
|
19
|
+
>('IsomorphicLogger', LoggerOptionsSchema)
|
|
@@ -108,7 +108,7 @@ export interface LoggerContextValue {
|
|
|
108
108
|
// Screen Service Types
|
|
109
109
|
// ============================================
|
|
110
110
|
|
|
111
|
-
export type ScreenStatus = 'waiting' | 'pending' | 'success' | 'fail'
|
|
111
|
+
export type ScreenStatus = 'waiting' | 'pending' | 'success' | 'fail' | 'static'
|
|
112
112
|
|
|
113
113
|
export interface BindOptions {
|
|
114
114
|
exitOnCtrlC?: boolean
|
|
@@ -119,6 +119,6 @@ export interface BindOptions {
|
|
|
119
119
|
autoClose?: boolean | number
|
|
120
120
|
/** Theme to use for the TUI (theme object or preset name) */
|
|
121
121
|
theme?: Theme | ThemePreset
|
|
122
|
-
/** Enable mouse support (default:
|
|
122
|
+
/** Enable mouse support (default: true) */
|
|
123
123
|
useMouse?: boolean
|
|
124
124
|
}
|
package/src/types/theme.types.ts
CHANGED
package/src/utils/format.ts
CHANGED
|
@@ -43,10 +43,11 @@ export function formatObject(obj: unknown, depth: number = 2, currentDepth: numb
|
|
|
43
43
|
/**
|
|
44
44
|
* Capture a stack trace, filtering out internal frames
|
|
45
45
|
*/
|
|
46
|
-
export function captureTrace(): string {
|
|
46
|
+
export function captureTrace(offset: number = 0): string {
|
|
47
47
|
const err = new Error()
|
|
48
48
|
const stack = err.stack ?? ''
|
|
49
49
|
const lines = stack.split('\n')
|
|
50
|
+
const begin = lines.findIndex((line) => line.includes('captureTrace'))
|
|
50
51
|
// Skip the first 4 lines: Error, captureTrace, trace method, and the log method
|
|
51
|
-
return lines.slice(4).join('\n')
|
|
52
|
+
return lines.slice(begin !== -1 ? begin + 1 + offset : 4).join('\n')
|
|
52
53
|
}
|