{ "$schema": "homarr-widget-package-v3", "manifest": { "id": "homarr.reference.timer", "version": "1.0.0", "sdkVersion": "1", "name": "Focus timer", "description": "A source-free deadline timer. Pausing, view changes and hidden tabs preserve the correct remaining time.", "entrypoints": { "tile": "widget.tsx", "advanced": "widget.tsx" }, "handlers": {} }, "files": { "widget.tsx": "import { Alert, Button, Group, NumberInput, Progress, Stack, Text } from \"@mantine/core\";\nimport { useWidgetClock, useWidgetCommands, useWidgetHost, useWidgetOptions, useWidgetState } from \"@homarr/widget-sdk\";\nexport default function Timer() {\n const host = useWidgetHost();\n const options = useWidgetOptions<{title:string;minutes:number}>();\n const now = useWidgetClock(100);\n const [minutes, setMinutes] = useWidgetState(\"minutes\", options.minutes);\n const [timer, setTimer] = useWidgetState<{remaining:number;deadline:number|null;duration:number}>(\"timer\", { remaining: options.minutes * 60000, deadline: null, duration: options.minutes * 60000 });\n const remaining = timer.deadline === null || now === null ? timer.remaining : Math.max(0, timer.deadline - now);\n const running = timer.deadline !== null && remaining > 0;\n const start = () => setTimer({ remaining, duration: timer.duration, deadline: Date.now() + remaining });\n const pause = () => setTimer({ ...timer, remaining, deadline: null });\n const reset = () => setTimer({ remaining: minutes * 60000, duration: minutes * 60000, deadline: null });\n useWidgetCommands([{ id: \"toggle\", label: running ? \"Pause timer\" : \"Start timer\", disabled: remaining <= 0, run: running ? pause : start }, { id: \"reset\", label: \"Reset timer\", run: reset }]);\n const seconds = Math.ceil(remaining / 1000);\n return \n {options.title}\n {Math.floor(seconds / 60)}:{String(seconds % 60).padStart(2, \"0\")}\n \n {remaining === 0 && Timer complete}\n \n {host.displayMode === \"advanced\" && { if (typeof value === \"number\") setMinutes(value); }} />}\n ;\n}\n" }, "dependencies": {}, "connections": {}, "options": { "title": { "control": "text", "label": "Title", "default": "Focus" }, "minutes": { "control": "number", "label": "Minutes", "default": 25, "min": 1, "max": 1440 } } }