Skip to content

Commit

Permalink
CLI: small refactor and add doctor and postprocess commands
Browse files Browse the repository at this point in the history
  • Loading branch information
miklschmidt committed Oct 24, 2024
1 parent bd9751c commit 464036f
Show file tree
Hide file tree
Showing 8 changed files with 1,080 additions and 747 deletions.
580 changes: 580 additions & 0 deletions src/cli/commands/frontend.tsx

Large diffs are not rendered by default.

86 changes: 86 additions & 0 deletions src/cli/commands/postprocessor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Command } from 'commander';
import { Progress } from 'progress-stream';
import { processGCode } from '@/server/gcode-processor/gcode-processor.ts';
import { echo } from 'zx';
import { ProgressBar, StatusMessage } from '@inkjs/ui';
import { Box, render, Text } from 'ink';
import React from 'react';
import { Container } from '@/cli/components/container.tsx';
import { Duration, DurationLikeObject } from 'luxon';
import path from 'path';

const ProgressReportUI: React.FC<{
report?: Progress;
done?: boolean;
fileName: string;
}> = ({ report, fileName, done }) => {
const eta = report ? report.eta / 60 : 0;
const percentage = report?.percentage ?? 0;
const duration = Duration.fromObject({ minutes: report ? report.eta / 60 / 60 : 0 }, { locale: 'en-GB' })
.normalize()
.shiftTo(
...([eta < 1 ? 'seconds' : 'minutes', eta > 60 ? 'hours' : null].filter(Boolean) as (keyof DurationLikeObject)[]),
)
.toHuman({ unitDisplay: 'short', listStyle: 'narrow', maximumFractionDigits: 0 });
return (
<Container>
<Text>Processing {fileName}...</Text>
<Box flexDirection="row" columnGap={1}>
{report ? (
<>
<Text>{percentage.toFixed(2).padStart(6, ' ')}%</Text>
<Box width={30}>
<ProgressBar value={report?.percentage ?? 0} />
</Box>
<Text>{duration} remaining</Text>
</>
) : done ? (
<StatusMessage variant="success">Done</StatusMessage>
) : (
<Text color="gray">Initializing post processor...</Text>
)}
</Box>
</Container>
);
};

export const postprocessor = (program: Command) => {
program
.command('postprocess')
.description('Postprocess a gcode file for RatOS')
.option('-r, --rmmu', 'Postprocess for a printer with an RMMU')
.option('-i, --idex', 'Postprocess for an IDEX printer')
.option('-o, --overwrite', 'Overwrite the output file if it exists')
.argument('<input>', 'Path to the gcode file to postprocess')
.argument('<output>', 'Path to the output gcode file')
.action(async (inputFile, outputFile, args) => {
let onProgress: ((report: Progress) => void) | undefined = undefined;
let rerender: ((element: React.ReactNode) => void) | undefined = undefined;
let lastProgressPercentage: number = 0;
if (process.stdout.isTTY) {
const { rerender: _rerender } = render(<ProgressReportUI fileName={path.basename(inputFile)} />);
rerender = _rerender;
onProgress = (report) => {
_rerender(<ProgressReportUI fileName={path.basename(inputFile)} report={report} />);
};
} else {
echo(`Processing ${path.basename(inputFile)}...`);
onProgress = (report) => {
const progressTens = Math.floor(report.percentage / 10) * 10;
if (progressTens > lastProgressPercentage && report.percentage > 10) {
lastProgressPercentage = progressTens;
echo(`${lastProgressPercentage}%`);
}
};
}
await processGCode(inputFile, outputFile, {
idex: args.idex,
rmmu: args.rmmu,
overwrite: args.overwrite,
onProgress,
});
if (rerender) {
rerender(<ProgressReportUI fileName={path.basename(inputFile)} done={true} />);
}
});
};
137 changes: 137 additions & 0 deletions src/cli/components/install-progress.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { Signal, useSignal } from '@/app/_helpers/signal.ts';
import { TextProps, Static, Box, Text, Transform } from 'ink';
import Spinner from 'ink-spinner';
import { useState, useCallback } from 'react';
import { formatCmd } from '@/cli/util.tsx';
import { Container } from '@/cli/components/container.tsx';

export type InstallStep = {
name: string;
status: 'success' | 'error' | 'pending' | 'running' | 'warning';
};

export const InstallProgressUI: React.FC<{
cmdSignal: Signal<string | null>;
status: string;
statusColor?: TextProps['color'];
stepTextBeforeSteps?: boolean;
stepText?: string;
stepTextColor?: TextProps['color'];
warnings?: string[];
errors?: string[];
steps?: InstallStep[];
isLoading?: boolean;
}> = (props) => {
const [currentCmd, setCurrentCmd] = useState<string | null>(null);
useSignal(
props.cmdSignal,
useCallback((cmd) => {
setCurrentCmd(cmd);
}, []),
);
return (
<Container>
<Box flexDirection="column" rowGap={0}>
<Box marginBottom={1} flexDirection="column">
<Text color={props.statusColor ?? 'white'} dimColor={false} bold={true}>
{['red', 'redBright'].includes(props.statusColor ?? 'white') ? (
<Text bold={true}>{' '}</Text>
) : ['green', 'greenBright'].includes(props.statusColor ?? 'white') ? (
<Text bold={true}>{' '}</Text>
) : (
' '
)}
{props.status}
</Text>
{props.stepText && props.stepTextBeforeSteps && (
<Text>
{props.isLoading ? (
<Text color="green" dimColor={false}>
<Spinner type="dots" />
{' '}
</Text>
) : (
' '
)}
<Text color={props.stepTextColor ?? 'gray'} dimColor={false} bold={false}>
{props.stepText}
</Text>
</Text>
)}
</Box>
<Static items={props.warnings ?? []}>
{(warning) => (
<Text color="yellow" dimColor={true} key={warning} bold={false}>
{' '}
{warning}
</Text>
)}
</Static>
<Static items={props.errors ?? []}>
{(error) => (
<Text color="red" dimColor={true} key={error} bold={false}>
{' '}
{error}
</Text>
)}
</Static>
{props.steps &&
props.steps.map((step) => (
<Text key={step.name}>
{step.status === 'running' && (
<Text bold={true}>
<Spinner type="dots" />
{' '}
</Text>
)}
{step.status === 'success' && (
<Text bold={true} color="green">
{' '}
</Text>
)}
{step.status === 'error' && (
<Text bold={true} color="red">
{' '}
</Text>
)}
{step.status === 'warning' && (
<Text bold={true} color="yellow">
{' '}
</Text>
)}
{step.status === 'pending' && (
<Text bold={true} color="gray">
{' '}
</Text>
)}
<Text color="gray" bold={false}>
{step.name}
</Text>
</Text>
))}
{props.stepText && !props.stepTextBeforeSteps && (
<Text>
{props.isLoading ? (
<Text color="green" dimColor={false}>
<Spinner type="dots" />
{' '}
</Text>
) : (
' '
)}
<Text color={props.stepTextColor ?? 'gray'} dimColor={false} bold={false}>
{props.stepText}
</Text>
</Text>
)}
</Box>
{currentCmd && (
<Box marginTop={1} flexDirection="column">
<Text color="white">
Running: <Transform transform={formatCmd}>{currentCmd}</Transform>
</Text>
</Box>
)}
</Container>
);
};
Loading

0 comments on commit 464036f

Please sign in to comment.