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,265 @@
1
+ * { box-sizing: border-box; margin: 0; padding: 0; }
2
+
3
+ html, body {
4
+ width: 100%;
5
+ height: 100%;
6
+ background: #0b1020;
7
+ color: #f7f8fa;
8
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
9
+ overflow: hidden;
10
+ user-select: none;
11
+ }
12
+
13
+ .bg-layer {
14
+ position: fixed;
15
+ inset: 0;
16
+ z-index: 0;
17
+ background-size: cover;
18
+ background-position: center;
19
+ background-repeat: no-repeat;
20
+ filter: blur(3px) brightness(0.8);
21
+ transform: scale(1.05);
22
+ display: none;
23
+ }
24
+
25
+ .bg-layer.has-bg { display: block; }
26
+
27
+ #root {
28
+ position: relative;
29
+ z-index: 1;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ width: 100%;
34
+ height: 100%;
35
+ }
36
+
37
+ .overlay {
38
+ width: min(680px, 92vw);
39
+ padding: 40px 36px;
40
+ text-align: center;
41
+ background: rgba(11, 16, 32, 0.55);
42
+ border-radius: 16px;
43
+ backdrop-filter: blur(8px);
44
+ }
45
+
46
+ .header { margin-bottom: 24px; }
47
+
48
+ .title {
49
+ font-size: 30px;
50
+ font-weight: 600;
51
+ letter-spacing: -0.01em;
52
+ }
53
+
54
+ .subtitle {
55
+ margin-top: 6px;
56
+ font-size: 15px;
57
+ color: #9aa4c0;
58
+ }
59
+
60
+ .progress-track {
61
+ width: 100%;
62
+ height: 6px;
63
+ background: #1e2540;
64
+ border-radius: 999px;
65
+ overflow: hidden;
66
+ margin: 20px 0 24px;
67
+ }
68
+
69
+ .progress-fill {
70
+ height: 100%;
71
+ width: 0%;
72
+ background: linear-gradient(90deg, #4f8cff, #6bd4c0);
73
+ transition: width 0.2s linear;
74
+ }
75
+
76
+ .exercise { min-height: 320px; }
77
+
78
+ .exercise-name {
79
+ font-size: 20px;
80
+ font-weight: 600;
81
+ color: #6bd4c0;
82
+ margin-bottom: 14px;
83
+ }
84
+
85
+ .guide-stage {
86
+ display: flex;
87
+ align-items: center;
88
+ justify-content: center;
89
+ width: 100%;
90
+ height: 240px;
91
+ margin: 0 auto 18px;
92
+ background: rgba(255, 255, 255, 0.03);
93
+ border: 1px solid rgba(255, 255, 255, 0.06);
94
+ border-radius: 12px;
95
+ }
96
+
97
+ .guide-svg {
98
+ width: 100%;
99
+ height: 100%;
100
+ max-width: 480px;
101
+ }
102
+
103
+ .step-text {
104
+ font-size: 18px;
105
+ line-height: 1.5;
106
+ color: #f7f8fa;
107
+ margin-bottom: 12px;
108
+ min-height: 27px;
109
+ }
110
+
111
+ .step-time {
112
+ font-size: 48px;
113
+ font-weight: 300;
114
+ color: #4f8cff;
115
+ font-variant-numeric: tabular-nums;
116
+ }
117
+
118
+ .source {
119
+ margin-top: 20px;
120
+ font-size: 12px;
121
+ color: #6b7494;
122
+ cursor: pointer;
123
+ text-decoration: underline;
124
+ }
125
+
126
+ .source:hover { color: #9aa4c0; }
127
+
128
+ .actions { margin-top: 24px; }
129
+
130
+ .btn-skip {
131
+ background: transparent;
132
+ border: 1px solid #3a4470;
133
+ color: #9aa4c0;
134
+ padding: 8px 18px;
135
+ border-radius: 6px;
136
+ font-size: 14px;
137
+ cursor: pointer;
138
+ transition: all 0.15s ease;
139
+ }
140
+
141
+ .btn-skip:hover {
142
+ border-color: #6bd4c0;
143
+ color: #f7f8fa;
144
+ }
145
+
146
+ /* ===== SVG guide shared ===== */
147
+ .guide-svg .label {
148
+ fill: #9aa4c0;
149
+ font-size: 12px;
150
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
151
+ }
152
+
153
+ .guide-svg .target {
154
+ fill: none;
155
+ stroke: #4f8cff;
156
+ stroke-width: 2;
157
+ opacity: 0.4;
158
+ }
159
+
160
+ .guide-svg .dot {
161
+ fill: #6bd4c0;
162
+ filter: drop-shadow(0 0 8px rgba(107, 212, 192, 0.7));
163
+ }
164
+
165
+ .guide-svg .eye-outline {
166
+ fill: none;
167
+ stroke: #4f8cff;
168
+ stroke-width: 2;
169
+ }
170
+
171
+ .guide-svg .eye-lid {
172
+ fill: #0b1020;
173
+ stroke: #4f8cff;
174
+ stroke-width: 2;
175
+ }
176
+
177
+ /* ===== 20-20-20: dot alternates center -> far-left -> center -> far-right ===== */
178
+ .dot-20-20-20 {
179
+ animation: dotFar 8s ease-in-out infinite;
180
+ }
181
+ @keyframes dotFar {
182
+ 0%, 100% { transform: translate(200px, 120px); }
183
+ 12% { transform: translate(200px, 120px); }
184
+ 25% { transform: translate(60px, 50px); }
185
+ 37% { transform: translate(60px, 50px); }
186
+ 50% { transform: translate(200px, 120px); }
187
+ 62% { transform: translate(200px, 120px); }
188
+ 75% { transform: translate(340px, 50px); }
189
+ 87% { transform: translate(340px, 50px); }
190
+ }
191
+
192
+ /* ===== Conscious blink: eyelid closes/opens ===== */
193
+ .lid-top { animation: lidTop 3s ease-in-out infinite; transform-origin: 200px 80px; }
194
+ .lid-bottom { animation: lidBottom 3s ease-in-out infinite; transform-origin: 200px 160px; }
195
+ @keyframes lidTop {
196
+ 0%, 100% { transform: scaleY(0); }
197
+ 35%, 65% { transform: scaleY(1); }
198
+ }
199
+ @keyframes lidBottom {
200
+ 0%, 100% { transform: scaleY(0); }
201
+ 35%, 65% { transform: scaleY(1); }
202
+ }
203
+
204
+ /* ===== Near-far focus shift: bar fixed at center, scales near (big) <-> far (small) ===== */
205
+ .dot-near-far {
206
+ animation: dotNearFar 8s ease-in-out infinite;
207
+ transform-origin: 200px 120px;
208
+ transform-box: view-box;
209
+ }
210
+ @keyframes dotNearFar {
211
+ 0%, 100% { transform: scale(1); }
212
+ 50% { transform: scale(0.35); }
213
+ }
214
+
215
+ /* ===== Figure-8 tracing: reverse direction halfway through ===== */
216
+ .dot-figure8 {
217
+ animation: dotFigure8 6s ease-in-out infinite alternate;
218
+ }
219
+ @keyframes dotFigure8 {
220
+ 0% { offset-distance: 0%; }
221
+ 100% { offset-distance: 100%; }
222
+ }
223
+
224
+ /* ===== Palming: expanding warm glow circle ===== */
225
+ .palm-glow {
226
+ animation: palmGlow 5s ease-in-out infinite;
227
+ transform-origin: 200px 120px;
228
+ }
229
+ @keyframes palmGlow {
230
+ 0%, 100% { transform: scale(0.7); opacity: 0.5; }
231
+ 50% { transform: scale(1.1); opacity: 0.95; }
232
+ }
233
+
234
+ .palm-breathe-text { animation: breatheText 5s ease-in-out infinite; }
235
+ @keyframes breatheText {
236
+ 0%, 100% { opacity: 0.4; }
237
+ 50% { opacity: 1; }
238
+ }
239
+
240
+ /* ===== Horizontal eye rolls: dot sweeps L -> R -> L (symmetric) ===== */
241
+ .dot-horizontal {
242
+ animation: dotHorizontal 4s ease-in-out infinite;
243
+ }
244
+ @keyframes dotHorizontal {
245
+ 0% { transform: translate(80px, 120px); }
246
+ 50% { transform: translate(320px, 120px); }
247
+ 100% { transform: translate(80px, 120px); }
248
+ }
249
+
250
+ /* ===== Rest fallback: slow breathing circle ===== */
251
+ .dot-rest {
252
+ animation: dotRest 8s ease-in-out infinite;
253
+ transform-origin: 200px 130px;
254
+ }
255
+ @keyframes dotRest {
256
+ 0%, 100% { transform: scale(0.7); opacity: 0.4; }
257
+ 50% { transform: scale(1.15); opacity: 0.85; }
258
+ }
259
+
260
+ @media (prefers-reduced-motion: reduce) {
261
+ .dot-20-20-20, .dot-near-far, .dot-figure8, .dot-horizontal, .dot-rest,
262
+ .lid-top, .lid-bottom, .palm-glow, .palm-breathe-text {
263
+ animation: none;
264
+ }
265
+ }
@@ -0,0 +1,38 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" />
6
+ <title>eye-care break</title>
7
+ <link rel="stylesheet" href="break.css" />
8
+ </head>
9
+ <body>
10
+ <div id="bgLayer" class="bg-layer"></div>
11
+ <div id="root">
12
+ <div class="overlay">
13
+ <div class="header">
14
+ <div class="title" id="title">Time for an eye break</div>
15
+ <div class="subtitle" id="subtitle">Follow the guided exercises below.</div>
16
+ </div>
17
+ <div class="progress-track"><div class="progress-fill" id="progress"></div></div>
18
+
19
+ <div class="exercise">
20
+ <div class="exercise-name" id="exerciseName"></div>
21
+
22
+ <div class="guide-stage" id="guideStage">
23
+ <svg class="guide-svg" id="guideSvg" viewBox="0 0 400 240" preserveAspectRatio="xMidYMid meet" aria-hidden="true"></svg>
24
+ </div>
25
+
26
+ <div class="step-text" id="stepText"></div>
27
+ <div class="step-time" id="stepTime"></div>
28
+ </div>
29
+
30
+ <div class="source" id="source"></div>
31
+ <div class="actions">
32
+ <button id="skipBtn" class="btn-skip">Skip break</button>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ <script src="break.js"></script>
37
+ </body>
38
+ </html>