sitevision-cli 0.6.0-beta.1 → 0.6.0-beta.2

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/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import { checkForUpdate } from './utils/version-check.js';
12
12
  import { isFirstRun, markFirstRunComplete, getLastSeenVersion, setLastSeenVersion, } from './utils/config.js';
13
13
  import { WelcomeScreen } from './components/WelcomeScreen.js';
14
14
  import { AnimatedLogo } from './components/AnimatedLogo.js';
15
- import { printBranding, BIG_LOGO_WIDTH } from './utils/branding.js';
15
+ import { printBranding, BIG_LOGO, BIG_LOGO_WIDTH, SMALL_LOGO, SMALL_LOGO_WIDTH, } from './utils/branding.js';
16
16
  const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
17
17
  const cli = meow(`
18
18
  Usage
@@ -85,10 +85,19 @@ function printMasthead(version) {
85
85
  `${spaces(gap)}${DIM}${right}${RESET}${spaces(padding)}${CYAN}│${RESET}`);
86
86
  console.log(`${CYAN}╰${border}╯${RESET}`);
87
87
  }
88
+ // Pick the widest wordmark that fits the terminal, or undefined if even the
89
+ // compact one would wrap (caller then falls back to the static masthead).
90
+ function pickIntroArt(columns) {
91
+ if (columns >= BIG_LOGO_WIDTH)
92
+ return BIG_LOGO;
93
+ if (columns >= SMALL_LOGO_WIDTH)
94
+ return SMALL_LOGO;
95
+ return undefined;
96
+ }
88
97
  // Play the one-shot animated wordmark and resolve once it finishes.
89
- async function playIntro() {
98
+ async function playIntro(art) {
90
99
  await new Promise(resolve => {
91
- const app = render(_jsx(AnimatedLogo, { onDone: () => app.unmount() }));
100
+ const app = render(_jsx(AnimatedLogo, { art: art, onDone: () => app.unmount() }));
92
101
  app.waitUntilExit().then(() => resolve(), () => resolve());
93
102
  });
94
103
  }
@@ -104,19 +113,18 @@ async function main() {
104
113
  const lastSeen = getLastSeenVersion();
105
114
  const isUpdate = !firstRun && lastSeen !== undefined && lastSeen !== pkg.version;
106
115
  // On the plain interactive `svc` (no command), play the animated wordmark
107
- // instead of the static masthead — but only when stdout is wide enough for
108
- // the art and stdin is a TTY (so it doesn't run in CI / piped input).
109
- const wantsIntro = !firstRun &&
110
- !isUpdate &&
111
- !commandName &&
112
- Boolean(process.stdin.isTTY) &&
113
- (process.stdout.columns ?? 0) >= BIG_LOGO_WIDTH;
116
+ // instead of the static masthead — sized to the terminal. Only on a TTY so it
117
+ // doesn't run in CI / piped input.
118
+ const introEligible = !firstRun && !isUpdate && !commandName && Boolean(process.stdin.isTTY);
119
+ const introArt = introEligible
120
+ ? pickIntroArt(process.stdout.columns ?? 0)
121
+ : undefined;
114
122
  if (!firstRun) {
115
123
  if (isUpdate) {
116
124
  printBranding();
117
125
  console.log(`\x1b[32m\n ✨ Updated to v${pkg.version}\x1b[0m \x1b[2m(from v${lastSeen})\x1b[0m\n`);
118
126
  }
119
- else if (!wantsIntro) {
127
+ else if (!introArt) {
120
128
  printMasthead(pkg.version);
121
129
  }
122
130
  // Record the current version so the banner shows once per upgrade.
@@ -152,8 +160,8 @@ async function main() {
152
160
  // If no command, show interactive menu (with the animated intro first when
153
161
  // the terminal can fit it).
154
162
  if (!commandName) {
155
- if (wantsIntro) {
156
- await playIntro();
163
+ if (introArt) {
164
+ await playIntro(introArt);
157
165
  }
158
166
  render(_jsx(App, { project: project }));
159
167
  return;
@@ -1,9 +1,10 @@
1
1
  interface Props {
2
+ art?: string[];
2
3
  onDone: () => void;
3
4
  }
4
5
  /**
5
6
  * One-shot startup flair: wipes the big wordmark in left-to-right while a
6
7
  * rainbow gradient drifts across it, then calls `onDone`. Purely decorative.
7
8
  */
8
- export declare function AnimatedLogo({ onDone }: Props): import("react").JSX.Element;
9
+ export declare function AnimatedLogo({ art, onDone }: Props): import("react").JSX.Element;
9
10
  export {};
@@ -1,15 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Fragment, useEffect, useRef, useState } from 'react';
3
3
  import { Box, Text } from 'ink';
4
- import { AUTHOR, BIG_LOGO, BIG_LOGO_WIDTH } from '../utils/branding.js';
4
+ import { AUTHOR, BIG_LOGO } from '../utils/branding.js';
5
5
  const FRAME_MS = 45;
6
6
  const SWEEP_COLS_PER_FRAME = 7; // how fast the wipe edge moves left → right
7
7
  const BAND = 18; // width of the rainbow zone trailing the sweep edge
8
8
  const HOLD_FRAMES = 6; // frames to hold the fully-settled logo before finishing
9
- // The sweep edge runs past the right side by BAND so the rainbow zone trails
10
- // all the way off, leaving every character settled to the terminal default.
11
- const SWEEP_FRAMES = Math.ceil((BIG_LOGO_WIDTH + BAND) / SWEEP_COLS_PER_FRAME);
12
- const TOTAL_FRAMES = SWEEP_FRAMES + HOLD_FRAMES;
9
+ // Frames to fully reveal and settle art `width` columns wide. The sweep edge
10
+ // runs past the right side by BAND so the rainbow zone trails all the way off,
11
+ // leaving every character settled to the terminal default.
12
+ function framesFor(width) {
13
+ return Math.ceil((width + BAND) / SWEEP_COLS_PER_FRAME) + HOLD_FRAMES;
14
+ }
13
15
  // Convert HSL (h in degrees, s/l in 0..1) to a #rrggbb string for ink/chalk.
14
16
  function hslToHex(h, s, l) {
15
17
  const hue = h / 360;
@@ -59,9 +61,11 @@ function buildSpans(line, y, frame, edge) {
59
61
  * One-shot startup flair: wipes the big wordmark in left-to-right while a
60
62
  * rainbow gradient drifts across it, then calls `onDone`. Purely decorative.
61
63
  */
62
- export function AnimatedLogo({ onDone }) {
64
+ export function AnimatedLogo({ art = BIG_LOGO, onDone }) {
63
65
  const [frame, setFrame] = useState(0);
64
66
  const intervalRef = useRef(undefined);
67
+ const width = Math.max(...art.map(line => [...line].length));
68
+ const total = framesFor(width);
65
69
  useEffect(() => {
66
70
  intervalRef.current = setInterval(() => {
67
71
  setFrame(current => current + 1);
@@ -73,11 +77,11 @@ export function AnimatedLogo({ onDone }) {
73
77
  // Stop the loop and notify the parent exactly once, when the last frame is
74
78
  // reached. Kept out of the setFrame updater so that updater stays pure.
75
79
  useEffect(() => {
76
- if (frame >= TOTAL_FRAMES) {
80
+ if (frame >= total) {
77
81
  clearInterval(intervalRef.current);
78
82
  onDone();
79
83
  }
80
- }, [frame, onDone]);
84
+ }, [frame, total, onDone]);
81
85
  const edge = (frame + 1) * SWEEP_COLS_PER_FRAME;
82
- return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [BIG_LOGO.map((line, y) => (_jsx(Text, { children: buildSpans(line, y, frame, edge).map((span, index) => (_jsx(Fragment, { children: span.color ? (_jsx(Text, { color: span.color, children: span.text })) : (_jsx(Text, { children: span.text })) }, index))) }, y))), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: ' a tool by ' }), _jsx(Text, { bold: true, children: AUTHOR })] })] }));
86
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [art.map((line, y) => (_jsx(Text, { children: buildSpans(line, y, frame, edge).map((span, index) => (_jsx(Fragment, { children: span.color ? (_jsx(Text, { color: span.color, children: span.text })) : (_jsx(Text, { children: span.text })) }, index))) }, y))), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: ' a tool by ' }), _jsx(Text, { bold: true, children: AUTHOR })] })] }));
83
87
  }
@@ -14,6 +14,13 @@ export declare const AUTHOR = "Rasmus S\u00F6derstr\u00F6m";
14
14
  export declare const BIG_LOGO: string[];
15
15
  /** Display width of the widest BIG_LOGO line. */
16
16
  export declare const BIG_LOGO_WIDTH: number;
17
+ /**
18
+ * Compact "Sitevision CLI" wordmark, animated on terminals too narrow for
19
+ * BIG_LOGO. ~72 columns wide.
20
+ */
21
+ export declare const SMALL_LOGO: string[];
22
+ /** Display width of the widest SMALL_LOGO line. */
23
+ export declare const SMALL_LOGO_WIDTH: number;
17
24
  /**
18
25
  * Print the logo + author line straight to stdout (non-interactive), mirroring
19
26
  * how the masthead is printed. Used for the update banner.
@@ -35,6 +35,17 @@ export const BIG_LOGO = [
35
35
  ];
36
36
  /** Display width of the widest BIG_LOGO line. */
37
37
  export const BIG_LOGO_WIDTH = Math.max(...BIG_LOGO.map(line => line.length));
38
+ /**
39
+ * Compact "Sitevision CLI" wordmark, animated on terminals too narrow for
40
+ * BIG_LOGO. ~72 columns wide.
41
+ */
42
+ export const SMALL_LOGO = [
43
+ '▄█████ ▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄ ▄▄ ▄▄ ▄▄ ▄▄▄▄ ▄▄ ▄▄▄ ▄▄ ▄▄ ▄█████ ██ ██ ',
44
+ '▀▀▀▄▄▄ ██ ██ ██▄▄ ██▄██ ██ ███▄▄ ██ ██▀██ ███▄██ ██ ██ ██ ',
45
+ '█████▀ ██ ██ ██▄▄▄ ▀█▀ ██ ▄▄██▀ ██ ▀███▀ ██ ▀██ ▀█████ ██████ ██ ',
46
+ ];
47
+ /** Display width of the widest SMALL_LOGO line. */
48
+ export const SMALL_LOGO_WIDTH = Math.max(...SMALL_LOGO.map(line => [...line].length));
38
49
  /**
39
50
  * Print the logo + author line straight to stdout (non-interactive), mirroring
40
51
  * how the masthead is printed. Used for the update banner.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sitevision-cli",
3
- "version": "0.6.0-beta.1",
3
+ "version": "0.6.0-beta.2",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "svc": "dist/cli.js"