eye-care 1.0.0

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 (63) hide show
  1. package/AGENTS.md +48 -0
  2. package/CODE_OF_CONDUCT.md +44 -0
  3. package/CONTRIBUTING.md +67 -0
  4. package/LICENSE +21 -0
  5. package/README.md +178 -0
  6. package/bin/eye-care.js +22 -0
  7. package/build/icon.ico +0 -0
  8. package/build/icon.png +0 -0
  9. package/build/tray.png +0 -0
  10. package/build/tray@2x.png +0 -0
  11. package/out/data/exercises.js +120 -0
  12. package/out/data/exercises.js.map +1 -0
  13. package/out/data/i18n.js +211 -0
  14. package/out/data/i18n.js.map +1 -0
  15. package/out/main/backgrounds.js +180 -0
  16. package/out/main/backgrounds.js.map +1 -0
  17. package/out/main/index.js +258 -0
  18. package/out/main/index.js.map +1 -0
  19. package/out/main/preferences.js +71 -0
  20. package/out/main/preferences.js.map +1 -0
  21. package/out/main/scheduler.js +106 -0
  22. package/out/main/scheduler.js.map +1 -0
  23. package/out/preload/break.js +9 -0
  24. package/out/preload/break.js.map +1 -0
  25. package/out/preload/settings.js +18 -0
  26. package/out/preload/settings.js.map +1 -0
  27. package/out/renderer/backgrounds/forest.svg +13 -0
  28. package/out/renderer/backgrounds/mountains.svg +13 -0
  29. package/out/renderer/backgrounds/sea.svg +13 -0
  30. package/out/renderer/backgrounds/sunny-sky.svg +16 -0
  31. package/out/renderer/backgrounds/sunset.svg +18 -0
  32. package/out/renderer/break.css +265 -0
  33. package/out/renderer/break.html +38 -0
  34. package/out/renderer/break.js +452 -0
  35. package/out/renderer/settings.css +198 -0
  36. package/out/renderer/settings.html +110 -0
  37. package/out/renderer/settings.js +407 -0
  38. package/out/shared/types.js +23 -0
  39. package/out/shared/types.js.map +1 -0
  40. package/package.json +100 -0
  41. package/scripts/copy-static.js +27 -0
  42. package/scripts/make-icon.js +120 -0
  43. package/src/data/exercises.ts +120 -0
  44. package/src/data/i18n.ts +297 -0
  45. package/src/main/backgrounds.ts +144 -0
  46. package/src/main/index.ts +233 -0
  47. package/src/main/preferences.ts +34 -0
  48. package/src/main/scheduler.ts +105 -0
  49. package/src/preload/break.ts +9 -0
  50. package/src/preload/settings.ts +18 -0
  51. package/src/renderer/backgrounds/forest.svg +13 -0
  52. package/src/renderer/backgrounds/mountains.svg +13 -0
  53. package/src/renderer/backgrounds/sea.svg +13 -0
  54. package/src/renderer/backgrounds/sunny-sky.svg +16 -0
  55. package/src/renderer/backgrounds/sunset.svg +18 -0
  56. package/src/renderer/break.css +265 -0
  57. package/src/renderer/break.html +38 -0
  58. package/src/renderer/break.js +452 -0
  59. package/src/renderer/settings.css +198 -0
  60. package/src/renderer/settings.html +110 -0
  61. package/src/renderer/settings.js +407 -0
  62. package/src/shared/types.ts +82 -0
  63. package/tsconfig.json +19 -0
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.t = t;
4
+ exports.getExerciseTranslations = getExerciseTranslations;
5
+ const en = {
6
+ appTitle: "eye-care",
7
+ settingsTitle: "eye-care Settings",
8
+ settingsHint: "Adjust break intervals and durations. Changes are saved automatically.",
9
+ miniBreakSection: "Mini break",
10
+ longBreakSection: "Long break",
11
+ generalSection: "General",
12
+ enableMiniBreaks: "Enable mini breaks",
13
+ enableLongBreaks: "Enable long breaks",
14
+ intervalMinutes: "Interval (minutes)",
15
+ durationSeconds: "Duration (seconds)",
16
+ soundNotifications: "Sound notifications",
17
+ strictMode: "Strict mode (no skip)",
18
+ strictModeHint: "",
19
+ languageLabel: "Language",
20
+ saved: "Saved",
21
+ medicalDisclaimer: "eye-care is for eye-fatigue relief only. It is not a medical device and does not diagnose, treat, or cure any condition. If you experience persistent eye discomfort, consult a licensed ophthalmologist.",
22
+ breakTitleMini: "Mini eye break",
23
+ breakSubtitleMini: "A quick reset for your eyes.",
24
+ breakTitleLong: "Long eye break",
25
+ breakSubtitleLong: "Follow the guided exercises.",
26
+ breakComplete: "Break complete",
27
+ breakCompleteSubtitle: "Great job. Back to work!",
28
+ skipBreak: "Skip break",
29
+ sourcePrefix: "Source: ",
30
+ restEyes: "Rest your eyes",
31
+ restEyesInstruction: "Look away from the screen and relax.",
32
+ trayNextBreak: "Next: ",
33
+ trayPaused: "Paused — resumes in ~",
34
+ trayTakeMiniNow: "Take a mini break now",
35
+ trayTakeLongNow: "Take a long break now",
36
+ trayPause1Hour: "Pause breaks for 1 hour",
37
+ trayResume: "Resume breaks",
38
+ traySettings: "Settings...",
39
+ trayAbout: "About eye-care",
40
+ trayQuit: "Quit",
41
+ noBreakScheduled: "No break scheduled",
42
+ exercise20_20_20: "20-20-20 Rule",
43
+ exerciseConsciousBlink: "Conscious Blinking",
44
+ exerciseNearFarFocus: "Near-Far Focus Shift",
45
+ exerciseFigure8: "Figure-8 Tracing",
46
+ exercisePalming: "Palming",
47
+ exerciseHorizontalRolls: "Horizontal Eye Rolls",
48
+ guideLook20ft: "Look 20 ft (6 m) away",
49
+ guideYou: "you",
50
+ guideFarL: "far L",
51
+ guideFarR: "far R",
52
+ guideSlowBlink: "Slow blink",
53
+ guideNear: "Near",
54
+ guideFar: "Far",
55
+ guideTrace8: "Trace the 8 with your eyes",
56
+ guideWarmPalms: "Warm palms over closed eyes",
57
+ guideBreathe: "breathe",
58
+ guideSweepLR: "Sweep your gaze left to right",
59
+ guideL: "L",
60
+ guideR: "R",
61
+ guideRestEyes: "Rest your eyes",
62
+ step20_20_20: "Look at an object at least 20 feet (6 m) away — out a window or across the room.",
63
+ stepBlinkClose: "Close your eyes gently and softly for 2 seconds.",
64
+ stepBlinkOpen: "Open slowly. Repeat the slow blink at a relaxed pace.",
65
+ stepNearFocus: "Hold your finger 10-15 inches (25-38 cm) from your eyes and focus on it.",
66
+ stepFarFocus: "Shift focus to an object 20 feet (6 m) or farther away.",
67
+ stepNearBack: "Back to your finger.",
68
+ stepFarBack: "Back to the distant object. Repeat smoothly.",
69
+ stepFigure8Imagine: "Imagine a large figure-8 on the wall about 10 feet (3 m) away.",
70
+ stepFigure8Trace: "Slowly trace it one way with your eyes only (head still).",
71
+ stepFigure8Reverse: "Reverse direction and trace it back.",
72
+ stepPalmRub: "Rub your palms together until they feel warm.",
73
+ stepPalmCover: "Close your eyes and cup your palms over them without pressing on the eyeballs.",
74
+ stepPalmBreathe: "Breathe slowly and relax. Keep the position.",
75
+ stepHoriRight: "Look as far right as comfortable (head still).",
76
+ stepHoriLeft: "Slowly move your gaze to the far left.",
77
+ stepHoriCenter: "Back to center.",
78
+ stepHoriRepeat: "Repeat the slow sweep a few more times.",
79
+ };
80
+ const ko = {
81
+ appTitle: "eye-care",
82
+ settingsTitle: "eye-care 설정",
83
+ settingsHint: "휴식 간격과 시간을 조정하세요. 변경사항은 자동 저장됩니다.",
84
+ miniBreakSection: "미니 휴식",
85
+ longBreakSection: "긴 휴식",
86
+ generalSection: "일반",
87
+ enableMiniBreaks: "미니 휴식 사용",
88
+ enableLongBreaks: "긴 휴식 사용",
89
+ intervalMinutes: "간격 (분)",
90
+ durationSeconds: "시간 (초)",
91
+ soundNotifications: "소리 알림",
92
+ strictMode: "엄격 모드 (건너뛰기 금지)",
93
+ strictModeHint: "",
94
+ languageLabel: "언어",
95
+ saved: "저장됨",
96
+ medicalDisclaimer: "eye-care는 눈 피로 완화 목적입니다. 의료기기가 아니며 질환을 진단·치료·완치하지 않습니다. 지속적인 눈 불편함이 있다면 안과 전문의와 상담하세요.",
97
+ breakTitleMini: "미니 눈 휴식",
98
+ breakSubtitleMini: "눈에 빠른 휴식을 주세요.",
99
+ breakTitleLong: "긴 눈 휴식",
100
+ breakSubtitleLong: "가이드를 따라 운동해주세요.",
101
+ breakComplete: "휴식 완료",
102
+ breakCompleteSubtitle: "잘했어요. 다시 일어볼까요!",
103
+ skipBreak: "휴식 건너뛰기",
104
+ sourcePrefix: "출처: ",
105
+ restEyes: "눈 휴식",
106
+ restEyesInstruction: "화면에서 눈을 떼고 편안히 쉬세요.",
107
+ trayNextBreak: "다음: ",
108
+ trayPaused: "일시정지 — 약 ",
109
+ trayTakeMiniNow: "지금 미니 휴식하기",
110
+ trayTakeLongNow: "지금 긴 휴식하기",
111
+ trayPause1Hour: "1시간 동안 휴식 일시정지",
112
+ trayResume: "휴식 재개",
113
+ traySettings: "설정...",
114
+ trayAbout: "eye-care 정보",
115
+ trayQuit: "종료",
116
+ noBreakScheduled: "예정된 휴식 없음",
117
+ exercise20_20_20: "20-20-20 규칙",
118
+ exerciseConsciousBlink: "의식적 깜빡임",
119
+ exerciseNearFarFocus: "원근 촛점 교대",
120
+ exerciseFigure8: "8자 추적",
121
+ exercisePalming: "파밍 (손바닥 덮기)",
122
+ exerciseHorizontalRolls: "좌우 눈 운동",
123
+ guideLook20ft: "20피트(6m) 너머 보기",
124
+ guideYou: "나",
125
+ guideFarL: "왼쪽 먼 곳",
126
+ guideFarR: "오른쪽 먼 곳",
127
+ guideSlowBlink: "천천히 깜빡이기",
128
+ guideNear: "가까이",
129
+ guideFar: "멀리",
130
+ guideTrace8: "눈으로 8자를 따라가세요",
131
+ guideWarmPalms: "따뜻한 손바닥으로 눈 덮기",
132
+ guideBreathe: "호흡",
133
+ guideSweepLR: "시선을 좌에서 우로 천천히",
134
+ guideL: "좌",
135
+ guideR: "우",
136
+ guideRestEyes: "눈 휴식",
137
+ step20_20_20: "20피트(6m) 이상 먼 곳을 바라보세요 — 창밖이나 방 반대편.",
138
+ stepBlinkClose: "부드럽게 2초간 눈을 감으세요.",
139
+ stepBlinkOpen: "천천히 뜨세요. 편안한 속도로 반복합니다.",
140
+ stepNearFocus: "손가락을 눈에서 25-38cm 거리에 두고 응시하세요.",
141
+ stepFarFocus: "20피트(6m) 이상 먼 곳의 사물로 초점을 옮기세요.",
142
+ stepNearBack: "다시 손가락으로.",
143
+ stepFarBack: "다시 먼 곳으로. 부드럽게 반복하세요.",
144
+ stepFigure8Imagine: "약 3m 앞 벽에 큰 8자가 있다고 상상하세요.",
145
+ stepFigure8Trace: "머리는 움직이지 말고 눈만으로 한 방향으로 천천히 따라가세요.",
146
+ stepFigure8Reverse: "반대 방향으로 되돌아 가세요.",
147
+ stepPalmRub: "손바닥을 비벼 따뜻하게 만드세요.",
148
+ stepPalmCover: "눈을 감고 손바닥을 눈 위에 덮으세요 (안구를 누르지 마세요).",
149
+ stepPalmBreathe: "천천히 호흡하며 편안히 유지하세요.",
150
+ stepHoriRight: "머리는 그대로 둔 채 시선을 편안한 오른쪽 끝으로.",
151
+ stepHoriLeft: "시선을 천천히 왼쪽 끝으로 옮기세요.",
152
+ stepHoriCenter: "다시 중앙으로.",
153
+ stepHoriRepeat: "느린 좌우 스윕을 몇 번 더 반복하세요.",
154
+ };
155
+ const DICTS = { en, ko };
156
+ function t(lang, key) {
157
+ return DICTS[lang][key] ?? DICTS.en[key] ?? key;
158
+ }
159
+ function getExerciseTranslations(lang) {
160
+ return {
161
+ "20-20-20": {
162
+ name: t(lang, "exercise20_20_20"),
163
+ shortDescription: lang === "ko" ? "20피트(6m) 너머를 20초간 바라보세요." : "Look at something 20 feet away for 20 seconds.",
164
+ steps: [t(lang, "step20_20_20")],
165
+ },
166
+ "conscious-blink": {
167
+ name: t(lang, "exerciseConsciousBlink"),
168
+ shortDescription: lang === "ko" ? "천천히 깜빡여 눈을 적셔주세요." : "Blink slowly to remoisten your eyes.",
169
+ steps: [t(lang, "stepBlinkClose"), t(lang, "stepBlinkOpen")],
170
+ },
171
+ "near-far-focus": {
172
+ name: t(lang, "exerciseNearFarFocus"),
173
+ shortDescription: lang === "ko" ? "가까운 곳과 먼 곳을 번갈아 응시하세요." : "Alternate focus between a near and distant target.",
174
+ steps: [
175
+ t(lang, "stepNearFocus"),
176
+ t(lang, "stepFarFocus"),
177
+ t(lang, "stepNearBack"),
178
+ t(lang, "stepFarBack"),
179
+ ],
180
+ },
181
+ "figure-eight": {
182
+ name: t(lang, "exerciseFigure8"),
183
+ shortDescription: lang === "ko" ? "상상의 8자를 눈으로 추적하며 안근을 이완하세요." : "Trace an imaginary figure-8 with your eyes to relax eye muscles.",
184
+ steps: [
185
+ t(lang, "stepFigure8Imagine"),
186
+ t(lang, "stepFigure8Trace"),
187
+ t(lang, "stepFigure8Reverse"),
188
+ ],
189
+ },
190
+ "palming": {
191
+ name: t(lang, "exercisePalming"),
192
+ shortDescription: lang === "ko" ? "따뜻한 손바닥으로 감은 눈을 덮어 휴식하세요." : "Cover closed eyes with warm palms to relax.",
193
+ steps: [
194
+ t(lang, "stepPalmRub"),
195
+ t(lang, "stepPalmCover"),
196
+ t(lang, "stepPalmBreathe"),
197
+ ],
198
+ },
199
+ "horizontal-rolls": {
200
+ name: t(lang, "exerciseHorizontalRolls"),
201
+ shortDescription: lang === "ko" ? "느린 좌우 눈 운동으로 피로를 풀어주세요." : "Slow horizontal eye movement to relieve fatigue.",
202
+ steps: [
203
+ t(lang, "stepHoriRight"),
204
+ t(lang, "stepHoriLeft"),
205
+ t(lang, "stepHoriCenter"),
206
+ t(lang, "stepHoriRepeat"),
207
+ ],
208
+ },
209
+ };
210
+ }
211
+ //# sourceMappingURL=i18n.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.js","sourceRoot":"","sources":["../../src/data/i18n.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ensureUserBackgroundsDir = ensureUserBackgroundsDir;
37
+ exports.listUserBackgrounds = listUserBackgrounds;
38
+ exports.listBuiltinBackgrounds = listBuiltinBackgrounds;
39
+ exports.resolveBackgroundPath = resolveBackgroundPath;
40
+ exports.pickRandomBackground = pickRandomBackground;
41
+ exports.registerBackgroundIpc = registerBackgroundIpc;
42
+ const electron_1 = require("electron");
43
+ const path = __importStar(require("path"));
44
+ const fs = __importStar(require("fs"));
45
+ const BUILTIN_BACKGROUNDS = ["sunny-sky", "forest", "sea", "mountains", "sunset"];
46
+ function userBackgroundsDir() {
47
+ return path.join(electron_1.app.getPath("userData"), "backgrounds");
48
+ }
49
+ function builtinBackgroundsDir() {
50
+ return path.join(__dirname, "..", "renderer", "backgrounds");
51
+ }
52
+ function ensureUserBackgroundsDir() {
53
+ const dir = userBackgroundsDir();
54
+ if (!fs.existsSync(dir))
55
+ fs.mkdirSync(dir, { recursive: true });
56
+ }
57
+ function listUserBackgrounds() {
58
+ const dir = userBackgroundsDir();
59
+ if (!fs.existsSync(dir))
60
+ return [];
61
+ return fs
62
+ .readdirSync(dir)
63
+ .filter((f) => /\.(png|jpe?g|webp|gif|svg)$/i.test(f))
64
+ .map((f) => path.parse(f).name);
65
+ }
66
+ function listBuiltinBackgrounds() {
67
+ return BUILTIN_BACKGROUNDS;
68
+ }
69
+ function resolveBackgroundPath(name, mode) {
70
+ if (mode === "builtin") {
71
+ const file = path.join(builtinBackgroundsDir(), name + ".svg");
72
+ return fs.existsSync(file) ? file : null;
73
+ }
74
+ if (mode === "user") {
75
+ const dir = userBackgroundsDir();
76
+ if (!fs.existsSync(dir))
77
+ return null;
78
+ const candidates = fs
79
+ .readdirSync(dir)
80
+ .filter((f) => path.parse(f).name === name && /\.(png|jpe?g|webp|gif|svg)$/i.test(f));
81
+ if (candidates.length > 0)
82
+ return path.join(dir, candidates[0]);
83
+ }
84
+ return null;
85
+ }
86
+ function pickRandomBackground(pool) {
87
+ let poolNames = [];
88
+ if (pool === "builtin" || pool === "all") {
89
+ poolNames = poolNames.concat(listBuiltinBackgrounds().map((n) => ({ name: n, mode: "builtin" })));
90
+ }
91
+ if (pool === "user" || pool === "all") {
92
+ poolNames = poolNames.concat(listUserBackgrounds().map((n) => ({ name: n, mode: "user" })));
93
+ }
94
+ if (poolNames.length === 0)
95
+ return null;
96
+ const pick = poolNames[Math.floor(Math.random() * poolNames.length)];
97
+ const file = resolveBackgroundPath(pick.name, pick.mode);
98
+ return file;
99
+ }
100
+ const ALLOWED_EXT = [".png", ".jpg", ".jpeg", ".webp", ".gif", ".svg"];
101
+ function registerBackgroundIpc(getConfig, setConfig) {
102
+ ensureUserBackgroundsDir();
103
+ electron_1.ipcMain.handle("bg:listBuiltin", () => listBuiltinBackgrounds());
104
+ electron_1.ipcMain.handle("bg:listUser", () => listUserBackgrounds());
105
+ electron_1.ipcMain.handle("bg:addUser", async (e) => {
106
+ const parentWindow = electron_1.BrowserWindow.fromWebContents(e.sender);
107
+ const opts = {
108
+ title: "Select background image",
109
+ properties: ["openFile", "multiSelections"],
110
+ filters: [
111
+ { name: "Images", extensions: ["png", "jpg", "jpeg", "webp", "gif", "svg"] },
112
+ ],
113
+ };
114
+ const result = parentWindow
115
+ ? await electron_1.dialog.showOpenDialog(parentWindow, opts)
116
+ : await electron_1.dialog.showOpenDialog(opts);
117
+ if (result.canceled || result.filePaths.length === 0)
118
+ return { added: [] };
119
+ const dir = userBackgroundsDir();
120
+ const added = [];
121
+ for (const src of result.filePaths) {
122
+ const ext = path.extname(src).toLowerCase();
123
+ if (!ALLOWED_EXT.includes(ext))
124
+ continue;
125
+ const base = path.parse(src).name.replace(/[^\w-]/g, "_");
126
+ const dest = path.join(dir, base + ext);
127
+ try {
128
+ fs.copyFileSync(src, dest);
129
+ added.push(base);
130
+ }
131
+ catch (err) {
132
+ console.error("Failed to copy background:", err);
133
+ }
134
+ }
135
+ if (added.length > 0) {
136
+ const cfg = getConfig();
137
+ const merged = Array.from(new Set([...cfg.userImages, ...added]));
138
+ setConfig({ ...cfg, userImages: merged });
139
+ }
140
+ return { added };
141
+ });
142
+ electron_1.ipcMain.handle("bg:deleteUser", (_e, name) => {
143
+ const dir = userBackgroundsDir();
144
+ const candidates = fs
145
+ .readdirSync(dir)
146
+ .filter((f) => path.parse(f).name === name && /\.(png|jpe?g|webp|gif|svg)$/i.test(f));
147
+ for (const c of candidates) {
148
+ try {
149
+ fs.unlinkSync(path.join(dir, c));
150
+ }
151
+ catch (err) {
152
+ console.error("Failed to delete background:", err);
153
+ }
154
+ }
155
+ const cfg = getConfig();
156
+ setConfig({ ...cfg, userImages: cfg.userImages.filter((n) => n !== name) });
157
+ return true;
158
+ });
159
+ electron_1.ipcMain.handle("bg:set", (_e, cfg) => {
160
+ setConfig(cfg);
161
+ return cfg;
162
+ });
163
+ electron_1.ipcMain.handle("bg:get", () => getConfig());
164
+ electron_1.ipcMain.handle("bg:loadFile", (_e, name, mode) => {
165
+ const p = resolveBackgroundPath(name, mode);
166
+ if (!p)
167
+ return null;
168
+ try {
169
+ const data = fs.readFileSync(p);
170
+ const ext = path.extname(p).slice(1).toLowerCase();
171
+ const mime = ext === "svg" ? "image/svg+xml" : "image/" + ext;
172
+ return { mime, data: data.toString("base64") };
173
+ }
174
+ catch (err) {
175
+ console.error("Failed to load background file:", err);
176
+ return null;
177
+ }
178
+ });
179
+ }
180
+ //# sourceMappingURL=backgrounds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backgrounds.js","sourceRoot":"","sources":["../../src/main/backgrounds.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const electron_1 = require("electron");
37
+ const path = __importStar(require("path"));
38
+ const fs = __importStar(require("fs"));
39
+ const types_1 = require("../shared/types");
40
+ const exercises_1 = require("../data/exercises");
41
+ const i18n_1 = require("../data/i18n");
42
+ const preferences_1 = require("./preferences");
43
+ const scheduler_1 = require("./scheduler");
44
+ const backgrounds_1 = require("./backgrounds");
45
+ let tray = null;
46
+ let breakWindow = null;
47
+ let settingsWindow = null;
48
+ let scheduler = null;
49
+ let preferences = { ...types_1.DEFAULT_PREFERENCES };
50
+ function windowOptionsForOverlay() {
51
+ return {
52
+ width: 600,
53
+ height: 400,
54
+ fullscreen: true,
55
+ alwaysOnTop: true,
56
+ frame: false,
57
+ movable: false,
58
+ resizable: false,
59
+ skipTaskbar: true,
60
+ show: false,
61
+ backgroundColor: "#0b1020",
62
+ webPreferences: {
63
+ preload: path.join(__dirname, "..", "preload", "break.js"),
64
+ contextIsolation: true,
65
+ nodeIntegration: false,
66
+ sandbox: false,
67
+ },
68
+ };
69
+ }
70
+ function showBreakWindow(plan) {
71
+ if (breakWindow && !breakWindow.isDestroyed()) {
72
+ breakWindow.close();
73
+ }
74
+ breakWindow = new electron_1.BrowserWindow(windowOptionsForOverlay());
75
+ breakWindow.loadFile(path.join(__dirname, "..", "renderer", "break.html"));
76
+ breakWindow.once("ready-to-show", () => {
77
+ breakWindow?.webContents.send("break:start", plan);
78
+ breakWindow?.show();
79
+ });
80
+ breakWindow.on("closed", () => {
81
+ breakWindow = null;
82
+ scheduler?.resumeAfterBreak();
83
+ });
84
+ }
85
+ function buildBreakPlan(type) {
86
+ const duration = type === "mini"
87
+ ? preferences.miniBreakDurationSeconds
88
+ : preferences.longBreakDurationSeconds;
89
+ let bgData = null;
90
+ if (preferences.background.mode !== "none") {
91
+ let file = null;
92
+ if (preferences.background.mode === "random") {
93
+ const pool = preferences.background.randomPool || "all";
94
+ file = (0, backgrounds_1.pickRandomBackground)(pool);
95
+ }
96
+ else {
97
+ file = (0, backgrounds_1.resolveBackgroundPath)(preferences.background.selected, preferences.background.mode);
98
+ }
99
+ if (file) {
100
+ try {
101
+ const buf = fs.readFileSync(file);
102
+ const ext = path.extname(file).slice(1).toLowerCase();
103
+ const mime = ext === "svg" ? "image/svg+xml" : "image/" + ext;
104
+ bgData = { mime, base64: buf.toString("base64") };
105
+ }
106
+ catch (err) {
107
+ console.error("Failed to read background:", err);
108
+ }
109
+ }
110
+ }
111
+ return {
112
+ type,
113
+ totalDurationSeconds: duration,
114
+ language: preferences.language,
115
+ soundEnabled: preferences.soundEnabled,
116
+ background: bgData ? { ...bgData, blurStrength: preferences.background.blurStrength ?? 3 } : null,
117
+ exercises: (0, exercises_1.exercisesForBreak)(type, duration),
118
+ };
119
+ }
120
+ function showSettingsWindow() {
121
+ if (settingsWindow && !settingsWindow.isDestroyed()) {
122
+ settingsWindow.focus();
123
+ return;
124
+ }
125
+ settingsWindow = new electron_1.BrowserWindow({
126
+ width: 520,
127
+ height: 640,
128
+ title: "eye-care — Settings",
129
+ resizable: false,
130
+ backgroundColor: "#f7f8fa",
131
+ webPreferences: {
132
+ preload: path.join(__dirname, "..", "preload", "settings.js"),
133
+ contextIsolation: true,
134
+ nodeIntegration: false,
135
+ sandbox: false,
136
+ },
137
+ });
138
+ settingsWindow.loadFile(path.join(__dirname, "..", "renderer", "settings.html"));
139
+ settingsWindow.on("closed", () => {
140
+ settingsWindow = null;
141
+ });
142
+ }
143
+ function buildTrayMenu() {
144
+ const lang = preferences.language;
145
+ const tt = (k) => (0, i18n_1.t)(lang, k);
146
+ const nextLabel = scheduler?.nextBreakLabel(lang) ?? tt("noBreakScheduled");
147
+ return electron_1.Menu.buildFromTemplate([
148
+ { label: tt("appTitle"), enabled: false },
149
+ { type: "separator" },
150
+ { label: nextLabel, enabled: false },
151
+ { type: "separator" },
152
+ {
153
+ label: tt("trayTakeMiniNow"),
154
+ click: () => scheduler?.triggerNow("mini"),
155
+ },
156
+ {
157
+ label: tt("trayTakeLongNow"),
158
+ click: () => scheduler?.triggerNow("long"),
159
+ },
160
+ { type: "separator" },
161
+ {
162
+ label: tt("trayPause1Hour"),
163
+ click: () => scheduler?.pauseFor(60 * 60 * 1000),
164
+ },
165
+ {
166
+ label: tt("trayResume"),
167
+ click: () => scheduler?.resume(),
168
+ },
169
+ { type: "separator" },
170
+ {
171
+ label: tt("traySettings"),
172
+ click: () => showSettingsWindow(),
173
+ },
174
+ {
175
+ label: tt("trayAbout"),
176
+ click: () => {
177
+ electron_1.shell.openExternal("https://github.com/uptodatelabs/eye-care");
178
+ },
179
+ },
180
+ { type: "separator" },
181
+ {
182
+ label: tt("trayQuit"),
183
+ click: () => electron_1.app.quit(),
184
+ },
185
+ ]);
186
+ }
187
+ function updateTrayMenu() {
188
+ if (tray)
189
+ tray.setContextMenu(buildTrayMenu());
190
+ }
191
+ function createTray() {
192
+ const iconPath = path.join(__dirname, "..", "..", "build", "tray.png");
193
+ let icon = electron_1.nativeImage.createEmpty();
194
+ if (fs.existsSync(iconPath)) {
195
+ icon = electron_1.nativeImage.createFromPath(iconPath);
196
+ if (process.platform === "darwin") {
197
+ icon.setTemplateImage(true);
198
+ }
199
+ }
200
+ tray = new electron_1.Tray(icon);
201
+ tray.setToolTip("eye-care");
202
+ tray.on("double-click", () => showSettingsWindow());
203
+ updateTrayMenu();
204
+ }
205
+ function registerIpc() {
206
+ electron_1.ipcMain.handle("prefs:get", () => preferences);
207
+ electron_1.ipcMain.handle("prefs:update", (_e, next) => {
208
+ preferences = { ...preferences, ...next };
209
+ (0, preferences_1.savePreferences)(preferences);
210
+ scheduler?.applyPreferences(preferences);
211
+ updateTrayMenu();
212
+ return preferences;
213
+ });
214
+ electron_1.ipcMain.handle("app:info", () => {
215
+ return {
216
+ version: electron_1.app.getVersion(),
217
+ author: "uptodatelabs",
218
+ name: "eye-care",
219
+ homepage: "https://github.com/uptodatelabs/eye-care",
220
+ };
221
+ });
222
+ electron_1.ipcMain.handle("break:skip", () => {
223
+ if (breakWindow && !breakWindow.isDestroyed())
224
+ breakWindow.close();
225
+ });
226
+ electron_1.ipcMain.handle("break:openSource", (_e, url) => {
227
+ if (/^https:\/\//.test(url))
228
+ electron_1.shell.openExternal(url);
229
+ });
230
+ }
231
+ electron_1.app.whenReady().then(() => {
232
+ electron_1.Menu.setApplicationMenu(null);
233
+ preferences = (0, preferences_1.loadPreferences)();
234
+ createTray();
235
+ registerIpc();
236
+ (0, backgrounds_1.registerBackgroundIpc)(() => preferences.background, (c) => {
237
+ preferences.background = c;
238
+ (0, preferences_1.savePreferences)(preferences);
239
+ });
240
+ scheduler = new scheduler_1.Scheduler(preferences, (type) => {
241
+ showBreakWindow(buildBreakPlan(type));
242
+ updateTrayMenu();
243
+ });
244
+ scheduler.start();
245
+ if (preferences.firstRun) {
246
+ showSettingsWindow();
247
+ preferences.firstRun = false;
248
+ (0, preferences_1.savePreferences)(preferences);
249
+ }
250
+ });
251
+ electron_1.app.on("window-all-closed", () => {
252
+ // Do nothing — keep app running in tray. Prevents default quit behavior.
253
+ });
254
+ electron_1.app.on("before-quit", () => {
255
+ scheduler?.stop();
256
+ tray?.destroy();
257
+ });
258
+ //# sourceMappingURL=index.js.map