@jjlmoya/utils-science 1.25.0 → 1.27.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 (57) hide show
  1. package/package.json +1 -1
  2. package/src/category/index.ts +3 -1
  3. package/src/entries.ts +5 -1
  4. package/src/index.ts +2 -1
  5. package/src/tests/locale_completeness.test.ts +2 -3
  6. package/src/tests/tool_validation.test.ts +2 -2
  7. package/src/tool/natural-selection-drift/bibliography.astro +14 -0
  8. package/src/tool/natural-selection-drift/bibliography.ts +16 -0
  9. package/src/tool/natural-selection-drift/component.astro +104 -0
  10. package/src/tool/natural-selection-drift/entry.ts +29 -0
  11. package/src/tool/natural-selection-drift/i18n/de.ts +65 -0
  12. package/src/tool/natural-selection-drift/i18n/en.ts +180 -0
  13. package/src/tool/natural-selection-drift/i18n/es.ts +64 -0
  14. package/src/tool/natural-selection-drift/i18n/fr.ts +204 -0
  15. package/src/tool/natural-selection-drift/i18n/id.ts +48 -0
  16. package/src/tool/natural-selection-drift/i18n/it.ts +203 -0
  17. package/src/tool/natural-selection-drift/i18n/ja.ts +48 -0
  18. package/src/tool/natural-selection-drift/i18n/ko.ts +48 -0
  19. package/src/tool/natural-selection-drift/i18n/nl.ts +53 -0
  20. package/src/tool/natural-selection-drift/i18n/pl.ts +48 -0
  21. package/src/tool/natural-selection-drift/i18n/pt.ts +52 -0
  22. package/src/tool/natural-selection-drift/i18n/ru.ts +48 -0
  23. package/src/tool/natural-selection-drift/i18n/sv.ts +48 -0
  24. package/src/tool/natural-selection-drift/i18n/tr.ts +48 -0
  25. package/src/tool/natural-selection-drift/i18n/zh.ts +48 -0
  26. package/src/tool/natural-selection-drift/index.ts +9 -0
  27. package/src/tool/natural-selection-drift/logic.ts +114 -0
  28. package/src/tool/natural-selection-drift/natural-selection-drift.css +429 -0
  29. package/src/tool/natural-selection-drift/render.ts +219 -0
  30. package/src/tool/natural-selection-drift/runtime.ts +89 -0
  31. package/src/tool/natural-selection-drift/seo.astro +15 -0
  32. package/src/tool/natural-selection-drift/simulation.ts +161 -0
  33. package/src/tool/radioactive-decay/bibliography.astro +15 -0
  34. package/src/tool/radioactive-decay/bibliography.ts +17 -0
  35. package/src/tool/radioactive-decay/component.astro +346 -0
  36. package/src/tool/radioactive-decay/entry.ts +26 -0
  37. package/src/tool/radioactive-decay/i18n/de.ts +78 -0
  38. package/src/tool/radioactive-decay/i18n/en.ts +223 -0
  39. package/src/tool/radioactive-decay/i18n/es.ts +106 -0
  40. package/src/tool/radioactive-decay/i18n/fr.ts +78 -0
  41. package/src/tool/radioactive-decay/i18n/id.ts +66 -0
  42. package/src/tool/radioactive-decay/i18n/it.ts +79 -0
  43. package/src/tool/radioactive-decay/i18n/ja.ts +65 -0
  44. package/src/tool/radioactive-decay/i18n/ko.ts +65 -0
  45. package/src/tool/radioactive-decay/i18n/nl.ts +72 -0
  46. package/src/tool/radioactive-decay/i18n/pl.ts +65 -0
  47. package/src/tool/radioactive-decay/i18n/pt.ts +78 -0
  48. package/src/tool/radioactive-decay/i18n/ru.ts +66 -0
  49. package/src/tool/radioactive-decay/i18n/sv.ts +66 -0
  50. package/src/tool/radioactive-decay/i18n/tr.ts +66 -0
  51. package/src/tool/radioactive-decay/i18n/zh.ts +65 -0
  52. package/src/tool/radioactive-decay/index.ts +12 -0
  53. package/src/tool/radioactive-decay/logic.test.ts +20 -0
  54. package/src/tool/radioactive-decay/logic.ts +120 -0
  55. package/src/tool/radioactive-decay/radioactive-decay-half-life-calculator.css +435 -0
  56. package/src/tool/radioactive-decay/seo.astro +16 -0
  57. package/src/tools.ts +4 -2
@@ -0,0 +1,435 @@
1
+ .decay-lab {
2
+ --decay-serif: "Playfair Display", "Bodoni 72", "Didot", georgia, serif;
3
+ --decay-sans: "Inter", "Avenir Next", "Segoe UI", arial, sans-serif;
4
+ --decay-ink: #161a16;
5
+ --decay-muted: #687166;
6
+ --decay-paper: #fbfaf6;
7
+ --decay-panel: rgba(247, 244, 234, 0.72);
8
+ --decay-line: rgba(22, 26, 22, 0.16);
9
+ --decay-live: #219f73;
10
+ --decay-spent: #262b31;
11
+ --decay-hot: #e34d3d;
12
+ --decay-gold: #d5a21b;
13
+ --decay-field-bg: rgba(255, 255, 255, 0.34);
14
+ --decay-control-bg: rgba(255, 255, 255, 0.66);
15
+ --decay-grid-line: rgba(22, 26, 22, 0.045);
16
+ --decay-curve-ghost: rgba(22, 26, 22, 0.12);
17
+ --decay-shadow: rgba(28, 24, 16, 0.12);
18
+
19
+ display: grid;
20
+ gap: clamp(1.75rem, 4vw, 3rem);
21
+ width: min(100%, 1120px);
22
+ padding: clamp(1.25rem, 3vw, 2.75rem);
23
+ color: var(--decay-ink);
24
+ background:
25
+ linear-gradient(90deg, var(--decay-grid-line) 1px, transparent 1px),
26
+ linear-gradient(0deg, var(--decay-grid-line) 1px, transparent 1px),
27
+ radial-gradient(circle at 18% 16%, rgba(227, 77, 61, 0.08), transparent 32%),
28
+ var(--decay-paper);
29
+ background-size: 22px 22px, 22px 22px, auto, auto;
30
+ box-shadow: 0 28px 90px var(--decay-shadow);
31
+ }
32
+
33
+ .theme-dark .decay-lab {
34
+ --decay-ink: #f5f1e3;
35
+ --decay-muted: #aeb8ad;
36
+ --decay-paper: #111611;
37
+ --decay-panel: rgba(27, 35, 29, 0.7);
38
+ --decay-line: rgba(245, 241, 227, 0.17);
39
+ --decay-live: #59dda0;
40
+ --decay-spent: #727883;
41
+ --decay-hot: #ff705c;
42
+ --decay-gold: #f0be45;
43
+ --decay-field-bg: rgba(6, 10, 8, 0.38);
44
+ --decay-control-bg: rgba(21, 28, 24, 0.82);
45
+ --decay-grid-line: rgba(245, 241, 227, 0.055);
46
+ --decay-curve-ghost: rgba(245, 241, 227, 0.15);
47
+ --decay-shadow: rgba(0, 0, 0, 0.36);
48
+ }
49
+
50
+ .decay-stage,
51
+ .decay-console {
52
+ min-width: 0;
53
+ }
54
+
55
+ .decay-stage {
56
+ display: grid;
57
+ align-content: start;
58
+ min-height: 520px;
59
+ }
60
+
61
+ .decay-stage-header {
62
+ display: flex;
63
+ align-items: start;
64
+ justify-content: space-between;
65
+ gap: 1rem;
66
+ margin-bottom: clamp(1rem, 3vw, 2rem);
67
+ }
68
+
69
+ .decay-kicker,
70
+ .decay-field span,
71
+ .decay-results span {
72
+ margin: 0;
73
+ color: var(--decay-muted);
74
+ font-size: 0.72rem;
75
+ font-weight: 500;
76
+ letter-spacing: 0;
77
+ text-transform: uppercase;
78
+ }
79
+
80
+ .decay-kicker {
81
+ color: var(--decay-hot);
82
+ }
83
+
84
+ .decay-stage h2 {
85
+ max-width: 8ch;
86
+ margin: 0.25rem 0 0;
87
+ font-size: 3.6rem;
88
+ font-weight: 700;
89
+ letter-spacing: 0;
90
+ line-height: 0.82;
91
+ }
92
+
93
+ .decay-seed-button,
94
+ .decay-jumps button {
95
+ min-height: 42px;
96
+ border: 0;
97
+ color: var(--decay-ink);
98
+ background: transparent;
99
+ font-size: 0.72rem;
100
+ font-weight: 650;
101
+ letter-spacing: 0;
102
+ text-transform: uppercase;
103
+ cursor: pointer;
104
+ }
105
+
106
+ .decay-seed-button {
107
+ padding: 0.4rem 0;
108
+ border-bottom: 1px solid var(--decay-hot);
109
+ }
110
+
111
+ .decay-curve {
112
+ width: 100%;
113
+ height: auto;
114
+ margin-bottom: clamp(0.8rem, 3vw, 1.6rem);
115
+ overflow: visible;
116
+ }
117
+
118
+ .decay-curve-ghost,
119
+ .decay-curve-line {
120
+ fill: none;
121
+ stroke-linecap: round;
122
+ }
123
+
124
+ .decay-curve-ghost {
125
+ stroke: var(--decay-curve-ghost);
126
+ stroke-width: 2;
127
+ }
128
+
129
+ .decay-curve-line {
130
+ stroke: var(--decay-live);
131
+ stroke-width: 4;
132
+ transition: stroke-dashoffset 400ms cubic-bezier(0.25, 1, 0.5, 1);
133
+ }
134
+
135
+ .decay-curve-marker {
136
+ fill: var(--decay-hot);
137
+ stroke: var(--decay-paper);
138
+ stroke-width: 4;
139
+ filter: drop-shadow(0 0 14px rgba(227, 77, 61, 0.45));
140
+ transition:
141
+ cx 400ms cubic-bezier(0.25, 1, 0.5, 1),
142
+ cy 400ms cubic-bezier(0.25, 1, 0.5, 1),
143
+ opacity 400ms ease;
144
+ }
145
+
146
+ .decay-field-art {
147
+ width: 100%;
148
+ min-height: 280px;
149
+ overflow: visible;
150
+ background:
151
+ radial-gradient(circle at 50% 50%, var(--decay-field-bg), transparent 64%);
152
+ }
153
+
154
+ .decay-atom {
155
+ fill: var(--decay-live);
156
+ filter: drop-shadow(0 0 10px rgba(33, 159, 115, 0.32));
157
+ opacity: 0.94;
158
+ transform-origin: center;
159
+ transform-box: fill-box;
160
+ animation: atom-materialize 420ms cubic-bezier(0.22, 1, 0.36, 1) both;
161
+ animation-delay: var(--delay);
162
+ transition:
163
+ fill 600ms ease,
164
+ opacity 600ms ease,
165
+ filter 600ms ease,
166
+ transform 600ms cubic-bezier(0.25, 1, 0.5, 1);
167
+ }
168
+
169
+ .decay-atom-spent {
170
+ fill: var(--decay-spent);
171
+ filter: blur(4px) drop-shadow(0 0 4px rgba(38, 43, 49, 0.12));
172
+ opacity: 0.3;
173
+ transform: scale(0.82);
174
+ }
175
+
176
+ .decay-atom-decaying {
177
+ animation: atom-quantum-decay 600ms cubic-bezier(0.25, 1, 0.5, 1) both;
178
+ }
179
+
180
+ .decay-legend {
181
+ display: flex;
182
+ flex-wrap: wrap;
183
+ gap: 1rem;
184
+ color: var(--decay-muted);
185
+ font-size: 0.78rem;
186
+ font-weight: 500;
187
+ }
188
+
189
+ .decay-legend span {
190
+ display: inline-flex;
191
+ align-items: center;
192
+ gap: 0.45rem;
193
+ }
194
+
195
+ .decay-dot {
196
+ width: 0.7rem;
197
+ height: 0.7rem;
198
+ border-radius: 50%;
199
+ }
200
+
201
+ .decay-dot-live {
202
+ background: var(--decay-live);
203
+ }
204
+
205
+ .decay-dot-spent {
206
+ background: var(--decay-spent);
207
+ opacity: 0.48;
208
+ }
209
+
210
+ .decay-console {
211
+ display: grid;
212
+ align-content: start;
213
+ gap: clamp(1.25rem, 3vw, 2rem);
214
+ }
215
+
216
+ .decay-select-block,
217
+ .decay-field {
218
+ display: grid;
219
+ gap: 0.72rem;
220
+ }
221
+
222
+ .decay-field output,
223
+ .decay-results strong {
224
+ font-weight: 700;
225
+ letter-spacing: 0;
226
+ line-height: 0.9;
227
+ max-width: 100%;
228
+ overflow-wrap: anywhere;
229
+ font-variant-numeric: tabular-nums;
230
+ }
231
+
232
+ .decay-field output {
233
+ font-size: 2.15rem;
234
+ }
235
+
236
+ .decay-select-shell {
237
+ position: relative;
238
+ display: block;
239
+ }
240
+
241
+ .decay-select-shell::after {
242
+ position: absolute;
243
+ top: 50%;
244
+ right: 0.05rem;
245
+ width: 0.52rem;
246
+ height: 0.52rem;
247
+ border-right: 1px solid var(--decay-ink);
248
+ border-bottom: 1px solid var(--decay-ink);
249
+ content: "";
250
+ pointer-events: none;
251
+ transform: translateY(-70%) rotate(45deg);
252
+ }
253
+
254
+ .decay-field select {
255
+ width: 100%;
256
+ min-height: 56px;
257
+ padding: 0.15rem 2rem 0.15rem 0;
258
+ border: 0;
259
+ border-bottom: 1px solid var(--decay-line);
260
+ border-radius: 0;
261
+ outline: none;
262
+ appearance: none;
263
+ color: var(--decay-ink);
264
+ background: transparent;
265
+ font-size: 1.7rem;
266
+ font-weight: 700;
267
+ line-height: 1;
268
+ text-overflow: ellipsis;
269
+ }
270
+
271
+ .decay-field select option {
272
+ color: var(--decay-ink);
273
+ background: var(--decay-paper);
274
+ }
275
+
276
+ .theme-dark .decay-field select option {
277
+ color: #f5f1e3;
278
+ background: #111611;
279
+ }
280
+
281
+ .decay-field select::-ms-expand {
282
+ display: none;
283
+ }
284
+
285
+ .decay-field input[type='range'] {
286
+ width: 100%;
287
+ height: 30px;
288
+ margin: 0;
289
+ appearance: none;
290
+ background: transparent;
291
+ }
292
+
293
+ .decay-field input[type='range']::-webkit-slider-runnable-track {
294
+ height: 1px;
295
+ background: var(--decay-line);
296
+ }
297
+
298
+ .decay-field input[type='range']::-webkit-slider-thumb {
299
+ width: 18px;
300
+ height: 18px;
301
+ margin-top: -8.5px;
302
+ border: 3px solid var(--decay-paper);
303
+ border-radius: 50%;
304
+ appearance: none;
305
+ background: var(--decay-hot);
306
+ box-shadow: 0 0 0 1px var(--decay-hot), 0 10px 24px rgba(227, 77, 61, 0.24);
307
+ transition: transform 360ms ease, box-shadow 360ms ease;
308
+ }
309
+
310
+ .decay-field input[type='range']::-moz-range-track {
311
+ height: 1px;
312
+ background: var(--decay-line);
313
+ }
314
+
315
+ .decay-field input[type='range']::-moz-range-thumb {
316
+ width: 18px;
317
+ height: 18px;
318
+ border: 3px solid var(--decay-paper);
319
+ border-radius: 50%;
320
+ background: var(--decay-hot);
321
+ box-shadow: 0 0 0 1px var(--decay-hot), 0 10px 24px rgba(227, 77, 61, 0.24);
322
+ transition: transform 360ms ease, box-shadow 360ms ease;
323
+ }
324
+
325
+ .decay-field input[type='range']:is(:hover, :focus-visible)::-webkit-slider-thumb {
326
+ transform: scale(1.18);
327
+ box-shadow: 0 0 0 1px var(--decay-hot), 0 14px 30px rgba(227, 77, 61, 0.32);
328
+ }
329
+
330
+ .decay-field input[type='range']:is(:hover, :focus-visible)::-moz-range-thumb {
331
+ transform: scale(1.18);
332
+ box-shadow: 0 0 0 1px var(--decay-hot), 0 14px 30px rgba(227, 77, 61, 0.32);
333
+ }
334
+
335
+ .decay-use {
336
+ display: grid;
337
+ min-height: 58px;
338
+ place-items: center;
339
+ padding: 0.9rem 1.15rem;
340
+ color: var(--decay-muted);
341
+ background: var(--decay-panel);
342
+ font-size: 0.86rem;
343
+ font-weight: 450;
344
+ line-height: 1.35;
345
+ text-align: center;
346
+ }
347
+
348
+ .decay-jumps {
349
+ display: flex;
350
+ flex-wrap: wrap;
351
+ gap: 0.8rem;
352
+ }
353
+
354
+ .decay-jumps button {
355
+ padding: 0;
356
+ color: var(--decay-muted);
357
+ }
358
+
359
+ .decay-jumps button:hover {
360
+ color: var(--decay-hot);
361
+ }
362
+
363
+ .decay-results {
364
+ display: grid;
365
+ gap: 1.1rem;
366
+ }
367
+
368
+ .decay-metric {
369
+ display: grid;
370
+ gap: 0.18rem;
371
+ min-width: 0;
372
+ padding: 0;
373
+ }
374
+
375
+ .decay-results strong {
376
+ display: block;
377
+ font-size: 1.95rem;
378
+ }
379
+
380
+ @keyframes atom-materialize {
381
+ from {
382
+ opacity: 0;
383
+ transform: scale(0.62);
384
+ }
385
+ }
386
+
387
+ @keyframes atom-quantum-decay {
388
+ 0% {
389
+ fill: var(--decay-live);
390
+ opacity: 0.94;
391
+ filter: blur(0) drop-shadow(0 0 12px rgba(33, 159, 115, 0.34));
392
+ transform: scale(1);
393
+ }
394
+
395
+ 38% {
396
+ fill: var(--decay-hot);
397
+ opacity: 1;
398
+ filter: blur(0.6px) drop-shadow(0 0 20px rgba(227, 77, 61, 0.36));
399
+ transform: scale(1.15);
400
+ }
401
+
402
+ 100% {
403
+ fill: var(--decay-spent);
404
+ opacity: 0.3;
405
+ filter: blur(4px) drop-shadow(0 0 2px rgba(38, 43, 49, 0.1));
406
+ transform: scale(0.82);
407
+ }
408
+ }
409
+
410
+ @media (min-width: 860px) {
411
+ .decay-lab {
412
+ grid-template-columns: minmax(0, 1.45fr) minmax(310px, 0.72fr);
413
+ align-items: start;
414
+ }
415
+
416
+ .decay-results {
417
+ margin-top: 0.2rem;
418
+ }
419
+
420
+ .decay-stage h2 {
421
+ font-size: 8rem;
422
+ }
423
+
424
+ .decay-field output {
425
+ font-size: 3.05rem;
426
+ }
427
+
428
+ .decay-field select {
429
+ font-size: 2.2rem;
430
+ }
431
+
432
+ .decay-results strong {
433
+ font-size: 2.55rem;
434
+ }
435
+ }
@@ -0,0 +1,16 @@
1
+ ---
2
+ import { SEORenderer } from '@jjlmoya/utils-shared';
3
+ import { radioactiveDecay } from './index';
4
+ import type { KnownLocale } from '../../types';
5
+
6
+ interface Props {
7
+ locale?: KnownLocale;
8
+ }
9
+
10
+ const { locale = 'en' } = Astro.props;
11
+ const content = await radioactiveDecay.i18n[locale]?.();
12
+ if (!content) return null;
13
+ ---
14
+
15
+ {content.seo?.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
16
+
package/src/tools.ts CHANGED
@@ -9,6 +9,8 @@ import { COSMIC_INFLATION_TOOL } from './tool/cosmic-inflation/index';
9
9
  import { TEMPERATURE_TIMELINE_TOOL } from './tool/temperature-timeline/index';
10
10
  import { LORENZ_ATTRACTOR_TOOL } from './tool/lorenz-attractor/index';
11
11
  import { STELLAR_HABITABILITY_ZONE_TOOL } from './tool/stellar-habitability-zone/index';
12
+ import { RADIOACTIVE_DECAY_TOOL } from './tool/radioactive-decay/index';
13
+ import { NATURAL_SELECTION_DRIFT_TOOL } from './tool/natural-selection-drift/index';
12
14
 
13
15
  export const ALL_TOOLS: ToolDefinition[] = [
14
16
  COLONY_COUNTER_TOOL,
@@ -20,6 +22,6 @@ export const ALL_TOOLS: ToolDefinition[] = [
20
22
  TEMPERATURE_TIMELINE_TOOL,
21
23
  LORENZ_ATTRACTOR_TOOL,
22
24
  STELLAR_HABITABILITY_ZONE_TOOL,
25
+ RADIOACTIVE_DECAY_TOOL,
26
+ NATURAL_SELECTION_DRIFT_TOOL,
23
27
  ];
24
-
25
-