react-os-shell 0.2.43 → 0.2.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/{Browser-5ZCLRIRU.js → Browser-LSJORULM.js} +3 -3
  2. package/dist/{Browser-5ZCLRIRU.js.map → Browser-LSJORULM.js.map} +1 -1
  3. package/dist/{Calculator-OHL2AZ52.js → Calculator-ZKEH53OV.js} +4 -4
  4. package/dist/{Calculator-OHL2AZ52.js.map → Calculator-ZKEH53OV.js.map} +1 -1
  5. package/dist/{Calendar-6AHL3UJY.js → Calendar-PKRZ5MBU.js} +154 -55
  6. package/dist/Calendar-PKRZ5MBU.js.map +1 -0
  7. package/dist/{CurrencyConverter-XZVZ7XOF.js → CurrencyConverter-5N44NZ6Z.js} +56 -50
  8. package/dist/CurrencyConverter-5N44NZ6Z.js.map +1 -0
  9. package/dist/{Documents-IPVZ47JX.js → Documents-CQVIIFZV.js} +3 -3
  10. package/dist/{Documents-IPVZ47JX.js.map → Documents-CQVIIFZV.js.map} +1 -1
  11. package/dist/{Email-Z7FDKAFD.js → Email-CR6XS2AD.js} +5 -5
  12. package/dist/{Email-Z7FDKAFD.js.map → Email-CR6XS2AD.js.map} +1 -1
  13. package/dist/Files-ITIKVHIE.js +8 -0
  14. package/dist/{Files-KEHRZ2UY.js.map → Files-ITIKVHIE.js.map} +1 -1
  15. package/dist/{GeminiChat-ITU46EH4.js → GeminiChat-XTEBZIVK.js} +3 -3
  16. package/dist/{GeminiChat-ITU46EH4.js.map → GeminiChat-XTEBZIVK.js.map} +1 -1
  17. package/dist/{Minesweeper-NGN4O6C4.js → Minesweeper-QGUPDVRS.js} +3 -3
  18. package/dist/{Minesweeper-NGN4O6C4.js.map → Minesweeper-QGUPDVRS.js.map} +1 -1
  19. package/dist/{Notepad-W3YYZ3GS.js → Notepad-74CQPZCV.js} +3 -3
  20. package/dist/{Notepad-W3YYZ3GS.js.map → Notepad-74CQPZCV.js.map} +1 -1
  21. package/dist/PomodoroTimer-FHSOLF3O.js +627 -0
  22. package/dist/PomodoroTimer-FHSOLF3O.js.map +1 -0
  23. package/dist/Preview-4MBQI66Q.js +7 -0
  24. package/dist/{Preview-4354N46U.js.map → Preview-4MBQI66Q.js.map} +1 -1
  25. package/dist/{Spreadsheet-FCFII6DW.js → Spreadsheet-MKXPPSKV.js} +3 -3
  26. package/dist/{Spreadsheet-FCFII6DW.js.map → Spreadsheet-MKXPPSKV.js.map} +1 -1
  27. package/dist/TodoList-7JZ2SLDI.js +494 -0
  28. package/dist/TodoList-7JZ2SLDI.js.map +1 -0
  29. package/dist/Weather-YXSCSPQT.js +424 -0
  30. package/dist/Weather-YXSCSPQT.js.map +1 -0
  31. package/dist/WorldClock-XHM7WAUV.js +196 -0
  32. package/dist/WorldClock-XHM7WAUV.js.map +1 -0
  33. package/dist/apps/index.d.ts +10 -11
  34. package/dist/apps/index.js +23 -21
  35. package/dist/apps/index.js.map +1 -1
  36. package/dist/chunk-25L4DIKH.js +90 -0
  37. package/dist/chunk-25L4DIKH.js.map +1 -0
  38. package/dist/{chunk-62MVMTBT.js → chunk-5VXRBUEH.js} +20 -3
  39. package/dist/chunk-5VXRBUEH.js.map +1 -0
  40. package/dist/chunk-7CCHEEYC.js +94 -0
  41. package/dist/chunk-7CCHEEYC.js.map +1 -0
  42. package/dist/{chunk-T2NQXP2J.js → chunk-7M3BBAHQ.js} +10 -4
  43. package/dist/chunk-7M3BBAHQ.js.map +1 -0
  44. package/dist/{chunk-IY7JJVHR.js → chunk-DUUANLLE.js} +3 -3
  45. package/dist/{chunk-IY7JJVHR.js.map → chunk-DUUANLLE.js.map} +1 -1
  46. package/dist/chunk-MK3HLUO4.js +380 -0
  47. package/dist/chunk-MK3HLUO4.js.map +1 -0
  48. package/dist/{chunk-2SRU4BYH.js → chunk-MTLVXT2C.js} +4 -4
  49. package/dist/{chunk-2SRU4BYH.js.map → chunk-MTLVXT2C.js.map} +1 -1
  50. package/dist/{chunk-46LICZUM.js → chunk-MVWEL34Y.js} +3 -2
  51. package/dist/chunk-MVWEL34Y.js.map +1 -0
  52. package/dist/{chunk-TVOBLSSV.js → chunk-UK2AA3J6.js} +3 -3
  53. package/dist/{chunk-TVOBLSSV.js.map → chunk-UK2AA3J6.js.map} +1 -1
  54. package/dist/index.d.ts +16 -1
  55. package/dist/index.js +4089 -11
  56. package/dist/index.js.map +1 -1
  57. package/dist/styles.css +6 -4
  58. package/package.json +1 -1
  59. package/dist/Calendar-6AHL3UJY.js.map +0 -1
  60. package/dist/CurrencyConverter-XZVZ7XOF.js.map +0 -1
  61. package/dist/Files-KEHRZ2UY.js +0 -8
  62. package/dist/PomodoroTimer-T2J5NDJR.js +0 -196
  63. package/dist/PomodoroTimer-T2J5NDJR.js.map +0 -1
  64. package/dist/Preview-4354N46U.js +0 -7
  65. package/dist/Weather-XTADR7Z3.js +0 -266
  66. package/dist/Weather-XTADR7Z3.js.map +0 -1
  67. package/dist/WorldClock-OFK2EA2H.js +0 -126
  68. package/dist/WorldClock-OFK2EA2H.js.map +0 -1
  69. package/dist/chunk-46LICZUM.js.map +0 -1
  70. package/dist/chunk-62MVMTBT.js.map +0 -1
  71. package/dist/chunk-7KZWBIDL.js +0 -4144
  72. package/dist/chunk-7KZWBIDL.js.map +0 -1
  73. package/dist/chunk-T2NQXP2J.js.map +0 -1
@@ -0,0 +1,627 @@
1
+ import { useTodoTasks, migratePomodoroTasksOnce } from './chunk-25L4DIKH.js';
2
+ import { subscribePomo, getPomoSnapshot, setPomoDurations, setPomoAlarm, setPomoAlarmConfig, setPomoBehaviour, setPomoFocusSound, pomoSwitchMode, pomoPause, pomoStart, ALARM_OPTIONS, playAlarm, FOCUS_SOUND_OPTIONS, previewFocusSound } from './chunk-MK3HLUO4.js';
3
+ import { useShellPrefs } from './chunk-36VM54SC.js';
4
+ import './chunk-WIJ45SYD.js';
5
+ import { loadAppearance, WidgetSettingsModal } from './chunk-UK2AA3J6.js';
6
+ import { useWidgetSettings } from './chunk-7M3BBAHQ.js';
7
+ import './chunk-PLGHQ7QW.js';
8
+ import { useSyncExternalStore, useEffect, useMemo, useState, useRef, useCallback } from 'react';
9
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
10
+
11
+ var POMO_SETTINGS_KEY = "pomodoro_appearance";
12
+ var ACTIVE_TASK_KEY = "pomodoro_active_task_id";
13
+ var MODE_LABELS = { focus: "Pomodoro", short: "Short Break", long: "Long Break" };
14
+ var MODE_COLORS = {
15
+ focus: "#0f172a",
16
+ // unused for the panel; only for focus-mode accents
17
+ short: "#508a52",
18
+ // muted green
19
+ long: "#5b7898"
20
+ // slate blue
21
+ };
22
+ function hexToRgba(hex, alpha) {
23
+ const r = parseInt(hex.slice(1, 3), 16);
24
+ const g = parseInt(hex.slice(3, 5), 16);
25
+ const b = parseInt(hex.slice(5, 7), 16);
26
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
27
+ }
28
+ var DEFAULT_PREFS = {
29
+ focusMinutes: 25,
30
+ shortBreakMinutes: 5,
31
+ longBreakMinutes: 15,
32
+ autoStartBreaks: false,
33
+ autoStartPomodoros: false,
34
+ longBreakInterval: 4,
35
+ autoCheckTasks: true,
36
+ checkToBottom: true,
37
+ alarmSound: "bell",
38
+ alarmVolume: 100,
39
+ alarmRepeat: 1,
40
+ focusSound: "none",
41
+ focusVolume: 50
42
+ };
43
+ var intInRange = (v, min, max, fallback) => typeof v === "number" && isFinite(v) ? Math.max(min, Math.min(max, Math.round(v))) : fallback;
44
+ function readPrefs(raw) {
45
+ const p = raw && typeof raw === "object" ? raw : {};
46
+ return {
47
+ focusMinutes: intInRange(p.focusMinutes, 1, 120, DEFAULT_PREFS.focusMinutes),
48
+ shortBreakMinutes: intInRange(p.shortBreakMinutes, 1, 120, DEFAULT_PREFS.shortBreakMinutes),
49
+ longBreakMinutes: intInRange(p.longBreakMinutes, 1, 120, DEFAULT_PREFS.longBreakMinutes),
50
+ autoStartBreaks: typeof p.autoStartBreaks === "boolean" ? p.autoStartBreaks : DEFAULT_PREFS.autoStartBreaks,
51
+ autoStartPomodoros: typeof p.autoStartPomodoros === "boolean" ? p.autoStartPomodoros : DEFAULT_PREFS.autoStartPomodoros,
52
+ longBreakInterval: intInRange(p.longBreakInterval, 1, 20, DEFAULT_PREFS.longBreakInterval),
53
+ autoCheckTasks: typeof p.autoCheckTasks === "boolean" ? p.autoCheckTasks : DEFAULT_PREFS.autoCheckTasks,
54
+ checkToBottom: typeof p.checkToBottom === "boolean" ? p.checkToBottom : DEFAULT_PREFS.checkToBottom,
55
+ alarmSound: ALARM_OPTIONS.some((o) => o.id === p.alarmSound) ? p.alarmSound : DEFAULT_PREFS.alarmSound,
56
+ alarmVolume: intInRange(p.alarmVolume, 0, 100, DEFAULT_PREFS.alarmVolume),
57
+ alarmRepeat: intInRange(p.alarmRepeat, 1, 5, DEFAULT_PREFS.alarmRepeat),
58
+ focusSound: FOCUS_SOUND_OPTIONS.some((o) => o.id === p.focusSound) ? p.focusSound : DEFAULT_PREFS.focusSound,
59
+ focusVolume: intInRange(p.focusVolume, 0, 100, DEFAULT_PREFS.focusVolume)
60
+ };
61
+ }
62
+ var sortDoneToBottom = (arr) => {
63
+ const undone = [];
64
+ const done = [];
65
+ for (const t of arr) (t.done ? done : undone).push(t);
66
+ return [...undone, ...done];
67
+ };
68
+ function useMemoSortedTasks(rawTasks, checkToBottom) {
69
+ return useMemo(
70
+ () => checkToBottom ? sortDoneToBottom(rawTasks) : rawTasks,
71
+ [rawTasks, checkToBottom]
72
+ );
73
+ }
74
+ function PomodoroTimer() {
75
+ const snap = useSyncExternalStore(subscribePomo, getPomoSnapshot, getPomoSnapshot);
76
+ const { prefs: shellPrefs, save: saveShellPrefs } = useShellPrefs();
77
+ const userPrefs = readPrefs(shellPrefs.pomodoro_settings);
78
+ useEffect(() => {
79
+ setPomoDurations({
80
+ focus: userPrefs.focusMinutes * 60,
81
+ short: userPrefs.shortBreakMinutes * 60,
82
+ long: userPrefs.longBreakMinutes * 60
83
+ });
84
+ }, [userPrefs.focusMinutes, userPrefs.shortBreakMinutes, userPrefs.longBreakMinutes]);
85
+ useEffect(() => {
86
+ setPomoAlarm(userPrefs.alarmSound);
87
+ setPomoAlarmConfig(userPrefs.alarmVolume, userPrefs.alarmRepeat);
88
+ }, [userPrefs.alarmSound, userPrefs.alarmVolume, userPrefs.alarmRepeat]);
89
+ useEffect(() => {
90
+ setPomoBehaviour({
91
+ autoStartBreaks: userPrefs.autoStartBreaks,
92
+ autoStartPomodoros: userPrefs.autoStartPomodoros,
93
+ longBreakInterval: userPrefs.longBreakInterval
94
+ });
95
+ }, [userPrefs.autoStartBreaks, userPrefs.autoStartPomodoros, userPrefs.longBreakInterval]);
96
+ useEffect(() => {
97
+ setPomoFocusSound(userPrefs.focusSound, userPrefs.focusVolume);
98
+ }, [userPrefs.focusSound, userPrefs.focusVolume]);
99
+ useEffect(() => {
100
+ if (typeof Notification !== "undefined" && Notification.permission === "default") {
101
+ try {
102
+ Notification.requestPermission();
103
+ } catch {
104
+ }
105
+ }
106
+ }, []);
107
+ const todo = useTodoTasks();
108
+ const rawTasks = todo.tasks;
109
+ const todayStr = useMemo(() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), []);
110
+ const visibleRawTasks = useMemo(
111
+ () => rawTasks.filter((t) => {
112
+ if (!t.dueDate) return false;
113
+ if (t.dueDate === todayStr) return true;
114
+ if (t.dueDate < todayStr && !t.done) return true;
115
+ return false;
116
+ }),
117
+ [rawTasks, todayStr]
118
+ );
119
+ const tasks = useMemoSortedTasks(visibleRawTasks, userPrefs.checkToBottom);
120
+ const [activeTaskId, setActiveTaskId] = useState(() => localStorage.getItem(ACTIVE_TASK_KEY));
121
+ const [adding, setAdding] = useState(false);
122
+ const [menuOpenId, setMenuOpenId] = useState(null);
123
+ const migratedRef = useRef(false);
124
+ useEffect(() => {
125
+ if (migratedRef.current) return;
126
+ migratedRef.current = true;
127
+ migratePomodoroTasksOnce(rawTasks, todo.setAllTasks);
128
+ }, []);
129
+ useEffect(() => {
130
+ try {
131
+ if (activeTaskId) localStorage.setItem(ACTIVE_TASK_KEY, activeTaskId);
132
+ else localStorage.removeItem(ACTIVE_TASK_KEY);
133
+ } catch {
134
+ }
135
+ }, [activeTaskId]);
136
+ useEffect(() => {
137
+ if (!menuOpenId) return;
138
+ const handler = () => setMenuOpenId(null);
139
+ const t = setTimeout(() => document.addEventListener("click", handler), 0);
140
+ return () => {
141
+ clearTimeout(t);
142
+ document.removeEventListener("click", handler);
143
+ };
144
+ }, [menuOpenId]);
145
+ const lastStreakRef = useRef(snap.streak);
146
+ useEffect(() => {
147
+ if (snap.streak > lastStreakRef.current && activeTaskId) {
148
+ const t = rawTasks.find((x) => x.id === activeTaskId);
149
+ if (t) {
150
+ const completed = (t.completed ?? 0) + 1;
151
+ const estimated = t.estimated ?? 0;
152
+ const shouldAutoCheck = userPrefs.autoCheckTasks && estimated > 0 && completed >= estimated;
153
+ todo.updateTask(activeTaskId, { completed, done: t.done || shouldAutoCheck });
154
+ }
155
+ }
156
+ lastStreakRef.current = snap.streak;
157
+ }, [snap.streak, activeTaskId, userPrefs.autoCheckTasks, rawTasks, todo]);
158
+ const addTask = (name, estimated) => {
159
+ const trimmed = name.trim();
160
+ if (!trimmed) return;
161
+ const id = todo.addTask({ name: trimmed, estimated, completed: 0, dueDate: todayStr });
162
+ if (!activeTaskId) setActiveTaskId(id);
163
+ setAdding(false);
164
+ };
165
+ const removeTask = (id) => {
166
+ todo.removeTask(id);
167
+ if (activeTaskId === id) setActiveTaskId(null);
168
+ setMenuOpenId(null);
169
+ };
170
+ const toggleDone = (id) => {
171
+ todo.toggleDone(id);
172
+ };
173
+ const totalCompleted = tasks.reduce((acc, t) => acc + (t.completed ?? 0), 0);
174
+ const totalEstimated = tasks.reduce((acc, t) => acc + Math.max(t.estimated ?? 0, t.completed ?? 0), 0);
175
+ const remainingPomos = tasks.reduce((acc, t) => t.done ? acc : acc + Math.max(0, (t.estimated ?? 0) - (t.completed ?? 0)), 0);
176
+ const remainingSecsFromTimer = snap.running && snap.mode === "focus" ? snap.remaining : 0;
177
+ const remainingSecs = remainingPomos * userPrefs.focusMinutes * 60 + remainingSecsFromTimer;
178
+ const finishAt = new Date(Date.now() + remainingSecs * 1e3);
179
+ const finishAtStr = finishAt.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false });
180
+ const totalHours = (remainingSecs / 3600).toFixed(1);
181
+ const activeTask = activeTaskId ? rawTasks.find((t) => t.id === activeTaskId) ?? null : null;
182
+ const mm = String(Math.floor(snap.remaining / 60)).padStart(2, "0");
183
+ const ss = String(snap.remaining % 60).padStart(2, "0");
184
+ const [appearance, setAppearance] = useState(() => loadAppearance(POMO_SETTINGS_KEY));
185
+ const [settingsOpen, setSettingsOpen] = useState(false);
186
+ const [configAppearance, setConfigAppearance] = useState(appearance);
187
+ const [configPrefs, setConfigPrefs] = useState(userPrefs);
188
+ useWidgetSettings(useCallback(() => {
189
+ setConfigAppearance({ ...appearance });
190
+ setConfigPrefs({ ...userPrefs });
191
+ setSettingsOpen(true);
192
+ }, [appearance, userPrefs]));
193
+ const onSave = () => {
194
+ setAppearance(configAppearance);
195
+ localStorage.setItem(POMO_SETTINGS_KEY, JSON.stringify(configAppearance));
196
+ saveShellPrefs({ pomodoro_settings: configPrefs });
197
+ setSettingsOpen(false);
198
+ };
199
+ const isColored = snap.mode !== "focus";
200
+ const bg = MODE_COLORS[snap.mode];
201
+ const panelBg = isColored ? hexToRgba(bg, appearance.activeOpacity / 100) : `rgb(var(--taskbar-bg-rgb, 243 244 246) / ${appearance.activeOpacity / 100})`;
202
+ const tx = {
203
+ primary: isColored ? "text-white" : "text-gray-900",
204
+ secondary: isColored ? "text-white/85" : "text-gray-700",
205
+ muted: isColored ? "text-white/65" : "text-gray-500",
206
+ tabActive: isColored ? "bg-white/15 text-white" : "bg-gray-200 text-gray-900",
207
+ tabInactive: isColored ? "text-white/75 hover:bg-white/10" : "text-gray-500 hover:bg-gray-200",
208
+ divider: isColored ? "border-white/30" : "border-gray-200",
209
+ softDivider: isColored ? "border-white/20" : "border-gray-200",
210
+ iconBtn: isColored ? "bg-black/15 hover:bg-black/25 text-white/85" : "bg-gray-200 hover:bg-gray-300 text-gray-600",
211
+ addTaskBtn: isColored ? "border-white/45 text-white/90 hover:bg-white/[0.06]" : "border-gray-300 text-gray-500 hover:bg-gray-200/50"
212
+ };
213
+ const accentColor = isColored ? bg : "#0f172a";
214
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
215
+ /* @__PURE__ */ jsxs(
216
+ "div",
217
+ {
218
+ className: `flex flex-col h-full select-none transition-colors duration-300 ${tx.primary}`,
219
+ style: {
220
+ backgroundColor: panelBg,
221
+ backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : void 0
222
+ },
223
+ children: [
224
+ /* @__PURE__ */ jsx("div", { className: "px-3 pt-3 flex justify-center gap-1.5", children: ["focus", "short", "long"].map((m) => /* @__PURE__ */ jsx(
225
+ "button",
226
+ {
227
+ onClick: () => pomoSwitchMode(m),
228
+ className: `px-2.5 py-1 text-[13px] font-bold rounded transition-colors ${snap.mode === m ? tx.tabActive : tx.tabInactive}`,
229
+ children: MODE_LABELS[m]
230
+ },
231
+ m
232
+ )) }),
233
+ /* @__PURE__ */ jsxs("div", { className: "text-center font-bold tabular-nums leading-none tracking-tight mt-3", style: { fontSize: "4.5rem" }, children: [
234
+ mm,
235
+ ":",
236
+ ss
237
+ ] }),
238
+ /* @__PURE__ */ jsx("div", { className: "flex justify-center mt-3 mb-2", children: /* @__PURE__ */ jsx(
239
+ "button",
240
+ {
241
+ onClick: () => snap.running ? pomoPause() : pomoStart(),
242
+ className: "bg-white px-12 py-2.5 rounded-md font-bold text-base shadow-[0_4px_0_rgba(0,0,0,0.12)] hover:shadow-[0_3px_0_rgba(0,0,0,0.12)] active:translate-y-1 active:shadow-none transition-all",
243
+ style: { color: accentColor, letterSpacing: "0.08em" },
244
+ children: snap.running ? "PAUSE" : "START"
245
+ }
246
+ ) }),
247
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-3 text-center", children: activeTask ? /* @__PURE__ */ jsxs(Fragment, { children: [
248
+ /* @__PURE__ */ jsxs("div", { className: `text-xs leading-tight ${tx.muted}`, children: [
249
+ "#",
250
+ (activeTask.completed ?? 0) + 1
251
+ ] }),
252
+ /* @__PURE__ */ jsx("div", { className: `text-[15px] font-medium leading-tight truncate mt-0.5 ${tx.primary}`, children: activeTask.name })
253
+ ] }) : /* @__PURE__ */ jsx("div", { className: `text-xs italic ${tx.muted}`, children: "No task selected" }) }),
254
+ /* @__PURE__ */ jsxs("div", { className: "px-3 mt-1 flex items-center justify-between", children: [
255
+ /* @__PURE__ */ jsx("h3", { className: `text-base font-bold tracking-tight ${tx.primary}`, children: "Tasks" }),
256
+ /* @__PURE__ */ jsx("button", { className: `rounded p-1 transition-colors ${tx.iconBtn}`, "aria-label": "Tasks menu", tabIndex: -1, children: /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "currentColor", children: [
257
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.4" }),
258
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.4" }),
259
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.4" })
260
+ ] }) })
261
+ ] }),
262
+ /* @__PURE__ */ jsx("div", { className: `border-t mx-3 mt-2 ${tx.divider}` }),
263
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 px-3 py-3 space-y-2 overflow-y-auto", children: [
264
+ tasks.map((task) => {
265
+ const isActive = task.id === activeTaskId;
266
+ return /* @__PURE__ */ jsxs(
267
+ "div",
268
+ {
269
+ onClick: () => setActiveTaskId(task.id),
270
+ className: `bg-white text-gray-800 rounded-md flex items-center pr-2 py-2.5 shadow-sm cursor-pointer transition-shadow hover:shadow-md ${isActive ? "border-l-4 border-gray-700 pl-2" : "pl-3 border-l-4 border-transparent"}`,
271
+ children: [
272
+ /* @__PURE__ */ jsx(
273
+ "button",
274
+ {
275
+ onClick: (e) => {
276
+ e.stopPropagation();
277
+ toggleDone(task.id);
278
+ },
279
+ className: "shrink-0",
280
+ children: task.done ? /* @__PURE__ */ jsx("svg", { className: "h-5 w-5", fill: "currentColor", viewBox: "0 0 20 20", style: { color: accentColor }, children: /* @__PURE__ */ jsx("path", { d: "M10 0a10 10 0 100 20 10 10 0 000-20zm-1 14.5l-4.5-4.5 1.4-1.4 3.1 3.1 6.1-6.1 1.4 1.4z" }) }) : /* @__PURE__ */ jsx("svg", { className: "h-5 w-5 text-gray-300", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }) })
281
+ }
282
+ ),
283
+ /* @__PURE__ */ jsx("span", { className: `ml-3 flex-1 font-semibold truncate ${task.done ? "line-through text-gray-400" : "text-gray-800"}`, children: task.name }),
284
+ /* @__PURE__ */ jsxs("span", { className: "text-sm tabular-nums mr-1.5 shrink-0", children: [
285
+ /* @__PURE__ */ jsx("span", { className: "font-bold text-gray-500", children: task.completed ?? 0 }),
286
+ /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: " / " }),
287
+ /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: task.estimated ?? "\u2013" })
288
+ ] }),
289
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
290
+ /* @__PURE__ */ jsx(
291
+ "button",
292
+ {
293
+ onClick: (e) => {
294
+ e.stopPropagation();
295
+ setMenuOpenId(menuOpenId === task.id ? null : task.id);
296
+ },
297
+ className: "rounded p-1 bg-gray-100 hover:bg-gray-200",
298
+ children: /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4 text-gray-500", viewBox: "0 0 24 24", fill: "currentColor", children: [
299
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.4" }),
300
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.4" }),
301
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.4" })
302
+ ] })
303
+ }
304
+ ),
305
+ menuOpenId === task.id && /* @__PURE__ */ jsx(
306
+ "div",
307
+ {
308
+ className: "absolute right-0 top-full mt-1 bg-white text-gray-800 rounded shadow-lg border border-gray-200 z-10 min-w-[110px] overflow-hidden",
309
+ onClick: (e) => e.stopPropagation(),
310
+ children: /* @__PURE__ */ jsx(
311
+ "button",
312
+ {
313
+ onClick: (e) => {
314
+ e.stopPropagation();
315
+ removeTask(task.id);
316
+ },
317
+ className: "block w-full px-3 py-1.5 text-sm text-left hover:bg-red-50 text-red-600",
318
+ children: "Delete"
319
+ }
320
+ )
321
+ }
322
+ )
323
+ ] })
324
+ ]
325
+ },
326
+ task.id
327
+ );
328
+ }),
329
+ !adding ? /* @__PURE__ */ jsxs(
330
+ "button",
331
+ {
332
+ onClick: () => setAdding(true),
333
+ className: `w-full border-2 border-dashed rounded-md py-3 font-semibold flex items-center justify-center gap-2 transition-colors ${tx.addTaskBtn}`,
334
+ children: [
335
+ /* @__PURE__ */ jsxs("svg", { className: "h-5 w-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
336
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
337
+ /* @__PURE__ */ jsx("path", { strokeLinecap: "round", d: "M8 12h8M12 8v8" })
338
+ ] }),
339
+ "Add Task"
340
+ ]
341
+ }
342
+ ) : /* @__PURE__ */ jsx(AddTaskForm, { onSubmit: addTask, onCancel: () => setAdding(false), accentColor })
343
+ ] }),
344
+ /* @__PURE__ */ jsxs("div", { className: `border-t px-3 py-2.5 flex items-center justify-center gap-5 text-sm ${tx.softDivider}`, children: [
345
+ /* @__PURE__ */ jsxs("span", { className: tx.secondary, children: [
346
+ "Pomos: ",
347
+ /* @__PURE__ */ jsx("span", { className: `font-bold ${tx.primary}`, children: totalCompleted }),
348
+ /* @__PURE__ */ jsx("span", { className: tx.muted, children: " / " }),
349
+ /* @__PURE__ */ jsx("span", { className: `font-bold ${tx.primary}`, children: totalEstimated })
350
+ ] }),
351
+ remainingSecs > 0 && /* @__PURE__ */ jsxs("span", { className: tx.secondary, children: [
352
+ "Finish At: ",
353
+ /* @__PURE__ */ jsx("span", { className: `font-bold ${tx.primary}`, children: finishAtStr }),
354
+ /* @__PURE__ */ jsxs("span", { className: tx.muted, children: [
355
+ " (",
356
+ totalHours,
357
+ "h)"
358
+ ] })
359
+ ] })
360
+ ] })
361
+ ]
362
+ }
363
+ ),
364
+ /* @__PURE__ */ jsx(
365
+ PomodoroSettings,
366
+ {
367
+ open: settingsOpen,
368
+ onClose: () => setSettingsOpen(false),
369
+ onSave,
370
+ configAppearance,
371
+ setConfigAppearance,
372
+ configPrefs,
373
+ setConfigPrefs
374
+ }
375
+ )
376
+ ] });
377
+ }
378
+ function PomodoroSettings({
379
+ open,
380
+ onClose,
381
+ onSave,
382
+ configAppearance,
383
+ setConfigAppearance,
384
+ configPrefs,
385
+ setConfigPrefs
386
+ }) {
387
+ const set = (k, v) => setConfigPrefs((p) => ({ ...p, [k]: v }));
388
+ return /* @__PURE__ */ jsxs(
389
+ WidgetSettingsModal,
390
+ {
391
+ open,
392
+ onClose,
393
+ title: "Pomodoro Settings",
394
+ appearance: configAppearance,
395
+ onAppearanceChange: setConfigAppearance,
396
+ onSave,
397
+ children: [
398
+ /* @__PURE__ */ jsxs(Section, { icon: /* @__PURE__ */ jsx(ClockIcon, {}), label: "TIMER", children: [
399
+ /* @__PURE__ */ jsxs("div", { children: [
400
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-bold text-gray-800 mb-2", children: "Time (minutes)" }),
401
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-3 gap-3", children: [
402
+ /* @__PURE__ */ jsx(NumberField, { label: "Pomodoro", value: configPrefs.focusMinutes, onChange: (v) => set("focusMinutes", v), max: 120 }),
403
+ /* @__PURE__ */ jsx(NumberField, { label: "Short Break", value: configPrefs.shortBreakMinutes, onChange: (v) => set("shortBreakMinutes", v), max: 120 }),
404
+ /* @__PURE__ */ jsx(NumberField, { label: "Long Break", value: configPrefs.longBreakMinutes, onChange: (v) => set("longBreakMinutes", v), max: 120 })
405
+ ] })
406
+ ] }),
407
+ /* @__PURE__ */ jsx(Row, { label: "Auto Start Breaks", children: /* @__PURE__ */ jsx(Toggle, { checked: configPrefs.autoStartBreaks, onChange: (v) => set("autoStartBreaks", v) }) }),
408
+ /* @__PURE__ */ jsx(Row, { label: "Auto Start Pomodoros", children: /* @__PURE__ */ jsx(Toggle, { checked: configPrefs.autoStartPomodoros, onChange: (v) => set("autoStartPomodoros", v) }) }),
409
+ /* @__PURE__ */ jsx(Row, { label: "Long Break interval", children: /* @__PURE__ */ jsx(NumberInput, { value: configPrefs.longBreakInterval, onChange: (v) => set("longBreakInterval", v), min: 1, max: 20, className: "w-14 text-center" }) })
410
+ ] }),
411
+ /* @__PURE__ */ jsxs(Section, { icon: /* @__PURE__ */ jsx(TaskIcon, {}), label: "TASK", children: [
412
+ /* @__PURE__ */ jsx(
413
+ Row,
414
+ {
415
+ label: "Auto Check Tasks",
416
+ info: 'If you enable "Auto Check Tasks", the active task will be automatically checked when the actual pomodoro count reaches the estimated count.',
417
+ children: /* @__PURE__ */ jsx(Toggle, { checked: configPrefs.autoCheckTasks, onChange: (v) => set("autoCheckTasks", v) })
418
+ }
419
+ ),
420
+ /* @__PURE__ */ jsx(
421
+ Row,
422
+ {
423
+ label: "Check to Bottom",
424
+ info: 'If you enable "Check to Bottom", the checked task will be automatically moved to the bottom of the task list.',
425
+ children: /* @__PURE__ */ jsx(Toggle, { checked: configPrefs.checkToBottom, onChange: (v) => set("checkToBottom", v) })
426
+ }
427
+ )
428
+ ] }),
429
+ /* @__PURE__ */ jsxs(Section, { icon: /* @__PURE__ */ jsx(SpeakerIcon, {}), label: "SOUND", children: [
430
+ /* @__PURE__ */ jsx(Row, { label: "Alarm Sound", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
431
+ /* @__PURE__ */ jsx(
432
+ Dropdown,
433
+ {
434
+ value: configPrefs.alarmSound,
435
+ onChange: (v) => set("alarmSound", v),
436
+ options: ALARM_OPTIONS.map((o) => ({ id: o.id, label: o.label }))
437
+ }
438
+ ),
439
+ configPrefs.alarmSound !== "off" && /* @__PURE__ */ jsx(
440
+ "button",
441
+ {
442
+ type: "button",
443
+ onClick: () => playAlarm(configPrefs.alarmSound, { volume: configPrefs.alarmVolume, repeat: 1 }),
444
+ className: "px-2 py-1 text-[11px] rounded border border-gray-300 text-gray-600 hover:bg-gray-100",
445
+ children: "Play"
446
+ }
447
+ )
448
+ ] }) }),
449
+ /* @__PURE__ */ jsx(SliderRow, { value: configPrefs.alarmVolume, onChange: (v) => set("alarmVolume", v) }),
450
+ /* @__PURE__ */ jsx(Row, { label: "repeat", children: /* @__PURE__ */ jsx(NumberInput, { value: configPrefs.alarmRepeat, onChange: (v) => set("alarmRepeat", v), min: 1, max: 5, className: "w-14 text-center" }) }),
451
+ /* @__PURE__ */ jsx(Row, { label: "Focus Sound", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
452
+ /* @__PURE__ */ jsx(
453
+ Dropdown,
454
+ {
455
+ value: configPrefs.focusSound,
456
+ onChange: (v) => set("focusSound", v),
457
+ options: FOCUS_SOUND_OPTIONS
458
+ }
459
+ ),
460
+ configPrefs.focusSound !== "none" && /* @__PURE__ */ jsx(
461
+ "button",
462
+ {
463
+ type: "button",
464
+ onClick: () => previewFocusSound(configPrefs.focusSound, configPrefs.focusVolume),
465
+ className: "px-2 py-1 text-[11px] rounded border border-gray-300 text-gray-600 hover:bg-gray-100",
466
+ children: "Play"
467
+ }
468
+ )
469
+ ] }) }),
470
+ /* @__PURE__ */ jsx(SliderRow, { value: configPrefs.focusVolume, onChange: (v) => set("focusVolume", v) })
471
+ ] })
472
+ ]
473
+ }
474
+ );
475
+ }
476
+ function Section({ icon, label, children }) {
477
+ return /* @__PURE__ */ jsxs("div", { className: "border-t border-gray-200 pt-3 first:border-0 first:pt-0", children: [
478
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mb-3 text-gray-400", children: [
479
+ icon,
480
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-bold tracking-[0.15em]", children: label })
481
+ ] }),
482
+ /* @__PURE__ */ jsx("div", { className: "space-y-3", children })
483
+ ] });
484
+ }
485
+ function Row({ label, info, children }) {
486
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
487
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 min-w-0", children: [
488
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-gray-800", children: label }),
489
+ info && /* @__PURE__ */ jsx(InfoIcon, { title: info })
490
+ ] }),
491
+ /* @__PURE__ */ jsx("div", { className: "shrink-0", children })
492
+ ] });
493
+ }
494
+ function SliderRow({ value, onChange }) {
495
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3", children: [
496
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400 w-7 text-right tabular-nums", children: value }),
497
+ /* @__PURE__ */ jsx(
498
+ "input",
499
+ {
500
+ type: "range",
501
+ min: 0,
502
+ max: 100,
503
+ value,
504
+ onChange: (e) => onChange(parseInt(e.target.value, 10)),
505
+ className: "w-40 accent-blue-500"
506
+ }
507
+ )
508
+ ] });
509
+ }
510
+ function NumberField({ label, value, onChange, min = 1, max = 99 }) {
511
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col", children: [
512
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-gray-500 mb-1", children: label }),
513
+ /* @__PURE__ */ jsx(NumberInput, { value, onChange, min, max })
514
+ ] });
515
+ }
516
+ function NumberInput({ value, onChange, min = 1, max = 99, className = "" }) {
517
+ return /* @__PURE__ */ jsx(
518
+ "input",
519
+ {
520
+ type: "number",
521
+ min,
522
+ max,
523
+ value,
524
+ onChange: (e) => onChange(Math.max(min, Math.min(max, parseInt(e.target.value, 10) || min))),
525
+ className: `bg-gray-100 border-0 rounded-md px-2 py-1.5 text-sm font-medium text-gray-800 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full ${className}`
526
+ }
527
+ );
528
+ }
529
+ function Dropdown({ value, onChange, options }) {
530
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
531
+ /* @__PURE__ */ jsx(
532
+ "select",
533
+ {
534
+ value,
535
+ onChange: (e) => onChange(e.target.value),
536
+ className: "appearance-none bg-gray-100 border-0 rounded-md pl-3 pr-8 py-1.5 text-sm font-medium text-gray-800 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer",
537
+ children: options.map((o) => /* @__PURE__ */ jsx("option", { value: o.id, children: o.label }, o.id))
538
+ }
539
+ ),
540
+ /* @__PURE__ */ jsx("svg", { className: "absolute right-2 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-gray-400 pointer-events-none", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.5, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9l6 6 6-6" }) })
541
+ ] });
542
+ }
543
+ function Toggle({ checked, onChange }) {
544
+ return /* @__PURE__ */ jsx(
545
+ "button",
546
+ {
547
+ type: "button",
548
+ role: "switch",
549
+ "aria-checked": checked,
550
+ onClick: () => onChange(!checked),
551
+ className: `relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${checked ? "bg-emerald-500" : "bg-gray-300"}`,
552
+ children: /* @__PURE__ */ jsx("span", { className: `absolute h-5 w-5 rounded-full bg-white shadow transition-transform ${checked ? "translate-x-[22px]" : "translate-x-0.5"}` })
553
+ }
554
+ );
555
+ }
556
+ function InfoIcon({ title }) {
557
+ return /* @__PURE__ */ jsx("span", { title, className: "inline-flex items-center justify-center w-4 h-4 rounded-full bg-gray-300 text-white text-[10px] font-bold cursor-help", children: "i" });
558
+ }
559
+ function ClockIcon() {
560
+ return /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
561
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
562
+ /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 7v5l3 2" })
563
+ ] });
564
+ }
565
+ function TaskIcon() {
566
+ return /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
567
+ /* @__PURE__ */ jsx("rect", { x: "4", y: "4", width: "16", height: "16", rx: "2" }),
568
+ /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 12l2 2 4-4" })
569
+ ] });
570
+ }
571
+ function SpeakerIcon() {
572
+ return /* @__PURE__ */ jsxs("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, children: [
573
+ /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M11 5L6 9H2v6h4l5 4V5z" }),
574
+ /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15.5 8.5a5 5 0 010 7M19 5a9 9 0 010 14" })
575
+ ] });
576
+ }
577
+ function AddTaskForm({ onSubmit, onCancel, accentColor }) {
578
+ const [name, setName] = useState("");
579
+ const [est, setEst] = useState(1);
580
+ const submit = () => onSubmit(name, est);
581
+ return /* @__PURE__ */ jsxs("div", { className: "bg-white text-gray-800 rounded-md p-3 shadow-md", children: [
582
+ /* @__PURE__ */ jsx(
583
+ "input",
584
+ {
585
+ autoFocus: true,
586
+ value: name,
587
+ onChange: (e) => setName(e.target.value),
588
+ placeholder: "What are you working on?",
589
+ onKeyDown: (e) => {
590
+ if (e.key === "Enter") submit();
591
+ if (e.key === "Escape") onCancel();
592
+ },
593
+ className: "w-full text-base font-medium bg-transparent border-0 outline-none placeholder-gray-400"
594
+ }
595
+ ),
596
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mt-2", children: [
597
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold text-gray-500", children: "Est Pomodoros" }),
598
+ /* @__PURE__ */ jsx(
599
+ "input",
600
+ {
601
+ type: "number",
602
+ min: 1,
603
+ max: 20,
604
+ value: est,
605
+ onChange: (e) => setEst(Math.max(1, Math.min(20, parseInt(e.target.value, 10) || 1))),
606
+ className: "w-16 bg-gray-100 rounded px-2 py-1 text-sm text-right border-0 focus:outline-none focus:ring-2 focus:ring-blue-500"
607
+ }
608
+ )
609
+ ] }),
610
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 mt-3 justify-end", children: [
611
+ /* @__PURE__ */ jsx("button", { onClick: onCancel, className: "px-3 py-1 text-sm text-gray-500 hover:bg-gray-100 rounded", children: "Cancel" }),
612
+ /* @__PURE__ */ jsx(
613
+ "button",
614
+ {
615
+ onClick: submit,
616
+ className: "px-4 py-1 text-sm font-semibold text-white rounded shadow-sm",
617
+ style: { backgroundColor: accentColor },
618
+ children: "Save"
619
+ }
620
+ )
621
+ ] })
622
+ ] });
623
+ }
624
+
625
+ export { PomodoroTimer as default };
626
+ //# sourceMappingURL=PomodoroTimer-FHSOLF3O.js.map
627
+ //# sourceMappingURL=PomodoroTimer-FHSOLF3O.js.map