Skip to content

Commit 307d50e

Browse files
committed
fix(preset-cli): Added banner.override option to provide custom titles
1 parent a9a2764 commit 307d50e

13 files changed

Lines changed: 381 additions & 331 deletions

File tree

devenv.lock

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@
5555
},
5656
"nixpkgs-unstable": {
5757
"locked": {
58-
"lastModified": 1780930886,
59-
"narHash": "sha256-rppURzHviaQN131F+nLiLdGfcb0uCd9gGP0E5+iw9MI=",
58+
"lastModified": 1781141223,
59+
"narHash": "sha256-Eye4UQJjC4TLobclolFCMl6MrjgiF6Bk1cOI5x8SH00=",
6060
"owner": "NixOS",
6161
"repo": "nixpkgs",
62-
"rev": "8c3cede7ddc26bd659d2d383b5610efbd2c7a16e",
62+
"rev": "b503dde361500433ca25a32e8f4d218bf58fb659",
6363
"type": "github"
6464
},
6565
"original": {
@@ -81,15 +81,16 @@
8181
"storm-ops": {
8282
"flake": false,
8383
"locked": {
84-
"lastModified": 1781181255,
85-
"narHash": "sha256-a2LbgoxKL3fmXWtpNZmKqQ75O2L9ucxNbXXNivfrKrw=",
84+
"lastModified": 1781200987,
85+
"narHash": "sha256-Zj5MmrvZ3kLOubVm/ezGa05xrllwa9JW3HW9qw/7h0U=",
8686
"owner": "storm-software",
8787
"repo": "storm-ops",
88-
"rev": "af8d3637180172149f50f593cb72bd5865e530a6",
88+
"rev": "60e2fdb43a4fdb30ced3c45900c1c2b6dfdab661",
8989
"type": "github"
9090
},
9191
"original": {
9292
"owner": "storm-software",
93+
"ref": "main",
9394
"repo": "storm-ops",
9495
"type": "github"
9596
}

devenv.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# yaml-language-server: $schema=https://devenv.sh/devenv.schema.json
22
inputs:
33
storm-ops:
4-
url: github:storm-software/storm-ops
4+
url: github:storm-software/storm-ops?ref=main
55
flake: false
66
nixpkgs:
77
url: github:cachix/devenv-nixpkgs/rolling

packages/core/package.json

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
"version": "0.17.19",
44
"private": false,
55
"description": "A package containing the core Shell Shock functionality used to build and manage a command-line application.",
6-
"keywords": ["shell-shock", "powerlines", "storm-software"],
6+
"keywords": [
7+
"shell-shock",
8+
"powerlines",
9+
"storm-software"
10+
],
711
"homepage": "https://stormsoftware.com",
812
"bugs": {
913
"url": "https://stormsoftware.com/support",
@@ -362,27 +366,15 @@
362366
"types": "./dist/types/env.d.mts",
363367
"default": "./dist/types/env.mjs"
364368
}
365-
},
366-
"./types/options": {
367-
"require": {
368-
"types": "./dist/types/options.d.cts",
369-
"default": "./dist/types/options.cjs"
370-
},
371-
"import": {
372-
"types": "./dist/types/options.d.mts",
373-
"default": "./dist/types/options.mjs"
374-
},
375-
"default": {
376-
"types": "./dist/types/options.d.mts",
377-
"default": "./dist/types/options.mjs"
378-
}
379369
}
380370
},
381371
"main": "./dist/index.cjs",
382372
"module": "./dist/index.mjs",
383373
"types": "./dist/index.d.cts",
384374
"typings": "dist/index.d.mts",
385-
"files": ["dist"],
375+
"files": [
376+
"dist"
377+
],
386378
"dependencies": {
387379
"@alloy-js/core": "catalog:",
388380
"@alloy-js/markdown": "catalog:",

packages/core/src/plugin.tsx

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ import {
6464
resolveCommandName,
6565
resolveCommandPath
6666
} from "./helpers/paths";
67-
import { writeCommandsPersistence } from "./helpers/persistence";
67+
import {
68+
readCommandsPersistence,
69+
writeCommandsPersistence
70+
} from "./helpers/persistence";
6871
import {
6972
formatBinaryPath,
7073
updatePackageJsonBinary
@@ -196,9 +199,7 @@ export const plugin = <TContext extends Context = Context>(
196199
alias: [],
197200
virtual: false
198201
})
199-
);
200-
201-
this.globalOptions = this.globalOptions.map(option => ({
202+
).map(option => ({
202203
...option,
203204
name: camelCase(option.name),
204205
alias: option.alias ?? [],
@@ -498,17 +499,17 @@ export const plugin = <TContext extends Context = Context>(
498499
}
499500

500501
const env = getProperties(this.env.config);
501-
for (const option of Object.values(this.globalOptions)
502-
.filter(option => Boolean(option.env))
503-
.filter(option => isSetString(option.env) && !env[option.env])) {
504-
addProperty(this.env.config, option.env as string, {
505-
...option,
506-
name: option.env as string,
507-
alias: option.alias
508-
.filter(alias => alias.length > 1)
509-
.map(alias => constantCase(alias))
502+
this.globalOptions
503+
.filter(option => isSetString(option.env) && !env[option.env])
504+
.forEach(option => {
505+
addProperty(this.env.config, option.env as string, {
506+
...option,
507+
name: option.env as string,
508+
alias: option.alias
509+
.filter(alias => alias.length > 1)
510+
.map(alias => constantCase(alias))
511+
});
510512
});
511-
}
512513

513514
await writeEnv(this);
514515
}
@@ -533,6 +534,7 @@ export const plugin = <TContext extends Context = Context>(
533534
);
534535

535536
await extractEnv(this);
537+
await readCommandsPersistence(this);
536538
} else {
537539
for (const input of this.inputs.filter(
538540
input =>

packages/plugin-banner/src/components/banner-function-declaration.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,11 @@ export function BannerFunctionBodyDeclaration(
151151
return (
152152
<>
153153
{code`
154-
if (useMeta().get("banner") || hasFlag("no-banner") || hasFlag("hide-banner") || isMinimal) {
154+
if (useMeta().get("is-banner-displayed") || hasFlag("no-banner") || hasFlag("hide-banner") || isMinimal) {
155155
return;
156156
}
157157
158-
useMeta().set("banner", true); `}
158+
useMeta().set("is-banner-displayed", true); `}
159159
<Spacing />
160160
<Show when={insertNewlineBeforeBanner}>{code`writeLine(""); `}</Show>
161161
<Spacing />

packages/plugin-help/src/components/help-builtin.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,13 @@ export function HelpBuiltin(props: HelpBuiltinProps) {
153153
command.segments && command.segments.length > 0
154154
? ` ${command.segments.join(" ")}`
155155
: ""
156-
} <specific command> --help")}) or the help command with the specific command as arguments (via: \${inlineCode("${getAppBin(
156+
} <command> --help")}) or the help command with the specific command as arguments (via: \${inlineCode("${getAppBin(
157157
context
158158
)}${
159159
command.segments && command.segments.length > 0
160160
? ` ${command.segments.join(" ")}`
161161
: ""
162-
} help <specific command>")}) will provide additional information that is specific to that command.\`);
162+
} help <command>")}) will provide additional information that is specific to that command.\`);
163163
writeLine("");`}
164164
</Show>
165165
</FunctionDeclaration>

packages/plugin-update/src/types/env.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
// eslint-disable-next-line ts/consistent-type-imports
2020
import { ShellShockEnv } from "@shell-shock/core/types/env";
2121

22-
export type ShellShockUpdateEnv = ShellShockEnv & {
22+
export interface ShellShockUpdateEnv extends ShellShockEnv {
2323
/**
2424
* An environment variable that can be set to skip the version check when determining if a check for updates is required. If this variable is set to any value, the `isCheckForUpdatesRequired` function will return `false`, indicating that a check for updates is not required. This can be useful in CI environments or other non-interactive contexts where you want to avoid performing a version check, which may involve file system operations or network requests. By setting this environment variable, you can ensure that the update process proceeds without checking for updates, which can help speed up the process in certain scenarios.
2525
*/
2626
SKIP_UPDATE_CHECK?: boolean;
27-
};
27+
}

packages/preset-cli/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,9 @@
177177
"module": "./dist/index.mjs",
178178
"types": "./dist/index.d.cts",
179179
"typings": "dist/index.d.mts",
180-
"files": ["dist/**/*"],
180+
"files": [
181+
"dist"
182+
],
181183
"dependencies": {
182184
"@alloy-js/core": "catalog:",
183185
"@alloy-js/typescript": "catalog:",
@@ -201,7 +203,8 @@
201203
"@stryke/types": "catalog:",
202204
"defu": "catalog:",
203205
"figlet": "catalog:",
204-
"powerlines": "catalog:"
206+
"powerlines": "catalog:",
207+
"strip-ansi": "catalog:"
205208
},
206209
"devDependencies": {
207210
"@stryke/types": "catalog:",

packages/preset-cli/src/components/banner-builtin.tsx

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
------------------------------------------------------------------- */
1818

1919
import { code, computed, Show, splitProps } from "@alloy-js/core";
20+
import { VarDeclaration } from "@alloy-js/typescript";
2021
import { Spacing } from "@powerlines/plugin-alloy/core/components/spacing";
2122
import { usePowerlines } from "@powerlines/plugin-alloy/core/contexts/context";
2223
import type { BuiltinFileProps } from "@powerlines/plugin-alloy/typescript/components/builtin-file";
@@ -33,6 +34,7 @@ import { isSetObject } from "@stryke/type-checks/is-set-object";
3334
import { isSetString } from "@stryke/type-checks/is-set-string";
3435
import defu from "defu";
3536
import figlet from "figlet";
37+
import stripAnsi from "strip-ansi";
3638
import type { CLIPresetContext } from "../types/plugin";
3739

3840
/**
@@ -66,25 +68,6 @@ export function BannerFunctionBodyDeclaration(
6668
() => command?.description || getAppDescription(context)
6769
);
6870

69-
const titleLines = computed(() => {
70-
const result = figlet.textSync(
71-
isSetString(context.config.banner)
72-
? context.config.banner
73-
: isSetObject(context.config.banner) &&
74-
isSetString(context.config.banner.text)
75-
? context.config.banner.text
76-
: getAppTitle(context, true),
77-
defu(isSetObject(context.config.banner) ? context.config.banner : {}, {
78-
font: "ANSI Compact"
79-
})
80-
);
81-
if (!result) {
82-
return [`${getAppTitle(context, true)} Command-Line Interface`];
83-
}
84-
85-
return result.split("\n").filter(line => line.trim().length > 0);
86-
});
87-
8871
const bannerPadding = computed(
8972
() =>
9073
Math.max(theme.padding.app, 0) * 2 +
@@ -103,15 +86,16 @@ export function BannerFunctionBodyDeclaration(
10386
variant={variant}
10487
consoleFnName={consoleFnName}
10588
command={
106-
{ ...command, title: command?.path ? command.title : "" } as CommandTree
89+
{
90+
...command,
91+
title: command?.path ? command.title : ""
92+
} as CommandTree
10793
}
10894
insertNewlineAfterDescription>
109-
{code`const titleLines = [${titleLines.value
110-
.map(line => JSON.stringify(line.trim()))
111-
.join(", ")}];
112-
const title = Math.max(...titleLines.map(line => stripAnsi(line).length)) > Math.max(getTerminalSize().columns + ${
95+
{code`
96+
const title = Math.max(...TITLE_LINES.map(line => stripAnsi(line).length)) > Math.max(getTerminalSize().columns + ${
11397
totalPadding.value
114-
}, 20) ? "${title.value}" : \`\\n\${titleLines.join("\\n")}\\n\`;
98+
}, 20) ? "${title.value}" : \`\\n\${TITLE_LINES\.join("\\n")}\\n\`;
11599
116100
splitText(title,
117101
Math.max(getTerminalSize().columns - ${totalPadding.value}, 20)
@@ -149,8 +133,62 @@ export function BannerBuiltin(props: BannerBuiltinProps) {
149133
"children"
150134
]);
151135

136+
const context = usePowerlines<CLIPresetContext>();
137+
138+
const titleLines = computed(() => {
139+
const result = figlet.textSync(
140+
isSetObject(context.config.banner) &&
141+
isSetString(context.config.banner.override)
142+
? context.config.banner.override
143+
: isSetString(context.config.banner)
144+
? context.config.banner
145+
: isSetObject(context.config.banner) &&
146+
isSetString(context.config.banner.text)
147+
? context.config.banner.text
148+
: getAppTitle(context, true),
149+
defu(isSetObject(context.config.banner) ? context.config.banner : {}, {
150+
font: "ANSI Compact"
151+
})
152+
);
153+
if (!result) {
154+
return [`${getAppTitle(context, true)} Command-Line Interface`];
155+
}
156+
157+
const lines = result.trim().split("\n");
158+
const maxLength = Math.max(...lines.map(line => stripAnsi(line).length));
159+
const paddedLines = lines.map(line => {
160+
const paddingNeeded = maxLength - stripAnsi(line).length;
161+
const leftPadding = Math.floor(paddingNeeded / 2);
162+
const rightPadding = Math.ceil(paddingNeeded / 2);
163+
164+
return " ".repeat(leftPadding) + line + " ".repeat(rightPadding);
165+
});
166+
167+
return paddedLines;
168+
});
169+
152170
return (
153-
<BaseBannerBuiltin {...rest} command={command}>
171+
<BaseBannerBuiltin
172+
{...rest}
173+
builtinImports={
174+
command?.path
175+
? defu(rest.builtinImports ?? {}, {
176+
banner: ["TITLE_LINES"]
177+
})
178+
: rest.builtinImports
179+
}
180+
command={command}>
181+
<Show when={!command?.path}>
182+
<VarDeclaration
183+
const
184+
export
185+
name="TITLE_LINES"
186+
initializer={code` [${titleLines.value
187+
.map(line => JSON.stringify(line))
188+
.join(", ")}];`}
189+
/>
190+
<Spacing />
191+
</Show>
154192
<BannerFunctionDeclarationWrapper command={command}>
155193
<BannerFunctionBodyDeclaration command={command} />
156194
<Spacing />

packages/preset-cli/src/types/plugin.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ export interface CLIPresetBannerOption extends FigletOptions {
101101
text?: string;
102102
}
103103

104+
export interface CLIPresetBannerOverrideOption {
105+
/**
106+
* The text to display in the banner header verbatim, without applying figlet styling. If this option is set, it will take precedence over the `banner` option and any figlet styling will be ignored. This can be useful if you want to display a custom banner that does not conform to the constraints of figlet fonts, or if you want to include special characters or formatting that may not be supported by figlet. When this option is set, the specified text will be displayed exactly as provided in the banner header, without any modifications or styling applied.
107+
*/
108+
override: string;
109+
}
110+
104111
export type CLIPresetOptions = Omit<ScriptPresetOptions, "globalOptions"> &
105112
HelpPluginOptions &
106113
PromptsPluginOptions &
@@ -159,7 +166,7 @@ export type CLIPresetOptions = Omit<ScriptPresetOptions, "globalOptions"> &
159166
* @remarks
160167
* This option can be set to a string to specify the banner text directly, or an object with `figlet` options to customize the appearance of the banner. If an object is provided, the `text` property can be used to specify the banner text, and other properties can be used to customize the font, alignment, and other aspects of the banner's appearance.
161168
*/
162-
banner?: string | CLIPresetBannerOption;
169+
banner?: string | CLIPresetBannerOption | CLIPresetBannerOverrideOption;
163170

164171
/**
165172
* Configuration options for the `completions` plugin. This field allows you to customize the behavior of the `completions` plugin, which provides commands for generating shell completion scripts for the CLI application. You can specify which shells to generate completions for, and other related settings.

0 commit comments

Comments
 (0)