sitevision-cli 0.6.0-beta.1 → 1.0.0-beta.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/dist/app.js +44 -8
- package/dist/cli.js +25 -13
- package/dist/commands/dev.d.ts +3 -2
- package/dist/commands/dev.js +17 -3
- package/dist/commands/index.js +2 -0
- package/dist/commands/watch.d.ts +2 -0
- package/dist/commands/watch.js +44 -0
- package/dist/components/AnimatedLogo.d.ts +2 -1
- package/dist/components/AnimatedLogo.js +13 -9
- package/dist/components/MainMenu.js +10 -0
- package/dist/utils/branding.d.ts +7 -0
- package/dist/utils/branding.js +11 -0
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -24,6 +24,15 @@ export default function App({ project }) {
|
|
|
24
24
|
// slow and this component re-renders frequently).
|
|
25
25
|
const signingUsername = project.devProperties?.signingUsername;
|
|
26
26
|
const storedSigningPassword = useMemo(() => (signingUsername ? getSigningPassword(signingUsername) : null), [signingUsername]);
|
|
27
|
+
// Map a signing-capable command to the screen it lands on once the signing
|
|
28
|
+
// password is resolved.
|
|
29
|
+
const signedDestination = (command = currentCommand) => {
|
|
30
|
+
if (command === 'dev-signed')
|
|
31
|
+
return 'dev';
|
|
32
|
+
if (command === 'watch-signed')
|
|
33
|
+
return 'watch';
|
|
34
|
+
return 'sign';
|
|
35
|
+
};
|
|
27
36
|
// Decide the next step once a signing password is needed: proceed if we already
|
|
28
37
|
// have one this session, offer the keychain choice if one is saved, otherwise
|
|
29
38
|
// prompt for manual entry.
|
|
@@ -34,7 +43,7 @@ export default function App({ project }) {
|
|
|
34
43
|
isRetry: signingRetry,
|
|
35
44
|
});
|
|
36
45
|
if (step === 'proceed') {
|
|
37
|
-
setState(command
|
|
46
|
+
setState(signedDestination(command));
|
|
38
47
|
}
|
|
39
48
|
else if (step === 'choice') {
|
|
40
49
|
setState('signing-password-choice');
|
|
@@ -78,7 +87,7 @@ export default function App({ project }) {
|
|
|
78
87
|
if (storedSigningPassword) {
|
|
79
88
|
setSigningPassword(storedSigningPassword);
|
|
80
89
|
}
|
|
81
|
-
setState(
|
|
90
|
+
setState(signedDestination());
|
|
82
91
|
};
|
|
83
92
|
const handleEnterNewSigning = () => {
|
|
84
93
|
setState('signing-password-input');
|
|
@@ -88,12 +97,7 @@ export default function App({ project }) {
|
|
|
88
97
|
if (remember && project.devProperties?.signingUsername && password) {
|
|
89
98
|
saveSigningPassword(project.devProperties.signingUsername, password);
|
|
90
99
|
}
|
|
91
|
-
|
|
92
|
-
setState('dev');
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
setState('sign');
|
|
96
|
-
}
|
|
100
|
+
setState(signedDestination());
|
|
97
101
|
};
|
|
98
102
|
const handleCommandSelect = (command) => {
|
|
99
103
|
setCurrentCommand(command);
|
|
@@ -135,6 +139,21 @@ export default function App({ project }) {
|
|
|
135
139
|
routeToSigningStep(command);
|
|
136
140
|
}
|
|
137
141
|
break;
|
|
142
|
+
case 'watch':
|
|
143
|
+
// Build/sign-only: no deploy and no signing, so no credentials needed.
|
|
144
|
+
setState('watch');
|
|
145
|
+
break;
|
|
146
|
+
case 'watch-signed':
|
|
147
|
+
if (!project.hasDevProperties || !project.devProperties) {
|
|
148
|
+
console.log('\x1b[31mDevelopment properties not configured. Create a .dev_properties.json file first.\x1b[0m');
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (!project.hasSigningProperties) {
|
|
152
|
+
console.log('\x1b[31mSigning credentials not configured. Run svc setup-signing first.\x1b[0m');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
routeToSigningStep(command);
|
|
156
|
+
break;
|
|
138
157
|
case 'sign':
|
|
139
158
|
if (!project.hasDevProperties || !project.devProperties) {
|
|
140
159
|
console.log('\x1b[31mDevelopment properties not configured. Create a .dev_properties.json file first.\x1b[0m');
|
|
@@ -205,6 +224,23 @@ export default function App({ project }) {
|
|
|
205
224
|
}
|
|
206
225
|
: undefined }));
|
|
207
226
|
}
|
|
227
|
+
if (state === 'watch') {
|
|
228
|
+
const watchSigned = currentCommand === 'watch-signed';
|
|
229
|
+
return (_jsx(DevScreen, { projectRoot: project.root, manifest: project.manifest, devProperties: project.devProperties, signed: watchSigned, deploy: false, onBack: () => setState('menu'), onRetryCredentials: watchSigned
|
|
230
|
+
? () => {
|
|
231
|
+
setSigningPassword('');
|
|
232
|
+
// The saved password may be what failed — don't re-offer it.
|
|
233
|
+
setSigningRetry(true);
|
|
234
|
+
routeToSigningStep('watch-signed');
|
|
235
|
+
}
|
|
236
|
+
: undefined, signingCredentials: watchSigned && project.devProperties?.signingUsername
|
|
237
|
+
? {
|
|
238
|
+
username: project.devProperties.signingUsername,
|
|
239
|
+
password: signingPassword,
|
|
240
|
+
certificateName: project.devProperties.certificateName,
|
|
241
|
+
}
|
|
242
|
+
: undefined }));
|
|
243
|
+
}
|
|
208
244
|
if (state === 'build') {
|
|
209
245
|
return (_jsx(BuildScreen, { projectRoot: project.root, manifest: project.manifest, createZip: true, onBack: () => setState('menu') }));
|
|
210
246
|
}
|
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
|
|
@@ -21,7 +21,9 @@ const cli = meow(`
|
|
|
21
21
|
|
|
22
22
|
Commands
|
|
23
23
|
dev Start development server with watch mode
|
|
24
|
+
watch Watch and rebuild (optionally signing) without deploying
|
|
24
25
|
build Build the application for production
|
|
26
|
+
sign Sign the app for production deployment
|
|
25
27
|
deploy Deploy the application
|
|
26
28
|
info Show project information
|
|
27
29
|
|
|
@@ -33,6 +35,8 @@ const cli = meow(`
|
|
|
33
35
|
$ svc # Interactive menu
|
|
34
36
|
$ svc dev
|
|
35
37
|
$ svc dev --signed
|
|
38
|
+
$ svc watch
|
|
39
|
+
$ svc watch --signed
|
|
36
40
|
$ svc build
|
|
37
41
|
$ svc deploy --force
|
|
38
42
|
$ svc deploy --production
|
|
@@ -85,10 +89,19 @@ function printMasthead(version) {
|
|
|
85
89
|
`${spaces(gap)}${DIM}${right}${RESET}${spaces(padding)}${CYAN}│${RESET}`);
|
|
86
90
|
console.log(`${CYAN}╰${border}╯${RESET}`);
|
|
87
91
|
}
|
|
92
|
+
// Pick the widest wordmark that fits the terminal, or undefined if even the
|
|
93
|
+
// compact one would wrap (caller then falls back to the static masthead).
|
|
94
|
+
function pickIntroArt(columns) {
|
|
95
|
+
if (columns >= BIG_LOGO_WIDTH)
|
|
96
|
+
return BIG_LOGO;
|
|
97
|
+
if (columns >= SMALL_LOGO_WIDTH)
|
|
98
|
+
return SMALL_LOGO;
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
88
101
|
// Play the one-shot animated wordmark and resolve once it finishes.
|
|
89
|
-
async function playIntro() {
|
|
102
|
+
async function playIntro(art) {
|
|
90
103
|
await new Promise(resolve => {
|
|
91
|
-
const app = render(_jsx(AnimatedLogo, { onDone: () => app.unmount() }));
|
|
104
|
+
const app = render(_jsx(AnimatedLogo, { art: art, onDone: () => app.unmount() }));
|
|
92
105
|
app.waitUntilExit().then(() => resolve(), () => resolve());
|
|
93
106
|
});
|
|
94
107
|
}
|
|
@@ -104,19 +117,18 @@ async function main() {
|
|
|
104
117
|
const lastSeen = getLastSeenVersion();
|
|
105
118
|
const isUpdate = !firstRun && lastSeen !== undefined && lastSeen !== pkg.version;
|
|
106
119
|
// On the plain interactive `svc` (no command), play the animated wordmark
|
|
107
|
-
// instead of the static masthead —
|
|
108
|
-
//
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
(process.stdout.columns ?? 0) >= BIG_LOGO_WIDTH;
|
|
120
|
+
// instead of the static masthead — sized to the terminal. Only on a TTY so it
|
|
121
|
+
// doesn't run in CI / piped input.
|
|
122
|
+
const introEligible = !firstRun && !isUpdate && !commandName && Boolean(process.stdin.isTTY);
|
|
123
|
+
const introArt = introEligible
|
|
124
|
+
? pickIntroArt(process.stdout.columns ?? 0)
|
|
125
|
+
: undefined;
|
|
114
126
|
if (!firstRun) {
|
|
115
127
|
if (isUpdate) {
|
|
116
128
|
printBranding();
|
|
117
129
|
console.log(`\x1b[32m\n ✨ Updated to v${pkg.version}\x1b[0m \x1b[2m(from v${lastSeen})\x1b[0m\n`);
|
|
118
130
|
}
|
|
119
|
-
else if (!
|
|
131
|
+
else if (!introArt) {
|
|
120
132
|
printMasthead(pkg.version);
|
|
121
133
|
}
|
|
122
134
|
// Record the current version so the banner shows once per upgrade.
|
|
@@ -152,8 +164,8 @@ async function main() {
|
|
|
152
164
|
// If no command, show interactive menu (with the animated intro first when
|
|
153
165
|
// the terminal can fit it).
|
|
154
166
|
if (!commandName) {
|
|
155
|
-
if (
|
|
156
|
-
await playIntro();
|
|
167
|
+
if (introArt) {
|
|
168
|
+
await playIntro(introArt);
|
|
157
169
|
}
|
|
158
170
|
render(_jsx(App, { project: project }));
|
|
159
171
|
return;
|
package/dist/commands/dev.d.ts
CHANGED
|
@@ -4,12 +4,13 @@ import type { SitevisionManifest, DevProperties, SigningCredentials } from '../t
|
|
|
4
4
|
interface DevScreenProps {
|
|
5
5
|
projectRoot: string;
|
|
6
6
|
manifest: SitevisionManifest;
|
|
7
|
-
devProperties
|
|
7
|
+
devProperties?: DevProperties;
|
|
8
8
|
signed: boolean;
|
|
9
|
+
deploy?: boolean;
|
|
9
10
|
signingCredentials?: SigningCredentials;
|
|
10
11
|
onBack?: () => void;
|
|
11
12
|
onRetryCredentials?: () => void;
|
|
12
13
|
}
|
|
13
|
-
export declare function DevScreen({ projectRoot, manifest, devProperties, signed, signingCredentials, onBack, onRetryCredentials, }: DevScreenProps): React.JSX.Element;
|
|
14
|
+
export declare function DevScreen({ projectRoot, manifest, devProperties, signed, deploy, signingCredentials, onBack, onRetryCredentials, }: DevScreenProps): React.JSX.Element;
|
|
14
15
|
export declare const devCommand: Command;
|
|
15
16
|
export {};
|
package/dist/commands/dev.js
CHANGED
|
@@ -12,7 +12,7 @@ import { setDeployPassword } from '../utils/keychain.js';
|
|
|
12
12
|
import { resolveSigningPassword } from '../utils/signing-password.js';
|
|
13
13
|
import { copyStaticToBuild, createBuildZip, cleanBuild } from '../utils/zip.js';
|
|
14
14
|
import { isBundledApp, getAppType, getFullAppId, getZipPath, getSignedZipPath, } from '../utils/project-detection.js';
|
|
15
|
-
export function DevScreen({ projectRoot, manifest, devProperties, signed, signingCredentials, onBack, onRetryCredentials, }) {
|
|
15
|
+
export function DevScreen({ projectRoot, manifest, devProperties, signed, deploy = true, signingCredentials, onBack, onRetryCredentials, }) {
|
|
16
16
|
const { exit } = useApp();
|
|
17
17
|
const [state, setState] = React.useState({
|
|
18
18
|
status: 'initializing',
|
|
@@ -57,6 +57,20 @@ export function DevScreen({ projectRoot, manifest, devProperties, signed, signin
|
|
|
57
57
|
}
|
|
58
58
|
deployZipPath = signedZipPath;
|
|
59
59
|
}
|
|
60
|
+
// Watch/build-only mode: stop after building (and signing).
|
|
61
|
+
if (!deploy || !devProperties) {
|
|
62
|
+
setState(prev => ({
|
|
63
|
+
...prev,
|
|
64
|
+
status: 'ready',
|
|
65
|
+
message: signed
|
|
66
|
+
? 'Signed. Watching for changes...'
|
|
67
|
+
: 'Built. Watching for changes...',
|
|
68
|
+
buildCount: prev.buildCount + 1,
|
|
69
|
+
lastBuildTime: buildTime,
|
|
70
|
+
error: undefined,
|
|
71
|
+
}));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
60
74
|
// Deploy
|
|
61
75
|
setState(prev => ({
|
|
62
76
|
...prev,
|
|
@@ -99,7 +113,7 @@ export function DevScreen({ projectRoot, manifest, devProperties, signed, signin
|
|
|
99
113
|
error: error instanceof Error ? error.message : String(error),
|
|
100
114
|
}));
|
|
101
115
|
}
|
|
102
|
-
}, [projectRoot, manifest, devProperties, signed, signingCredentials]);
|
|
116
|
+
}, [projectRoot, manifest, devProperties, signed, deploy, signingCredentials]);
|
|
103
117
|
// In-house webpack path: copy static, zip, then sign + deploy.
|
|
104
118
|
const handleBuildComplete = React.useCallback(async (result) => {
|
|
105
119
|
if (!result.success) {
|
|
@@ -339,7 +353,7 @@ export function DevScreen({ projectRoot, manifest, devProperties, signed, signin
|
|
|
339
353
|
};
|
|
340
354
|
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Box, { marginBottom: 1, children: _jsx(StatusIndicator, { status: getStatusType(), label: getStatusLabel(), message: state.message }) }), state.warning && (_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: "yellow", children: ["\u26A0 ", state.warning] }) })), state.buildCount > 0 && (_jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: ["Builds: ", state.buildCount, state.lastBuildTime
|
|
341
355
|
? ` | Last build: ${state.lastBuildTime}ms`
|
|
342
|
-
: '', signed ? ' | Signed mode' : ''] }) })), _jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: [manifest.name, " v", manifest.version] }) }), devProperties && (_jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: ["Target: ", devProperties.domain, "/", devProperties.siteName, "/", devProperties.addonName] }) })), state.status === 'error' && state.error && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: "red", children: state.error }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [state.status === 'error' && onRetryCredentials && (_jsx(Text, { dimColor: true, children: "Press r to retry with new credentials" })), onBack ? (_jsx(Text, { dimColor: true, children: "Press q or Esc to return to menu (Ctrl+C to stop process)" })) : (_jsx(Text, { dimColor: true, children: "Press Ctrl+C to stop" }))] })] }));
|
|
356
|
+
: '', signed ? ' | Signed mode' : ''] }) })), _jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: [manifest.name, " v", manifest.version] }) }), deploy && devProperties && (_jsx(Box, { marginLeft: 2, marginBottom: 1, children: _jsxs(Text, { dimColor: true, children: ["Target: ", devProperties.domain, "/", devProperties.siteName, "/", devProperties.addonName] }) })), state.status === 'error' && state.error && (_jsx(Box, { flexDirection: "column", marginTop: 1, children: _jsx(Text, { color: "red", children: state.error }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [state.status === 'error' && onRetryCredentials && (_jsx(Text, { dimColor: true, children: "Press r to retry with new credentials" })), onBack ? (_jsx(Text, { dimColor: true, children: "Press q or Esc to return to menu (Ctrl+C to stop process)" })) : (_jsx(Text, { dimColor: true, children: "Press Ctrl+C to stop" }))] })] }));
|
|
343
357
|
}
|
|
344
358
|
export const devCommand = {
|
|
345
359
|
name: 'dev',
|
package/dist/commands/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { devCommand } from './dev.js';
|
|
2
|
+
import { watchCommand } from './watch.js';
|
|
2
3
|
import { buildCommand } from './build.js';
|
|
3
4
|
import { deployCommand } from './deploy.js';
|
|
4
5
|
import { infoCommand } from './info.js';
|
|
@@ -6,6 +7,7 @@ import { setupSigningCommand } from './setup-signing.js';
|
|
|
6
7
|
import { signCommand } from './sign.js';
|
|
7
8
|
export const commands = [
|
|
8
9
|
devCommand,
|
|
10
|
+
watchCommand,
|
|
9
11
|
buildCommand,
|
|
10
12
|
signCommand,
|
|
11
13
|
deployCommand,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render } from 'ink';
|
|
3
|
+
import { DevScreen } from './dev.js';
|
|
4
|
+
import { resolveSigningPassword } from '../utils/signing-password.js';
|
|
5
|
+
export const watchCommand = {
|
|
6
|
+
name: 'watch',
|
|
7
|
+
description: 'Watch and rebuild (optionally signing) without deploying',
|
|
8
|
+
requiresProject: true,
|
|
9
|
+
flags: {
|
|
10
|
+
signed: {
|
|
11
|
+
type: 'boolean',
|
|
12
|
+
description: 'Sign after each build',
|
|
13
|
+
alias: 's',
|
|
14
|
+
default: false,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
async execute({ project, flags }) {
|
|
18
|
+
const signed = Boolean(flags['signed']);
|
|
19
|
+
let signingCredentials;
|
|
20
|
+
// Signed mode: resolve signing credentials (keychain → env → prompt).
|
|
21
|
+
// No deploy credentials are needed — watch never deploys.
|
|
22
|
+
if (signed) {
|
|
23
|
+
if (!project.hasSigningProperties ||
|
|
24
|
+
!project.devProperties?.signingUsername) {
|
|
25
|
+
console.log('\n\x1b[33mSigning credentials not configured.\x1b[0m');
|
|
26
|
+
console.log('Run \x1b[36msetup-signing\x1b[0m to configure credentials.\n');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const signingUsername = project.devProperties.signingUsername;
|
|
30
|
+
const password = await resolveSigningPassword(signingUsername);
|
|
31
|
+
if (!password) {
|
|
32
|
+
console.log('\x1b[31mError: Password is required for signed mode\x1b[0m');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
signingCredentials = {
|
|
36
|
+
username: signingUsername,
|
|
37
|
+
password,
|
|
38
|
+
certificateName: project.devProperties.certificateName,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const { waitUntilExit } = render(_jsx(DevScreen, { projectRoot: project.root, manifest: project.manifest, devProperties: project.devProperties, signed: signed, deploy: false, signingCredentials: signingCredentials }));
|
|
42
|
+
await waitUntilExit();
|
|
43
|
+
},
|
|
44
|
+
};
|
|
@@ -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
|
|
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
|
-
//
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
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 >=
|
|
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: [
|
|
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
|
}
|
|
@@ -16,6 +16,16 @@ export function MainMenu({ project, onSelect }) {
|
|
|
16
16
|
value: 'dev-signed',
|
|
17
17
|
description: 'Development with automatic signing',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
label: '👀 Watch',
|
|
21
|
+
value: 'watch',
|
|
22
|
+
description: 'Rebuild on change without deploying',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: '👀 Watch (Signed)',
|
|
26
|
+
value: 'watch-signed',
|
|
27
|
+
description: 'Rebuild and sign on change without deploying',
|
|
28
|
+
},
|
|
19
29
|
{
|
|
20
30
|
label: '🔨 Build',
|
|
21
31
|
value: 'build',
|
package/dist/utils/branding.d.ts
CHANGED
|
@@ -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.
|
package/dist/utils/branding.js
CHANGED
|
@@ -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.
|