@flowscripter/dynamic-cli-framework 1.1.6 → 1.2.1

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.
Files changed (160) hide show
  1. package/.github/workflows/release-bun-library.yml +1 -0
  2. package/README/core-commands.md +40 -0
  3. package/README/core-service-providers.md +198 -0
  4. package/README/development.md +60 -0
  5. package/README/implementation-details.md +474 -0
  6. package/README/key-concepts.md +751 -0
  7. package/README.md +12 -1413
  8. package/bun.lock +22 -18
  9. package/index.ts +97 -1
  10. package/package.json +8 -7
  11. package/src/api/BaseCLIFeatureOptions.ts +10 -0
  12. package/src/api/CLIConfig.ts +5 -0
  13. package/src/api/RunResult.ts +16 -0
  14. package/src/api/argument/Argument.ts +24 -0
  15. package/src/api/service/ServiceProvider.ts +3 -3
  16. package/src/api/service/core/ArgumentPrompterService.ts +10 -0
  17. package/src/api/service/core/AsciiBannerGeneratorService.ts +44 -7
  18. package/src/api/service/core/CompletionService.ts +36 -0
  19. package/src/api/service/core/DataDumpGeneratorService.ts +27 -0
  20. package/src/api/service/core/ImagePrinterService.ts +26 -0
  21. package/src/api/service/core/KeyValueService.ts +22 -5
  22. package/src/api/service/core/PrinterService.ts +127 -1
  23. package/src/api/service/core/PrompterService.ts +49 -0
  24. package/src/api/service/core/SecretService.ts +27 -0
  25. package/src/api/service/core/ShutdownService.ts +17 -1
  26. package/src/api/service/core/Table.ts +85 -0
  27. package/src/api/service/core/TableGeneratorService.ts +38 -0
  28. package/src/api/service/core/TreePrinterService.ts +11 -0
  29. package/src/cli/BaseCLI.ts +116 -44
  30. package/src/cli/DefaultRuntimeCLI.ts +10 -18
  31. package/src/command/CompletionCommand.ts +179 -0
  32. package/src/command/MultiCommandCliHelpCommand.ts +18 -2
  33. package/src/command/SingleCommandCliHelpCommand.ts +10 -1
  34. package/src/launcher.ts +16 -58
  35. package/src/runtime/command/CommandValidator.ts +3 -3
  36. package/src/runtime/runner.ts +102 -30
  37. package/src/runtime/scanner.ts +18 -15
  38. package/src/runtime/values/argumentValueValidation.ts +49 -5
  39. package/src/runtime/values/globalCommandValuePopulation.ts +1 -1
  40. package/src/runtime/values/subCommandValuePopulation.ts +9 -8
  41. package/src/service/argumentPrompter/ArgumentPrompterServiceProvider.ts +34 -0
  42. package/src/service/argumentPrompter/DefaultArgumentPrompterService.ts +330 -0
  43. package/src/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.ts +1 -1
  44. package/src/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.ts +254 -17
  45. package/src/service/banner/BannerServiceProvider.ts +2 -2
  46. package/src/service/chiselAsciiBannerGenerator/ChiselFontAsciiBannerGeneratorService.ts +85 -0
  47. package/src/service/chiselAsciiBannerGenerator/chisel.flf.json +3 -0
  48. package/src/service/completion/CompletionServiceProvider.ts +212 -0
  49. package/src/service/completion/DefaultCompletionService.ts +364 -0
  50. package/src/service/completion/shell/ShellHandler.ts +16 -0
  51. package/src/service/completion/shell/bash.ts +47 -0
  52. package/src/service/completion/shell/fish.ts +43 -0
  53. package/src/service/completion/shell/powershell.ts +66 -0
  54. package/src/service/completion/shell/zsh.ts +50 -0
  55. package/src/service/configuration/ConfigurationServiceProvider.ts +138 -42
  56. package/src/service/configuration/DefaultKeyValueService.ts +53 -7
  57. package/src/service/configuration/DefaultSecretService.ts +85 -0
  58. package/src/service/dataDumpGenerator/DataDumpGeneratorServiceProvider.ts +41 -0
  59. package/src/service/dataDumpGenerator/DefaultDataDumpGeneratorService.ts +80 -0
  60. package/src/service/imagePrinter/DefaultImagePrinterService.ts +23 -0
  61. package/src/service/imagePrinter/ImagePrinterServiceProvider.ts +32 -0
  62. package/src/service/prettyPrinter/PrettyPrinterServiceProvider.ts +1 -1
  63. package/src/service/printer/DefaultPrinterService.ts +147 -27
  64. package/src/service/printer/PrinterServiceProvider.ts +7 -7
  65. package/src/service/printer/command/NoHyperlinksCommand.ts +40 -0
  66. package/src/service/printer/terminal/Progress.ts +21 -4
  67. package/src/service/printer/terminal/Spinner.ts +32 -8
  68. package/src/service/printer/terminal/Theme.ts +1 -1
  69. package/src/service/prompter/DefaultPrompterService.ts +87 -0
  70. package/src/service/prompter/PrompterServiceProvider.ts +36 -0
  71. package/src/service/prompter/command/NoPromptCommand.ts +40 -0
  72. package/src/service/prompter/prompt/PromptContext.ts +47 -0
  73. package/src/service/prompter/prompt/promptAcknowledge.ts +28 -0
  74. package/src/service/prompter/prompt/promptMultiSelect.ts +99 -0
  75. package/src/service/prompter/prompt/promptOpenUrl.ts +97 -0
  76. package/src/service/prompter/prompt/promptSingleSelect.ts +102 -0
  77. package/src/service/prompter/prompt/promptText.ts +107 -0
  78. package/src/service/prompter/prompt/promptToggle.ts +28 -0
  79. package/src/service/shutdown/DefaultShutdownService.ts +13 -0
  80. package/src/service/shutdown/ShutdownServiceProvider.ts +28 -9
  81. package/src/service/shutdown/ShutdownState.ts +5 -0
  82. package/src/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.ts +1 -1
  83. package/src/service/tableGenerator/DefaultTableGeneratorService.ts +415 -0
  84. package/src/service/tableGenerator/TableGeneratorServiceProvider.ts +43 -0
  85. package/src/service/treePrinter/DefaultTreePrinterService.ts +49 -0
  86. package/src/service/treePrinter/TreePrinterServiceProvider.ts +40 -0
  87. package/src/terminal/Ansi.ts +27 -0
  88. package/src/terminal/ImageRenderer.ts +199 -0
  89. package/src/terminal/KeyReader.ts +23 -0
  90. package/src/terminal/PngDecoder.ts +143 -0
  91. package/src/{service/printer/terminal → terminal}/Styler.ts +3 -0
  92. package/src/{service/printer/terminal → terminal}/Terminal.ts +1 -0
  93. package/src/terminal/TtyKeyReader.ts +83 -0
  94. package/src/terminal/TtyStyler.ts +78 -0
  95. package/src/{service/printer/terminal → terminal}/TtyTerminal.ts +9 -1
  96. package/src/terminal/supportsHyperlinks.ts +110 -0
  97. package/src/util/configHelper.ts +2 -2
  98. package/src/util/envVarHelper.ts +2 -2
  99. package/src/util/helpHelper.ts +66 -122
  100. package/src/util/logger.ts +16 -14
  101. package/src/util/runnerHelper.ts +1 -1
  102. package/tests/cli/BaseCLI.test.ts +50 -54
  103. package/tests/command/CompletionCommand.test.ts +68 -0
  104. package/tests/command/UsageCommand.test.ts +3 -2
  105. package/tests/command/VersionCommand.test.ts +3 -2
  106. package/tests/fixtures/Context.ts +9 -2
  107. package/tests/fixtures/util.ts +1 -1
  108. package/tests/runtime/parser.test.ts +9 -7
  109. package/tests/runtime/registry/DefaultServiceProviderRegistry.test.ts +3 -3
  110. package/tests/runtime/values/argumentValueValidation.test.ts +193 -0
  111. package/tests/service/argumentPrompter/ArgumentPrompterServiceProvider.test.ts +47 -0
  112. package/tests/service/argumentPrompter/DefaultArgumentPrompterService.test.ts +754 -0
  113. package/tests/service/asciiBannerGenerator/AsciiBannerGeneratorServiceProvider.test.ts +5 -4
  114. package/tests/service/asciiBannerGenerator/DefaultAsciiBannerGeneratorService.test.ts +163 -3
  115. package/tests/service/asciiBannerGenerator/__snapshots__/DefaultAsciiBannerGeneratorService.test.ts.snap +1 -1
  116. package/tests/service/banner/BannerServiceProvider.test.ts +8 -4
  117. package/tests/service/chiselAsciiBannerGenerator/ChiselFontAsciiBannerGeneratorService.test.ts +52 -0
  118. package/tests/service/completion/CompletionServiceProvider.test.ts +404 -0
  119. package/tests/service/completion/DefaultCompletionService.test.ts +278 -0
  120. package/tests/service/completion/shell/bash.test.ts +73 -0
  121. package/tests/service/completion/shell/fish.test.ts +62 -0
  122. package/tests/service/completion/shell/powershell.test.ts +59 -0
  123. package/tests/service/completion/shell/zsh.test.ts +60 -0
  124. package/tests/service/configuration/ConfigurationServiceProvider.test.ts +24 -12
  125. package/tests/service/configuration/DefaultKeyValueService.test.ts +118 -16
  126. package/tests/service/configuration/DefaultSecretService.test.ts +148 -0
  127. package/tests/service/configuration/command/ConfigCommand.test.ts +29 -0
  128. package/tests/service/configuration/command/DumpConfigCommand.test.ts +60 -0
  129. package/tests/service/dataDumpGenerator/DataDumpGeneratorServiceProvider.test.ts +57 -0
  130. package/tests/service/dataDumpGenerator/DefaultDataDumpGeneratorService.test.ts +217 -0
  131. package/tests/service/imagePrinter/DefaultImagePrinterService.test.ts +96 -0
  132. package/tests/service/imagePrinter/ImagePrinterServiceProvider.test.ts +17 -0
  133. package/tests/service/prettyPrinter/DefaultPrettyPrinterService.test.ts +2 -2
  134. package/tests/service/prettyPrinter/PrettyPrinterServiceProvider.test.ts +5 -4
  135. package/tests/service/printer/DefaultPrinterService.test.ts +233 -2
  136. package/tests/service/printer/PrinterServiceProvider.test.ts +6 -5
  137. package/tests/service/printer/command/DarkModeCommand.test.ts +83 -0
  138. package/tests/service/printer/command/LogLevelCommand.test.ts +7 -5
  139. package/tests/service/printer/command/NoColorCommand.test.ts +83 -0
  140. package/tests/service/printer/command/NoHyperlinksCommand.test.ts +89 -0
  141. package/tests/service/printer/terminal/Progress.test.ts +64 -4
  142. package/tests/service/printer/terminal/Spinner.test.ts +44 -3
  143. package/tests/service/prompter/DefaultPrompterService.test.ts +615 -0
  144. package/tests/service/prompter/PrompterServiceProvider.test.ts +47 -0
  145. package/tests/service/prompter/command/NoPromptCommand.test.ts +58 -0
  146. package/tests/service/shutdown/ShutdownServiceProvider.test.ts +4 -2
  147. package/tests/service/syntaxHighlighter/DefaultSyntaxHighlighterService.test.ts +3 -2
  148. package/tests/service/syntaxHighlighter/SyntaxHighlighterServiceProvider.test.ts +5 -4
  149. package/tests/service/tableGenerator/DefaultTableGeneratorService.test.ts +363 -0
  150. package/tests/service/tableGenerator/TableGeneratorServiceProvider.test.ts +47 -0
  151. package/tests/service/treePrinter/DefaultTreePrinterService.test.ts +144 -0
  152. package/tests/service/treePrinter/TreePrinterServiceProvider.test.ts +55 -0
  153. package/tests/terminal/ImageRenderer.test.ts +161 -0
  154. package/tests/terminal/PngDecoder.test.ts +223 -0
  155. package/tests/terminal/supportsHyperlinks.test.ts +149 -0
  156. package/tests/util/configHelper.test.ts +4 -4
  157. package/tests/util/logger.test.ts +23 -1
  158. package/tsconfig.json +31 -0
  159. package/src/service/printer/terminal/Ansi.ts +0 -10
  160. package/src/service/printer/terminal/TtyStyler.ts +0 -39
@@ -0,0 +1,80 @@
1
+ import { Buffer } from "node:buffer";
2
+ import type DataDumpGeneratorService from "../../api/service/core/DataDumpGeneratorService.ts";
3
+ import {
4
+ type ByteRangeColor,
5
+ DumpFormat,
6
+ type HexDumpGenerateOptions,
7
+ } from "../../api/service/core/DataDumpGeneratorService.ts";
8
+
9
+ export default class DefaultDataDumpGeneratorService
10
+ implements DataDumpGeneratorService {
11
+ colorEnabled = true;
12
+ colorFunction: (text: string, hexFormattedColor: string) => string = (text) =>
13
+ text;
14
+
15
+ #colorByte(
16
+ text: string,
17
+ byteValue: number,
18
+ colorScheme?: ByteRangeColor[],
19
+ ): string {
20
+ if (!this.colorEnabled || !colorScheme) {
21
+ return text;
22
+ }
23
+ for (const range of colorScheme) {
24
+ if (byteValue >= range.start && byteValue <= range.end) {
25
+ return this.colorFunction(text, range.color);
26
+ }
27
+ }
28
+ return text;
29
+ }
30
+
31
+ generate(data: Buffer, options?: HexDumpGenerateOptions): string {
32
+ if (data.length === 0) {
33
+ return "";
34
+ }
35
+
36
+ const format = options?.format ?? DumpFormat.HEX;
37
+ const bytesPerGroup = options?.bytesPerGroup ?? 4;
38
+ const groupsPerRow = options?.groupsPerRow ?? 8;
39
+ const spacesBetweenGroups = options?.spacesBetweenGroups ?? 1;
40
+ const colorScheme = options?.colorScheme;
41
+
42
+ const bytesPerRow = bytesPerGroup * groupsPerRow;
43
+ const groupSeparator = " ".repeat(spacesBetweenGroups);
44
+ const rows: string[] = [];
45
+
46
+ for (let offset = 0; offset < data.length; offset += bytesPerRow) {
47
+ const groups: string[] = [];
48
+
49
+ for (let g = 0; g < groupsPerRow; g++) {
50
+ const groupStart = offset + g * bytesPerGroup;
51
+ let groupStr = "";
52
+
53
+ for (let b = 0; b < bytesPerGroup; b++) {
54
+ const byteIndex = groupStart + b;
55
+ if (byteIndex < data.length) {
56
+ const byteValue = data[byteIndex]!;
57
+ let rendered: string;
58
+ if (format === DumpFormat.ASCII) {
59
+ rendered = byteValue >= 0x20 && byteValue <= 0x7e
60
+ ? String.fromCharCode(byteValue)
61
+ : ".";
62
+ } else {
63
+ rendered = byteValue.toString(16).toUpperCase().padStart(2, "0");
64
+ }
65
+ groupStr += this.#colorByte(rendered, byteValue, colorScheme);
66
+ } else {
67
+ // Pad partial group
68
+ groupStr += format === DumpFormat.ASCII ? " " : " ";
69
+ }
70
+ }
71
+
72
+ groups.push(groupStr);
73
+ }
74
+
75
+ rows.push(groups.join(groupSeparator));
76
+ }
77
+
78
+ return rows.join("\n");
79
+ }
80
+ }
@@ -0,0 +1,23 @@
1
+ import ImageRenderer from "../../terminal/ImageRenderer.ts";
2
+ import type ImagePrinterService from "../../api/service/core/ImagePrinterService.ts";
3
+ import type Terminal from "../../terminal/Terminal.ts";
4
+
5
+ export default class DefaultImagePrinterService implements ImagePrinterService {
6
+ readonly #renderer: ImageRenderer;
7
+
8
+ constructor(terminal: Terminal) {
9
+ this.#renderer = new ImageRenderer(terminal);
10
+ }
11
+
12
+ image(
13
+ imageBuffer: Uint8Array,
14
+ widthPercentage: number = 100,
15
+ hexFormattedBackgroundColor?: string,
16
+ ): Promise<string> {
17
+ return this.#renderer.renderImage(
18
+ imageBuffer,
19
+ widthPercentage,
20
+ hexFormattedBackgroundColor,
21
+ );
22
+ }
23
+ }
@@ -0,0 +1,32 @@
1
+ import type {
2
+ ServiceInfo,
3
+ ServiceProvider,
4
+ } from "../../api/service/ServiceProvider.ts";
5
+ import DefaultImagePrinterService from "./DefaultImagePrinterService.ts";
6
+ import { IMAGE_PRINTER_SERVICE_ID } from "../../api/service/core/ImagePrinterService.ts";
7
+ import type Context from "../../api/Context.ts";
8
+ import type CLIConfig from "../../api/CLIConfig.ts";
9
+ import type Terminal from "../../terminal/Terminal.ts";
10
+
11
+ export default class ImagePrinterServiceProvider implements ServiceProvider {
12
+ readonly serviceId: string = IMAGE_PRINTER_SERVICE_ID;
13
+ readonly #defaultImagePrinterService: DefaultImagePrinterService;
14
+
15
+ public constructor(
16
+ readonly servicePriority: number,
17
+ terminal: Terminal,
18
+ ) {
19
+ this.#defaultImagePrinterService = new DefaultImagePrinterService(terminal);
20
+ }
21
+
22
+ public getServiceInfo(_cliConfig: CLIConfig): Promise<ServiceInfo> {
23
+ return Promise.resolve({
24
+ service: this.#defaultImagePrinterService,
25
+ commands: [],
26
+ });
27
+ }
28
+
29
+ initService(_context: Context): Promise<void> {
30
+ return Promise.resolve(undefined);
31
+ }
32
+ }
@@ -27,7 +27,7 @@ export default class PrettyPrinterServiceProvider implements ServiceProvider {
27
27
  this.#defaultPrettyPrinterService = new DefaultPrettyPrinterService();
28
28
  }
29
29
 
30
- public provide(_cliConfig: CLIConfig): Promise<ServiceInfo> {
30
+ public getServiceInfo(_cliConfig: CLIConfig): Promise<ServiceInfo> {
31
31
  return Promise.resolve({
32
32
  service: this.#defaultPrettyPrinterService,
33
33
  commands: [],
@@ -1,17 +1,22 @@
1
1
  import { default as Spinner } from "./terminal/Spinner.ts";
2
2
  import { default as Progress } from "./terminal/Progress.ts";
3
3
  import type PrinterService from "../../api/service/core/PrinterService.ts";
4
- import { type Icon, Level } from "../../api/service/core/PrinterService.ts";
5
- import Terminal from "./terminal/Terminal.ts";
4
+ import {
5
+ type Icon,
6
+ Level,
7
+ type ProgressStyle,
8
+ type SpinnerStyle,
9
+ } from "../../api/service/core/PrinterService.ts";
10
+ import type Terminal from "../../terminal/Terminal.ts";
6
11
  import { Color } from "./terminal/Color.ts";
7
12
  import { getDarkModeTheme, getLightModeTheme } from "./terminal/Theme.ts";
8
13
  import { WritableStream } from "node:stream/web";
9
- import Styler from "./terminal/Styler.ts";
10
-
14
+ import type Styler from "../../terminal/Styler.ts";
11
15
  export default class DefaultPrinterService implements PrinterService {
12
16
  readonly stdoutWritable: WritableStream;
13
17
  readonly stderrWritable: WritableStream;
14
18
  #stdoutIsColor: boolean;
19
+ #stdoutTerminal: Terminal;
15
20
  #stderrTerminal: Terminal;
16
21
  #styler: Styler;
17
22
  #isDarkMode = true;
@@ -46,12 +51,14 @@ export default class DefaultPrinterService implements PrinterService {
46
51
  stderrWritableStream: WritableStream,
47
52
  stdoutIsColor: boolean,
48
53
  stderrIsColor: boolean,
54
+ stdoutTerminal: Terminal,
49
55
  stderrTerminal: Terminal,
50
56
  styler: Styler,
51
57
  ) {
52
58
  this.stdoutWritable = stdoutWritableStream;
53
59
  this.stderrWritable = stderrWritableStream;
54
60
  this.#stdoutIsColor = stdoutIsColor;
61
+ this.#stdoutTerminal = stdoutTerminal;
55
62
  this.#stderrTerminal = stderrTerminal;
56
63
  this.#styler = styler;
57
64
  this.#styler.colorEnabled = stderrIsColor;
@@ -90,6 +97,26 @@ export default class DefaultPrinterService implements PrinterService {
90
97
  return this.#styler.colorEnabled;
91
98
  }
92
99
 
100
+ set hyperlinksEnabled(enabled: boolean) {
101
+ this.#styler.hyperlinksEnabled = enabled;
102
+ }
103
+
104
+ get hyperlinksEnabled(): boolean {
105
+ return this.#styler.hyperlinksEnabled;
106
+ }
107
+
108
+ public hyperlink(text: string, url: string): string {
109
+ return this.#styler.hyperlink(text, url);
110
+ }
111
+
112
+ public stdoutColumns(): number {
113
+ return this.#stdoutTerminal.columns();
114
+ }
115
+
116
+ public stderrColumns(): number {
117
+ return this.#stderrTerminal.columns();
118
+ }
119
+
93
120
  set darkMode(isDarkMode: boolean) {
94
121
  this.#isDarkMode = isDarkMode;
95
122
  if (isDarkMode) {
@@ -97,12 +124,12 @@ export default class DefaultPrinterService implements PrinterService {
97
124
  } else {
98
125
  this.#theme = getLightModeTheme();
99
126
  }
100
- this.#spinner.spinnerColor = this.#theme[Color.EMPHASISED];
101
- this.#spinner.messageColor = this.#theme[Color.PRIMARY];
102
- this.#progress.completeColor = this.#theme[Color.GREEN];
103
- this.#progress.remainingColor = this.#theme[Color.SECONDARY];
104
- this.#progress.labelColor = this.#theme[Color.SECONDARY];
105
- this.#progress.valueColor = this.#theme[Color.PRIMARY];
127
+ this.#spinner.spinnerColor = this.#theme[Color.EMPHASISED]!;
128
+ this.#spinner.messageColor = this.#theme[Color.PRIMARY]!;
129
+ this.#progress.completeColor = this.#theme[Color.GREEN]!;
130
+ this.#progress.remainingColor = this.#theme[Color.SECONDARY]!;
131
+ this.#progress.labelColor = this.#theme[Color.SECONDARY]!;
132
+ this.#progress.valueColor = this.#theme[Color.PRIMARY]!;
106
133
  }
107
134
 
108
135
  get darkMode(): boolean {
@@ -110,19 +137,19 @@ export default class DefaultPrinterService implements PrinterService {
110
137
  }
111
138
 
112
139
  public primary(message: string): string {
113
- return this.#styler.colorText(message, this.#theme[Color.PRIMARY]);
140
+ return this.#styler.colorText(message, this.#theme[Color.PRIMARY]!);
114
141
  }
115
142
 
116
143
  public secondary(message: string): string {
117
- return this.#styler.colorText(message, this.#theme[Color.SECONDARY]);
144
+ return this.#styler.colorText(message, this.#theme[Color.SECONDARY]!);
118
145
  }
119
146
 
120
147
  public emphasised(message: string): string {
121
- return this.#styler.colorText(message, this.#theme[Color.EMPHASISED]);
148
+ return this.#styler.colorText(message, this.#theme[Color.EMPHASISED]!);
122
149
  }
123
150
 
124
151
  public selected(message: string): string {
125
- return this.#styler.colorText(message, this.#theme[Color.SELECTED]);
152
+ return this.#styler.colorText(message, this.#theme[Color.SELECTED]!);
126
153
  }
127
154
 
128
155
  public italic(message: string): string {
@@ -130,52 +157,135 @@ export default class DefaultPrinterService implements PrinterService {
130
157
  }
131
158
 
132
159
  public yellow(message: string): string {
133
- return this.#styler.colorText(message, this.#theme[Color.YELLOW]);
160
+ return this.#styler.colorText(message, this.#theme[Color.YELLOW]!);
134
161
  }
135
162
 
136
163
  public orange(message: string): string {
137
- return this.#styler.colorText(message, this.#theme[Color.ORANGE]);
164
+ return this.#styler.colorText(message, this.#theme[Color.ORANGE]!);
138
165
  }
139
166
 
140
167
  public red(message: string): string {
141
- return this.#styler.colorText(message, this.#theme[Color.RED]);
168
+ return this.#styler.colorText(message, this.#theme[Color.RED]!);
142
169
  }
143
170
 
144
171
  public magenta(message: string): string {
145
- return this.#styler.colorText(message, this.#theme[Color.MAGENTA]);
172
+ return this.#styler.colorText(message, this.#theme[Color.MAGENTA]!);
146
173
  }
147
174
 
148
175
  public violet(message: string): string {
149
- return this.#styler.colorText(message, this.#theme[Color.VIOLET]);
176
+ return this.#styler.colorText(message, this.#theme[Color.VIOLET]!);
150
177
  }
151
178
 
152
179
  public blue(message: string): string {
153
- return this.#styler.colorText(message, this.#theme[Color.BLUE]);
180
+ return this.#styler.colorText(message, this.#theme[Color.BLUE]!);
154
181
  }
155
182
 
156
183
  public cyan(message: string): string {
157
- return this.#styler.colorText(message, this.#theme[Color.CYAN]);
184
+ return this.#styler.colorText(message, this.#theme[Color.CYAN]!);
158
185
  }
159
186
 
160
187
  public green(message: string): string {
161
- return this.#styler.colorText(message, this.#theme[Color.GREEN]);
188
+ return this.#styler.colorText(message, this.#theme[Color.GREEN]!);
162
189
  }
163
190
 
164
- public color(message: string, hexFormattedColor: string): string {
191
+ #parseHexColor(hexFormattedColor: string): number {
165
192
  if (
166
193
  (hexFormattedColor.length !== 7) ||
167
194
  (!hexFormattedColor.toLowerCase().startsWith("#"))
168
195
  ) {
169
196
  throw new Error(`Invalid color: ${hexFormattedColor}`);
170
197
  }
171
-
172
198
  const colorValue = parseInt(hexFormattedColor.slice(1), 16);
173
-
174
199
  if (isNaN(colorValue) || (colorValue < 0 || colorValue > 0xffffff)) {
175
200
  throw new Error(`Invalid color: ${hexFormattedColor}`);
176
201
  }
202
+ return colorValue;
203
+ }
204
+
205
+ public color(message: string, hexFormattedColor: string): string {
206
+ return this.#styler.colorText(
207
+ message,
208
+ this.#parseHexColor(hexFormattedColor),
209
+ );
210
+ }
211
+
212
+ public backgroundPrimary(message: string): string {
213
+ return this.#styler.backgroundColorText(
214
+ message,
215
+ this.#theme[Color.PRIMARY]!,
216
+ );
217
+ }
218
+
219
+ public backgroundSecondary(message: string): string {
220
+ return this.#styler.backgroundColorText(
221
+ message,
222
+ this.#theme[Color.SECONDARY]!,
223
+ );
224
+ }
225
+
226
+ public backgroundEmphasised(message: string): string {
227
+ return this.#styler.backgroundColorText(
228
+ message,
229
+ this.#theme[Color.EMPHASISED]!,
230
+ );
231
+ }
177
232
 
178
- return this.#styler.colorText(message, colorValue);
233
+ public backgroundSelected(message: string): string {
234
+ return this.#styler.backgroundColorText(
235
+ message,
236
+ this.#theme[Color.SELECTED]!,
237
+ );
238
+ }
239
+
240
+ public backgroundYellow(message: string): string {
241
+ return this.#styler.backgroundColorText(
242
+ message,
243
+ this.#theme[Color.YELLOW]!,
244
+ );
245
+ }
246
+
247
+ public backgroundOrange(message: string): string {
248
+ return this.#styler.backgroundColorText(
249
+ message,
250
+ this.#theme[Color.ORANGE]!,
251
+ );
252
+ }
253
+
254
+ public backgroundRed(message: string): string {
255
+ return this.#styler.backgroundColorText(message, this.#theme[Color.RED]!);
256
+ }
257
+
258
+ public backgroundMagenta(message: string): string {
259
+ return this.#styler.backgroundColorText(
260
+ message,
261
+ this.#theme[Color.MAGENTA]!,
262
+ );
263
+ }
264
+
265
+ public backgroundViolet(message: string): string {
266
+ return this.#styler.backgroundColorText(
267
+ message,
268
+ this.#theme[Color.VIOLET]!,
269
+ );
270
+ }
271
+
272
+ public backgroundBlue(message: string): string {
273
+ return this.#styler.backgroundColorText(message, this.#theme[Color.BLUE]!);
274
+ }
275
+
276
+ public backgroundCyan(message: string): string {
277
+ return this.#styler.backgroundColorText(message, this.#theme[Color.CYAN]!);
278
+ }
279
+
280
+ public backgroundGreen(message: string): string {
281
+ return this.#styler.backgroundColorText(message, this.#theme[Color.GREEN]!);
282
+ }
283
+
284
+ public backgroundColor(message: string, hexFormattedColor: string): string {
285
+ return this.#styler.backgroundColorText(
286
+ message,
287
+ this.#parseHexColor(hexFormattedColor),
288
+ );
179
289
  }
180
290
 
181
291
  public async debug(message: string, icon?: Icon): Promise<void> {
@@ -226,11 +336,17 @@ export default class DefaultPrinterService implements PrinterService {
226
336
  return this.#threshold;
227
337
  }
228
338
 
229
- public async showSpinner(message?: string): Promise<void> {
339
+ public async showSpinner(
340
+ message?: string,
341
+ style?: SpinnerStyle,
342
+ ): Promise<void> {
230
343
  await this.#progress.hideAll();
231
344
  if (this.#threshold > Level.INFO) {
232
345
  return;
233
346
  }
347
+ if (style != null) {
348
+ this.#spinner.spinnerStyle = style;
349
+ }
234
350
  await this.#spinner.show(message);
235
351
  }
236
352
 
@@ -243,11 +359,15 @@ export default class DefaultPrinterService implements PrinterService {
243
359
  message = "",
244
360
  total = 100,
245
361
  current = 0,
362
+ style?: ProgressStyle,
246
363
  ): Promise<number> {
247
364
  await this.#spinner.hide();
248
365
  if (this.#threshold > Level.INFO) {
249
366
  return -1;
250
367
  }
368
+ if (style != null) {
369
+ this.#progress.progressStyle = style;
370
+ }
251
371
 
252
372
  return this.#progress.add(units, message, total, current);
253
373
  }
@@ -4,15 +4,14 @@ import type {
4
4
  ServiceInfo,
5
5
  ServiceProvider,
6
6
  } from "../../api/service/ServiceProvider.ts";
7
- import PrinterService, {
8
- PRINTER_SERVICE_ID,
9
- } from "../../api/service/core/PrinterService.ts";
7
+ import type PrinterService from "../../api/service/core/PrinterService.ts";
8
+ import { PRINTER_SERVICE_ID } from "../../api/service/core/PrinterService.ts";
10
9
  import DarkModeCommand from "./command/DarkModeCommand.ts";
11
10
  import NoColorCommand from "./command/NoColorCommand.ts";
11
+ import NoHyperlinksCommand from "./command/NoHyperlinksCommand.ts";
12
12
  import type CLIConfig from "../../api/CLIConfig.ts";
13
- import ShutdownService, {
14
- SHUTDOWN_SERVICE_ID,
15
- } from "../../api/service/core/ShutdownService.ts";
13
+ import type ShutdownService from "../../api/service/core/ShutdownService.ts";
14
+ import { SHUTDOWN_SERVICE_ID } from "../../api/service/core/ShutdownService.ts";
16
15
 
17
16
  /**
18
17
  * Provides a {@link PrinterService}.
@@ -36,10 +35,11 @@ export default class PrinterServiceProvider implements ServiceProvider {
36
35
  this.printerService = printerService;
37
36
  }
38
37
 
39
- public provide(_cliConfig: CLIConfig): Promise<ServiceInfo> {
38
+ public getServiceInfo(_cliConfig: CLIConfig): Promise<ServiceInfo> {
40
39
  return Promise.resolve({
41
40
  service: this.printerService,
42
41
  commands: [
42
+ new NoHyperlinksCommand(this, this.servicePriority + 3),
43
43
  new DarkModeCommand(this, this.servicePriority + 2),
44
44
  new NoColorCommand(this, this.servicePriority + 1),
45
45
  new LogLevelCommand(this, this.servicePriority),
@@ -0,0 +1,40 @@
1
+ import type GlobalModifierCommand from "../../../api/command/GlobalModifierCommand.ts";
2
+ import type GlobalCommandArgument from "../../../api/argument/GlobalCommandArgument.ts";
3
+ import {
4
+ type ArgumentSingleValueType,
5
+ ArgumentValueTypeName,
6
+ } from "../../../api/argument/ArgumentValueTypes.ts";
7
+ import type Context from "../../../api/Context.ts";
8
+ import type PrinterServiceProvider from "../PrinterServiceProvider.ts";
9
+
10
+ export default class NoHyperlinksCommand implements GlobalModifierCommand {
11
+ readonly name = "no-hyperlinks";
12
+ readonly description = "Disable hyperlinks for output";
13
+ readonly enableConfiguration = true;
14
+ readonly argument: GlobalCommandArgument = {
15
+ type: ArgumentValueTypeName.BOOLEAN,
16
+ defaultValue: false,
17
+ configurationKey: "NO_HYPERLINKS",
18
+ };
19
+ readonly executePriority: number;
20
+
21
+ readonly #printerServiceProvider: PrinterServiceProvider;
22
+
23
+ public constructor(
24
+ printerServiceProvider: PrinterServiceProvider,
25
+ executePriority: number,
26
+ ) {
27
+ this.#printerServiceProvider = printerServiceProvider;
28
+ this.executePriority = executePriority;
29
+ }
30
+
31
+ public execute(
32
+ _context: Context,
33
+ argumentValue: ArgumentSingleValueType,
34
+ ): Promise<void> {
35
+ this.#printerServiceProvider.printerService!.hyperlinksEnabled =
36
+ !argumentValue as boolean;
37
+
38
+ return Promise.resolve();
39
+ }
40
+ }
@@ -1,8 +1,19 @@
1
- import Styler from "./Styler.ts";
2
- import type Terminal from "./Terminal.ts";
1
+ import type Styler from "../../../terminal/Styler.ts";
2
+ import type Terminal from "../../../terminal/Terminal.ts";
3
3
 
4
4
  const RATE_SMOOTHING_FACTOR = 0.005;
5
5
 
6
+ export { ProgressStyle } from "../../../api/service/core/PrinterService.ts";
7
+ import { ProgressStyle } from "../../../api/service/core/PrinterService.ts";
8
+
9
+ const STYLE_CHARS: Record<
10
+ ProgressStyle,
11
+ { complete: string; remaining: string }
12
+ > = {
13
+ [ProgressStyle.STROKE]: { complete: "=", remaining: "-" },
14
+ [ProgressStyle.FILL]: { complete: "▰", remaining: "▱" },
15
+ };
16
+
6
17
  interface Bar {
7
18
  name: string;
8
19
  current: number;
@@ -26,6 +37,7 @@ export default class Progress {
26
37
  #remColor = 0x585858;
27
38
  #labColor = 0x585858;
28
39
  #valColor = 0x808080;
40
+ #style: ProgressStyle = ProgressStyle.STROKE;
29
41
 
30
42
  public constructor(terminal: Terminal, styler: Styler) {
31
43
  this.#terminal = terminal;
@@ -282,12 +294,13 @@ export default class Progress {
282
294
  const completeLength = Math.floor(
283
295
  totalLength * barInfo.bar.current / barInfo.bar.total,
284
296
  );
297
+ const chars = STYLE_CHARS[this.#style];
285
298
  const complete = this.#styler.colorText(
286
- "=".repeat(completeLength),
299
+ chars.complete.repeat(completeLength),
287
300
  this.#comColor,
288
301
  );
289
302
  const remaining = this.#styler.colorText(
290
- "-".repeat(totalLength - completeLength),
303
+ chars.remaining.repeat(totalLength - completeLength),
291
304
  this.#remColor,
292
305
  );
293
306
 
@@ -331,6 +344,10 @@ export default class Progress {
331
344
  this.#valColor = color;
332
345
  }
333
346
 
347
+ set progressStyle(style: ProgressStyle) {
348
+ this.#style = style;
349
+ }
350
+
334
351
  #formatTime(millis: number): string {
335
352
  let sec = millis / 1000;
336
353
  if (sec < 60) {
@@ -1,7 +1,7 @@
1
- import Styler from "./Styler.ts";
2
- import type Terminal from "./Terminal.ts";
1
+ import type Styler from "../../../terminal/Styler.ts";
2
+ import type Terminal from "../../../terminal/Terminal.ts";
3
3
 
4
- const FRAMES = [
4
+ const BOX_FRAMES = [
5
5
  "⠋",
6
6
  "⠙",
7
7
  "⠹",
@@ -14,6 +14,20 @@ const FRAMES = [
14
14
  "⠏",
15
15
  ];
16
16
 
17
+ const STAR_FRAMES = [
18
+ "★",
19
+ "✶",
20
+ "✷",
21
+ "✹",
22
+ "✷",
23
+ "✶",
24
+ "★",
25
+ "✦",
26
+ ];
27
+
28
+ export { SpinnerStyle } from "../../../api/service/core/PrinterService.ts";
29
+ import { SpinnerStyle } from "../../../api/service/core/PrinterService.ts";
30
+
17
31
  export default class Spinner {
18
32
  #isShown = false;
19
33
  #message: string | undefined;
@@ -21,6 +35,7 @@ export default class Spinner {
21
35
  #timer: Timer | undefined;
22
36
  #spinColor = 0x8a8a8a;
23
37
  #msgColor = 0x808080;
38
+ #style: SpinnerStyle = SpinnerStyle.BOX;
24
39
  readonly #terminal: Terminal;
25
40
  readonly #styler: Styler;
26
41
 
@@ -29,23 +44,28 @@ export default class Spinner {
29
44
  this.#styler = styler;
30
45
  }
31
46
 
47
+ get #frames(): string[] {
48
+ return this.#style === SpinnerStyle.STAR ? STAR_FRAMES : BOX_FRAMES;
49
+ }
50
+
32
51
  async #nextFrame(): Promise<void> {
33
52
  if (!this.#isShown) {
34
53
  return;
35
54
  }
55
+ const frames = this.#frames;
36
56
  await this.#terminal.clearLine();
37
57
  if (this.#message) {
38
58
  await this.#terminal.write(
39
- `${this.#styler.colorText(FRAMES[this.#frameIndex], this.#spinColor)} ${
40
- this.#styler.colorText(this.#message!, this.#msgColor)
41
- }`,
59
+ `${
60
+ this.#styler.colorText(frames[this.#frameIndex]!, this.#spinColor)
61
+ } ${this.#styler.colorText(this.#message!, this.#msgColor)}`,
42
62
  );
43
63
  } else {
44
64
  await this.#terminal.write(
45
- this.#styler.colorText(FRAMES[this.#frameIndex], this.#spinColor),
65
+ this.#styler.colorText(frames[this.#frameIndex]!, this.#spinColor),
46
66
  );
47
67
  }
48
- this.#frameIndex = (this.#frameIndex + 1) % FRAMES.length;
68
+ this.#frameIndex = (this.#frameIndex + 1) % frames.length;
49
69
  }
50
70
 
51
71
  public async show(message?: string): Promise<void> {
@@ -97,4 +117,8 @@ export default class Spinner {
97
117
  set messageColor(color: number) {
98
118
  this.#msgColor = color;
99
119
  }
120
+
121
+ set spinnerStyle(style: SpinnerStyle) {
122
+ this.#style = style;
123
+ }
100
124
  }
@@ -1,4 +1,4 @@
1
- import { Color } from "./Color";
1
+ import { Color } from "./Color.ts";
2
2
 
3
3
  const commonTheme: Array<number> = [];
4
4