vanta-auditor-tui
Version:
Beautiful terminal UI for exporting Vanta audit evidence with ZIP support and progress tracking
75 lines • 3.82 kB
JavaScript
import React, { useState, useEffect } from 'react';
import { Box, Text } from 'ink';
import { theme } from '../theme.js';
export function AnimatedProgressBar({ value, label, width = 30, showPercentage = true, animationSpeed = 20 }) {
const [displayValue, setDisplayValue] = useState(0);
const [springVelocity, setSpringVelocity] = useState(0);
useEffect(() => {
const targetValue = Math.max(0, Math.min(100, value));
let currentValue = displayValue;
let velocity = springVelocity;
const interval = setInterval(() => {
// Spring physics simulation
const stiffness = 0.15;
const damping = 0.85;
const force = (targetValue - currentValue) * stiffness;
velocity = velocity * damping + force;
currentValue += velocity;
// Check if we're close enough to the target
if (Math.abs(targetValue - currentValue) < 0.1 && Math.abs(velocity) < 0.1) {
currentValue = targetValue;
clearInterval(interval);
}
setDisplayValue(currentValue);
setSpringVelocity(velocity);
}, animationSpeed);
return () => clearInterval(interval);
}, [value]);
const filledWidth = Math.round((displayValue / 100) * width);
const emptyWidth = width - filledWidth;
// Create gradient effect with multiple characters
const getBarCharacter = (position, filled, total) => {
const relativePos = position / total;
const fillRatio = filled / total;
if (relativePos < fillRatio - 0.1)
return '█';
if (relativePos < fillRatio - 0.05)
return '▓';
if (relativePos < fillRatio)
return '▒';
return '░';
};
const barContent = Array.from({ length: width }, (_, i) => getBarCharacter(i, filledWidth, width)).join('');
// Pulsing effect for active downloads
const pulseIntensity = Math.sin(Date.now() / 200) * 0.5 + 0.5;
const barColor = displayValue < 100
? pulseIntensity > 0.5 ? theme.colors.primary : theme.colors.primaryLight
: theme.colors.success;
return (React.createElement(Box, { flexDirection: "column" },
label && (React.createElement(Box, { marginBottom: 0 },
React.createElement(Text, { color: theme.colors.text }, label))),
React.createElement(Box, null,
React.createElement(Text, { color: theme.colors.dim }, "["),
React.createElement(Text, { color: barColor }, barContent),
React.createElement(Text, { color: theme.colors.dim }, "]"),
showPercentage && (React.createElement(Box, { marginLeft: 1 },
React.createElement(Text, { color: displayValue === 100 ? theme.colors.success : theme.colors.text },
Math.round(displayValue),
"%"))))));
}
export function MultiLevelProgress({ overall, current = 0, label, stats }) {
return (React.createElement(Box, { flexDirection: "column", paddingY: 1 },
React.createElement(AnimatedProgressBar, { value: overall, label: "Overall Progress", width: 40 }),
current > 0 && (React.createElement(Box, { marginTop: 1 },
React.createElement(AnimatedProgressBar, { value: current, label: label || "Current File", width: 40 }))),
stats && (React.createElement(Box, { marginTop: 1, flexDirection: "column" },
React.createElement(Text, { color: theme.colors.dim },
"\uD83D\uDCCA ",
stats.downloaded,
"/",
stats.total,
" files",
stats.speed && ` • ${stats.speed}`,
stats.eta && ` • ETA: ${stats.eta}`)))));
}
//# sourceMappingURL=AnimatedProgressBar.js.map