pomo-tui
Version:
> A beautiful terminal-based Pomodoro timer built with React Ink
108 lines (107 loc) • 4.11 kB
JavaScript
import React, { useState, useEffect } from 'react';
import { Box, Text, useInput } from 'ink';
import Gradient from 'ink-gradient';
import BigText from 'ink-big-text';
import { cycleGradient, cyclePreviousGradient } from './colors.js';
const StopWatch = ({ onActiveChange, initialTheme = 'rainbow', }) => {
const [seconds, setSeconds] = useState(0);
const [isActive, setIsActive] = useState(false);
const [stopTime, setStopTime] = useState(300); // Default 5 minutes
const [theme, setTheme] = useState(initialTheme);
useEffect(() => {
let interval = null;
if (isActive && seconds > 0) {
interval = setInterval(() => {
setSeconds(seconds => {
const newSeconds = seconds - 1;
if (newSeconds === 0) {
setIsActive(false);
onActiveChange?.(false);
}
return newSeconds;
});
}, 1000);
}
return () => {
if (interval)
clearInterval(interval);
};
}, [isActive, onActiveChange]);
const formatTime = () => {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
return [
hours.toString().padStart(2, '0'),
minutes.toString().padStart(2, '0'),
secs.toString().padStart(2, '0'),
].join(':');
};
const toggle = () => {
const newActive = !isActive;
if (newActive && seconds === 0) {
setSeconds(stopTime);
}
setIsActive(newActive);
if (onActiveChange) {
onActiveChange(newActive);
}
};
const reset = () => {
setSeconds(stopTime);
setIsActive(false);
onActiveChange?.(false);
};
useInput((input, key) => {
if (input === 'p') {
toggle();
}
if (input === 'r') {
reset();
}
if (!isActive && (key.leftArrow || key.rightArrow)) {
const step = 60; // Change by 1 minute
const newTime = key.leftArrow
? Math.max(60, stopTime - step)
: stopTime + step;
setStopTime(newTime);
setSeconds(newTime);
}
if (input === '[' || input === ']') {
setTheme(input === '[' ? cyclePreviousGradient(theme) : cycleGradient(theme));
}
});
return (React.createElement(Box, { flexDirection: "column", padding: 1, alignItems: "center", justifyContent: "center" },
React.createElement(Box, { marginBottom: 1, alignItems: "center" },
React.createElement(Text, null,
"Set Time: ",
Math.floor(stopTime / 60),
" minutes (\u2190 \u2192)")),
React.createElement(Box, { marginBottom: 1, alignItems: "center" },
React.createElement(Text, null,
"Theme: ",
theme,
" ([ ])")),
React.createElement(Box, { marginBottom: 1, alignItems: "center" },
React.createElement(Gradient, { name: theme },
React.createElement(BigText, { font: "chrome", text: formatTime() }))),
React.createElement(Box, { alignItems: "center" },
React.createElement(Text, null,
"Status:",
' ',
React.createElement(Text, { color: isActive ? 'green' : 'yellow' }, isActive ? 'Running' : 'Paused'))),
React.createElement(Box, { marginTop: 1, alignItems: "center" },
React.createElement(Text, null,
"Press",
' ',
React.createElement(Text, { bold: true, color: "blue" }, "p"),
' ',
"to ",
isActive ? 'pause' : 'start',
",",
' ',
React.createElement(Text, { bold: true, color: "red" }, "r"),
' ',
"to reset"))));
};
export default StopWatch;