@jjlmoya/utils-science 1.1.0 → 1.2.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.
- package/package.json +1 -1
- package/src/tool/asteroid-impact/AsteroidImpact.css +52 -26
- package/src/tool/asteroid-impact/component.astro +28 -24
- package/src/tool/asteroid-impact/i18n/es.ts +4 -0
- package/src/tool/cellular-renewal/component.astro +14 -9
- package/src/tool/colony-counter/component.astro +21 -16
- package/src/tool/microwave-detector/component.astro +25 -21
- package/src/tool/microwave-detector/constants.ts +87 -0
- package/src/tool/microwave-detector/i18n/en.ts +12 -0
- package/src/tool/microwave-detector/i18n/es.ts +12 -0
- package/src/tool/microwave-detector/i18n/fr.ts +12 -0
- package/src/tool/microwave-detector/logic/MicrowaveEngine.ts +46 -34
- package/src/tool/simulation-probability/component.astro +20 -12
- package/src/tool/simulation-probability/constants.ts +89 -0
- package/src/tool/simulation-probability/i18n/en.ts +5 -0
- package/src/tool/simulation-probability/i18n/es.ts +5 -0
- package/src/tool/simulation-probability/i18n/fr.ts +5 -0
- package/src/tool/simulation-probability/logic/SimulationEngine.ts +62 -21
package/package.json
CHANGED
|
@@ -29,11 +29,18 @@
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
.theme-dark {
|
|
32
|
+
--asteroid-primary: #ff9f43;
|
|
33
|
+
--asteroid-secondary: #ff6b5b;
|
|
34
|
+
--asteroid-accent: #94a3b8;
|
|
35
|
+
--asteroid-success: #2ecc71;
|
|
36
|
+
--asteroid-warning: #ffa502;
|
|
37
|
+
--asteroid-danger: #ff4757;
|
|
32
38
|
--asteroid-bg-light: #0f172a;
|
|
33
|
-
--asteroid-bg-card: #
|
|
34
|
-
--asteroid-bg-modal: rgba(
|
|
35
|
-
--asteroid-border-light: #
|
|
36
|
-
--asteroid-
|
|
39
|
+
--asteroid-bg-card: #1a2332;
|
|
40
|
+
--asteroid-bg-modal: rgba(26, 35, 50, 0.95);
|
|
41
|
+
--asteroid-border-light: #2d3748;
|
|
42
|
+
--asteroid-border-dark: #0f172a;
|
|
43
|
+
--asteroid-text-primary: #f8f9fa;
|
|
37
44
|
--asteroid-text-secondary: #cbd5e1;
|
|
38
45
|
}
|
|
39
46
|
|
|
@@ -133,6 +140,11 @@
|
|
|
133
140
|
transition: var(--asteroid-transition);
|
|
134
141
|
}
|
|
135
142
|
|
|
143
|
+
.theme-dark .asteroid-gps-btn {
|
|
144
|
+
background: rgba(30, 41, 59, 0.95);
|
|
145
|
+
border: 1px solid rgba(100, 116, 139, 0.4);
|
|
146
|
+
}
|
|
147
|
+
|
|
136
148
|
.asteroid-gps-btn:hover {
|
|
137
149
|
transform: scale(1.05);
|
|
138
150
|
}
|
|
@@ -151,8 +163,15 @@
|
|
|
151
163
|
}
|
|
152
164
|
|
|
153
165
|
@keyframes pulse {
|
|
154
|
-
|
|
155
|
-
|
|
166
|
+
|
|
167
|
+
0%,
|
|
168
|
+
100% {
|
|
169
|
+
opacity: 1;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
50% {
|
|
173
|
+
opacity: 0.5;
|
|
174
|
+
}
|
|
156
175
|
}
|
|
157
176
|
|
|
158
177
|
/* Píldora de Veredicto */
|
|
@@ -172,7 +191,7 @@
|
|
|
172
191
|
|
|
173
192
|
.asteroid-verdict-container {
|
|
174
193
|
background: var(--asteroid-bg-dark);
|
|
175
|
-
color:
|
|
194
|
+
color: var(--asteroid-text-primary);
|
|
176
195
|
padding: 0.75rem 1.25rem;
|
|
177
196
|
border-radius: var(--asteroid-radius-lg);
|
|
178
197
|
box-shadow: var(--asteroid-shadow-xl);
|
|
@@ -251,9 +270,9 @@
|
|
|
251
270
|
}
|
|
252
271
|
|
|
253
272
|
.asteroid-lab-panel {
|
|
254
|
-
background:
|
|
273
|
+
background: var(--asteroid-bg-modal);
|
|
255
274
|
backdrop-filter: blur(32px);
|
|
256
|
-
border: 1px solid
|
|
275
|
+
border: 1px solid var(--asteroid-border-light);
|
|
257
276
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
258
277
|
border-radius: var(--asteroid-radius-lg);
|
|
259
278
|
padding: 1.25rem;
|
|
@@ -263,6 +282,7 @@
|
|
|
263
282
|
pointer-events: auto;
|
|
264
283
|
height: 100%;
|
|
265
284
|
overflow: hidden;
|
|
285
|
+
color: var(--asteroid-text-primary);
|
|
266
286
|
}
|
|
267
287
|
|
|
268
288
|
.asteroid-lab-header {
|
|
@@ -270,7 +290,7 @@
|
|
|
270
290
|
align-items: center;
|
|
271
291
|
justify-content: space-between;
|
|
272
292
|
padding-bottom: 0.5rem;
|
|
273
|
-
border-bottom: 1px solid
|
|
293
|
+
border-bottom: 1px solid var(--asteroid-border-light);
|
|
274
294
|
flex-shrink: 0;
|
|
275
295
|
}
|
|
276
296
|
|
|
@@ -288,7 +308,7 @@
|
|
|
288
308
|
display: flex;
|
|
289
309
|
align-items: center;
|
|
290
310
|
justify-content: center;
|
|
291
|
-
color:
|
|
311
|
+
color: var(--asteroid-text-primary);
|
|
292
312
|
box-shadow: 0 4px 8px rgba(var(--asteroid-primary-rgb), 0.3);
|
|
293
313
|
}
|
|
294
314
|
|
|
@@ -375,7 +395,7 @@
|
|
|
375
395
|
|
|
376
396
|
.asteroid-drag-tooltip-text {
|
|
377
397
|
background: rgba(0, 0, 0, 0.85);
|
|
378
|
-
color:
|
|
398
|
+
color: var(--asteroid-text-primary);
|
|
379
399
|
font-size: 0.65rem;
|
|
380
400
|
font-weight: 700;
|
|
381
401
|
padding: 0.4rem 0.75rem;
|
|
@@ -432,7 +452,7 @@
|
|
|
432
452
|
display: flex;
|
|
433
453
|
align-items: center;
|
|
434
454
|
justify-content: center;
|
|
435
|
-
color:
|
|
455
|
+
color: var(--asteroid-text-primary);
|
|
436
456
|
z-index: 20;
|
|
437
457
|
opacity: 1;
|
|
438
458
|
transition: var(--asteroid-transition);
|
|
@@ -462,8 +482,13 @@
|
|
|
462
482
|
}
|
|
463
483
|
|
|
464
484
|
@keyframes asteroid-spin {
|
|
465
|
-
from {
|
|
466
|
-
|
|
485
|
+
from {
|
|
486
|
+
transform: rotate(0deg);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
to {
|
|
490
|
+
transform: rotate(360deg);
|
|
491
|
+
}
|
|
467
492
|
}
|
|
468
493
|
|
|
469
494
|
.asteroid-params-badge {
|
|
@@ -480,7 +505,7 @@
|
|
|
480
505
|
z-index: 20;
|
|
481
506
|
font-size: 0.7rem;
|
|
482
507
|
font-weight: 700;
|
|
483
|
-
color:
|
|
508
|
+
color: var(--asteroid-text-primary);
|
|
484
509
|
white-space: nowrap;
|
|
485
510
|
opacity: 1;
|
|
486
511
|
transition: opacity var(--asteroid-transition-slow);
|
|
@@ -514,7 +539,7 @@
|
|
|
514
539
|
.asteroid-preset-btn {
|
|
515
540
|
position: relative;
|
|
516
541
|
overflow: hidden;
|
|
517
|
-
background:
|
|
542
|
+
background: var(--asteroid-bg-card);
|
|
518
543
|
border: 1px solid var(--asteroid-border-light);
|
|
519
544
|
border-radius: var(--asteroid-radius-md);
|
|
520
545
|
padding: 0.75rem;
|
|
@@ -673,22 +698,23 @@
|
|
|
673
698
|
|
|
674
699
|
/* Botón Limpiar */
|
|
675
700
|
.asteroid-clear-btn {
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
border-radius: var(--asteroid-radius-md);
|
|
701
|
+
padding: 0.25rem 0.35rem;
|
|
702
|
+
border-radius: 0.35rem;
|
|
679
703
|
background: var(--asteroid-bg-light);
|
|
680
704
|
color: var(--asteroid-text-secondary);
|
|
681
|
-
font-weight:
|
|
682
|
-
font-size: 0.
|
|
705
|
+
font-weight: 600;
|
|
706
|
+
font-size: 0.5rem;
|
|
683
707
|
text-transform: uppercase;
|
|
684
|
-
letter-spacing: 0.
|
|
708
|
+
letter-spacing: 0.04em;
|
|
685
709
|
border: none;
|
|
686
710
|
cursor: pointer;
|
|
687
711
|
transition: var(--asteroid-transition);
|
|
688
712
|
display: flex;
|
|
689
713
|
align-items: center;
|
|
690
714
|
justify-content: center;
|
|
691
|
-
gap: 0.
|
|
715
|
+
gap: 0.2rem;
|
|
716
|
+
height: auto;
|
|
717
|
+
min-height: 1.5rem;
|
|
692
718
|
flex-shrink: 0;
|
|
693
719
|
}
|
|
694
720
|
|
|
@@ -753,7 +779,7 @@
|
|
|
753
779
|
position: absolute;
|
|
754
780
|
top: -0.75rem;
|
|
755
781
|
background: var(--asteroid-bg-dark);
|
|
756
|
-
color:
|
|
782
|
+
color: var(--asteroid-text-primary);
|
|
757
783
|
font-size: 0.5625rem;
|
|
758
784
|
font-weight: 900;
|
|
759
785
|
letter-spacing: 0.125em;
|
|
@@ -796,4 +822,4 @@
|
|
|
796
822
|
width: 1px;
|
|
797
823
|
background: var(--asteroid-border-light);
|
|
798
824
|
margin: 0 0.5rem;
|
|
799
|
-
}
|
|
825
|
+
}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
import "./AsteroidImpact.css";
|
|
3
3
|
import "leaflet/dist/leaflet.css";
|
|
4
4
|
import { Icon } from "astro-icon/components";
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
ui: Record<string, string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { ui } = Astro.props;
|
|
5
11
|
---
|
|
6
12
|
|
|
7
13
|
<div class="asteroid-app" id="asteroid-app">
|
|
@@ -17,7 +23,7 @@ import { Icon } from "astro-icon/components";
|
|
|
17
23
|
<div>
|
|
18
24
|
<button id="asteroid-gps-btn" class="asteroid-gps-btn">
|
|
19
25
|
<div id="asteroid-gps-dot" class="asteroid-gps-dot"></div>
|
|
20
|
-
<span id="asteroid-gps-text">
|
|
26
|
+
<span id="asteroid-gps-text">{ui.activateGPS}</span>
|
|
21
27
|
</button>
|
|
22
28
|
</div>
|
|
23
29
|
|
|
@@ -25,7 +31,7 @@ import { Icon } from "astro-icon/components";
|
|
|
25
31
|
<div id="asteroid-verdict-container" class="asteroid-verdict-container">
|
|
26
32
|
<div id="asteroid-verdict-icon" class="asteroid-verdict-icon"></div>
|
|
27
33
|
<div class="asteroid-verdict-text">
|
|
28
|
-
<div id="asteroid-verdict-label" class="asteroid-verdict-label">
|
|
34
|
+
<div id="asteroid-verdict-label" class="asteroid-verdict-label">{ui.analysisLabel}</div>
|
|
29
35
|
<div id="asteroid-verdict-value" class="asteroid-verdict-value">--</div>
|
|
30
36
|
</div>
|
|
31
37
|
</div>
|
|
@@ -52,12 +58,12 @@ import { Icon } from "astro-icon/components";
|
|
|
52
58
|
<div class="asteroid-drag-source" id="drag-source-desktop">
|
|
53
59
|
<div class="asteroid-drag-bg"></div>
|
|
54
60
|
<div class="asteroid-drag-tooltip">
|
|
55
|
-
<span class="asteroid-drag-tooltip-text">
|
|
61
|
+
<span class="asteroid-drag-tooltip-text">{ui.dragToMap}</span>
|
|
56
62
|
</div>
|
|
57
63
|
<div class="asteroid-params-badge">
|
|
58
|
-
<div id="asteroid-param-size" class="asteroid-param-item">
|
|
59
|
-
<div id="asteroid-param-vel" class="asteroid-param-item">
|
|
60
|
-
<div id="asteroid-param-type" class="asteroid-param-item">Tipo: <span id="asteroid-param-type-val">
|
|
64
|
+
<div id="asteroid-param-size" class="asteroid-param-item">{ui.diameterLabel}: <span id="asteroid-param-size-val">50m</span></div>
|
|
65
|
+
<div id="asteroid-param-vel" class="asteroid-param-item">{ui.velocityLabel}: <span id="asteroid-param-vel-val">15 km/s</span></div>
|
|
66
|
+
<div id="asteroid-param-type" class="asteroid-param-item">Tipo: <span id="asteroid-param-type-val">{ui.rock}</span></div>
|
|
61
67
|
</div>
|
|
62
68
|
<div class="asteroid-drag-icon-parent">
|
|
63
69
|
<Icon name="mdi:drag" class="w-6 h-6" />
|
|
@@ -74,20 +80,20 @@ import { Icon } from "astro-icon/components";
|
|
|
74
80
|
|
|
75
81
|
<div>
|
|
76
82
|
<div class="asteroid-control-label" style="margin-bottom: 1rem;">
|
|
77
|
-
<span class="asteroid-control-text">
|
|
83
|
+
<span class="asteroid-control-text">{ui.historicalData}</span>
|
|
78
84
|
</div>
|
|
79
85
|
<div class="asteroid-presets">
|
|
80
86
|
<button class="asteroid-preset-btn" data-dia="20" data-vel="19" data-comp="rock">
|
|
81
87
|
<div class="asteroid-preset-title">CHELYABINSK</div>
|
|
82
|
-
<div class="asteroid-preset-subtitle">20m ·
|
|
88
|
+
<div class="asteroid-preset-subtitle">20m · {ui.presetAerial}</div>
|
|
83
89
|
</button>
|
|
84
90
|
<button class="asteroid-preset-btn" data-dia="50" data-vel="15" data-comp="rock">
|
|
85
91
|
<div class="asteroid-preset-title">TUNGUSKA</div>
|
|
86
|
-
<div class="asteroid-preset-subtitle">50m ·
|
|
92
|
+
<div class="asteroid-preset-subtitle">50m · {ui.presetForest}</div>
|
|
87
93
|
</button>
|
|
88
94
|
<button class="asteroid-preset-btn" data-dia="200" data-vel="25" data-comp="ice">
|
|
89
95
|
<div class="asteroid-preset-title">3I ATLAS</div>
|
|
90
|
-
<div class="asteroid-preset-subtitle">200m ·
|
|
96
|
+
<div class="asteroid-preset-subtitle">200m · {ui.presetComet}</div>
|
|
91
97
|
</button>
|
|
92
98
|
<button class="asteroid-preset-btn" data-dia="10000" data-vel="20" data-comp="rock">
|
|
93
99
|
<div class="asteroid-preset-title">CHICXULUB</div>
|
|
@@ -98,7 +104,7 @@ import { Icon } from "astro-icon/components";
|
|
|
98
104
|
|
|
99
105
|
<div class="asteroid-control-group">
|
|
100
106
|
<div class="asteroid-control-label">
|
|
101
|
-
<span class="asteroid-control-text">
|
|
107
|
+
<span class="asteroid-control-text">{ui.diameterLabel}</span>
|
|
102
108
|
<span id="display-size" class="asteroid-control-value">100m</span>
|
|
103
109
|
</div>
|
|
104
110
|
<input type="range" id="input-size" min="10" max="5000" step="10" value="5000" class="asteroid-slider" />
|
|
@@ -106,35 +112,33 @@ import { Icon } from "astro-icon/components";
|
|
|
106
112
|
|
|
107
113
|
<div class="asteroid-control-group">
|
|
108
114
|
<div class="asteroid-control-label">
|
|
109
|
-
<span class="asteroid-control-text">
|
|
115
|
+
<span class="asteroid-control-text">{ui.velocityLabel}</span>
|
|
110
116
|
<span id="display-velocity" class="asteroid-control-value">20 km/s</span>
|
|
111
117
|
</div>
|
|
112
118
|
<input type="range" id="input-velocity" min="10" max="70" step="1" value="20" class="asteroid-slider" />
|
|
113
119
|
</div>
|
|
114
120
|
|
|
115
121
|
<div>
|
|
116
|
-
<div class="asteroid-control-text" style="margin-bottom: 0.5rem; display: block;">
|
|
122
|
+
<div class="asteroid-control-text" style="margin-bottom: 0.5rem; display: block;">{ui.composition}</div>
|
|
117
123
|
<div class="asteroid-material-buttons">
|
|
118
124
|
<button class="asteroid-material-btn active" data-type="rock">
|
|
119
125
|
<div class="asteroid-material-dot" style="background: #64748b;"></div>
|
|
120
|
-
<span class="asteroid-material-name">
|
|
126
|
+
<span class="asteroid-material-name">{ui.rock}</span>
|
|
121
127
|
</button>
|
|
122
128
|
<button class="asteroid-material-btn" data-type="iron">
|
|
123
129
|
<div class="asteroid-material-dot" style="background: #1e293b;"></div>
|
|
124
|
-
<span class="asteroid-material-name">
|
|
130
|
+
<span class="asteroid-material-name">{ui.iron}</span>
|
|
125
131
|
</button>
|
|
126
132
|
<button class="asteroid-material-btn" data-type="ice">
|
|
127
133
|
<div class="asteroid-material-dot" style="background: #67e8f9;"></div>
|
|
128
|
-
<span class="asteroid-material-name">
|
|
134
|
+
<span class="asteroid-material-name">{ui.ice}</span>
|
|
129
135
|
</button>
|
|
130
136
|
</div>
|
|
131
137
|
</div>
|
|
132
138
|
|
|
133
139
|
<button id="clear-btn" class="asteroid-clear-btn">
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
</svg>
|
|
137
|
-
Limpiar todo
|
|
140
|
+
<Icon name="mdi:trash-can-outline" class="w-4 h-4" />
|
|
141
|
+
{ui.clearAll}
|
|
138
142
|
</button>
|
|
139
143
|
</div>
|
|
140
144
|
</div>
|
|
@@ -224,18 +228,18 @@ import { Icon } from "astro-icon/components";
|
|
|
224
228
|
|
|
225
229
|
sizeInput?.addEventListener("input", (e) => {
|
|
226
230
|
config.diameter = parseInt((e.target as HTMLInputElement).value);
|
|
227
|
-
updateControlUI();
|
|
231
|
+
updateControlUI(config);
|
|
228
232
|
});
|
|
229
233
|
|
|
230
234
|
velInput?.addEventListener("input", (e) => {
|
|
231
235
|
config.velocity = parseInt((e.target as HTMLInputElement).value);
|
|
232
|
-
updateControlUI();
|
|
236
|
+
updateControlUI(config);
|
|
233
237
|
});
|
|
234
238
|
|
|
235
239
|
matBtns.forEach((btn) => {
|
|
236
240
|
btn.addEventListener("click", () => {
|
|
237
241
|
config.composition = btn.getAttribute("data-type") as Composition;
|
|
238
|
-
updateControlUI();
|
|
242
|
+
updateControlUI(config);
|
|
239
243
|
});
|
|
240
244
|
});
|
|
241
245
|
|
|
@@ -244,7 +248,7 @@ import { Icon } from "astro-icon/components";
|
|
|
244
248
|
config.diameter = parseInt(btn.getAttribute("data-dia")!);
|
|
245
249
|
config.velocity = parseInt(btn.getAttribute("data-vel")!);
|
|
246
250
|
config.composition = btn.getAttribute("data-comp") as Composition;
|
|
247
|
-
updateControlUI();
|
|
251
|
+
updateControlUI(config);
|
|
248
252
|
});
|
|
249
253
|
});
|
|
250
254
|
|
|
@@ -34,6 +34,10 @@ export const content: ToolLocaleContent = {
|
|
|
34
34
|
verdictBurnedSub: 'Peligro extremo',
|
|
35
35
|
verdictVaporized: 'ZONA CERO',
|
|
36
36
|
verdictVaporizedSub: 'Impacto directo',
|
|
37
|
+
presetAerial: 'Aéreo',
|
|
38
|
+
presetForest: 'Bosque',
|
|
39
|
+
presetComet: 'Cometa',
|
|
40
|
+
presetELE: 'E.L.E.',
|
|
37
41
|
},
|
|
38
42
|
seo: [
|
|
39
43
|
{
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
---
|
|
2
|
+
interface Props {
|
|
3
|
+
ui: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { ui } = Astro.props;
|
|
2
7
|
---
|
|
3
8
|
|
|
4
9
|
<div class="cellular-app" id="cellular-app">
|
|
5
10
|
<div class="cellular-card">
|
|
6
11
|
<div class="input-section">
|
|
7
12
|
<div class="input-group">
|
|
8
|
-
<div class="input-label">
|
|
13
|
+
<div class="input-label">{ui.biologicalTimeline}</div>
|
|
9
14
|
<div class="age-display">
|
|
10
15
|
<span id="ageDisplay">25</span>
|
|
11
|
-
<span class="age-unit">
|
|
16
|
+
<span class="age-unit">{ui.ageUnit}</span>
|
|
12
17
|
</div>
|
|
13
18
|
<input
|
|
14
19
|
type="range"
|
|
@@ -24,18 +29,18 @@
|
|
|
24
29
|
|
|
25
30
|
<div class="results-grid">
|
|
26
31
|
<div class="main-stat">
|
|
27
|
-
<span class="main-stat-label">
|
|
32
|
+
<span class="main-stat-label">{ui.matterNewPercent}</span>
|
|
28
33
|
<div class="main-stat-value">
|
|
29
34
|
<span id="newStatVal">93</span>
|
|
30
35
|
<span class="percent-sign">%</span>
|
|
31
36
|
</div>
|
|
32
|
-
<div class="main-stat-sublabel">
|
|
37
|
+
<div class="main-stat-sublabel">{ui.atomicRenewal}</div>
|
|
33
38
|
</div>
|
|
34
39
|
|
|
35
40
|
<div class="tissue-list">
|
|
36
41
|
<div class="tissue-item">
|
|
37
42
|
<div class="tissue-header">
|
|
38
|
-
<span class="tissue-name">
|
|
43
|
+
<span class="tissue-name">{ui.skinAndBlood}</span>
|
|
39
44
|
<span class="tissue-percentage" id="skinVal">100%</span>
|
|
40
45
|
</div>
|
|
41
46
|
<div class="bar-container">
|
|
@@ -45,7 +50,7 @@
|
|
|
45
50
|
|
|
46
51
|
<div class="tissue-item">
|
|
47
52
|
<div class="tissue-header">
|
|
48
|
-
<span class="tissue-name">
|
|
53
|
+
<span class="tissue-name">{ui.boneRemodeling}</span>
|
|
49
54
|
<span class="tissue-percentage" id="boneVal">85%</span>
|
|
50
55
|
</div>
|
|
51
56
|
<div class="bar-container">
|
|
@@ -55,7 +60,7 @@
|
|
|
55
60
|
|
|
56
61
|
<div class="tissue-item">
|
|
57
62
|
<div class="tissue-header">
|
|
58
|
-
<span class="tissue-name">
|
|
63
|
+
<span class="tissue-name">{ui.organicMatrix}</span>
|
|
59
64
|
<span class="tissue-percentage" id="organVal">60%</span>
|
|
60
65
|
</div>
|
|
61
66
|
<div class="bar-container">
|
|
@@ -65,7 +70,7 @@
|
|
|
65
70
|
|
|
66
71
|
<div class="tissue-item">
|
|
67
72
|
<div class="tissue-header">
|
|
68
|
-
<span class="tissue-name">
|
|
73
|
+
<span class="tissue-name">{ui.perennialCells}</span>
|
|
69
74
|
<span class="tissue-percentage" id="brainVal">12%</span>
|
|
70
75
|
</div>
|
|
71
76
|
<div class="bar-container">
|
|
@@ -76,7 +81,7 @@
|
|
|
76
81
|
</div>
|
|
77
82
|
|
|
78
83
|
<p class="disclaimer">
|
|
79
|
-
|
|
84
|
+
{ui.disclaimerText}
|
|
80
85
|
</p>
|
|
81
86
|
</div>
|
|
82
87
|
</div>
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
import "./ColonyCounter.css";
|
|
3
3
|
import { Icon } from "astro-icon/components";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
ui: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { ui } = Astro.props;
|
|
4
10
|
---
|
|
5
11
|
|
|
6
12
|
<div class="colony-container">
|
|
@@ -14,10 +20,10 @@ import { Icon } from "astro-icon/components";
|
|
|
14
20
|
<Icon name="mdi:upload" class="colony-upload-icon" />
|
|
15
21
|
<div class="colony-upload-text">
|
|
16
22
|
<p class="colony-upload-title">
|
|
17
|
-
|
|
23
|
+
{ui.uploadTitle}
|
|
18
24
|
</p>
|
|
19
25
|
<p class="colony-upload-subtitle">
|
|
20
|
-
|
|
26
|
+
{ui.uploadSubtitle}
|
|
21
27
|
</p>
|
|
22
28
|
</div>
|
|
23
29
|
</div>
|
|
@@ -29,10 +35,10 @@ import { Icon } from "astro-icon/components";
|
|
|
29
35
|
|
|
30
36
|
<div id="mode-indicator" class="colony-hidden colony-mode-indicator">
|
|
31
37
|
<p class="colony-mode-label">
|
|
32
|
-
|
|
38
|
+
{ui.currentModeLabel}
|
|
33
39
|
</p>
|
|
34
40
|
<p id="current-mode" class="colony-mode-value">
|
|
35
|
-
|
|
41
|
+
{ui.typeA}
|
|
36
42
|
</p>
|
|
37
43
|
</div>
|
|
38
44
|
</div>
|
|
@@ -41,7 +47,7 @@ import { Icon } from "astro-icon/components";
|
|
|
41
47
|
<div class="colony-control-panel">
|
|
42
48
|
<div class="colony-control-section">
|
|
43
49
|
<h3 class="colony-control-title">
|
|
44
|
-
|
|
50
|
+
{ui.colonyType}
|
|
45
51
|
</h3>
|
|
46
52
|
|
|
47
53
|
<div class="colony-button-grid">
|
|
@@ -50,14 +56,14 @@ import { Icon } from "astro-icon/components";
|
|
|
50
56
|
class="colony-mode-btn colony-mode-btn-active"
|
|
51
57
|
>
|
|
52
58
|
<span class="colony-color-dot colony-color-a"></span>
|
|
53
|
-
|
|
59
|
+
{ui.typeA}
|
|
54
60
|
</button>
|
|
55
61
|
<button
|
|
56
62
|
id="mode-b"
|
|
57
63
|
class="colony-mode-btn"
|
|
58
64
|
>
|
|
59
65
|
<span class="colony-color-dot colony-color-b"></span>
|
|
60
|
-
|
|
66
|
+
{ui.typeB}
|
|
61
67
|
</button>
|
|
62
68
|
</div>
|
|
63
69
|
</div>
|
|
@@ -66,13 +72,13 @@ import { Icon } from "astro-icon/components";
|
|
|
66
72
|
|
|
67
73
|
<div class="colony-control-section">
|
|
68
74
|
<h3 class="colony-control-title">
|
|
69
|
-
|
|
75
|
+
{ui.counting}
|
|
70
76
|
</h3>
|
|
71
77
|
|
|
72
78
|
<div class="colony-count-grid">
|
|
73
79
|
<div class="colony-count-box colony-count-box-a">
|
|
74
80
|
<p class="colony-count-label colony-count-label-a">
|
|
75
|
-
|
|
81
|
+
{ui.typeA}
|
|
76
82
|
</p>
|
|
77
83
|
<p id="count-a" class="colony-count-value">
|
|
78
84
|
0
|
|
@@ -80,7 +86,7 @@ import { Icon } from "astro-icon/components";
|
|
|
80
86
|
</div>
|
|
81
87
|
<div class="colony-count-box colony-count-box-b">
|
|
82
88
|
<p class="colony-count-label colony-count-label-b">
|
|
83
|
-
|
|
89
|
+
{ui.typeB}
|
|
84
90
|
</p>
|
|
85
91
|
<p id="count-b" class="colony-count-value">
|
|
86
92
|
0
|
|
@@ -90,7 +96,7 @@ import { Icon } from "astro-icon/components";
|
|
|
90
96
|
|
|
91
97
|
<div class="colony-count-total">
|
|
92
98
|
<p class="colony-count-label-total">
|
|
93
|
-
|
|
99
|
+
{ui.totalCFU}
|
|
94
100
|
</p>
|
|
95
101
|
<p id="count-total" class="colony-count-value-total">
|
|
96
102
|
0
|
|
@@ -106,7 +112,7 @@ import { Icon } from "astro-icon/components";
|
|
|
106
112
|
class="colony-action-btn colony-undo-btn"
|
|
107
113
|
>
|
|
108
114
|
<Icon name="mdi:undo" class="colony-btn-icon" />
|
|
109
|
-
|
|
115
|
+
{ui.undo}
|
|
110
116
|
</button>
|
|
111
117
|
|
|
112
118
|
<button
|
|
@@ -114,17 +120,16 @@ import { Icon } from "astro-icon/components";
|
|
|
114
120
|
class="colony-action-btn colony-clear-btn"
|
|
115
121
|
>
|
|
116
122
|
<Icon name="mdi:delete-sweep" class="colony-btn-icon" />
|
|
117
|
-
|
|
123
|
+
{ui.clearAll}
|
|
118
124
|
</button>
|
|
119
125
|
</div>
|
|
120
126
|
|
|
121
127
|
<div class="colony-info-text">
|
|
122
128
|
<p>
|
|
123
|
-
<Icon name="mdi:information" class="colony-info-icon" />
|
|
124
|
-
marcar colonias
|
|
129
|
+
<Icon name="mdi:information" class="colony-info-icon" /> {ui.infoClick}
|
|
125
130
|
</p>
|
|
126
131
|
<p>
|
|
127
|
-
<Icon name="mdi:palette" class="colony-info-icon" />
|
|
132
|
+
<Icon name="mdi:palette" class="colony-info-icon" /> {ui.infoChange}
|
|
128
133
|
</p>
|
|
129
134
|
</div>
|
|
130
135
|
</div>
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
import "./MicrowaveDetector.css";
|
|
3
3
|
import { Icon } from "astro-icon/components";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
ui: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { ui } = Astro.props;
|
|
4
10
|
---
|
|
5
11
|
|
|
6
|
-
<div class="microwave-detector-container" id="microwave-detector-root">
|
|
12
|
+
<div class="microwave-detector-container" id="microwave-detector-root" data-detecting-interference={ui.detectingInterference}>
|
|
7
13
|
<div class="microwave-modal" id="initial-modal">
|
|
8
14
|
<div class="microwave-modal-content">
|
|
9
15
|
<div class="microwave-modal-icon">
|
|
10
16
|
<Icon name="mdi:wifi-alert" />
|
|
11
17
|
</div>
|
|
12
|
-
<h2>
|
|
13
|
-
<p>
|
|
14
|
-
Este detector utiliza la interferencia en la banda de <strong>2.4GHz</strong> (la frecuencia de
|
|
15
|
-
los microondas).
|
|
16
|
-
<br /><br />
|
|
17
|
-
Para que funcione, asegúrate de estar conectado a una red <strong>WiFi 2.4GHz</strong> (no 5GHz/6GHz)
|
|
18
|
-
o usa tu teléfono cerca del aparato.
|
|
19
|
-
</p>
|
|
18
|
+
<h2>{ui.physicalRequirement}</h2>
|
|
19
|
+
<p set:html={ui.physicalDesc}></p>
|
|
20
20
|
<button id="start-btn" class="microwave-btn-start">
|
|
21
|
-
|
|
21
|
+
{ui.understandStart}
|
|
22
22
|
</button>
|
|
23
23
|
</div>
|
|
24
24
|
</div>
|
|
@@ -26,12 +26,12 @@ import { Icon } from "astro-icon/components";
|
|
|
26
26
|
<div class="microwave-detector-panel">
|
|
27
27
|
<div class="microwave-header">
|
|
28
28
|
<div>
|
|
29
|
-
<span class="microwave-label">
|
|
30
|
-
<h3 class="microwave-title">
|
|
29
|
+
<span class="microwave-label">{ui.rfInterferenceMonitor}</span>
|
|
30
|
+
<h3 class="microwave-title">{ui.mwLeakDetector}</h3>
|
|
31
31
|
</div>
|
|
32
32
|
<div class="microwave-status-badge">
|
|
33
33
|
<div id="status-dot" class="microwave-status-dot"></div>
|
|
34
|
-
<span id="status-text" class="microwave-status-text">
|
|
34
|
+
<span id="status-text" class="microwave-status-text">{ui.static}</span>
|
|
35
35
|
</div>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
@@ -41,7 +41,7 @@ import { Icon } from "astro-icon/components";
|
|
|
41
41
|
<div id="big-value-bg" class="microwave-big-value">00</div>
|
|
42
42
|
<div class="microwave-jitter-display">
|
|
43
43
|
<span id="jitter-value" class="microwave-jitter-value">0.0</span>
|
|
44
|
-
<span class="microwave-jitter-unit">
|
|
44
|
+
<span class="microwave-jitter-unit">{ui.jitterUnit}</span>
|
|
45
45
|
</div>
|
|
46
46
|
</div>
|
|
47
47
|
<div class="microwave-canvas-grid"></div>
|
|
@@ -50,9 +50,9 @@ import { Icon } from "astro-icon/components";
|
|
|
50
50
|
<div class="microwave-content-grid">
|
|
51
51
|
<div>
|
|
52
52
|
<div id="verdict-container" class="microwave-verdict">
|
|
53
|
-
<h4 id="verdict-label" class="microwave-verdict-label">
|
|
53
|
+
<h4 id="verdict-label" class="microwave-verdict-label">{ui.systemReady}</h4>
|
|
54
54
|
<p id="verdict-description" class="microwave-verdict-desc">
|
|
55
|
-
|
|
55
|
+
{ui.connectToAnalyze}
|
|
56
56
|
</p>
|
|
57
57
|
</div>
|
|
58
58
|
<div class="microwave-latency-info">
|
|
@@ -65,12 +65,12 @@ import { Icon } from "astro-icon/components";
|
|
|
65
65
|
|
|
66
66
|
<div class="microwave-audio-section">
|
|
67
67
|
<div class="microwave-audio-label">
|
|
68
|
-
<span>
|
|
68
|
+
<span>{ui.audioFeedback}</span>
|
|
69
69
|
<span id="audio-status">ON</span>
|
|
70
70
|
</div>
|
|
71
71
|
<button id="toggle-audio-btn" class="microwave-btn-audio">
|
|
72
72
|
<Icon name="mdi:volume-high" />
|
|
73
|
-
|
|
73
|
+
{ui.muteUnmute}
|
|
74
74
|
</button>
|
|
75
75
|
</div>
|
|
76
76
|
</div>
|
|
@@ -80,6 +80,7 @@ import { Icon } from "astro-icon/components";
|
|
|
80
80
|
</div>
|
|
81
81
|
|
|
82
82
|
<style is:inline define:vars={{}}>
|
|
83
|
+
|
|
83
84
|
:root {
|
|
84
85
|
--microwave-primary: #e11d48;
|
|
85
86
|
--microwave-text: #0f172a;
|
|
@@ -517,7 +518,8 @@ import { Icon } from "astro-icon/components";
|
|
|
517
518
|
const statusText = document.getElementById("status-text");
|
|
518
519
|
const toggleAudioBtn = document.getElementById("toggle-audio-btn");
|
|
519
520
|
|
|
520
|
-
const
|
|
521
|
+
const currentLang = document.documentElement.lang || 'es';
|
|
522
|
+
const engine = new MicrowaveEngine(currentLang);
|
|
521
523
|
let running = false;
|
|
522
524
|
let audioEnabled = true;
|
|
523
525
|
let audioCtx = null;
|
|
@@ -620,7 +622,7 @@ import { Icon } from "astro-icon/components";
|
|
|
620
622
|
if (bigValueBg) bigValueBg.textContent = Math.floor(result.jitter).toString().padStart(2, "0");
|
|
621
623
|
if (latencyVal) latencyVal.textContent = `${Math.floor(result.latency)}ms`;
|
|
622
624
|
|
|
623
|
-
const level =
|
|
625
|
+
const level = engine.getInterferenceLevel(result.jitter);
|
|
624
626
|
updateVerdictDisplay(level);
|
|
625
627
|
|
|
626
628
|
if (result.jitter > 1) {
|
|
@@ -631,11 +633,13 @@ import { Icon } from "astro-icon/components";
|
|
|
631
633
|
setTimeout(scan, 200 + Math.random() * 100);
|
|
632
634
|
}
|
|
633
635
|
|
|
636
|
+
const detectingInterferenceText = root?.getAttribute('data-detecting-interference') || 'Detectando interferencias...';
|
|
637
|
+
|
|
634
638
|
startBtn?.addEventListener("click", () => {
|
|
635
639
|
if (modal) modal.classList.add("hidden");
|
|
636
640
|
running = true;
|
|
637
641
|
resizeCanvas();
|
|
638
|
-
if (statusText) statusText.textContent =
|
|
642
|
+
if (statusText) statusText.textContent = detectingInterferenceText;
|
|
639
643
|
scan();
|
|
640
644
|
});
|
|
641
645
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
export interface InterferenceLevel {
|
|
2
|
+
threshold: number;
|
|
3
|
+
label: string;
|
|
4
|
+
color: string;
|
|
5
|
+
description: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const INTERFERENCE_LEVELS_ES: InterferenceLevel[] = [
|
|
9
|
+
{
|
|
10
|
+
threshold: 2,
|
|
11
|
+
label: "Señal Limpia",
|
|
12
|
+
color: "emerald",
|
|
13
|
+
description: "Tu conexión es estable. No hay interferencias electromagnéticas significativas detectadas.",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
threshold: 10,
|
|
17
|
+
label: "Interferencia Leve",
|
|
18
|
+
color: "yellow",
|
|
19
|
+
description: "Se detecta algo de ruido en la señal. Podría ser actividad normal o dispositivos Bluetooth cercanos.",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
threshold: 30,
|
|
23
|
+
label: "Interferencia Alta",
|
|
24
|
+
color: "orange",
|
|
25
|
+
description: "Ruido electromagnético considerable detectado. Si el microondas está encendido, es posible que tenga fugas leves.",
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export const INTERFERENCE_LEVELS_EN: InterferenceLevel[] = [
|
|
30
|
+
{
|
|
31
|
+
threshold: 2,
|
|
32
|
+
label: "Clean Signal",
|
|
33
|
+
color: "emerald",
|
|
34
|
+
description: "Your connection is stable. No significant electromagnetic interference detected.",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
threshold: 10,
|
|
38
|
+
label: "Light Interference",
|
|
39
|
+
color: "yellow",
|
|
40
|
+
description: "Some noise detected in the signal. Could be normal activity or nearby Bluetooth devices.",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
threshold: 30,
|
|
44
|
+
label: "High Interference",
|
|
45
|
+
color: "orange",
|
|
46
|
+
description: "Considerable electromagnetic noise detected. If your microwave is on, it may have minor leaks.",
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
export const INTERFERENCE_LEVELS_FR: InterferenceLevel[] = [
|
|
51
|
+
{
|
|
52
|
+
threshold: 2,
|
|
53
|
+
label: "Signal Propre",
|
|
54
|
+
color: "emerald",
|
|
55
|
+
description: "Votre connexion est stable. Aucune interférence électromagnétique significative détectée.",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
threshold: 10,
|
|
59
|
+
label: "Interférence Légère",
|
|
60
|
+
color: "yellow",
|
|
61
|
+
description: "Du bruit détecté dans le signal. Pourrait être une activité normale ou des appareils Bluetooth à proximité.",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
threshold: 30,
|
|
65
|
+
label: "Interférence Élevée",
|
|
66
|
+
color: "orange",
|
|
67
|
+
description: "Bruit électromagnétique considérable détecté. Si votre micro-ondes est allumé, il peut y avoir des fuites mineures.",
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
export const CRITICAL_INTERFERENCE_ES = {
|
|
72
|
+
label: "FUGA CRÍTICA / RUIDO EXTREMO",
|
|
73
|
+
color: "red",
|
|
74
|
+
description: "Inestabilidad masiva en la señal. Si estás junto al microondas, apágalo: la protección RF podría estar degradada.",
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const CRITICAL_INTERFERENCE_EN = {
|
|
78
|
+
label: "CRITICAL LEAK / EXTREME NOISE",
|
|
79
|
+
color: "red",
|
|
80
|
+
description: "Massive signal instability. If you're near the microwave, turn it off: RF protection may be degraded.",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const CRITICAL_INTERFERENCE_FR = {
|
|
84
|
+
label: "FUITE CRITIQUE / BRUIT EXTRÊME",
|
|
85
|
+
color: "red",
|
|
86
|
+
description: "Instabilité massive du signal. Si vous êtes près du micro-ondes, éteignez-le : la protection RF pourrait être dégradée.",
|
|
87
|
+
};
|
|
@@ -25,6 +25,18 @@ export const content: ToolLocaleContent = {
|
|
|
25
25
|
highLeak: 'High',
|
|
26
26
|
pingLabel: 'Latency (ms)',
|
|
27
27
|
packetLossLabel: 'Packet Loss (%)',
|
|
28
|
+
rfInterferenceMonitor: 'RF Interference Monitor',
|
|
29
|
+
mwLeakDetector: 'MW LEAK DETECTOR v2.0',
|
|
30
|
+
jitterUnit: 'ms / jitter',
|
|
31
|
+
systemReady: 'System Ready',
|
|
32
|
+
physicalRequirement: 'Physical Requirement',
|
|
33
|
+
physicalDesc: 'This detector uses interference in the 2.4GHz band (the microwave frequency). To work properly, make sure you are connected to a 2.4GHz WiFi network (not 5GHz/6GHz) or use your phone near the device.',
|
|
34
|
+
understandStart: 'Understood, Start Scan',
|
|
35
|
+
rfInterferenceTitle: 'Interference Monitor',
|
|
36
|
+
connectToAnalyze: 'Connect to start thermal interference analysis.',
|
|
37
|
+
audioFeedback: 'Audio Feedback',
|
|
38
|
+
muteUnmute: 'MUTE / UNMUTE',
|
|
39
|
+
static: 'Static',
|
|
28
40
|
},
|
|
29
41
|
seo: [
|
|
30
42
|
{
|
|
@@ -25,6 +25,18 @@ export const content: ToolLocaleContent = {
|
|
|
25
25
|
highLeak: 'Alta',
|
|
26
26
|
pingLabel: 'Latencia (ms)',
|
|
27
27
|
packetLossLabel: 'Pérdida de Paquetes (%)',
|
|
28
|
+
rfInterferenceMonitor: 'Monitor de Interferencias RF',
|
|
29
|
+
mwLeakDetector: 'DETECTOR DE FUGAS MW v2.0',
|
|
30
|
+
jitterUnit: 'ms / interferencia',
|
|
31
|
+
systemReady: 'Sistema Listo',
|
|
32
|
+
physicalRequirement: 'Requisito de Física',
|
|
33
|
+
physicalDesc: 'Este detector utiliza la interferencia en la banda de 2.4GHz (la frecuencia de los microondas). Para que funcione, asegúrate de estar conectado a una red WiFi 2.4GHz (no 5GHz/6GHz) o usa tu teléfono cerca del aparato.',
|
|
34
|
+
understandStart: 'Entendido, Iniciar Escaneo',
|
|
35
|
+
rfInterferenceTitle: 'Monitor de Interferencias',
|
|
36
|
+
connectToAnalyze: 'Conecta para iniciar el análisis térmico de interferencia.',
|
|
37
|
+
audioFeedback: 'Audio Feedback',
|
|
38
|
+
muteUnmute: 'MUTEADO / ACTIVADO',
|
|
39
|
+
static: 'Estático',
|
|
28
40
|
},
|
|
29
41
|
seo: [
|
|
30
42
|
{
|
|
@@ -25,6 +25,18 @@ export const content: ToolLocaleContent = {
|
|
|
25
25
|
highLeak: 'Haute',
|
|
26
26
|
pingLabel: 'Latence (ms)',
|
|
27
27
|
packetLossLabel: 'Perte de paquets (%)',
|
|
28
|
+
rfInterferenceMonitor: 'Moniteur d\'Interférences RF',
|
|
29
|
+
mwLeakDetector: 'DÉTECTEUR DE FUITES MW v2.0',
|
|
30
|
+
jitterUnit: 'ms / gigue',
|
|
31
|
+
systemReady: 'Système Prêt',
|
|
32
|
+
physicalRequirement: 'Exigence Physique',
|
|
33
|
+
physicalDesc: 'Ce détecteur utilise l\'interférence dans la bande 2.4GHz (la fréquence des micro-ondes). Pour fonctionner correctement, assurez-vous que vous êtes connecté à un réseau WiFi 2.4GHz (pas 5GHz/6GHz) ou utilisez votre téléphone près de l\'appareil.',
|
|
34
|
+
understandStart: 'D\'accord, Démarrer l\'Analyse',
|
|
35
|
+
rfInterferenceTitle: 'Moniteur d\'Interférences',
|
|
36
|
+
connectToAnalyze: 'Connectez-vous pour démarrer l\'analyse thermique des interférences.',
|
|
37
|
+
audioFeedback: 'Retour Audio',
|
|
38
|
+
muteUnmute: 'ACTIVER / DÉSACTIVER',
|
|
39
|
+
static: 'Statique',
|
|
28
40
|
},
|
|
29
41
|
seo: [
|
|
30
42
|
{
|
|
@@ -1,40 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INTERFERENCE_LEVELS_ES,
|
|
3
|
+
INTERFERENCE_LEVELS_EN,
|
|
4
|
+
INTERFERENCE_LEVELS_FR,
|
|
5
|
+
CRITICAL_INTERFERENCE_ES,
|
|
6
|
+
CRITICAL_INTERFERENCE_EN,
|
|
7
|
+
CRITICAL_INTERFERENCE_FR,
|
|
8
|
+
type InterferenceLevel,
|
|
9
|
+
} from '../constants';
|
|
10
|
+
|
|
1
11
|
export interface PingResult {
|
|
2
12
|
latency: number;
|
|
3
13
|
jitter: number;
|
|
4
14
|
timestamp: number;
|
|
5
15
|
}
|
|
6
16
|
|
|
7
|
-
interface InterferenceLevel {
|
|
8
|
-
threshold: number;
|
|
9
|
-
label: string;
|
|
10
|
-
color: string;
|
|
11
|
-
description: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
17
|
export class MicrowaveEngine {
|
|
15
18
|
private lastPings: number[] = [];
|
|
16
19
|
private maxHistory = 100;
|
|
20
|
+
private lang: string = 'es';
|
|
21
|
+
|
|
22
|
+
constructor(lang: string = 'es') {
|
|
23
|
+
this.lang = lang;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setLanguage(lang: string) {
|
|
27
|
+
this.lang = lang;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private getInterferenceLevels(): InterferenceLevel[] {
|
|
31
|
+
switch (this.lang) {
|
|
32
|
+
case 'en':
|
|
33
|
+
return INTERFERENCE_LEVELS_EN;
|
|
34
|
+
case 'fr':
|
|
35
|
+
return INTERFERENCE_LEVELS_FR;
|
|
36
|
+
case 'es':
|
|
37
|
+
default:
|
|
38
|
+
return INTERFERENCE_LEVELS_ES;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
17
41
|
|
|
18
|
-
private
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
description: "Se detecta algo de ruido en la señal. Podría ser actividad normal o dispositivos Bluetooth cercanos.",
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
threshold: 30,
|
|
33
|
-
label: "Interferencia Alta",
|
|
34
|
-
color: "orange",
|
|
35
|
-
description: "Ruido electromagnético considerable detectado. Si el microondas está encendido, es posible que tenga fugas leves.",
|
|
36
|
-
},
|
|
37
|
-
];
|
|
42
|
+
private getCriticalInterference() {
|
|
43
|
+
switch (this.lang) {
|
|
44
|
+
case 'en':
|
|
45
|
+
return CRITICAL_INTERFERENCE_EN;
|
|
46
|
+
case 'fr':
|
|
47
|
+
return CRITICAL_INTERFERENCE_FR;
|
|
48
|
+
case 'es':
|
|
49
|
+
default:
|
|
50
|
+
return CRITICAL_INTERFERENCE_ES;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
38
53
|
|
|
39
54
|
async measurePing(): Promise<PingResult> {
|
|
40
55
|
const start = performance.now();
|
|
@@ -71,19 +86,16 @@ export class MicrowaveEngine {
|
|
|
71
86
|
return diffSum / (this.lastPings.length - 1);
|
|
72
87
|
}
|
|
73
88
|
|
|
74
|
-
|
|
89
|
+
getInterferenceLevel(jitter: number): {
|
|
75
90
|
label: string;
|
|
76
91
|
color: string;
|
|
77
92
|
description: string;
|
|
78
93
|
} {
|
|
79
|
-
const
|
|
94
|
+
const levels = this.getInterferenceLevels();
|
|
95
|
+
const level = levels.find((l) => jitter < l.threshold);
|
|
80
96
|
if (level) {
|
|
81
97
|
return { label: level.label, color: level.color, description: level.description };
|
|
82
98
|
}
|
|
83
|
-
return
|
|
84
|
-
label: "FUGA CRÍTICA / RUIDO EXTREMO",
|
|
85
|
-
color: "red",
|
|
86
|
-
description: "Inestabilidad masiva en la señal. Si estás junto al microondas, apágalo: la protección RF podría estar degradada.",
|
|
87
|
-
};
|
|
99
|
+
return this.getCriticalInterference();
|
|
88
100
|
}
|
|
89
101
|
}
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
---
|
|
2
|
+
interface Props {
|
|
3
|
+
ui: Record<string, string>;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const { ui } = Astro.props;
|
|
2
7
|
---
|
|
3
8
|
|
|
4
9
|
<div class="simulation-app" id="simulation-app">
|
|
5
10
|
<div class="simulation-header">
|
|
6
|
-
<h2>
|
|
11
|
+
<h2>{ui.probabilityTitle}</h2>
|
|
7
12
|
</div>
|
|
8
13
|
|
|
9
14
|
<div class="simulation-main">
|
|
10
15
|
<div class="parameter-card">
|
|
11
16
|
<div class="label-row">
|
|
12
17
|
<div class="label-info">
|
|
13
|
-
<label for="technological-progress">
|
|
18
|
+
<label for="technological-progress">{ui.fpSub} ({ui.fpLabel})</label>
|
|
14
19
|
</div>
|
|
15
20
|
<div class="value-display" id="fp-value">50%</div>
|
|
16
21
|
</div>
|
|
17
|
-
<small class="helper-text">
|
|
22
|
+
<small class="helper-text">{ui.fpDescription}</small>
|
|
18
23
|
<div class="control-row">
|
|
19
24
|
<div class="slider-container">
|
|
20
25
|
<input type="range" id="technological-progress" min="1" max="100" value="50" />
|
|
@@ -25,11 +30,11 @@
|
|
|
25
30
|
<div class="parameter-card">
|
|
26
31
|
<div class="label-row">
|
|
27
32
|
<div class="label-info">
|
|
28
|
-
<label for="societal-survival">
|
|
33
|
+
<label for="societal-survival">{ui.flSub} ({ui.flLabel})</label>
|
|
29
34
|
</div>
|
|
30
35
|
<div class="value-display" id="fl-value">50%</div>
|
|
31
36
|
</div>
|
|
32
|
-
<small class="helper-text">
|
|
37
|
+
<small class="helper-text">{ui.flDescription}</small>
|
|
33
38
|
<div class="control-row">
|
|
34
39
|
<div class="slider-container">
|
|
35
40
|
<input type="range" id="societal-survival" min="1" max="100" value="50" />
|
|
@@ -40,11 +45,11 @@
|
|
|
40
45
|
<div class="parameter-card">
|
|
41
46
|
<div class="label-row">
|
|
42
47
|
<div class="label-info">
|
|
43
|
-
<label for="simulation-interest">
|
|
48
|
+
<label for="simulation-interest">{ui.fiSub} ({ui.fiLabel})</label>
|
|
44
49
|
</div>
|
|
45
50
|
<div class="value-display" id="fi-value">20%</div>
|
|
46
51
|
</div>
|
|
47
|
-
<small class="helper-text">
|
|
52
|
+
<small class="helper-text">{ui.fiDescription}</small>
|
|
48
53
|
<div class="control-row">
|
|
49
54
|
<div class="slider-container">
|
|
50
55
|
<input type="range" id="simulation-interest" min="1" max="100" value="20" />
|
|
@@ -55,11 +60,11 @@
|
|
|
55
60
|
<div class="parameter-card">
|
|
56
61
|
<div class="label-row">
|
|
57
62
|
<div class="label-info">
|
|
58
|
-
<label for="ancestral-sims">
|
|
63
|
+
<label for="ancestral-sims">{ui.nSub} ({ui.nLabel})</label>
|
|
59
64
|
</div>
|
|
60
65
|
<div class="value-display" id="n-value">1,000</div>
|
|
61
66
|
</div>
|
|
62
|
-
<small class="helper-text">
|
|
67
|
+
<small class="helper-text">{ui.nDescription}</small>
|
|
63
68
|
<div class="control-row">
|
|
64
69
|
<div class="slider-container">
|
|
65
70
|
<input type="range" id="ancestral-sims" min="1" max="10000" value="1000" />
|
|
@@ -74,9 +79,9 @@
|
|
|
74
79
|
</div>
|
|
75
80
|
|
|
76
81
|
<div class="verdict-box">
|
|
77
|
-
<div class="verdict-title" id="verdict">
|
|
82
|
+
<div class="verdict-title" id="verdict">{ui.probabilityTitle}</div>
|
|
78
83
|
<div class="verdict-detail" id="verdict-detail">
|
|
79
|
-
|
|
84
|
+
{ui.verdictDetail}
|
|
80
85
|
</div>
|
|
81
86
|
</div>
|
|
82
87
|
</div>
|
|
@@ -323,6 +328,9 @@
|
|
|
323
328
|
throw new Error("Required simulation elements not found");
|
|
324
329
|
}
|
|
325
330
|
|
|
331
|
+
const currentLang = document.documentElement.lang || 'es';
|
|
332
|
+
const engine = new SimulationEngine(currentLang);
|
|
333
|
+
|
|
326
334
|
const calculateProbability = () => {
|
|
327
335
|
const fp = parseFloat(fpInput!.value) / 100;
|
|
328
336
|
const fl = parseFloat(flInput!.value) / 100;
|
|
@@ -334,7 +342,7 @@
|
|
|
334
342
|
fiText!.textContent = `${Math.round(fi * 100)}%`;
|
|
335
343
|
nText!.textContent = n.toLocaleString();
|
|
336
344
|
|
|
337
|
-
const result =
|
|
345
|
+
const result = engine.calculate(fp, fl, fi, n);
|
|
338
346
|
probDisplay!.textContent = `${result.probability.toFixed(2)}%`;
|
|
339
347
|
verdictLabel!.textContent = result.verdict;
|
|
340
348
|
verdictDetail!.textContent = result.detail;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export interface VerdictLevel {
|
|
2
|
+
percentage: number;
|
|
3
|
+
verdict: string;
|
|
4
|
+
detail: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const VERDICTS_ES: VerdictLevel[] = [
|
|
8
|
+
{
|
|
9
|
+
percentage: Infinity,
|
|
10
|
+
verdict: "Casi Ciertamente Simulado",
|
|
11
|
+
detail: "Estás casi ciertamente en una simulación. Los errores en el sistema son solo cuestión de tiempo.",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
percentage: 50,
|
|
15
|
+
verdict: "Probablemente Simulado",
|
|
16
|
+
detail: "Es más probable que seas un código que un ser biológico original.",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
percentage: 10,
|
|
20
|
+
verdict: "Incierto",
|
|
21
|
+
detail: "La realidad física parece resistir, pero la duda sistemática es razonable.",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
percentage: 0,
|
|
25
|
+
verdict: "Probablemente Original",
|
|
26
|
+
detail: "Probablemente seas un original biológico. Disfruta de la realidad 'real' mientras dure.",
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export const VERDICTS_EN: VerdictLevel[] = [
|
|
31
|
+
{
|
|
32
|
+
percentage: Infinity,
|
|
33
|
+
verdict: "Almost Certainly Simulated",
|
|
34
|
+
detail: "You are almost certainly in a simulation. System errors are just a matter of time.",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
percentage: 50,
|
|
38
|
+
verdict: "Probably Simulated",
|
|
39
|
+
detail: "It's more likely you're code than an original biological being.",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
percentage: 10,
|
|
43
|
+
verdict: "Uncertain",
|
|
44
|
+
detail: "Physical reality seems to resist, but systematic doubt is reasonable.",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
percentage: 0,
|
|
48
|
+
verdict: "Probably Original",
|
|
49
|
+
detail: "You're probably an original biological being. Enjoy the 'real' reality while it lasts.",
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
export const VERDICTS_FR: VerdictLevel[] = [
|
|
54
|
+
{
|
|
55
|
+
percentage: Infinity,
|
|
56
|
+
verdict: "Presque Certainement Simulé",
|
|
57
|
+
detail: "Vous êtes presque certainement dans une simulation. Les erreurs du système ne sont qu'une question de temps.",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
percentage: 50,
|
|
61
|
+
verdict: "Probablement Simulé",
|
|
62
|
+
detail: "Il est plus probable que vous soyez du code qu'un être biologique original.",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
percentage: 10,
|
|
66
|
+
verdict: "Incertain",
|
|
67
|
+
detail: "La réalité physique semble résister, mais le doute systématique est raisonnable.",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
percentage: 0,
|
|
71
|
+
verdict: "Probablement Original",
|
|
72
|
+
detail: "Vous êtes probablement un être biologique original. Profitez de la réalité 'réelle' tant qu'elle dure.",
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
export const DEFAULT_VERDICT_ES = {
|
|
77
|
+
verdict: "Probabilidad de Simulación",
|
|
78
|
+
detail: "Analiza los parámetros para ver si vives en una simulación.",
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const DEFAULT_VERDICT_EN = {
|
|
82
|
+
verdict: "Simulation Probability",
|
|
83
|
+
detail: "Analyze the parameters to see if you live in a simulation.",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const DEFAULT_VERDICT_FR = {
|
|
87
|
+
verdict: "Probabilité de Simulation",
|
|
88
|
+
detail: "Analysez les paramètres pour voir si vous vivez dans une simulation.",
|
|
89
|
+
};
|
|
@@ -25,6 +25,11 @@ export const content: ToolLocaleContent = {
|
|
|
25
25
|
scenario1: 'Scenario 1: Extinction',
|
|
26
26
|
scenario2: 'Scenario 2: Disinterest',
|
|
27
27
|
scenario3: 'Scenario 3: We Are Simulated',
|
|
28
|
+
fpDescription: 'Probability that humanity achieves the technical capability to simulate universes with consciousness.',
|
|
29
|
+
flDescription: 'Probability of avoiding collapse (extinction, war) before reaching post-human level.',
|
|
30
|
+
fiDescription: 'Percentage of advanced civilizations that decide to create simulations of their ancestors.',
|
|
31
|
+
nDescription: 'Number of simulated worlds that each advanced civilization typically runs simultaneously.',
|
|
32
|
+
verdictDetail: 'The data suggests it is extremely probable that your consciousness is a software process.',
|
|
28
33
|
},
|
|
29
34
|
seo: [
|
|
30
35
|
{
|
|
@@ -25,6 +25,11 @@ export const content: ToolLocaleContent = {
|
|
|
25
25
|
scenario1: 'Escenario 1: Extinción',
|
|
26
26
|
scenario2: 'Escenario 2: Desinterés',
|
|
27
27
|
scenario3: 'Escenario 3: Estamos Simulados',
|
|
28
|
+
fpDescription: 'Probabilidad de que la humanidad alcance la capacidad técnica para simular universos con conciencia.',
|
|
29
|
+
flDescription: 'Probabilidad de evitar el colapso (extinción, guerras) antes de alcanzar el nivel post-humano.',
|
|
30
|
+
fiDescription: 'Porcentaje de sociedades avanzadas que deciden crear simulaciones de sus antepasados.',
|
|
31
|
+
nDescription: 'Número de mundos simulados que cada civilización avanzada suele ejecutar simultáneamente.',
|
|
32
|
+
verdictDetail: 'Los datos sugieren que es extremadamente probable que tu conciencia sea un proceso de software.',
|
|
28
33
|
},
|
|
29
34
|
seo: [
|
|
30
35
|
{
|
|
@@ -25,6 +25,11 @@ export const content: ToolLocaleContent = {
|
|
|
25
25
|
scenario1: 'Scénario 1 : Extinction',
|
|
26
26
|
scenario2: 'Scénario 2 : Désintérêt',
|
|
27
27
|
scenario3: 'Scénario 3 : Nous sommes simulés',
|
|
28
|
+
fpDescription: 'Probabilité que l\'humanité atteigne la capacité technique de simuler des univers avec conscience.',
|
|
29
|
+
flDescription: 'Probabilité d\'éviter l\'effondrement (extinction, guerre) avant d\'atteindre le niveau post-humain.',
|
|
30
|
+
fiDescription: 'Pourcentage de civilisations avancées qui décident de créer des simulations de leurs ancêtres.',
|
|
31
|
+
nDescription: 'Nombre de mondes simulés que chaque civilisation avancée exécute généralement simultanément.',
|
|
32
|
+
verdictDetail: 'Les données suggèrent qu\'il est extrêmement probable que votre conscience soit un processus logiciel.',
|
|
28
33
|
},
|
|
29
34
|
seo: [
|
|
30
35
|
{
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VERDICTS_ES,
|
|
3
|
+
VERDICTS_EN,
|
|
4
|
+
VERDICTS_FR,
|
|
5
|
+
DEFAULT_VERDICT_ES,
|
|
6
|
+
DEFAULT_VERDICT_EN,
|
|
7
|
+
DEFAULT_VERDICT_FR,
|
|
8
|
+
type VerdictLevel,
|
|
9
|
+
} from '../constants';
|
|
10
|
+
|
|
1
11
|
export interface SimulationResult {
|
|
2
12
|
probability: number;
|
|
3
13
|
verdict: string;
|
|
@@ -5,32 +15,63 @@ export interface SimulationResult {
|
|
|
5
15
|
}
|
|
6
16
|
|
|
7
17
|
export class SimulationEngine {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
private lang: string = 'es';
|
|
19
|
+
|
|
20
|
+
constructor(lang: string = 'es') {
|
|
21
|
+
this.lang = lang;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
setLanguage(lang: string) {
|
|
25
|
+
this.lang = lang;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private getVerdicts(): VerdictLevel[] {
|
|
29
|
+
switch (this.lang) {
|
|
30
|
+
case 'en':
|
|
31
|
+
return VERDICTS_EN;
|
|
32
|
+
case 'fr':
|
|
33
|
+
return VERDICTS_FR;
|
|
34
|
+
case 'es':
|
|
35
|
+
default:
|
|
36
|
+
return VERDICTS_ES;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private getDefaultVerdict() {
|
|
41
|
+
switch (this.lang) {
|
|
42
|
+
case 'en':
|
|
43
|
+
return DEFAULT_VERDICT_EN;
|
|
44
|
+
case 'fr':
|
|
45
|
+
return DEFAULT_VERDICT_FR;
|
|
46
|
+
case 'es':
|
|
47
|
+
default:
|
|
48
|
+
return DEFAULT_VERDICT_ES;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
calculate(
|
|
53
|
+
fp: number,
|
|
54
|
+
fl: number,
|
|
55
|
+
fi: number,
|
|
56
|
+
n: number
|
|
13
57
|
): SimulationResult {
|
|
14
|
-
|
|
58
|
+
|
|
15
59
|
const numerator = fp * fl * fi * n;
|
|
16
60
|
const pSim = numerator / (1 + numerator);
|
|
17
61
|
const percentage = pSim * 100;
|
|
18
62
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
} else {
|
|
32
|
-
verdict = "Probablemente Original";
|
|
33
|
-
detail = "Probablemente seas un original biológico. Disfruta de la realidad 'real' mientras dure.";
|
|
63
|
+
const verdicts = this.getVerdicts();
|
|
64
|
+
const defaultVerdict = this.getDefaultVerdict();
|
|
65
|
+
|
|
66
|
+
let verdict = defaultVerdict.verdict;
|
|
67
|
+
let detail = defaultVerdict.detail;
|
|
68
|
+
|
|
69
|
+
for (const v of verdicts) {
|
|
70
|
+
if (percentage > v.percentage) {
|
|
71
|
+
verdict = v.verdict;
|
|
72
|
+
detail = v.detail;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
34
75
|
}
|
|
35
76
|
|
|
36
77
|
return {
|