react-os-shell 0.2.43 → 0.2.44

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 (59) 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-4UQDQ3NT.js} +3 -3
  6. package/dist/{Calendar-6AHL3UJY.js.map → Calendar-4UQDQ3NT.js.map} +1 -1
  7. package/dist/{CurrencyConverter-XZVZ7XOF.js → CurrencyConverter-TXBFDFG2.js} +56 -50
  8. package/dist/CurrencyConverter-TXBFDFG2.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-HRBZUWPY.js} +3 -3
  12. package/dist/{Email-Z7FDKAFD.js.map → Email-HRBZUWPY.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/{Minesweeper-NGN4O6C4.js → Minesweeper-QGUPDVRS.js} +3 -3
  16. package/dist/{Minesweeper-NGN4O6C4.js.map → Minesweeper-QGUPDVRS.js.map} +1 -1
  17. package/dist/{Notepad-W3YYZ3GS.js → Notepad-74CQPZCV.js} +3 -3
  18. package/dist/{Notepad-W3YYZ3GS.js.map → Notepad-74CQPZCV.js.map} +1 -1
  19. package/dist/PomodoroTimer-PRP5CZ3S.js +627 -0
  20. package/dist/PomodoroTimer-PRP5CZ3S.js.map +1 -0
  21. package/dist/Preview-4MBQI66Q.js +7 -0
  22. package/dist/{Preview-4354N46U.js.map → Preview-4MBQI66Q.js.map} +1 -1
  23. package/dist/{Spreadsheet-FCFII6DW.js → Spreadsheet-MKXPPSKV.js} +3 -3
  24. package/dist/{Spreadsheet-FCFII6DW.js.map → Spreadsheet-MKXPPSKV.js.map} +1 -1
  25. package/dist/Weather-YXSCSPQT.js +424 -0
  26. package/dist/Weather-YXSCSPQT.js.map +1 -0
  27. package/dist/WorldClock-QO5PVJQQ.js +250 -0
  28. package/dist/WorldClock-QO5PVJQQ.js.map +1 -0
  29. package/dist/apps/index.d.ts +6 -10
  30. package/dist/apps/index.js +18 -18
  31. package/dist/apps/index.js.map +1 -1
  32. package/dist/chunk-7CCHEEYC.js +94 -0
  33. package/dist/chunk-7CCHEEYC.js.map +1 -0
  34. package/dist/{chunk-T2NQXP2J.js → chunk-7M3BBAHQ.js} +10 -4
  35. package/dist/chunk-7M3BBAHQ.js.map +1 -0
  36. package/dist/{chunk-IY7JJVHR.js → chunk-DUUANLLE.js} +3 -3
  37. package/dist/{chunk-IY7JJVHR.js.map → chunk-DUUANLLE.js.map} +1 -1
  38. package/dist/chunk-MK3HLUO4.js +380 -0
  39. package/dist/chunk-MK3HLUO4.js.map +1 -0
  40. package/dist/{chunk-2SRU4BYH.js → chunk-MTLVXT2C.js} +4 -4
  41. package/dist/{chunk-2SRU4BYH.js.map → chunk-MTLVXT2C.js.map} +1 -1
  42. package/dist/{chunk-TVOBLSSV.js → chunk-UK2AA3J6.js} +3 -3
  43. package/dist/{chunk-TVOBLSSV.js.map → chunk-UK2AA3J6.js.map} +1 -1
  44. package/dist/index.d.ts +16 -1
  45. package/dist/index.js +4089 -11
  46. package/dist/index.js.map +1 -1
  47. package/package.json +1 -1
  48. package/dist/CurrencyConverter-XZVZ7XOF.js.map +0 -1
  49. package/dist/Files-KEHRZ2UY.js +0 -8
  50. package/dist/PomodoroTimer-T2J5NDJR.js +0 -196
  51. package/dist/PomodoroTimer-T2J5NDJR.js.map +0 -1
  52. package/dist/Preview-4354N46U.js +0 -7
  53. package/dist/Weather-XTADR7Z3.js +0 -266
  54. package/dist/Weather-XTADR7Z3.js.map +0 -1
  55. package/dist/WorldClock-OFK2EA2H.js +0 -126
  56. package/dist/WorldClock-OFK2EA2H.js.map +0 -1
  57. package/dist/chunk-7KZWBIDL.js +0 -4144
  58. package/dist/chunk-7KZWBIDL.js.map +0 -1
  59. package/dist/chunk-T2NQXP2J.js.map +0 -1
@@ -0,0 +1,627 @@
1
+ import { subscribePomo, getPomoSnapshot, setPomoDurations, setPomoAlarm, setPomoAlarmConfig, setPomoBehaviour, setPomoFocusSound, pomoSwitchMode, pomoPause, pomoStart, ALARM_OPTIONS, playAlarm, FOCUS_SOUND_OPTIONS, previewFocusSound } from './chunk-MK3HLUO4.js';
2
+ import { useShellPrefs } from './chunk-36VM54SC.js';
3
+ import './chunk-WIJ45SYD.js';
4
+ import { loadAppearance, WidgetSettingsModal } from './chunk-UK2AA3J6.js';
5
+ import { useWidgetSettings } from './chunk-7M3BBAHQ.js';
6
+ import './chunk-PLGHQ7QW.js';
7
+ import { useSyncExternalStore, useEffect, useState, useRef, useCallback } from 'react';
8
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
9
+
10
+ var POMO_SETTINGS_KEY = "pomodoro_appearance";
11
+ var TASKS_KEY = "pomodoro_tasks";
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
+ function loadTasks() {
63
+ try {
64
+ const raw = JSON.parse(localStorage.getItem(TASKS_KEY) || "[]");
65
+ if (Array.isArray(raw)) return raw;
66
+ } catch {
67
+ }
68
+ return [];
69
+ }
70
+ function uid() {
71
+ return Math.random().toString(36).slice(2, 9);
72
+ }
73
+ var sortDoneToBottom = (arr) => {
74
+ const undone = [];
75
+ const done = [];
76
+ for (const t of arr) (t.done ? done : undone).push(t);
77
+ return [...undone, ...done];
78
+ };
79
+ function PomodoroTimer() {
80
+ const snap = useSyncExternalStore(subscribePomo, getPomoSnapshot, getPomoSnapshot);
81
+ const { prefs: shellPrefs, save: saveShellPrefs } = useShellPrefs();
82
+ const userPrefs = readPrefs(shellPrefs.pomodoro_settings);
83
+ useEffect(() => {
84
+ setPomoDurations({
85
+ focus: userPrefs.focusMinutes * 60,
86
+ short: userPrefs.shortBreakMinutes * 60,
87
+ long: userPrefs.longBreakMinutes * 60
88
+ });
89
+ }, [userPrefs.focusMinutes, userPrefs.shortBreakMinutes, userPrefs.longBreakMinutes]);
90
+ useEffect(() => {
91
+ setPomoAlarm(userPrefs.alarmSound);
92
+ setPomoAlarmConfig(userPrefs.alarmVolume, userPrefs.alarmRepeat);
93
+ }, [userPrefs.alarmSound, userPrefs.alarmVolume, userPrefs.alarmRepeat]);
94
+ useEffect(() => {
95
+ setPomoBehaviour({
96
+ autoStartBreaks: userPrefs.autoStartBreaks,
97
+ autoStartPomodoros: userPrefs.autoStartPomodoros,
98
+ longBreakInterval: userPrefs.longBreakInterval
99
+ });
100
+ }, [userPrefs.autoStartBreaks, userPrefs.autoStartPomodoros, userPrefs.longBreakInterval]);
101
+ useEffect(() => {
102
+ setPomoFocusSound(userPrefs.focusSound, userPrefs.focusVolume);
103
+ }, [userPrefs.focusSound, userPrefs.focusVolume]);
104
+ useEffect(() => {
105
+ if (typeof Notification !== "undefined" && Notification.permission === "default") {
106
+ try {
107
+ Notification.requestPermission();
108
+ } catch {
109
+ }
110
+ }
111
+ }, []);
112
+ const [tasks, setTasks] = useState(loadTasks);
113
+ const [activeTaskId, setActiveTaskId] = useState(() => localStorage.getItem(ACTIVE_TASK_KEY));
114
+ const [adding, setAdding] = useState(false);
115
+ const [menuOpenId, setMenuOpenId] = useState(null);
116
+ useEffect(() => {
117
+ try {
118
+ localStorage.setItem(TASKS_KEY, JSON.stringify(tasks));
119
+ } catch {
120
+ }
121
+ }, [tasks]);
122
+ useEffect(() => {
123
+ try {
124
+ if (activeTaskId) localStorage.setItem(ACTIVE_TASK_KEY, activeTaskId);
125
+ else localStorage.removeItem(ACTIVE_TASK_KEY);
126
+ } catch {
127
+ }
128
+ }, [activeTaskId]);
129
+ useEffect(() => {
130
+ if (!menuOpenId) return;
131
+ const handler = () => setMenuOpenId(null);
132
+ const t = setTimeout(() => document.addEventListener("click", handler), 0);
133
+ return () => {
134
+ clearTimeout(t);
135
+ document.removeEventListener("click", handler);
136
+ };
137
+ }, [menuOpenId]);
138
+ const lastStreakRef = useRef(snap.streak);
139
+ useEffect(() => {
140
+ if (snap.streak > lastStreakRef.current) {
141
+ setTasks((prev) => {
142
+ const next = prev.map((t) => {
143
+ if (t.id !== activeTaskId) return t;
144
+ const completed = t.completed + 1;
145
+ const shouldAutoCheck = userPrefs.autoCheckTasks && completed >= t.estimated;
146
+ return { ...t, completed, done: t.done || shouldAutoCheck };
147
+ });
148
+ return userPrefs.checkToBottom ? sortDoneToBottom(next) : next;
149
+ });
150
+ }
151
+ lastStreakRef.current = snap.streak;
152
+ }, [snap.streak, activeTaskId, userPrefs.autoCheckTasks, userPrefs.checkToBottom]);
153
+ const addTask = (name, estimated) => {
154
+ const trimmed = name.trim();
155
+ if (!trimmed) return;
156
+ const t = { id: uid(), name: trimmed, estimated, completed: 0, done: false };
157
+ setTasks((prev) => [...prev, t]);
158
+ if (!activeTaskId) setActiveTaskId(t.id);
159
+ setAdding(false);
160
+ };
161
+ const removeTask = (id) => {
162
+ setTasks((prev) => prev.filter((t) => t.id !== id));
163
+ if (activeTaskId === id) setActiveTaskId(null);
164
+ setMenuOpenId(null);
165
+ };
166
+ const toggleDone = (id) => {
167
+ setTasks((prev) => {
168
+ const next = prev.map((t) => t.id === id ? { ...t, done: !t.done } : t);
169
+ return userPrefs.checkToBottom ? sortDoneToBottom(next) : next;
170
+ });
171
+ };
172
+ const totalCompleted = tasks.reduce((acc, t) => acc + t.completed, 0);
173
+ const totalEstimated = tasks.reduce((acc, t) => acc + Math.max(t.estimated, t.completed), 0);
174
+ const remainingPomos = tasks.reduce((acc, t) => t.done ? acc : acc + Math.max(0, t.estimated - t.completed), 0);
175
+ const remainingSecsFromTimer = snap.running && snap.mode === "focus" ? snap.remaining : 0;
176
+ const remainingSecs = remainingPomos * userPrefs.focusMinutes * 60 + remainingSecsFromTimer;
177
+ const finishAt = new Date(Date.now() + remainingSecs * 1e3);
178
+ const finishAtStr = finishAt.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", hour12: false });
179
+ const totalHours = (remainingSecs / 3600).toFixed(1);
180
+ const activeIdx = activeTaskId ? tasks.findIndex((t) => t.id === activeTaskId) : -1;
181
+ const activeTask = activeIdx >= 0 ? tasks[activeIdx] : 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 rounded-2xl overflow-hidden 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 + 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 }),
286
+ /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: " / " }),
287
+ /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: task.estimated })
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-PRP5CZ3S.js.map
627
+ //# sourceMappingURL=PomodoroTimer-PRP5CZ3S.js.map