@jjlmoya/utils-chrono 1.2.0 → 1.4.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 (161) hide show
  1. package/package.json +1 -1
  2. package/src/category/ChronoCategorySEO.astro +9 -2
  3. package/src/category/i18n/de.ts +11 -9
  4. package/src/category/i18n/en.ts +11 -9
  5. package/src/category/i18n/fr.ts +11 -9
  6. package/src/category/i18n/id.ts +11 -9
  7. package/src/category/i18n/it.ts +11 -9
  8. package/src/category/i18n/ja.ts +11 -9
  9. package/src/category/i18n/ko.ts +11 -9
  10. package/src/category/i18n/nl.ts +11 -9
  11. package/src/category/i18n/pl.ts +11 -9
  12. package/src/category/i18n/pt.ts +11 -9
  13. package/src/category/i18n/ru.ts +11 -9
  14. package/src/category/i18n/sv.ts +11 -9
  15. package/src/category/i18n/tr.ts +11 -9
  16. package/src/category/i18n/zh.ts +11 -9
  17. package/src/category/index.ts +6 -0
  18. package/src/entries.ts +10 -1
  19. package/src/index.ts +3 -0
  20. package/src/tests/locale_completeness.test.ts +1 -1
  21. package/src/tests/no_h1_in_components.test.ts +1 -1
  22. package/src/tests/tool_validation.test.ts +1 -1
  23. package/src/tool/beat-rate-converter/bibliography.ts +1 -1
  24. package/src/tool/beat-rate-converter/components/ConverterPanel.astro +57 -20
  25. package/src/tool/beat-rate-converter/i18n/en.ts +5 -5
  26. package/src/tool/crown-reference-guide/bibliography.ts +3 -3
  27. package/src/tool/crown-reference-guide/i18n/de.ts +37 -29
  28. package/src/tool/crown-reference-guide/i18n/en.ts +38 -30
  29. package/src/tool/crown-reference-guide/i18n/es.ts +36 -28
  30. package/src/tool/crown-reference-guide/i18n/fr.ts +38 -30
  31. package/src/tool/crown-reference-guide/i18n/id.ts +38 -30
  32. package/src/tool/crown-reference-guide/i18n/it.ts +38 -30
  33. package/src/tool/crown-reference-guide/i18n/ja.ts +38 -30
  34. package/src/tool/crown-reference-guide/i18n/ko.ts +38 -30
  35. package/src/tool/crown-reference-guide/i18n/nl.ts +38 -30
  36. package/src/tool/crown-reference-guide/i18n/pl.ts +38 -30
  37. package/src/tool/crown-reference-guide/i18n/pt.ts +38 -30
  38. package/src/tool/crown-reference-guide/i18n/ru.ts +41 -33
  39. package/src/tool/crown-reference-guide/i18n/sv.ts +38 -30
  40. package/src/tool/crown-reference-guide/i18n/tr.ts +38 -30
  41. package/src/tool/crown-reference-guide/i18n/zh.ts +37 -29
  42. package/src/tool/demagnetizing-timer/i18n/de.ts +1 -1
  43. package/src/tool/demagnetizing-timer/i18n/en.ts +1 -1
  44. package/src/tool/demagnetizing-timer/i18n/es.ts +1 -1
  45. package/src/tool/demagnetizing-timer/i18n/fr.ts +1 -1
  46. package/src/tool/demagnetizing-timer/i18n/id.ts +1 -1
  47. package/src/tool/demagnetizing-timer/i18n/it.ts +1 -1
  48. package/src/tool/demagnetizing-timer/i18n/ja.ts +1 -1
  49. package/src/tool/demagnetizing-timer/i18n/ko.ts +1 -1
  50. package/src/tool/demagnetizing-timer/i18n/nl.ts +1 -1
  51. package/src/tool/demagnetizing-timer/i18n/pl.ts +2 -2
  52. package/src/tool/demagnetizing-timer/i18n/pt.ts +1 -1
  53. package/src/tool/demagnetizing-timer/i18n/ru.ts +7 -7
  54. package/src/tool/demagnetizing-timer/i18n/sv.ts +1 -1
  55. package/src/tool/demagnetizing-timer/i18n/tr.ts +1 -1
  56. package/src/tool/demagnetizing-timer/i18n/zh.ts +1 -1
  57. package/src/tool/lume-color-simulator/bibliography.astro +16 -0
  58. package/src/tool/lume-color-simulator/bibliography.ts +16 -0
  59. package/src/tool/lume-color-simulator/client.ts +186 -0
  60. package/src/tool/lume-color-simulator/component.astro +17 -0
  61. package/src/tool/lume-color-simulator/components/LumePanel.astro +98 -0
  62. package/src/tool/lume-color-simulator/entry.ts +57 -0
  63. package/src/tool/lume-color-simulator/i18n/de.ts +174 -0
  64. package/src/tool/lume-color-simulator/i18n/en.ts +174 -0
  65. package/src/tool/lume-color-simulator/i18n/es.ts +174 -0
  66. package/src/tool/lume-color-simulator/i18n/fr.ts +174 -0
  67. package/src/tool/lume-color-simulator/i18n/id.ts +174 -0
  68. package/src/tool/lume-color-simulator/i18n/it.ts +175 -0
  69. package/src/tool/lume-color-simulator/i18n/ja.ts +174 -0
  70. package/src/tool/lume-color-simulator/i18n/ko.ts +174 -0
  71. package/src/tool/lume-color-simulator/i18n/nl.ts +175 -0
  72. package/src/tool/lume-color-simulator/i18n/pl.ts +174 -0
  73. package/src/tool/lume-color-simulator/i18n/pt.ts +174 -0
  74. package/src/tool/lume-color-simulator/i18n/ru.ts +174 -0
  75. package/src/tool/lume-color-simulator/i18n/sv.ts +174 -0
  76. package/src/tool/lume-color-simulator/i18n/tr.ts +174 -0
  77. package/src/tool/lume-color-simulator/i18n/zh.ts +174 -0
  78. package/src/tool/lume-color-simulator/index.ts +11 -0
  79. package/src/tool/lume-color-simulator/lume-color-simulator.css +208 -0
  80. package/src/tool/lume-color-simulator/seo.astro +16 -0
  81. package/src/tool/moon-phase-visualizer/bibliography.astro +16 -0
  82. package/src/tool/moon-phase-visualizer/bibliography.ts +16 -0
  83. package/src/tool/moon-phase-visualizer/client.ts +243 -0
  84. package/src/tool/moon-phase-visualizer/component.astro +17 -0
  85. package/src/tool/moon-phase-visualizer/components/MoonPanel.astro +63 -0
  86. package/src/tool/moon-phase-visualizer/entry.ts +51 -0
  87. package/src/tool/moon-phase-visualizer/i18n/de.ts +175 -0
  88. package/src/tool/moon-phase-visualizer/i18n/en.ts +175 -0
  89. package/src/tool/moon-phase-visualizer/i18n/es.ts +175 -0
  90. package/src/tool/moon-phase-visualizer/i18n/fr.ts +175 -0
  91. package/src/tool/moon-phase-visualizer/i18n/id.ts +175 -0
  92. package/src/tool/moon-phase-visualizer/i18n/it.ts +176 -0
  93. package/src/tool/moon-phase-visualizer/i18n/ja.ts +175 -0
  94. package/src/tool/moon-phase-visualizer/i18n/ko.ts +175 -0
  95. package/src/tool/moon-phase-visualizer/i18n/nl.ts +176 -0
  96. package/src/tool/moon-phase-visualizer/i18n/pl.ts +175 -0
  97. package/src/tool/moon-phase-visualizer/i18n/pt.ts +175 -0
  98. package/src/tool/moon-phase-visualizer/i18n/ru.ts +175 -0
  99. package/src/tool/moon-phase-visualizer/i18n/sv.ts +175 -0
  100. package/src/tool/moon-phase-visualizer/i18n/tr.ts +175 -0
  101. package/src/tool/moon-phase-visualizer/i18n/zh.ts +175 -0
  102. package/src/tool/moon-phase-visualizer/index.ts +11 -0
  103. package/src/tool/moon-phase-visualizer/moon-phase-visualizer.css +216 -0
  104. package/src/tool/moon-phase-visualizer/seo.astro +16 -0
  105. package/src/tool/power-reserve-estimator/bibliography.ts +2 -2
  106. package/src/tool/power-reserve-estimator/components/EstimatorPanel.astro +146 -39
  107. package/src/tool/power-reserve-estimator/i18n/en.ts +3 -3
  108. package/src/tool/strap-taper-calculator/i18n/en.ts +2 -2
  109. package/src/tool/strap-taper-calculator/i18n/ru.ts +3 -3
  110. package/src/tool/watch-accuracy-tracker/i18n/ru.ts +2 -2
  111. package/src/tool/watch-savings-planner/i18n/en.ts +5 -5
  112. package/src/tool/watch-size-comparator/bibliography.astro +16 -0
  113. package/src/tool/watch-size-comparator/bibliography.ts +16 -0
  114. package/src/tool/watch-size-comparator/client.ts +287 -0
  115. package/src/tool/watch-size-comparator/component.astro +17 -0
  116. package/src/tool/watch-size-comparator/components/WatchForm.astro +121 -0
  117. package/src/tool/watch-size-comparator/drawing/index.ts +79 -0
  118. package/src/tool/watch-size-comparator/drawing/measures.ts +57 -0
  119. package/src/tool/watch-size-comparator/drawing/utils.ts +37 -0
  120. package/src/tool/watch-size-comparator/drawing/watch.ts +78 -0
  121. package/src/tool/watch-size-comparator/entry.ts +62 -0
  122. package/src/tool/watch-size-comparator/i18n/de.ts +189 -0
  123. package/src/tool/watch-size-comparator/i18n/en.ts +189 -0
  124. package/src/tool/watch-size-comparator/i18n/es.ts +189 -0
  125. package/src/tool/watch-size-comparator/i18n/fr.ts +189 -0
  126. package/src/tool/watch-size-comparator/i18n/id.ts +189 -0
  127. package/src/tool/watch-size-comparator/i18n/it.ts +190 -0
  128. package/src/tool/watch-size-comparator/i18n/ja.ts +189 -0
  129. package/src/tool/watch-size-comparator/i18n/ko.ts +189 -0
  130. package/src/tool/watch-size-comparator/i18n/nl.ts +190 -0
  131. package/src/tool/watch-size-comparator/i18n/pl.ts +189 -0
  132. package/src/tool/watch-size-comparator/i18n/pt.ts +189 -0
  133. package/src/tool/watch-size-comparator/i18n/ru.ts +189 -0
  134. package/src/tool/watch-size-comparator/i18n/sv.ts +189 -0
  135. package/src/tool/watch-size-comparator/i18n/tr.ts +189 -0
  136. package/src/tool/watch-size-comparator/i18n/zh.ts +189 -0
  137. package/src/tool/watch-size-comparator/index.ts +11 -0
  138. package/src/tool/watch-size-comparator/seo.astro +16 -0
  139. package/src/tool/watch-size-comparator/watch-size-comparator.css +373 -0
  140. package/src/tool/water-resistance-converter/bibliography.ts +2 -2
  141. package/src/tool/water-resistance-converter/i18n/de.ts +5 -5
  142. package/src/tool/water-resistance-converter/i18n/en.ts +6 -6
  143. package/src/tool/water-resistance-converter/i18n/es.ts +6 -6
  144. package/src/tool/water-resistance-converter/i18n/fr.ts +6 -6
  145. package/src/tool/water-resistance-converter/i18n/id.ts +6 -6
  146. package/src/tool/water-resistance-converter/i18n/it.ts +6 -6
  147. package/src/tool/water-resistance-converter/i18n/ja.ts +6 -6
  148. package/src/tool/water-resistance-converter/i18n/ko.ts +6 -6
  149. package/src/tool/water-resistance-converter/i18n/nl.ts +6 -6
  150. package/src/tool/water-resistance-converter/i18n/pl.ts +6 -6
  151. package/src/tool/water-resistance-converter/i18n/pt.ts +6 -6
  152. package/src/tool/water-resistance-converter/i18n/ru.ts +8 -8
  153. package/src/tool/water-resistance-converter/i18n/sv.ts +6 -6
  154. package/src/tool/water-resistance-converter/i18n/tr.ts +6 -6
  155. package/src/tool/water-resistance-converter/i18n/zh.ts +3 -3
  156. package/src/tool/wrist-presence-calculator/i18n/fr.ts +1 -1
  157. package/src/tool/wrist-presence-calculator/i18n/pl.ts +1 -1
  158. package/src/tool/wrist-presence-calculator/i18n/pt.ts +1 -1
  159. package/src/tool/wrist-presence-calculator/i18n/ru.ts +5 -5
  160. package/src/tool/wrist-presence-calculator/i18n/sv.ts +1 -1
  161. package/src/tools.ts +6 -0
@@ -0,0 +1,243 @@
1
+ const mainEl = document.querySelector('.tool-main-card') as HTMLElement;
2
+ const ui = mainEl ? JSON.parse(mainEl.dataset.ui || '{}') : {};
3
+
4
+ const canvas = document.getElementById('moon-canvas') as HTMLCanvasElement;
5
+ const ctx = canvas.getContext('2d')!;
6
+ const dateInput = document.getElementById('moon-date-input') as HTMLInputElement;
7
+ const todayBtn = document.getElementById('btn-today') as HTMLElement;
8
+ const phaseNameEl = document.getElementById('phase-name') as HTMLElement;
9
+ const infoPhase = document.getElementById('info-phase') as HTMLElement;
10
+ const infoIllumination = document.getElementById('info-illumination') as HTMLElement;
11
+ const infoAge = document.getElementById('info-age') as HTMLElement;
12
+ const infoProgress = document.getElementById('info-progress') as HTMLElement;
13
+ const nextFullEl = document.getElementById('next-full') as HTMLElement;
14
+ const nextNewEl = document.getElementById('next-new') as HTMLElement;
15
+
16
+ const PHASE_NAMES: Record<string, string> = {
17
+ 'New Moon': ui.newMoon || 'New Moon',
18
+ 'Waxing Crescent': ui.waxingCrescent || 'Waxing Crescent',
19
+ 'First Quarter': ui.firstQuarter || 'First Quarter',
20
+ 'Waxing Gibbous': ui.waxingGibbous || 'Waxing Gibbous',
21
+ 'Full Moon': ui.fullMoon || 'Full Moon',
22
+ 'Waning Gibbous': ui.waningGibbous || 'Waning Gibbous',
23
+ 'Last Quarter': ui.lastQuarter || 'Last Quarter',
24
+ 'Waning Crescent': ui.waningCrescent || 'Waning Crescent',
25
+ };
26
+
27
+ const LUNAR_CYCLE = 29.53058867;
28
+
29
+ function julianDay(year: number, month: number, day: number): number {
30
+ const y = month > 2 ? year : year - 1;
31
+ const m = month > 2 ? month : month + 12;
32
+ const a = Math.floor(y / 100);
33
+ const b = 2 - a + Math.floor(a / 4);
34
+ return Math.floor(365.25 * (y + 4716)) + Math.floor(30.6001 * (m + 1)) + day + b - 1524.5;
35
+ }
36
+
37
+ function getPhaseFromProgress(progress: number): string {
38
+ if (progress > 0.97) return 'New Moon';
39
+ const phases: [number, string][] = [
40
+ [0.03, 'New Moon'],
41
+ [0.22, 'Waxing Crescent'],
42
+ [0.28, 'First Quarter'],
43
+ [0.47, 'Waxing Gibbous'],
44
+ [0.53, 'Full Moon'],
45
+ [0.72, 'Waning Gibbous'],
46
+ [0.78, 'Last Quarter'],
47
+ ];
48
+ for (const [threshold, name] of phases) {
49
+ if (progress < threshold) return name;
50
+ }
51
+ return 'Waning Crescent';
52
+ }
53
+
54
+ function getMoonPhase(date: Date): { phase: string; illumination: number; age: number; progress: number } {
55
+ const year = date.getFullYear();
56
+ const month = date.getMonth() + 1;
57
+ const day = date.getDate();
58
+ const jd = julianDay(year, month, day);
59
+ const daysSinceNew = jd - 2451549.5;
60
+ const newMoons = daysSinceNew / LUNAR_CYCLE;
61
+ const age = (newMoons - Math.floor(newMoons)) * LUNAR_CYCLE;
62
+ const progress = age / LUNAR_CYCLE;
63
+
64
+ let illumination: number;
65
+ if (progress <= 0.5) {
66
+ illumination = 2 * progress;
67
+ } else {
68
+ illumination = 2 * (1 - progress);
69
+ }
70
+
71
+ return {
72
+ phase: getPhaseFromProgress(progress),
73
+ illumination: Math.round(illumination * 100),
74
+ age: Math.round(age * 10) / 10,
75
+ progress,
76
+ };
77
+ }
78
+
79
+ function findNextEvent(date: Date, targetPhase: number): Date {
80
+ let offset = 1;
81
+ while (offset < 35) {
82
+ const check = new Date(date);
83
+ check.setDate(check.getDate() + offset);
84
+ const { progress } = getMoonPhase(check);
85
+ if (Math.abs(progress - targetPhase) < 0.015) return check;
86
+ offset += 0.5;
87
+ }
88
+ return date;
89
+ }
90
+
91
+ function drawSkyBackground(w: number, h: number): void {
92
+ const skyGrad = ctx.createLinearGradient(0, 0, 0, h);
93
+ skyGrad.addColorStop(0, '#0a0e1a');
94
+ skyGrad.addColorStop(1, '#141b2d');
95
+ ctx.fillStyle = skyGrad;
96
+ ctx.fillRect(0, 0, w, h);
97
+
98
+ ctx.fillStyle = 'rgba(255,255,255,0.4)';
99
+ const starPositions = [
100
+ [50, 30], [120, 55], [180, 20], [280, 40], [340, 65],
101
+ [70, 140], [330, 150], [150, 170], [290, 170], [50, 190],
102
+ [200, 45], [370, 90], [30, 90], [350, 30], [100, 100],
103
+ ];
104
+ for (const [sx, sy] of starPositions) {
105
+ ctx.beginPath();
106
+ ctx.arc(sx, sy, Math.random() * 1.2 + 0.3, 0, Math.PI * 2);
107
+ ctx.fill();
108
+ }
109
+ }
110
+
111
+ function drawMoonGlow(cx: number, cy: number, radius: number): void {
112
+ ctx.save();
113
+ const glowGrad = ctx.createRadialGradient(cx, cy, radius * 0.5, cx, cy, radius * 1.5);
114
+ glowGrad.addColorStop(0, 'rgba(200, 200, 220, 0.08)');
115
+ glowGrad.addColorStop(1, 'rgba(200, 200, 220, 0)');
116
+ ctx.fillStyle = glowGrad;
117
+ ctx.fillRect(cx - radius * 1.5, cy - radius * 1.5, radius * 3, radius * 3);
118
+ ctx.restore();
119
+ }
120
+
121
+ function drawMoonBase(cx: number, cy: number, radius: number): void {
122
+ ctx.save();
123
+ ctx.shadowColor = 'rgba(200,200,220,0.15)';
124
+ ctx.shadowBlur = 30;
125
+ ctx.shadowOffsetY = 0;
126
+ ctx.beginPath();
127
+ ctx.arc(cx, cy, radius, 0, Math.PI * 2);
128
+ ctx.fillStyle = '#1a1a2e';
129
+ ctx.fill();
130
+ ctx.restore();
131
+
132
+ ctx.beginPath();
133
+ ctx.arc(cx, cy, radius, 0, Math.PI * 2);
134
+ ctx.strokeStyle = 'rgba(200,200,220,0.15)';
135
+ ctx.lineWidth = 1;
136
+ ctx.stroke();
137
+ }
138
+
139
+ function drawMoonCraters(cx: number, cy: number): void {
140
+ ctx.fillStyle = 'rgba(200,190,170,0.15)';
141
+ ctx.beginPath();
142
+ ctx.arc(cx - 20, cy - 20, 15, 0, Math.PI * 2);
143
+ ctx.fill();
144
+ ctx.beginPath();
145
+ ctx.arc(cx + 25, cy + 10, 10, 0, Math.PI * 2);
146
+ ctx.fill();
147
+ ctx.beginPath();
148
+ ctx.arc(cx - 5, cy + 25, 8, 0, Math.PI * 2);
149
+ ctx.fill();
150
+ ctx.beginPath();
151
+ ctx.arc(cx + 40, cy - 15, 12, 0, Math.PI * 2);
152
+ ctx.fill();
153
+ }
154
+
155
+ function drawIlluminatedPortion(cx: number, cy: number, radius: number, progress: number): void {
156
+ ctx.save();
157
+ ctx.beginPath();
158
+ ctx.arc(cx, cy, radius - 1, 0, Math.PI * 2);
159
+ ctx.clip();
160
+
161
+ const isWaxing = progress < 0.5;
162
+ const localProgress = progress <= 0.5 ? progress * 2 : (1 - progress) * 2;
163
+
164
+ if (localProgress > 0.01) {
165
+ const termX = cx + (isWaxing ? -1 : 1) * radius * (1 - localProgress * 2);
166
+ if (Math.abs(termX - cx) < radius) {
167
+ ctx.beginPath();
168
+ ctx.ellipse(cx, cy, radius, radius, 0, 0, Math.PI * 2);
169
+ ctx.clip();
170
+ ctx.beginPath();
171
+ if (isWaxing) {
172
+ ctx.rect(termX, cy - radius, radius * 2, radius * 2);
173
+ } else {
174
+ ctx.rect(cx - radius, cy - radius, radius + (cx - termX), radius * 2);
175
+ }
176
+ ctx.fillStyle = '#f0e6d0';
177
+ ctx.fill();
178
+
179
+ drawMoonCraters(cx, cy);
180
+ }
181
+ }
182
+ ctx.restore();
183
+ }
184
+
185
+ function drawMoonLabel(cx: number, h: number, phase: string, progress: number): void {
186
+ ctx.fillStyle = 'rgba(255,255,255,0.3)';
187
+ ctx.font = '10px sans-serif';
188
+ ctx.textAlign = 'center';
189
+ const localProgress = progress <= 0.5 ? progress * 2 : (1 - progress) * 2;
190
+ ctx.fillText(
191
+ `${phase} \u00b7 ${Math.round(localProgress * 100)}% ${ui.illumination || 'illuminated'}`,
192
+ cx, h - 12
193
+ );
194
+ }
195
+
196
+ function drawMoon(progress: number, phase: string): void {
197
+ const W = canvas.width;
198
+ const H = canvas.height;
199
+ const cx = W / 2;
200
+ const cy = H / 2;
201
+ const radius = 72;
202
+
203
+ drawSkyBackground(W, H);
204
+ drawMoonGlow(cx, cy, radius);
205
+ drawMoonBase(cx, cy, radius);
206
+ drawIlluminatedPortion(cx, cy, radius, progress);
207
+ drawMoonLabel(cx, H, phase, progress);
208
+ }
209
+
210
+ function updateMoon(): void {
211
+ const dateVal = dateInput.value;
212
+ if (!dateVal) return;
213
+ const date = new Date(dateVal + 'T12:00:00');
214
+ const { phase, illumination, age, progress } = getMoonPhase(date);
215
+
216
+ const displayName = PHASE_NAMES[phase] || phase;
217
+ phaseNameEl.textContent = displayName;
218
+ infoPhase.textContent = displayName;
219
+ infoIllumination.textContent = illumination + '%';
220
+ infoAge.textContent = age + ' days';
221
+ infoProgress.textContent = Math.round(progress * 100) + '%';
222
+
223
+ drawMoon(progress, displayName);
224
+
225
+ const nextFull = findNextEvent(date, 0.5);
226
+ const nextNew = findNextEvent(date, 0.0);
227
+ nextFullEl.textContent = nextFull.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
228
+ nextNewEl.textContent = nextNew.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
229
+ }
230
+
231
+ function setToday(): void {
232
+ const now = new Date();
233
+ const y = now.getFullYear();
234
+ const m = String(now.getMonth() + 1).padStart(2, '0');
235
+ const d = String(now.getDate()).padStart(2, '0');
236
+ dateInput.value = `${y}-${m}-${d}`;
237
+ updateMoon();
238
+ }
239
+
240
+ dateInput.addEventListener('change', updateMoon);
241
+ todayBtn.addEventListener('click', setToday);
242
+
243
+ setToday();
@@ -0,0 +1,17 @@
1
+ ---
2
+ import MoonPanel from './components/MoonPanel.astro';
3
+
4
+ interface Props {
5
+ ui: Record<string, string>;
6
+ }
7
+
8
+ const { ui } = Astro.props;
9
+ ---
10
+
11
+ <link href="./moon-phase-visualizer.css" rel="stylesheet" />
12
+
13
+ <div class="tool-main-card" data-ui={JSON.stringify(ui)}>
14
+ <MoonPanel labels={ui} />
15
+ </div>
16
+
17
+ <script src="./client.ts"></script>
@@ -0,0 +1,63 @@
1
+ ---
2
+ interface Props {
3
+ labels: Record<string, string>;
4
+ }
5
+
6
+ const { labels } = Astro.props;
7
+ ---
8
+
9
+ <div class="moon-panel">
10
+ <div class="panel-section">
11
+ <div class="section-label">{labels.selectDate || "Select Date"}</div>
12
+ <div class="date-row">
13
+ <input type="date" id="moon-date-input" />
14
+ <button type="button" class="btn-today" id="btn-today">{labels.today || "Today"}</button>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="moon-canvas-wrapper">
19
+ <canvas id="moon-canvas" width="400" height="220"></canvas>
20
+ </div>
21
+
22
+ <div class="phase-badge" id="phase-badge">
23
+ <svg viewBox="0 0 24 24" width="14" height="14"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" fill="currentColor"/></svg>
24
+ <span id="phase-name">{labels.fullMoon || "Full Moon"}</span>
25
+ </div>
26
+
27
+ <div class="phase-info" id="phase-info">
28
+ <div class="phase-info-card">
29
+ <span class="phase-info-label">{labels.moonPhase || "Phase"}</span>
30
+ <span class="phase-info-value accent" id="info-phase">Full Moon</span>
31
+ </div>
32
+ <div class="phase-info-card">
33
+ <span class="phase-info-label">{labels.illumination || "Illumination"}</span>
34
+ <span class="phase-info-value" id="info-illumination">100%</span>
35
+ </div>
36
+ <div class="phase-info-card">
37
+ <span class="phase-info-label">{labels.moonAge || "Moon Age"}</span>
38
+ <span class="phase-info-value" id="info-age">14.8 days</span>
39
+ </div>
40
+ <div class="phase-info-card">
41
+ <span class="phase-info-label">{labels.days || "Cycle Progress"}</span>
42
+ <span class="phase-info-value" id="info-progress">50%</span>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="upcoming-section">
47
+ <div class="upcoming-row">
48
+ <span class="upcoming-label">{labels.nextFullMoon || "Next Full Moon"}</span>
49
+ <span class="upcoming-date" id="next-full">--</span>
50
+ </div>
51
+ <div class="upcoming-row">
52
+ <span class="upcoming-label">{labels.nextNewMoon || "Next New Moon"}</span>
53
+ <span class="upcoming-date" id="next-new">--</span>
54
+ </div>
55
+ </div>
56
+
57
+ <div class="tip-row">
58
+ <svg class="tip-icon" viewBox="0 0 24 24" width="16" height="16">
59
+ <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" fill="currentColor" />
60
+ </svg>
61
+ <span class="tip-text">{labels.tipContent || "A moon phase complication on a watch tracks the 29.53-day lunar cycle. Most mechanical moon phases are accurate to within one day every 2 years, while some high-end watches achieve百年 accuracy."}</span>
62
+ </div>
63
+ </div>
@@ -0,0 +1,51 @@
1
+ import type { ChronoToolEntry, ToolLocaleContent } from '../../types';
2
+
3
+ export type MoonPhaseVisualizerUI = {
4
+ title: string;
5
+ selectDate: string;
6
+ today: string;
7
+ moonPhase: string;
8
+ illumination: string;
9
+ moonAge: string;
10
+ daysUntilFull: string;
11
+ daysUntilNew: string;
12
+ newMoon: string;
13
+ waxingCrescent: string;
14
+ firstQuarter: string;
15
+ waxingGibbous: string;
16
+ fullMoon: string;
17
+ waningGibbous: string;
18
+ lastQuarter: string;
19
+ waningCrescent: string;
20
+ nextFullMoon: string;
21
+ nextNewMoon: string;
22
+ tipTitle: string;
23
+ tipContent: string;
24
+ days: string;
25
+ day: string;
26
+ };
27
+
28
+ export type MoonPhaseVisualizerLocaleContent = ToolLocaleContent<MoonPhaseVisualizerUI>;
29
+
30
+ export const moonPhaseVisualizer: ChronoToolEntry<MoonPhaseVisualizerUI> = {
31
+ id: 'moon-phase-visualizer',
32
+ icons: { bg: 'mdi:weather-night', fg: 'mdi:brightness-7' },
33
+ i18n: {
34
+ en: () => import('./i18n/en').then((m) => m.content),
35
+ de: () => import('./i18n/de').then((m) => m.content),
36
+ es: () => import('./i18n/es').then((m) => m.content),
37
+ fr: () => import('./i18n/fr').then((m) => m.content),
38
+ id: () => import('./i18n/id').then((m) => m.content),
39
+ it: () => import('./i18n/it').then((m) => m.content),
40
+ ja: () => import('./i18n/ja').then((m) => m.content),
41
+ ko: () => import('./i18n/ko').then((m) => m.content),
42
+ nl: () => import('./i18n/nl').then((m) => m.content),
43
+ pl: () => import('./i18n/pl').then((m) => m.content),
44
+ pt: () => import('./i18n/pt').then((m) => m.content),
45
+ ru: () => import('./i18n/ru').then((m) => m.content),
46
+ sv: () => import('./i18n/sv').then((m) => m.content),
47
+ tr: () => import('./i18n/tr').then((m) => m.content),
48
+ zh: () => import('./i18n/zh').then((m) => m.content),
49
+ },
50
+ };
51
+
@@ -0,0 +1,175 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { MoonPhaseVisualizerUI } from '../entry';
3
+ import { bibliography } from '../bibliography';
4
+
5
+ export const content: ToolLocaleContent<MoonPhaseVisualizerUI> = {
6
+ slug: 'mondphasen-visualizer',
7
+ title: 'Mondphasen–Visualizer – Lunarer Kalender & Uhren–Komplikations–Tool',
8
+ description: 'Visualisiere die aktuelle Mondphase, Beleuchtung, Alter und bevorstehende Voll- und Neumonde. Perfekt zum Einstellen deiner Mondphasen-Komplikation.',
9
+ ui: {
10
+ title: 'Mondphasen–Visualizer',
11
+ selectDate: 'Datum auswählen',
12
+ today: 'Heute',
13
+ moonPhase: 'Mondphase',
14
+ illumination: 'Beleuchtung',
15
+ moonAge: 'Mondalter',
16
+ daysUntilFull: 'Tage bis Vollmond',
17
+ daysUntilNew: 'Tage bis Neumond',
18
+ newMoon: 'Neumond',
19
+ waxingCrescent: 'Zunehmende Sichel',
20
+ firstQuarter: 'Erstes Viertel',
21
+ waxingGibbous: 'Zunehmender Mond',
22
+ fullMoon: 'Vollmond',
23
+ waningGibbous: 'Abnehmender Mond',
24
+ lastQuarter: 'Letztes Viertel',
25
+ waningCrescent: 'Abnehmende Sichel',
26
+ nextFullMoon: 'Nächster Vollmond',
27
+ nextNewMoon: 'Nächster Neumond',
28
+ tipTitle: 'Tipp',
29
+ tipContent: 'Um eine Mondphasenuhr einzustellen, ermittle zunächst die heutige Mondphase mit diesem Tool und drehe dann an der Krone, bis die Mondscheibe übereinstimmt. Die meisten mechanischen Mondphasen sind auf etwa einen Tag Abweichung alle 2 Jahre genau.',
30
+ days: 'Zyklusfortschritt',
31
+ day: 'Tag',
32
+ },
33
+ seo: [
34
+ { type: 'title', text: 'Mondphasen-Visualizer – Mondphasen-Tool & Uhren-Einstellanleitung', level: 2 },
35
+ { type: 'paragraph', html: 'Der Mondphasen-Visualizer zeigt die <strong>aktuelle Mondphase</strong> mit einer realistischen Darstellung des Mondes. Sieh dir <strong>Beleuchtungsprozentsatz, Mondalter, Tage bis zum nächsten Vollmond und nächsten Neumond</strong> an. Entwickelt für Uhrenliebhaber, die ihre Mondphasen-Komplikation präzise einstellen müssen – funktioniert auch als allgemeiner Lunarkalender für jedes beliebige Datum in der Vergangenheit oder Zukunft.' },
36
+ { type: 'title', text: 'Mondphasen-Glossar', level: 3 },
37
+ {
38
+ type: 'glossary', items: [
39
+ { term: 'Neumond', definition: 'Der Mond befindet sich zwischen Erde und Sonne. Die beleuchtete Seite ist von uns abgewandt. 0 % Beleuchtung. Der Beginn des Mondzyklus.' },
40
+ { term: 'Zunehmende Sichel', definition: '1–49 % Beleuchtung. Eine dünne Sichel erscheint auf der rechten Seite (Nordhalbkugel). Der Mond bewegt sich auf das erste Viertel zu.' },
41
+ { term: 'Erstes Viertel', definition: '50 % Beleuchtung. Die Hälfte der sichtbaren Oberfläche ist beleuchtet. Der Mond hat ein Viertel seines Zyklus absolviert.' },
42
+ { term: 'Zunehmender Mond', definition: '51–99 % Beleuchtung. Mehr als die Hälfte ist beleuchtet. Der Mond nähert sich dem Vollmond.' },
43
+ { term: 'Vollmond', definition: '100 % Beleuchtung. Die gesamte sichtbare Oberfläche ist beleuchtet. Der Höhepunkt des Mondzyklus.' },
44
+ { term: 'Abnehmender Mond', definition: '99–51 % Beleuchtung. Der beleuchtete Bereich beginnt von der rechten Seite zu schrumpfen.' },
45
+ { term: 'Letztes Viertel', definition: '50 % Beleuchtung. Die linke Hälfte ist beleuchtet. Drei Viertel des Zyklus sind abgeschlossen.' },
46
+ { term: 'Abnehmende Sichel', definition: '49–1 % Beleuchtung. Eine dünne Sichel auf der linken Seite, bevor der Zyklus zum Neumond zurückkehrt.' },
47
+ ]
48
+ },
49
+ { type: 'title', text: 'So stellst du eine Mondphasenuhr ein', level: 3 },
50
+ {
51
+ type: 'list', items: [
52
+ 'Nutze dieses Tool, um die genaue Mondphase von heute zu ermitteln. Notiere dir den Phasennamen und das visuelle Erscheinungsbild.',
53
+ 'Ziehe an deiner Uhr die Krone in die Mondphasen-Einstellposition (normalerweise Position 2 oder 3).',
54
+ 'Drehe die Krone, bis die Mondscheibe dieselbe Phase wie das Tool anzeigt. Die Vollmond-Markierung befindet sich in der Regel mittig oben auf der Scheibe.',
55
+ 'Vermeide es, die Mondphase zwischen 21 Uhr und 3 Uhr morgens einzustellen, da dann der Datumswechsel-Mechanismus aktiv ist und beschädigt werden kann.',
56
+ 'Die meisten mechanischen Mondphasenuhren verwenden ein 59-Zahnrad, das etwa zwei Mondzyklen (59 Tage) annähert. Das bedeutet, dass sie etwa einen Tag Abweichung alle 2 Jahre ansammeln.',
57
+ 'Für hohe Präzision verwenden einige Uhren (Patek Philippe, A. Lange & Söhne) Zahnräder, die auf einen Tag Abweichung alle 100+ Jahre genau sind.',
58
+ ]
59
+ },
60
+ { type: 'diagnostic', variant: 'warning', title: 'Niemals während des Datumswechsels einstellen', icon: 'mdi:alert', html: 'Stelle die Mondphase (oder das Datum) nicht zwischen etwa <strong>21 Uhr und 3 Uhr morgens</strong> ein. In diesem Zeitfenster ist der Datumswechsel-Mechanismus der Uhr physisch mit dem Datumsrad verbunden. Gewaltsames Drehen an der Krone kann Zahnradzähne abreißen. Wenn du in diesem Zeitraum einstellen musst, drehe zuerst die Zeiger über 3 Uhr hinaus und stelle dann die Mondphase ein.' },
61
+ { type: 'title', text: 'Mechanische vs. hochpräzise Mondphase', level: 3 },
62
+ {
63
+ type: 'table', headers: ['Typ', 'Zahnrad', 'Fehlerakkumulation', '1 Tag Abweichung nach', 'Beispieluhren'], rows: [
64
+ ['Standard / Einsteiger', '59 Zähne', '~1 Tag alle 2 Jahre', '~2,5 Jahre', 'Seiko, Orient, Hamilton, Tissot'],
65
+ ['Mittelklasse', '135 Zähne', '~1 Tag alle 10 Jahre', '~10 Jahre', 'Omega, Tudor, IWC, Breitling'],
66
+ ['Hohe Präzision', 'Getriebezug', '~1 Tag pro 100+ Jahre', '100+ Jahre', 'Patek Philippe, A. Lange & Söhne'],
67
+ ]
68
+ },
69
+ { type: 'tip', title: 'Die Mondscheibe lesen', html: 'Bei den meisten Mondphasenuhren zeigt die Mondscheibe <strong>zwei Monde</strong> gegenüberliegend. Jeder Vollmond richtet sich an der mittig oberen Öffnung aus. Nach einem vollständigen Zyklus (29,5 Tage) nimmt der zweite Mond seinen Platz ein. Deshalb funktioniert ein 59-Zahnrad: Es absolviert eine vollständige Umdrehung in 59 Tagen – genau zwei Mondzyklen.' },
70
+ {
71
+ type: 'summary', title: 'Kurzübersicht', items: [
72
+ 'Der Mondzyklus (Synodischer Monat) beträgt exakt 29,53058867 Tage. Das Tool verwendet diesen Wert für maximale Genauigkeit.',
73
+ 'Acht Phasen wiederholen sich in einem ewigen Kreislauf: Neumond → zunehmende Sichel → erstes Viertel → zunehmender Mond → Vollmond → abnehmender Mond → letztes Viertel → abnehmende Sichel.',
74
+ 'Um eine Mondphasenuhr einzustellen: finde die heutige Phase hier heraus und gleiche sie dann auf der Scheibe deiner Uhr an.',
75
+ 'Zwinge die Krone niemals zwischen 21 Uhr und 3 Uhr. Stelle die Zeit zuerst über 3 Uhr hinaus.',
76
+ 'Standard-Mondphasen mit 59 Zähnen weichen etwa einen Tag alle 2,5 Jahre ab. Die Korrektur erfordert das Weiterdrehen der Scheibe um einen Klick.',
77
+ ]
78
+ },
79
+ ],
80
+ faq: [
81
+ {
82
+ question: 'Wie stelle ich eine Mondphase an meiner Uhr ein?',
83
+ answer: 'Ermittle die heutige Mondphase mit diesem Tool. Drehe die Krone (normalerweise Position 2), bis die Mondscheibe die passende Phase anzeigt. Vermeide das Einstellen zwischen 21 Uhr und 3 Uhr morgens, wenn der Datumsmechanismus aktiv ist.',
84
+ },
85
+ {
86
+ question: 'Wie genau sind mechanische Mondphasenuhren?',
87
+ answer: 'Die meisten verwenden ein 59-Zahnrad, das auf etwa einen Tag alle 2 Jahre genau ist. High-End-Uhren wie Patek Philippe und A. Lange & Söhne erreichen eine Genauigkeit von einem Tag alle 100+ Jahre.',
88
+ },
89
+ {
90
+ question: 'Was ist der Unterschied zwischen einem synodischen und einem siderischen Monat?',
91
+ answer: 'Der siderische Monat (27,3 Tage) ist die Umlaufbahn des Mondes relativ zu den Sternen. Der synodische Monat (29,53 Tage) ist die Zeit zwischen identischen Phasen und wird von Mondphasenuhren nachverfolgt.',
92
+ },
93
+ ],
94
+ bibliography,
95
+ howTo: [
96
+ {
97
+ name: 'Datum auswählen',
98
+ text: 'Nutze die Datumsauswahl, um ein beliebiges Datum in der Vergangenheit oder Zukunft zu wählen und die Mondphase zu sehen.',
99
+ },
100
+ {
101
+ name: 'Phasendaten ablesen',
102
+ text: 'Die Karten zeigen Phasenname, Beleuchtungsprozentsatz, Mondalter und Zyklusfortschritt.',
103
+ },
104
+ {
105
+ name: 'Bevorstehende Ereignisse finden',
106
+ text: 'Die Daten des nächsten Vollmonds und Neumonds werden automatisch angezeigt.',
107
+ },
108
+ {
109
+ name: 'Uhr einstellen',
110
+ text: 'Verwende die angezeigte Phase, um deine Mondphasen-Komplikation präzise einzustellen.',
111
+ },
112
+ ],
113
+ schemas: [
114
+ {
115
+ '@context': 'https://schema.org',
116
+ '@type': 'FAQPage',
117
+ 'mainEntity': [
118
+ {
119
+ '@type': 'Question',
120
+ 'name': 'Wie stelle ich eine Mondphase an meiner Uhr ein?',
121
+ 'acceptedAnswer': {
122
+ '@type': 'Answer',
123
+ 'text': 'Ermittle die heutige Mondphase mit diesem Tool. Drehe die Krone, bis die Mondscheibe die passende Phase anzeigt. Vermeide das Einstellen zwischen 21 Uhr und 3 Uhr morgens.',
124
+ },
125
+ },
126
+ {
127
+ '@type': 'Question',
128
+ 'name': 'Wie genau sind mechanische Mondphasenuhren?',
129
+ 'acceptedAnswer': {
130
+ '@type': 'Answer',
131
+ 'text': 'Die meisten verwenden ein 59-Zahnrad, das auf etwa einen Tag alle 2 Jahre genau ist. High-End-Uhren erreichen eine Genauigkeit von einem Tag alle 100+ Jahre.',
132
+ },
133
+ },
134
+ {
135
+ '@type': 'Question',
136
+ 'name': 'Was ist der Unterschied zwischen einem synodischen und einem siderischen Monat?',
137
+ 'acceptedAnswer': {
138
+ '@type': 'Answer',
139
+ 'text': 'Der siderische Monat (27,3 Tage) ist die Umlaufbahn des Mondes relativ zu den Sternen. Der synodische Monat (29,53 Tage) ist die Zeit zwischen identischen Phasen und wird von Mondphasenuhren nachverfolgt.',
140
+ },
141
+ },
142
+ ],
143
+ },
144
+ {
145
+ '@context': 'https://schema.org',
146
+ '@type': 'SoftwareApplication',
147
+ 'name': 'Mondphasen-Visualizer',
148
+ 'operatingSystem': 'All',
149
+ 'applicationCategory': 'UtilitiesApplication',
150
+ 'browserRequirements': 'Erfordert HTML5 und JavaScript.',
151
+ },
152
+ {
153
+ '@context': 'https://schema.org',
154
+ '@type': 'HowTo',
155
+ 'name': 'So visualisiert man Mondphasen',
156
+ 'step': [
157
+ {
158
+ '@type': 'HowToStep',
159
+ 'name': 'Datum auswählen',
160
+ 'text': 'Nutze die Datumsauswahl, um ein beliebiges Datum zu wählen und die Mondphase zu sehen.',
161
+ },
162
+ {
163
+ '@type': 'HowToStep',
164
+ 'name': 'Phasendaten ablesen',
165
+ 'text': 'Die Karten zeigen Phasenname, Beleuchtung, Mondalter und Zyklusfortschritt.',
166
+ },
167
+ {
168
+ '@type': 'HowToStep',
169
+ 'name': 'Uhr einstellen',
170
+ 'text': 'Verwende die angezeigte Phase, um deine Mondphasen-Komplikation einzustellen.',
171
+ },
172
+ ],
173
+ },
174
+ ],
175
+ };