@pokit/terminal 0.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.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Symbol Sets for CLI Output
3
+ *
4
+ * Provides Unicode and ASCII symbol sets for terminal output.
5
+ * The appropriate set is selected based on OutputConfig.
6
+ */
7
+
8
+ import type { OutputConfig } from '@pokit/core';
9
+
10
+ /**
11
+ * Complete set of symbols used in CLI output
12
+ */
13
+ export type SymbolSet = {
14
+ /** Success indicator (e.g., checkmark) */
15
+ success: string;
16
+ /** Error indicator (e.g., X mark) */
17
+ error: string;
18
+ /** Warning indicator */
19
+ warning: string;
20
+ /** Info indicator */
21
+ info: string;
22
+ /** Step/progress indicator */
23
+ step: string;
24
+ /** Group start (e.g., top-left corner) */
25
+ groupStart: string;
26
+ /** Group end (e.g., bottom-left corner) */
27
+ groupEnd: string;
28
+ /** Group line (e.g., vertical bar) */
29
+ groupLine: string;
30
+ /** Done message */
31
+ done: string;
32
+ /** Failed message */
33
+ failed: string;
34
+ };
35
+
36
+ /**
37
+ * Unicode symbols for rich terminal output
38
+ * Used when unicode support is detected
39
+ */
40
+ export const UNICODE_SYMBOLS: SymbolSet = {
41
+ success: '\u25C7', // ◇
42
+ error: '\u25A0', // ■
43
+ warning: '\u25B2', // ▲
44
+ info: '\u25CF', // ●
45
+ step: '\u25C7', // ◇
46
+ groupStart: '\u250C', // ┌
47
+ groupEnd: '\u2514', // └
48
+ groupLine: '\u2502', // │
49
+ done: '\u2714', // ✔
50
+ failed: '\u2718', // ✘
51
+ };
52
+
53
+ /**
54
+ * ASCII symbols for plain text output
55
+ * Used in CI environments or when unicode is not supported
56
+ */
57
+ export const ASCII_SYMBOLS: SymbolSet = {
58
+ success: '[OK]',
59
+ error: '[ERR]',
60
+ warning: '[WARN]',
61
+ info: '[INFO]',
62
+ step: '-',
63
+ groupStart: '[',
64
+ groupEnd: ']',
65
+ groupLine: '|',
66
+ done: 'Done',
67
+ failed: 'Failed',
68
+ };
69
+
70
+ /**
71
+ * Get the appropriate symbol set based on output configuration
72
+ *
73
+ * @param config - Output configuration
74
+ * @returns Symbol set to use
75
+ */
76
+ export function getSymbols(config: OutputConfig): SymbolSet {
77
+ return config.unicode ? UNICODE_SYMBOLS : ASCII_SYMBOLS;
78
+ }
package/src/screen.ts ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Shared terminal screen state.
3
+ *
4
+ * The terminal UI is a single screen owner. Loading indicators (spinners) are
5
+ * created here rather than ad-hoc inside individual prompts, so the prompter
6
+ * does not spawn its own competing clack spinners for dynamic selects. The
7
+ * reporter adapter and the prompter both belong to the same terminal UI and
8
+ * share this screen.
9
+ */
10
+
11
+ import * as p from '@clack/prompts';
12
+ import type { OutputConfig } from '@pokit/core';
13
+
14
+ export interface Screen {
15
+ /**
16
+ * Run `fn` while showing a loading indicator. The indicator is the single
17
+ * screen-owned spinner; the prompter never creates its own.
18
+ */
19
+ withLoading<T>(message: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T>;
20
+ }
21
+
22
+ function canUseInteractiveUI(outputConfig: OutputConfig): boolean {
23
+ return outputConfig.unicode && outputConfig.interactive;
24
+ }
25
+
26
+ /**
27
+ * Create the shared screen for a terminal UI instance.
28
+ */
29
+ export function createScreen(outputConfig: OutputConfig): Screen {
30
+ const interactive = canUseInteractiveUI(outputConfig);
31
+
32
+ return {
33
+ async withLoading<T>(message: string, fn: (signal: AbortSignal) => Promise<T>): Promise<T> {
34
+ const controller = new AbortController();
35
+
36
+ if (!interactive) {
37
+ return fn(controller.signal);
38
+ }
39
+
40
+ const spinner = p.spinner();
41
+ spinner.start(message);
42
+ try {
43
+ const result = await fn(controller.signal);
44
+ spinner.stop(message);
45
+ return result;
46
+ } catch (error) {
47
+ controller.abort();
48
+ spinner.stop('Failed to load');
49
+ throw error;
50
+ }
51
+ },
52
+ };
53
+ }