@jjlmoya/utils-science 1.33.0 → 1.35.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 (60) 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 -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/natural-selection-drift/component.astro +37 -6
  8. package/src/tool/natural-selection-drift/natural-selection-drift.css +134 -0
  9. package/src/tool/roche-limit-satellite-disruption/bibliography.astro +14 -0
  10. package/src/tool/roche-limit-satellite-disruption/bibliography.ts +16 -0
  11. package/src/tool/roche-limit-satellite-disruption/component.astro +97 -0
  12. package/src/tool/roche-limit-satellite-disruption/entry.ts +28 -0
  13. package/src/tool/roche-limit-satellite-disruption/i18n/de.ts +229 -0
  14. package/src/tool/roche-limit-satellite-disruption/i18n/en.ts +229 -0
  15. package/src/tool/roche-limit-satellite-disruption/i18n/es.ts +229 -0
  16. package/src/tool/roche-limit-satellite-disruption/i18n/fr.ts +229 -0
  17. package/src/tool/roche-limit-satellite-disruption/i18n/id.ts +229 -0
  18. package/src/tool/roche-limit-satellite-disruption/i18n/it.ts +229 -0
  19. package/src/tool/roche-limit-satellite-disruption/i18n/ja.ts +229 -0
  20. package/src/tool/roche-limit-satellite-disruption/i18n/ko.ts +229 -0
  21. package/src/tool/roche-limit-satellite-disruption/i18n/nl.ts +229 -0
  22. package/src/tool/roche-limit-satellite-disruption/i18n/pl.ts +229 -0
  23. package/src/tool/roche-limit-satellite-disruption/i18n/pt.ts +229 -0
  24. package/src/tool/roche-limit-satellite-disruption/i18n/ru.ts +229 -0
  25. package/src/tool/roche-limit-satellite-disruption/i18n/sv.ts +229 -0
  26. package/src/tool/roche-limit-satellite-disruption/i18n/tr.ts +229 -0
  27. package/src/tool/roche-limit-satellite-disruption/i18n/zh.ts +229 -0
  28. package/src/tool/roche-limit-satellite-disruption/index.ts +11 -0
  29. package/src/tool/roche-limit-satellite-disruption/logic.ts +102 -0
  30. package/src/tool/roche-limit-satellite-disruption/particle-system.ts +66 -0
  31. package/src/tool/roche-limit-satellite-disruption/roche-limit-satellite-disruption-calculator.css +568 -0
  32. package/src/tool/roche-limit-satellite-disruption/script.ts +274 -0
  33. package/src/tool/roche-limit-satellite-disruption/seo.astro +15 -0
  34. package/src/tool/roche-limit-satellite-disruption/storage.ts +28 -0
  35. package/src/tool/roche-limit-satellite-disruption/visual-data.ts +16 -0
  36. package/src/tool/three-body-problem/app.ts +274 -0
  37. package/src/tool/three-body-problem/bibliography.astro +14 -0
  38. package/src/tool/three-body-problem/bibliography.ts +16 -0
  39. package/src/tool/three-body-problem/component.astro +70 -0
  40. package/src/tool/three-body-problem/entry.ts +26 -0
  41. package/src/tool/three-body-problem/i18n/de.ts +162 -0
  42. package/src/tool/three-body-problem/i18n/en.ts +162 -0
  43. package/src/tool/three-body-problem/i18n/es.ts +162 -0
  44. package/src/tool/three-body-problem/i18n/fr.ts +162 -0
  45. package/src/tool/three-body-problem/i18n/id.ts +162 -0
  46. package/src/tool/three-body-problem/i18n/it.ts +162 -0
  47. package/src/tool/three-body-problem/i18n/ja.ts +162 -0
  48. package/src/tool/three-body-problem/i18n/ko.ts +162 -0
  49. package/src/tool/three-body-problem/i18n/nl.ts +162 -0
  50. package/src/tool/three-body-problem/i18n/pl.ts +162 -0
  51. package/src/tool/three-body-problem/i18n/pt.ts +162 -0
  52. package/src/tool/three-body-problem/i18n/ru.ts +162 -0
  53. package/src/tool/three-body-problem/i18n/sv.ts +162 -0
  54. package/src/tool/three-body-problem/i18n/tr.ts +162 -0
  55. package/src/tool/three-body-problem/i18n/zh.ts +162 -0
  56. package/src/tool/three-body-problem/index.ts +11 -0
  57. package/src/tool/three-body-problem/logic/ThreeBodyEngine.ts +179 -0
  58. package/src/tool/three-body-problem/seo.astro +15 -0
  59. package/src/tool/three-body-problem/three-body-problem-simulator.css +503 -0
  60. package/src/tools.ts +4 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-science",
3
- "version": "1.33.0",
3
+ "version": "1.35.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -15,10 +15,12 @@ import { phaseDiagramCriticalPoints } from '../tool/phase-diagram-critical-point
15
15
  import { twinParadoxVisualizer } from '../tool/twin-paradox-visualizer/index';
16
16
  import { mandelbrotFractal } from '../tool/mandelbrot-fractal/index';
17
17
  import { planetAtmosphereSurvival } from '../tool/planet-atmosphere-survival/index';
18
+ import { threeBodyProblem } from '../tool/three-body-problem/index';
19
+ import { rocheLimitSatelliteDisruption } from '../tool/roche-limit-satellite-disruption/index';
18
20
 
19
21
  export const scienceCategory: ScienceCategoryEntry = {
20
22
  icon: 'mdi:flask',
21
- tools: [colonyCounter, asteroidImpact, microwaveDetector, simulationProbability, cellularRenewal, cosmicInflation, temperatureTimeline, lorenzAttractor, stellarHabitabilityZone, radioactiveDecay, naturalSelectionDrift, entropySecondLaw, phaseDiagramCriticalPoints, twinParadoxVisualizer, mandelbrotFractal, planetAtmosphereSurvival],
23
+ tools: [colonyCounter, asteroidImpact, microwaveDetector, simulationProbability, cellularRenewal, cosmicInflation, temperatureTimeline, lorenzAttractor, stellarHabitabilityZone, radioactiveDecay, naturalSelectionDrift, entropySecondLaw, phaseDiagramCriticalPoints, twinParadoxVisualizer, mandelbrotFractal, planetAtmosphereSurvival, threeBodyProblem, rocheLimitSatelliteDisruption],
22
24
  i18n: {
23
25
  es: () => import('./i18n/es').then((m) => m.content),
24
26
  en: () => import('./i18n/en').then((m) => m.content),
package/src/entries.ts CHANGED
@@ -15,6 +15,8 @@ export { phaseDiagramCriticalPoints } from './tool/phase-diagram-critical-points
15
15
  export { twinParadoxVisualizer } from './tool/twin-paradox-visualizer/entry';
16
16
  export { mandelbrotFractal } from './tool/mandelbrot-fractal/entry';
17
17
  export { planetAtmosphereSurvival } from './tool/planet-atmosphere-survival/entry';
18
+ export { threeBodyProblem } from './tool/three-body-problem/entry';
19
+ export { rocheLimitSatelliteDisruption } from './tool/roche-limit-satellite-disruption/entry';
18
20
  export { scienceCategory } from './category';
19
21
  import { asteroidImpact } from './tool/asteroid-impact/entry';
20
22
  import { cellularRenewal } from './tool/cellular-renewal/entry';
@@ -32,4 +34,6 @@ import { phaseDiagramCriticalPoints } from './tool/phase-diagram-critical-points
32
34
  import { twinParadoxVisualizer } from './tool/twin-paradox-visualizer/entry';
33
35
  import { mandelbrotFractal } from './tool/mandelbrot-fractal/entry';
34
36
  import { planetAtmosphereSurvival } from './tool/planet-atmosphere-survival/entry';
35
- export const ALL_ENTRIES = [asteroidImpact, cellularRenewal, colonyCounter, microwaveDetector, simulationProbability, cosmicInflation, temperatureTimeline, lorenzAttractor, stellarHabitabilityZone, radioactiveDecay, naturalSelectionDrift, entropySecondLaw, phaseDiagramCriticalPoints, twinParadoxVisualizer, mandelbrotFractal, planetAtmosphereSurvival];
37
+ import { threeBodyProblem } from './tool/three-body-problem/entry';
38
+ import { rocheLimitSatelliteDisruption } from './tool/roche-limit-satellite-disruption/entry';
39
+ export const ALL_ENTRIES = [asteroidImpact, cellularRenewal, colonyCounter, microwaveDetector, simulationProbability, cosmicInflation, temperatureTimeline, lorenzAttractor, stellarHabitabilityZone, radioactiveDecay, naturalSelectionDrift, entropySecondLaw, phaseDiagramCriticalPoints, twinParadoxVisualizer, mandelbrotFractal, planetAtmosphereSurvival, threeBodyProblem, rocheLimitSatelliteDisruption];
package/src/index.ts CHANGED
@@ -16,6 +16,8 @@ export { PHASE_DIAGRAM_CRITICAL_POINTS_TOOL } from './tool/phase-diagram-critica
16
16
  export { TWIN_PARADOX_VISUALIZER_TOOL } from './tool/twin-paradox-visualizer/index';
17
17
  export { MANDELBROT_FRACTAL_TOOL } from './tool/mandelbrot-fractal/index';
18
18
  export { PLANET_ATMOSPHERE_SURVIVAL_TOOL } from './tool/planet-atmosphere-survival/index';
19
+ export { THREE_BODY_PROBLEM_TOOL } from './tool/three-body-problem/index';
20
+ export { ROCHE_LIMIT_SATELLITE_DISRUPTION_TOOL } from './tool/roche-limit-satellite-disruption/index';
19
21
 
20
22
  export type {
21
23
  KnownLocale,
@@ -18,7 +18,7 @@ describe('Locale Completeness Validation', () => {
18
18
  });
19
19
  });
20
20
 
21
- it('all 16 tools registered', () => {
22
- expect(ALL_TOOLS.length).toBe(16);
21
+ it('all 18 tools registered', () => {
22
+ expect(ALL_TOOLS.length).toBe(18);
23
23
  });
24
24
  });
@@ -4,8 +4,8 @@ import { scienceCategory } from '../data';
4
4
 
5
5
  describe('Tool Validation Suite', () => {
6
6
  describe('Library Registration', () => {
7
- it('should have 16 tools in ALL_TOOLS', () => {
8
- expect(ALL_TOOLS.length).toBe(16);
7
+ it('should have 18 tools in ALL_TOOLS', () => {
8
+ expect(ALL_TOOLS.length).toBe(18);
9
9
  });
10
10
 
11
11
  it('scienceCategory should be defined', () => {
@@ -8,7 +8,7 @@ interface Props {
8
8
  const { ui } = Astro.props;
9
9
  ---
10
10
 
11
- <div class="ns-app">
11
+ <div class="ns-app" data-alive-label={ui.aliveLabel} data-allele-default={ui.alleleDefault}>
12
12
  <section class="ns-card">
13
13
  <canvas id="simulation-canvas" class="ns-canvas"></canvas>
14
14
  <div class="ns-canvas-badge" id="alive-badge">0 {ui.aliveLabel}</div>
@@ -25,9 +25,19 @@ const { ui } = Astro.props;
25
25
 
26
26
  <section class="ns-alleles" id="allele-ranking" aria-label={ui.alleleCountsLabel}></section>
27
27
 
28
- <aside class="ns-console" aria-label="Simulation controls">
28
+ <button class="ns-console-trigger" id="ns-console-trigger" aria-label={ui.controls} aria-expanded="false">
29
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true"><path d="M2 4h12M2 8h8M2 12h10" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
30
+ {ui.controls}
31
+ </button>
32
+
33
+ <aside class="ns-console" id="ns-console" aria-label="Simulation controls">
29
34
  <div class="ns-console-header">
30
- <span class="ns-console-kicker">{ui.evolutionConsole}</span>
35
+ <div class="ns-console-header-row">
36
+ <span class="ns-console-kicker">{ui.evolutionConsole}</span>
37
+ <button class="ns-console-close" id="ns-console-close" aria-label="Close controls">
38
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>
39
+ </button>
40
+ </div>
31
41
  <h3>{ui.title}</h3>
32
42
  </div>
33
43
 
@@ -74,9 +84,10 @@ const { ui } = Astro.props;
74
84
  </section>
75
85
  </div>
76
86
 
77
- <script type="module" define:vars={{ ui }}>
78
- import { createNaturalSelectionRuntime } from '/src/tool/natural-selection-drift/runtime';
87
+ <script>
88
+ import { createNaturalSelectionRuntime } from './runtime';
79
89
 
90
+ const root = document.querySelector('.ns-app');
80
91
  createNaturalSelectionRuntime({
81
92
  populationInput: document.getElementById('population'),
82
93
  generationsInput: document.getElementById('generations'),
@@ -99,6 +110,26 @@ const { ui } = Astro.props;
99
110
  diversityScore: document.getElementById('diversity-score'),
100
111
  populationScore: document.getElementById('population-score'),
101
112
  alleleRanking: document.getElementById('allele-ranking'),
102
- ui: { ...ui },
113
+ ui: {
114
+ aliveLabel: root?.dataset.aliveLabel ?? '',
115
+ alleleDefault: root?.dataset.alleleDefault ?? '',
116
+ },
103
117
  });
118
+
119
+ const trigger = document.getElementById('ns-console-trigger');
120
+ const closeBtn = document.getElementById('ns-console-close');
121
+ const console = document.getElementById('ns-console');
122
+
123
+ function openConsole() {
124
+ console?.classList.add('is-open');
125
+ trigger?.setAttribute('aria-expanded', 'true');
126
+ }
127
+
128
+ function closeConsole() {
129
+ console?.classList.remove('is-open');
130
+ trigger?.setAttribute('aria-expanded', 'false');
131
+ }
132
+
133
+ trigger?.addEventListener('click', openConsole);
134
+ closeBtn?.addEventListener('click', closeConsole);
104
135
  </script>
@@ -427,3 +427,137 @@
427
427
  grid-template-columns: minmax(0, 1fr);
428
428
  }
429
429
  }
430
+
431
+ .ns-console-trigger {
432
+ display: none;
433
+ }
434
+
435
+ .ns-console-header-row {
436
+ display: flex;
437
+ align-items: center;
438
+ justify-content: space-between;
439
+ gap: 0.5rem;
440
+ }
441
+
442
+ .ns-console-close {
443
+ display: none;
444
+ align-items: center;
445
+ justify-content: center;
446
+ width: 28px;
447
+ height: 28px;
448
+ border-radius: 50%;
449
+ border: 1px solid var(--ns-border);
450
+ background: var(--ns-panel);
451
+ color: var(--ns-muted);
452
+ cursor: pointer;
453
+ flex-shrink: 0;
454
+ transition: background 0.18s ease, color 0.18s ease;
455
+ }
456
+
457
+ .ns-console-close:hover {
458
+ background: rgba(239, 68, 68, 0.12);
459
+ color: #ef4444;
460
+ }
461
+
462
+ @media (max-width: 640px) {
463
+ .ns-console-trigger {
464
+ display: inline-flex;
465
+ align-items: center;
466
+ gap: 0.5rem;
467
+ position: absolute;
468
+ left: clamp(16px, 4vw, 24px);
469
+ bottom: clamp(16px, 4vw, 24px);
470
+ z-index: 3;
471
+ padding: 0.6rem 1rem;
472
+ border-radius: 999px;
473
+ border: 1px solid rgba(255, 255, 255, 0.14);
474
+ background: rgba(2, 6, 23, 0.72);
475
+ backdrop-filter: blur(18px);
476
+ color: rgba(255, 255, 255, 0.9);
477
+ font-size: 0.78rem;
478
+ font-weight: 700;
479
+ letter-spacing: 0.06em;
480
+ text-transform: uppercase;
481
+ cursor: pointer;
482
+ transition: background 0.18s ease, transform 0.18s ease, opacity 0.18s ease;
483
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
484
+ }
485
+
486
+ .theme-light .ns-console-trigger {
487
+ background: rgba(255, 255, 255, 0.88);
488
+ border-color: rgba(148, 163, 184, 0.22);
489
+ color: #0f172a;
490
+ box-shadow: 0 8px 24px rgba(148, 163, 184, 0.22);
491
+ }
492
+
493
+ .ns-console-trigger:hover {
494
+ transform: translateY(-1px);
495
+ }
496
+
497
+ .ns-console-trigger[aria-expanded="true"] {
498
+ opacity: 0;
499
+ pointer-events: none;
500
+ }
501
+
502
+ .ns-console {
503
+ left: 0;
504
+ right: 0;
505
+ top: auto;
506
+ bottom: 0;
507
+ width: 100%;
508
+ border-radius: 24px 24px 0 0;
509
+ border-bottom: none;
510
+ padding: 1.25rem clamp(16px, 4vw, 24px) calc(clamp(16px, 4vw, 24px) + env(safe-area-inset-bottom, 0px));
511
+ transform: translateY(110%);
512
+ transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
513
+ background: rgba(7, 9, 13, 0.88);
514
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
515
+ backdrop-filter: blur(24px);
516
+ max-height: 80vh;
517
+ overflow-y: auto;
518
+ overscroll-behavior: contain;
519
+ }
520
+
521
+ .theme-light .ns-console {
522
+ background: rgba(255, 255, 255, 0.96);
523
+ border-top-color: rgba(148, 163, 184, 0.2);
524
+ }
525
+
526
+ .ns-console.is-open {
527
+ transform: translateY(0);
528
+ }
529
+
530
+ .ns-console-close {
531
+ display: inline-flex;
532
+ }
533
+
534
+ .ns-console::before {
535
+ content: "";
536
+ display: block;
537
+ width: 40px;
538
+ height: 4px;
539
+ border-radius: 999px;
540
+ background: rgba(255, 255, 255, 0.18);
541
+ margin: 0 auto 1rem;
542
+ }
543
+
544
+ .theme-light .ns-console::before {
545
+ background: rgba(15, 23, 42, 0.14);
546
+ }
547
+
548
+ .ns-alleles {
549
+ top: 72px;
550
+ right: clamp(12px, 3vw, 16px);
551
+ grid-template-columns: minmax(140px, 180px);
552
+ }
553
+
554
+ .ns-allele-chip {
555
+ padding: 0.28rem 0.4rem 0.28rem 0.6rem;
556
+ border-radius: 10px;
557
+ }
558
+
559
+ .ns-hud {
560
+ bottom: clamp(72px, 12vw, 96px);
561
+ }
562
+ }
563
+
@@ -0,0 +1,14 @@
1
+ ---
2
+ import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
3
+ import { rocheLimitSatelliteDisruption } 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 rocheLimitSatelliteDisruption.i18n[locale]?.();
12
+ ---
13
+
14
+ {content && <SharedBibliography links={content.bibliography} />}
@@ -0,0 +1,16 @@
1
+ import type { BibliographyEntry } from '../../types';
2
+
3
+ export const bibliography: BibliographyEntry[] = [
4
+ {
5
+ name: 'Murray, C. D. & Dermott, S. F. (1999). Solar System Dynamics. Cambridge University Press.',
6
+ url: 'https://www.cambridge.org/core/books/solar-system-dynamics/108745217E4A18190CBA340ED5E477A2',
7
+ },
8
+ {
9
+ name: 'NASA Solar System Exploration: Roche Limit and Saturn ring context.',
10
+ url: 'https://www.nasa.gov/solar-system/new-simulations-shed-light-on-origins-of-saturns-rings-and-icy-moons/',
11
+ },
12
+ {
13
+ name: 'Chandrasekhar, S. (1969). Ellipsoidal Figures of Equilibrium. Yale University Press.',
14
+ url: 'https://academic.oup.com/gji/article/21/1/103/729424',
15
+ },
16
+ ];
@@ -0,0 +1,97 @@
1
+ ---
2
+ import './roche-limit-satellite-disruption-calculator.css';
3
+
4
+ interface Props {
5
+ ui: Record<string, string>;
6
+ }
7
+
8
+ const { ui } = Astro.props;
9
+ const uiData = JSON.stringify(ui);
10
+ ---
11
+
12
+ <div class="roche-console" id="roche-console" data-ui={uiData}>
13
+ <div class="roche-status">
14
+ <strong id="roche-verdict">{ui.stable}</strong>
15
+ <span class="roche-distance-readout"><small>{ui.orbitDistance}</small><b id="roche-distance-output">-</b></span>
16
+ </div>
17
+
18
+ <section class="roche-visual" aria-label={ui.ringFormation}>
19
+ <div class="roche-orbit-stage">
20
+ <canvas id="roche-particle-canvas" class="roche-particle-canvas" aria-hidden="true"></canvas>
21
+ <svg class="roche-orbit-map" viewBox="0 0 560 560" role="img" aria-label={ui.rocheBoundary}>
22
+ <defs>
23
+ <radialGradient id="roche-planet-gradient" cx="42%" cy="35%">
24
+ <stop id="roche-planet-stop-a" offset="0%" stop-color="#f7efe2"></stop>
25
+ <stop id="roche-planet-stop-b" offset="42%" stop-color="#d19a66"></stop>
26
+ <stop id="roche-planet-stop-c" offset="100%" stop-color="#63425f"></stop>
27
+ </radialGradient>
28
+ <linearGradient id="roche-ring-gradient" x1="0" x2="1">
29
+ <stop offset="0%" stop-color="#7cd5ff"></stop>
30
+ <stop offset="48%" stop-color="#f8d66d"></stop>
31
+ <stop offset="100%" stop-color="#ff7f8a"></stop>
32
+ </linearGradient>
33
+ </defs>
34
+ <path class="roche-debris roche-debris-back" d="M 102 280 A 178 62 0 0 1 458 280"></path>
35
+ <path class="roche-debris roche-debris-back roche-debris-wide" d="M 66 280 A 214 74 0 0 1 494 280"></path>
36
+ <circle class="roche-boundary" id="roche-boundary" cx="280" cy="280" r="180"></circle>
37
+ <circle class="roche-orbit" id="roche-orbit" cx="280" cy="280" r="230"></circle>
38
+ <g id="roche-fragments"></g>
39
+ <circle class="roche-planet" id="roche-planet" cx="280" cy="280" r="88"></circle>
40
+ <path class="roche-debris roche-debris-front" d="M 458 280 A 178 62 0 0 1 102 280"></path>
41
+ <path class="roche-debris roche-debris-front roche-debris-wide" d="M 494 280 A 214 74 0 0 1 66 280"></path>
42
+ <g class="roche-moon" id="roche-moon">
43
+ <ellipse id="roche-moon-body" cx="0" cy="0" rx="22" ry="22"></ellipse>
44
+ <path id="roche-moon-scar" d="M -11 -13 C 5 -7 10 4 5 17"></path>
45
+ </g>
46
+ <text class="roche-map-label" x="280" y="512" id="roche-map-label">{ui.moonTrack}</text>
47
+ </svg>
48
+ </div>
49
+ </section>
50
+
51
+ <section class="roche-readouts" aria-label={ui.tidalStress}>
52
+ <div><span>{ui.activeLimit}</span><strong id="roche-active-limit">-</strong></div>
53
+ <div><span>{ui.safetyRatio}</span><strong id="roche-safety-ratio">-</strong></div>
54
+ <div><span>{ui.orbitalPeriod}</span><strong id="roche-period">-</strong></div>
55
+ <div><span>{ui.ringFormation}</span><strong id="roche-ring-progress">-</strong></div>
56
+ </section>
57
+
58
+ <section class="roche-controls" aria-label={ui.primaryBody}>
59
+ <div class="roche-field roche-picker" id="roche-primary-picker">
60
+ <span>{ui.primaryBody}</span>
61
+ <button class="roche-picker-trigger" type="button" id="roche-primary-trigger" aria-expanded="false">
62
+ <strong id="roche-primary-name">-</strong><small id="roche-primary-density">-</small><i aria-hidden="true"></i>
63
+ </button>
64
+ <div class="roche-picker-menu" id="roche-primary-menu" role="listbox"></div>
65
+ </div>
66
+
67
+ <div class="roche-field roche-picker" id="roche-satellite-picker">
68
+ <span>{ui.satelliteType}</span>
69
+ <button class="roche-picker-trigger" type="button" id="roche-satellite-trigger" aria-expanded="false">
70
+ <strong id="roche-satellite-name">-</strong><small id="roche-satellite-density">-</small><i aria-hidden="true"></i>
71
+ </button>
72
+ <div class="roche-picker-menu" id="roche-satellite-menu" role="listbox"></div>
73
+ </div>
74
+
75
+ <label class="roche-field roche-distance" for="roche-distance">
76
+ <span>{ui.orbitDistance}</span>
77
+ <input id="roche-distance" type="range" min="8000" max="260000" step="250" />
78
+ </label>
79
+
80
+ <div class="roche-button-row">
81
+ <button type="button" id="roche-close-pass">{ui.closePass}</button>
82
+ <button type="button" id="roche-reset">{ui.reset}</button>
83
+ </div>
84
+ </section>
85
+
86
+ <section class="roche-comparison" aria-label={ui.fluidLimit}>
87
+ <div class="roche-bar"><span>{ui.fluidLimit}</span><div><i id="roche-fluid-bar"></i></div><strong id="roche-fluid-limit">-</strong></div>
88
+ <div class="roche-bar"><span>{ui.rigidLimit}</span><div><i id="roche-rigid-bar"></i></div><strong id="roche-rigid-limit">-</strong></div>
89
+ <div class="roche-material">
90
+ <span id="roche-density">-</span>
91
+ <span id="roche-cohesion">-</span>
92
+ <span id="roche-radius">-</span>
93
+ </div>
94
+ </section>
95
+ </div>
96
+
97
+ <script src="./script.ts"></script>
@@ -0,0 +1,28 @@
1
+ import type { ScienceToolEntry } from '../../types';
2
+
3
+ export const rocheLimitSatelliteDisruption: ScienceToolEntry = {
4
+ id: 'roche-limit-satellite-disruption',
5
+ icons: {
6
+ bg: 'mdi:orbit',
7
+ fg: 'mdi:moon-waning-crescent',
8
+ },
9
+ i18n: {
10
+ de: () => import('./i18n/de').then((m) => m.content),
11
+ en: () => import('./i18n/en').then((m) => m.content),
12
+ es: () => import('./i18n/es').then((m) => m.content),
13
+ fr: () => import('./i18n/fr').then((m) => m.content),
14
+ id: () => import('./i18n/id').then((m) => m.content),
15
+ it: () => import('./i18n/it').then((m) => m.content),
16
+ ja: () => import('./i18n/ja').then((m) => m.content),
17
+ ko: () => import('./i18n/ko').then((m) => m.content),
18
+ nl: () => import('./i18n/nl').then((m) => m.content),
19
+ pl: () => import('./i18n/pl').then((m) => m.content),
20
+ pt: () => import('./i18n/pt').then((m) => m.content),
21
+ ru: () => import('./i18n/ru').then((m) => m.content),
22
+ sv: () => import('./i18n/sv').then((m) => m.content),
23
+ tr: () => import('./i18n/tr').then((m) => m.content),
24
+ zh: () => import('./i18n/zh').then((m) => m.content),
25
+ },
26
+ };
27
+
28
+ export type { ToolLocaleContent } from '../../types';