@lucidlayer/traceform-onboard
Version:
Interactive CLI tool and onboarding wizard for setting up, validating, and configuring Traceform in React projects. Automates project setup, developer onboarding, and toolchain validation for React, TypeScript, and monorepos.
153 lines (152 loc) • 7.42 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
/*
// SPDX-License-Identifier: Apache-2.0
*/
import React, { useState, useEffect } from 'react'; // Add useEffect here
import { Box, Text, useApp, useInput } from 'ink';
// Import step components
import PrerequisitesStep from './components/PrerequisitesStep.js';
import BabelStep from './components/BabelStep.js'; // Import default component
import VSCodeStep from './components/VSCodeStep.js';
import BrowserStep from './components/BrowserStep.js';
import ValidateStep from './components/ValidateStep.js';
const MIN_WIDTH = 60;
const MIN_HEIGHT = 15;
const MAX_WIDTH = 120;
// Custom hook to track terminal dimensions
const useTerminalDimensions = () => {
const getDimensions = () => ({
columns: process.stdout.columns || 80,
rows: process.stdout.rows || 24,
});
const [dimensions, setDimensions] = useState(getDimensions());
// Stable handler reference
const handleResize = React.useCallback(() => {
setDimensions(getDimensions());
}, []);
useEffect(() => {
process.stdout.on('resize', handleResize);
return () => {
process.stdout.off('resize', handleResize);
};
}, [handleResize]);
return dimensions;
};
const App = () => {
const { exit } = useApp();
const [currentStep, setCurrentStep] = useState('prerequisites');
const [finalMessage, setFinalMessage] = useState(null);
const [finalMessageColor, setFinalMessageColor] = useState('green');
const [prereqCompleted, setPrereqCompleted] = useState(false);
const [babelCompleted, setBabelCompleted] = useState(false);
const [vscodeCompleted, setVscodeCompleted] = useState(false);
const [browserCompleted, setBrowserCompleted] = useState(false);
const { columns, rows } = useTerminalDimensions();
const tooSmall = columns < MIN_WIDTH || rows < MIN_HEIGHT;
// Removed the raw mode effect to let Ink handle input management
// --- Step Completion Handlers ---
const handlePrereqComplete = (success) => {
setPrereqCompleted(true);
setCurrentStep(success ? 'babel' : 'failed');
if (!success) {
setFinalMessage('❌ Prerequisites not met. Exiting.');
setFinalMessageColor('red');
setTimeout(() => { console.log('Exiting...'); process.exit(0); }, 1500);
}
};
const handleBabelComplete = (status) => {
setBabelCompleted(true);
if (status === 'passed') {
setCurrentStep('vscode');
}
else if (status === 'failed_dependency' || status === 'failed_config') {
setFinalMessage('Onboarding cancelled.');
setFinalMessageColor('yellow');
setCurrentStep('failed');
setTimeout(() => { console.log('Exiting...'); process.exit(0); }, 1500);
}
};
const handleVSCodeComplete = (success) => {
setVscodeCompleted(true);
setCurrentStep(success ? 'browser' : 'failed');
if (!success) {
setFinalMessage('❌ VSCode extension setup failed. Exiting.');
setFinalMessageColor('red');
setTimeout(() => { console.log('Exiting...'); process.exit(0); }, 1500);
}
};
const handleBrowserComplete = (success) => {
setBrowserCompleted(true);
setCurrentStep(success ? 'validate' : 'failed');
if (!success) {
setFinalMessage('❌ Browser setup failed. Exiting.');
setFinalMessageColor('red');
setTimeout(() => { console.log('Exiting...'); process.exit(0); }, 1500);
}
};
const handleValidationComplete = (success) => {
setCurrentStep(success ? 'done' : 'failed');
setFinalMessage(success ? '🎉 All steps completed successfully!' : '❌ Validation failed. Exiting.');
setFinalMessageColor(success ? 'green' : 'red');
if (!success)
setTimeout(() => { console.log('Exiting...'); process.exit(0); }, 1500);
};
const stepOrder = ['prerequisites', 'babel', 'vscode', 'browser', 'validate'];
const stepTitles = {
prerequisites: 'Prerequisites',
babel: 'Babel Plugin',
vscode: 'VS Code Extension',
browser: 'Browser Extension',
validate: 'Final Validation',
done: '',
failed: '',
};
const getStepIndex = (step) => stepOrder.indexOf(step) + 1;
const totalSteps = stepOrder.length;
const renderStep = () => {
const stepIndex = getStepIndex(currentStep);
switch (currentStep) {
case 'prerequisites':
return _jsx(PrerequisitesStep, { onComplete: handlePrereqComplete, stepIndex: stepIndex, totalSteps: totalSteps });
case 'babel':
return _jsx(BabelStep, { onComplete: handleBabelComplete, stepIndex: stepIndex, totalSteps: totalSteps });
case 'vscode':
return _jsx(VSCodeStep, { onComplete: handleVSCodeComplete, stepIndex: stepIndex, totalSteps: totalSteps });
case 'browser':
return _jsx(BrowserStep, { onComplete: handleBrowserComplete, stepIndex: stepIndex, totalSteps: totalSteps });
case 'validate':
return _jsx(ValidateStep, { onComplete: handleValidationComplete, stepIndex: stepIndex, totalSteps: totalSteps });
case 'done':
case 'failed':
return _jsx(Text, { color: finalMessageColor, children: finalMessage });
default:
return _jsx(Text, { color: "red", children: "Internal error: Unknown step." });
}
};
// Keep the app alive by listening for input when terminal is too small
useInput(() => { }, { isActive: tooSmall });
// Dummy state to force periodic re-render when terminal is too small
const [dummyTick, setDummyTick] = useState(0);
useEffect(() => {
if (tooSmall) {
const timer = setInterval(() => {
setDummyTick(prev => prev + 1);
}, 1000);
return () => clearInterval(timer);
}
}, [tooSmall]);
const [shouldRenderWizard, setShouldRenderWizard] = useState(!tooSmall);
useEffect(() => {
if (!tooSmall) {
const timer = setTimeout(() => {
setShouldRenderWizard(true);
}, 500);
return () => clearTimeout(timer);
}
else {
setShouldRenderWizard(false);
}
}, [tooSmall]);
return (_jsx(Box, { borderStyle: "round", padding: 1, flexDirection: "column", width: columns < MAX_WIDTH ? columns : MAX_WIDTH, height: rows >= 2 ? rows - 1 : rows, alignItems: "center", justifyContent: "center", children: !shouldRenderWizard ? (_jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", children: [_jsxs(Text, { color: "red", children: ["Terminal too small for wizard. Please resize to at least ", MIN_WIDTH, "x", MIN_HEIGHT, " to continue."] }), _jsx(Text, { color: "yellow", children: "(The wizard will resume automatically when the terminal is large enough.)" })] })) : (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, justifyContent: "center", children: _jsx(Text, { bold: true, color: "cyan", children: "\uD83D\uDE80 Traceform Onboarding Wizard \uD83D\uDE80" }) }), _jsx(Box, { children: renderStep() })] })) }));
};
export default App;