@jjlmoya/utils-science 1.30.0 → 1.31.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 (30) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +2 -1
  3. package/src/entries.ts +3 -1
  4. package/src/index.ts +1 -0
  5. package/src/tests/locale_completeness.test.ts +2 -2
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/twin-paradox-visualizer/bibliography.astro +14 -0
  8. package/src/tool/twin-paradox-visualizer/bibliography.ts +12 -0
  9. package/src/tool/twin-paradox-visualizer/component.astro +205 -0
  10. package/src/tool/twin-paradox-visualizer/entry.ts +26 -0
  11. package/src/tool/twin-paradox-visualizer/i18n/de.ts +168 -0
  12. package/src/tool/twin-paradox-visualizer/i18n/en.ts +168 -0
  13. package/src/tool/twin-paradox-visualizer/i18n/es.ts +168 -0
  14. package/src/tool/twin-paradox-visualizer/i18n/fr.ts +168 -0
  15. package/src/tool/twin-paradox-visualizer/i18n/id.ts +168 -0
  16. package/src/tool/twin-paradox-visualizer/i18n/it.ts +168 -0
  17. package/src/tool/twin-paradox-visualizer/i18n/ja.ts +168 -0
  18. package/src/tool/twin-paradox-visualizer/i18n/ko.ts +168 -0
  19. package/src/tool/twin-paradox-visualizer/i18n/nl.ts +168 -0
  20. package/src/tool/twin-paradox-visualizer/i18n/pl.ts +168 -0
  21. package/src/tool/twin-paradox-visualizer/i18n/pt.ts +168 -0
  22. package/src/tool/twin-paradox-visualizer/i18n/ru.ts +168 -0
  23. package/src/tool/twin-paradox-visualizer/i18n/sv.ts +168 -0
  24. package/src/tool/twin-paradox-visualizer/i18n/tr.ts +168 -0
  25. package/src/tool/twin-paradox-visualizer/i18n/zh.ts +168 -0
  26. package/src/tool/twin-paradox-visualizer/index.ts +11 -0
  27. package/src/tool/twin-paradox-visualizer/logic.ts +33 -0
  28. package/src/tool/twin-paradox-visualizer/seo.astro +15 -0
  29. package/src/tool/twin-paradox-visualizer/twin-paradox-visualizer.css +395 -0
  30. package/src/tools.ts +2 -0
@@ -0,0 +1,395 @@
1
+ :root {
2
+ --twin-bg: #f8fbff;
3
+ --twin-ink: #112033;
4
+ --twin-muted: #536276;
5
+ --twin-panel: #fff;
6
+ --twin-line: rgba(17, 32, 51, 0.09);
7
+ --twin-rule: rgba(0, 0, 0, 0.05);
8
+ --twin-track: #e2e8f0;
9
+ --twin-blue: #183f8f;
10
+ --twin-red: #b92d3a;
11
+ --twin-gold: #c8a24d;
12
+ --twin-green: #16a085;
13
+ --twin-shadow: 0 24px 70px rgba(24, 54, 92, 0.16);
14
+ }
15
+
16
+ .theme-dark {
17
+ --twin-bg: #091014;
18
+ --twin-ink: #eef6ff;
19
+ --twin-muted: #b2c1ce;
20
+ --twin-panel: #10191f;
21
+ --twin-line: rgba(238, 246, 255, 0.1);
22
+ --twin-rule: rgba(255, 255, 255, 0.05);
23
+ --twin-track: rgba(238, 246, 255, 0.12);
24
+ --twin-blue: #8fb5ff;
25
+ --twin-red: #ff6b78;
26
+ --twin-gold: #ffd166;
27
+ --twin-green: #45d3b4;
28
+ --twin-shadow: 0 24px 70px rgba(0, 0, 0, 0.38);
29
+ }
30
+
31
+ .twin-lab {
32
+ display: grid;
33
+ gap: 1rem;
34
+ max-width: 1080px;
35
+ margin: 0 auto;
36
+ padding: 1rem;
37
+ color: var(--twin-ink);
38
+ background: var(--twin-bg);
39
+ font-variant-numeric: tabular-nums;
40
+ border: 1px solid var(--twin-line);
41
+ border-radius: 8px;
42
+ box-shadow: var(--twin-shadow);
43
+ }
44
+
45
+ @media (min-width: 860px) {
46
+ .twin-lab {
47
+ grid-template-columns: minmax(0, 1.25fr) minmax(320px, 0.75fr);
48
+ gap: 1.25rem;
49
+ padding: 1.25rem;
50
+ }
51
+ }
52
+
53
+ .twin-viewport,
54
+ .twin-console {
55
+ min-width: 0;
56
+ }
57
+
58
+ .twin-viewport {
59
+ display: grid;
60
+ gap: 1rem;
61
+ }
62
+
63
+ .twin-orbit {
64
+ position: relative;
65
+ min-height: 360px;
66
+ overflow: hidden;
67
+ background:
68
+ linear-gradient(180deg, color-mix(in srgb, var(--twin-panel) 92%, var(--twin-bg)), var(--twin-panel)),
69
+ var(--twin-panel);
70
+ border: 1px solid var(--twin-line);
71
+ border-radius: 8px;
72
+ }
73
+
74
+ .twin-earth,
75
+ .twin-ship-clock {
76
+ position: absolute;
77
+ z-index: 2;
78
+ display: flex;
79
+ flex-direction: column;
80
+ justify-content: space-between;
81
+ box-sizing: border-box;
82
+ width: 132px;
83
+ min-height: 116px;
84
+ padding: 1.25rem 1.5rem;
85
+ background: color-mix(in srgb, var(--twin-panel) 88%, transparent);
86
+ border: 1px solid var(--twin-line);
87
+ border-radius: 8px;
88
+ }
89
+
90
+ .twin-earth {
91
+ left: 1rem;
92
+ top: 1rem;
93
+ }
94
+
95
+ .twin-ship-clock {
96
+ right: 1rem;
97
+ bottom: 1rem;
98
+ }
99
+
100
+ .twin-earth span,
101
+ .twin-ship-clock span,
102
+ .twin-metric span,
103
+ .twin-field span {
104
+ color: var(--twin-muted);
105
+ font-size: 0.74rem;
106
+ font-weight: 700;
107
+ letter-spacing: 0;
108
+ text-transform: uppercase;
109
+ }
110
+
111
+ .twin-earth strong,
112
+ .twin-ship-clock strong {
113
+ font-size: 2rem;
114
+ line-height: 1;
115
+ letter-spacing: 0;
116
+ }
117
+
118
+ .twin-path {
119
+ position: absolute;
120
+ inset: 112px 18px;
121
+ display: flex;
122
+ align-items: center;
123
+ border-bottom: 0;
124
+ }
125
+
126
+ .twin-path::before {
127
+ content: "";
128
+ position: absolute;
129
+ left: 8%;
130
+ right: 8%;
131
+ top: 50%;
132
+ height: 1px;
133
+ background: repeating-linear-gradient(90deg, var(--twin-blue), var(--twin-blue) 10px, transparent 10px, transparent 27px);
134
+ transform: translateY(-50%);
135
+ opacity: 0.52;
136
+ }
137
+
138
+ .twin-turnaround {
139
+ position: absolute;
140
+ left: var(--turnaround-progress, 71%);
141
+ top: 50%;
142
+ width: 1px;
143
+ height: 118px;
144
+ background: var(--twin-red);
145
+ transform: translate(-50%, -50%);
146
+ opacity: 0.48;
147
+ }
148
+
149
+ .twin-turnaround::after {
150
+ content: "";
151
+ position: absolute;
152
+ left: -7px;
153
+ top: -7px;
154
+ width: 14px;
155
+ height: 14px;
156
+ background: var(--twin-red);
157
+ border-radius: 50%;
158
+ }
159
+
160
+ .twin-ship {
161
+ position: absolute;
162
+ left: var(--ship-progress, 71%);
163
+ top: 50%;
164
+ width: 46px;
165
+ height: 22px;
166
+ background: linear-gradient(90deg, color-mix(in srgb, var(--twin-gold) 80%, var(--twin-panel)), var(--twin-red));
167
+ border-radius: 999px 40% 40% 999px;
168
+ transform: translate(-50%, -50%);
169
+ box-shadow: none;
170
+ }
171
+
172
+ .twin-ship::before {
173
+ content: "";
174
+ position: absolute;
175
+ right: -12px;
176
+ top: 6px;
177
+ border-left: 16px solid var(--twin-blue);
178
+ border-top: 5px solid transparent;
179
+ border-bottom: 5px solid transparent;
180
+ }
181
+
182
+ .twin-ship::after {
183
+ content: var(--clock-rate, "50%");
184
+ position: absolute;
185
+ left: 50%;
186
+ top: 32px;
187
+ min-width: 58px;
188
+ padding: 0.2rem 0.35rem;
189
+ color: var(--twin-panel);
190
+ background: var(--twin-ink);
191
+ border-radius: 8px;
192
+ font-size: 0.72rem;
193
+ text-align: center;
194
+ transform: translateX(-50%);
195
+ }
196
+
197
+ .twin-worldline {
198
+ width: 100%;
199
+ min-height: 220px;
200
+ background: linear-gradient(180deg, var(--twin-panel), color-mix(in srgb, var(--twin-panel) 88%, var(--twin-bg)));
201
+ border: 1px solid var(--twin-line);
202
+ border-radius: 8px;
203
+ }
204
+
205
+ .twin-lab-grid circle {
206
+ fill: var(--twin-muted);
207
+ opacity: 0.18;
208
+ transition: opacity 0.18s ease;
209
+ }
210
+
211
+ .twin-earth-line,
212
+ .twin-ship-line {
213
+ fill: none;
214
+ stroke-linecap: round;
215
+ stroke-linejoin: round;
216
+ }
217
+
218
+ .twin-earth-line {
219
+ stroke: var(--twin-green);
220
+ stroke-width: 4;
221
+ opacity: 0.84;
222
+ }
223
+
224
+ .twin-ship-line {
225
+ stroke: var(--twin-red);
226
+ stroke-width: 4;
227
+ opacity: 0.88;
228
+ }
229
+
230
+ .twin-event {
231
+ fill: var(--twin-blue);
232
+ }
233
+
234
+ .twin-event-return {
235
+ fill: var(--twin-red);
236
+ }
237
+
238
+ .twin-console {
239
+ display: grid;
240
+ align-content: start;
241
+ gap: 1rem;
242
+ padding: 1rem 1rem 0.7rem;
243
+ background: color-mix(in srgb, var(--twin-panel) 82%, transparent);
244
+ border-left: 1px solid var(--twin-rule);
245
+ border-radius: 0;
246
+ }
247
+
248
+ .twin-presets {
249
+ display: grid;
250
+ grid-template-columns: repeat(3, minmax(0, 1fr));
251
+ gap: 0.45rem;
252
+ }
253
+
254
+ .twin-presets button {
255
+ min-height: 42px;
256
+ padding: 0.55rem 0.45rem;
257
+ color: var(--twin-ink);
258
+ background: transparent;
259
+ border: 1px solid transparent;
260
+ border-radius: 8px;
261
+ cursor: pointer;
262
+ font-size: 0.78rem;
263
+ opacity: 0.4;
264
+ }
265
+
266
+ .twin-presets button.active,
267
+ .twin-presets button:hover {
268
+ color: var(--twin-ink);
269
+ background: color-mix(in srgb, var(--twin-blue) 9%, transparent);
270
+ border-color: transparent;
271
+ backdrop-filter: blur(8px);
272
+ font-weight: 600;
273
+ opacity: 1;
274
+ }
275
+
276
+ .twin-field {
277
+ display: grid;
278
+ gap: 0.55rem;
279
+ padding-top: 1rem;
280
+ border-top: 1px solid var(--twin-rule);
281
+ }
282
+
283
+ .twin-field output {
284
+ justify-self: start;
285
+ color: var(--twin-blue);
286
+ font-size: 1.55rem;
287
+ font-weight: 800;
288
+ }
289
+
290
+ .twin-field input {
291
+ width: 100%;
292
+ height: 3px;
293
+ appearance: none;
294
+ background: linear-gradient(90deg, var(--twin-blue) 0 var(--fill, 50%), var(--twin-track) var(--fill, 50%) 100%);
295
+ border-radius: 999px;
296
+ cursor: pointer;
297
+ }
298
+
299
+ .twin-field input::-webkit-slider-thumb {
300
+ width: 13px;
301
+ height: 13px;
302
+ appearance: none;
303
+ background: color-mix(in srgb, var(--twin-gold) 72%, transparent);
304
+ border: 1px solid var(--twin-panel);
305
+ border-radius: 50%;
306
+ box-shadow: 0 0 0 1px var(--twin-line);
307
+ }
308
+
309
+ .twin-field input:active::-webkit-slider-thumb,
310
+ .twin-field input:focus-visible::-webkit-slider-thumb {
311
+ background: var(--twin-gold);
312
+ box-shadow: 0 0 0 5px color-mix(in srgb, var(--twin-gold) 24%, transparent);
313
+ }
314
+
315
+ .twin-readout {
316
+ display: grid;
317
+ gap: 1rem;
318
+ padding-top: 1rem;
319
+ border-top: 1px solid var(--twin-rule);
320
+ }
321
+
322
+ .twin-reunion {
323
+ display: grid;
324
+ gap: 0.15rem;
325
+ }
326
+
327
+ .twin-reunion > span {
328
+ color: var(--twin-muted);
329
+ font-size: 0.74rem;
330
+ font-weight: 700;
331
+ text-transform: uppercase;
332
+ }
333
+
334
+ .twin-reunion strong {
335
+ display: flex;
336
+ align-items: baseline;
337
+ gap: 0.45rem;
338
+ color: var(--twin-ink);
339
+ font-size: clamp(3.6rem, 11.8vw, 5.75rem);
340
+ font-weight: 800;
341
+ line-height: 0.9;
342
+ letter-spacing: 0;
343
+ }
344
+
345
+ .twin-reunion small {
346
+ color: var(--twin-muted);
347
+ font-size: clamp(1.45rem, 4vw, 2rem);
348
+ font-weight: 700;
349
+ line-height: 1;
350
+ }
351
+
352
+ .twin-results {
353
+ display: grid;
354
+ gap: 0;
355
+ border-top: 1px solid var(--twin-rule);
356
+ margin-inline: -1rem;
357
+ }
358
+
359
+ .twin-metric {
360
+ display: grid;
361
+ grid-template-columns: minmax(0, 1fr) auto;
362
+ align-items: baseline;
363
+ gap: 0.9rem;
364
+ padding: 1rem;
365
+ background: transparent;
366
+ border-bottom: 1px solid var(--twin-rule);
367
+ border-radius: 0;
368
+ }
369
+
370
+ .twin-metric strong {
371
+ color: var(--twin-ink);
372
+ font-size: 1.1rem;
373
+ font-weight: 800;
374
+ text-align: right;
375
+ }
376
+
377
+ @media (max-width: 520px) {
378
+ .twin-lab {
379
+ padding: 0.7rem;
380
+ }
381
+
382
+ .twin-orbit {
383
+ min-height: 320px;
384
+ }
385
+
386
+ .twin-earth,
387
+ .twin-ship-clock {
388
+ width: 116px;
389
+ padding: 0.7rem;
390
+ }
391
+
392
+ .twin-presets {
393
+ grid-template-columns: 1fr;
394
+ }
395
+ }
package/src/tools.ts CHANGED
@@ -13,6 +13,7 @@ import { RADIOACTIVE_DECAY_TOOL } from './tool/radioactive-decay/index';
13
13
  import { NATURAL_SELECTION_DRIFT_TOOL } from './tool/natural-selection-drift/index';
14
14
  import { ENTROPY_SECOND_LAW_TOOL } from './tool/entropy-second-law/index';
15
15
  import { PHASE_DIAGRAM_CRITICAL_POINTS_TOOL } from './tool/phase-diagram-critical-points/index';
16
+ import { TWIN_PARADOX_VISUALIZER_TOOL } from './tool/twin-paradox-visualizer/index';
16
17
 
17
18
  export const ALL_TOOLS: ToolDefinition[] = [
18
19
  COLONY_COUNTER_TOOL,
@@ -28,4 +29,5 @@ export const ALL_TOOLS: ToolDefinition[] = [
28
29
  NATURAL_SELECTION_DRIFT_TOOL,
29
30
  ENTROPY_SECOND_LAW_TOOL,
30
31
  PHASE_DIAGRAM_CRITICAL_POINTS_TOOL,
32
+ TWIN_PARADOX_VISUALIZER_TOOL,
31
33
  ];