From de4a60658e08790eec1eaa41d0d4c873a54c2679 Mon Sep 17 00:00:00 2001 From: Matthew Butcher Date: Mon, 15 Dec 2025 13:10:41 -0800 Subject: [PATCH] adding the current timecounter widget and the timerset widget to the main code. --- package-lock.json | 26 ++++---- package.json | 4 +- src/WidgetMap.tsx | 11 ++++ src/widgets/TimeCounter.css | 22 +++++++ src/widgets/TimeCounter.tsx | 45 ++++++++++++++ src/widgets/TimerSet.tsx | 120 ++++++++++++++++++++++++++++++++++++ 6 files changed, 213 insertions(+), 15 deletions(-) create mode 100644 src/widgets/TimeCounter.css create mode 100644 src/widgets/TimeCounter.tsx create mode 100644 src/widgets/TimerSet.tsx diff --git a/package-lock.json b/package-lock.json index 72eafff..e9644cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,8 @@ "html-to-image": "^1.11.13", "nanoid": "^5.1.6", "openmeteo": "^1.2.1", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "typescript": "^5.9.2" }, "devDependencies": { @@ -5665,24 +5665,24 @@ ] }, "node_modules/react": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", - "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", + "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", - "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", "dependencies": { - "scheduler": "^0.26.0" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.1.1" + "react": "^19.2.3" } }, "node_modules/react-is": { @@ -5780,9 +5780,9 @@ } }, "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "license": "MIT" }, "node_modules/semver": { diff --git a/package.json b/package.json index d2346ec..d421b4e 100644 --- a/package.json +++ b/package.json @@ -17,8 +17,8 @@ "html-to-image": "^1.11.13", "nanoid": "^5.1.6", "openmeteo": "^1.2.1", - "react": "^19.1.1", - "react-dom": "^19.1.1", + "react": "^19.2.3", + "react-dom": "^19.2.3", "typescript": "^5.9.2" }, "devDependencies": { diff --git a/src/WidgetMap.tsx b/src/WidgetMap.tsx index b20bafe..526419a 100644 --- a/src/WidgetMap.tsx +++ b/src/WidgetMap.tsx @@ -6,6 +6,9 @@ import { Search, SearchSettings } from "./widgets/Search"; import { Shortcut, ShortcutSettings } from "./widgets/Shortcut"; import { ToDoList } from "./widgets/ToDoList"; import { Weather } from "./widgets/Weather"; +import { TimerWidget } from "./widgets/TimeCounter"; +import TimerWidgetSet from "./widgets/TimerSet"; + const WidgetMap = { battery: { @@ -60,6 +63,14 @@ const WidgetMap = { component: Weather, size: { width: 6, height: 1 }, }, + timer: { + component: TimerWidget, + size: { width: 4, height: 1 }, + }, + Countdown: { + component: TimerWidgetSet, + size: { width: 4, height: 1 }, + }, }; export default WidgetMap; diff --git a/src/widgets/TimeCounter.css b/src/widgets/TimeCounter.css new file mode 100644 index 0000000..02c751e --- /dev/null +++ b/src/widgets/TimeCounter.css @@ -0,0 +1,22 @@ +.body { + width: 100%; + height: 100%; + + display: flex; + flex-flow: column nowrap; + align-items: center; + justify-content: center; + + font-family: Inter, sans-serif; + text-align: center; + + container-name: box; + + } + + .box { + font-size: 4rem; + font-weight: 700; + } + + \ No newline at end of file diff --git a/src/widgets/TimeCounter.tsx b/src/widgets/TimeCounter.tsx new file mode 100644 index 0000000..5d4c712 --- /dev/null +++ b/src/widgets/TimeCounter.tsx @@ -0,0 +1,45 @@ +import React, { useState, useEffect } from 'react'; +import { WidgetState } from "../Widget"; +import styles from "./TimeCounter.css"; + +export function TimerWidget() { + const [seconds, setSeconds] = useState(0); + const [isRunning, setIsRunning] = useState(false); + + useEffect(() => { + let intervalId; + if (isRunning) { + intervalId = setInterval(() => { + setSeconds(prevSeconds => prevSeconds + 1); + }, 1000); + // Update every second + } + + return () => clearInterval(intervalId); + // Cleanup on unmount or when isRunning changes + }, [isRunning]); + + const handleStart = () => { + setIsRunning(true); + }; + + const handleStop = () => { + setIsRunning(false); + }; + + const handleReset = () => { + setSeconds(0); + setIsRunning(false); + }; + + return ( +
+

Timer: {seconds}s

+ + + +
+ ); +} + +//export default TimerWidget; \ No newline at end of file diff --git a/src/widgets/TimerSet.tsx b/src/widgets/TimerSet.tsx new file mode 100644 index 0000000..38fb87b --- /dev/null +++ b/src/widgets/TimerSet.tsx @@ -0,0 +1,120 @@ +import React, { useState, useEffect, useRef, useCallback } from 'react'; +import { WidgetState } from "../Widget"; +// it finally worked holy shit +// after so many iteratiosn i finally got this to work. +const TimerWidgetSet = () => { + const [hours, setHours] = useState(0); + const [minutes, setMinutes] = useState(0); + const [seconds, setSeconds] = useState(0); + const [isRunning, setIsRunning] = useState(false); + const totalMilliseconds = useRef(0); + const intervalRef = useRef(null); + + const calculateTimeRemaining = useCallback(() => { + // Calculate remaining time in H:M:S from totalMilliseconds + const totalSeconds = Math.floor(totalMilliseconds.current / 1000); + const remainingSeconds = totalSeconds % 60; + const remainingMinutes = Math.floor((totalSeconds / 60) % 60); + const remainingHours = Math.floor(totalSeconds / 3600); + + setHours(remainingHours); + setMinutes(remainingMinutes); + setSeconds(remainingSeconds); + + if (totalMilliseconds.current <= 0) { + clearInterval(intervalRef.current!); + setIsRunning(false); + totalMilliseconds.current = 0; + } + }, []); + + useEffect(() => { + if (isRunning) { + intervalRef.current = setInterval(() => { + totalMilliseconds.current -= 1000; + calculateTimeRemaining(); + }, 1000); + } else { + if (intervalRef.current) clearInterval(intervalRef.current); + } + + return () => { + if (intervalRef.current) clearInterval(intervalRef.current); + }; + }, [isRunning, calculateTimeRemaining]); + + const handleStart = () => { + // Set total milliseconds based on current input values + const initialTimeInMs = (hours * 3600 + minutes * 60 + seconds) * 1000; + if (initialTimeInMs > 0) { + totalMilliseconds.current = initialTimeInMs; + setIsRunning(true); + } + }; + + const handleStop = () => { + setIsRunning(false); + }; + + const handleReset = () => { + setIsRunning(false); + totalMilliseconds.current = 0; + setHours(0); + setMinutes(0); + setSeconds(0); + }; + + // Helper to format time with leading zeros + const formatTime = (time: number) => time.toString().padStart(2, '0'); + + return ( +
+

Timer Widget

+ {isRunning ? ( +
+ {formatTime(hours)}: + {formatTime(minutes)}: + {formatTime(seconds)} +
+ ) : ( +
+ setHours(Number(e.target.value))} + min="0" + placeholder="HH" + style={{ width: '60px', fontSize: '18px' }} + /> + : + setMinutes(Number(e.target.value))} + min="0" + max="59" + placeholder="MM" + style={{ width: '60px', fontSize: '18px' }} + /> + : + setSeconds(Number(e.target.value))} + min="0" + max="59" + placeholder="SS" + style={{ width: '60px', fontSize: '18px' }} + /> +
+ )} +
+ + + +
+
+ ); +}; + +export default TimerWidgetSet;