@jjlmoya/utils-astronomy 1.35.1 → 1.37.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 +18 -5
- package/scripts/postbuild.mjs +17 -0
- package/scripts/postinstall.mjs +2 -4
- package/src/category/index.ts +2 -1
- package/src/components/ProductionBreadcrumb.astro +134 -0
- package/src/components/ProductionStructuredData.astro +46 -0
- package/src/components/ProductionWidget.astro +182 -0
- package/src/entries.ts +4 -2
- package/src/i18n/header-ui.ts +15 -0
- package/src/i18n/language-ui.ts +9 -0
- package/src/i18n/languages.ts +24 -0
- package/src/identity/brands.ts +5 -0
- package/src/index.ts +2 -1
- package/src/layouts/ProductionCategoryPage.astro +184 -0
- package/src/layouts/ProductionPage.astro +218 -0
- package/src/layouts/ProductionUtilityPage.astro +283 -0
- package/src/mfe/assets.ts +39 -0
- package/src/mfe/category-ui.ts +34 -0
- package/src/mfe/manifest.ts +45 -0
- package/src/mfe/routes.ts +50 -0
- package/src/pages/[locale]/[utilities]/[categories]/[category]/[slug]/manifest.json.ts +49 -0
- package/src/pages/[locale]/[utilities]/[categories]/[category]/[slug].astro +100 -0
- package/src/pages/[locale]/[utilities]/[categories]/[category].astro +44 -0
- package/src/pages/index.astro +4 -4
- package/src/pages/mfe-sitemaps/[locale]/[vertical]/sitemap.xml.ts +75 -0
- package/src/pages/utilidades/[slug]/manifest.json.ts +28 -0
- package/src/pages/utilidades/[slug].astro +90 -0
- package/src/pages/utilidades/categorias/[category].astro +38 -0
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/mfe_cache_contract.test.ts +14 -0
- package/src/tests/mfe_manifest.test.ts +28 -0
- package/src/tests/registry_contract.test.ts +67 -0
- package/src/tests/tool_validation.test.ts +4 -3
- package/src/tool/astrophotographyExposureStackPlanner/astrophotography-exposure-stacking-planner.css +496 -0
- package/src/tool/astrophotographyExposureStackPlanner/bibliography.astro +14 -0
- package/src/tool/astrophotographyExposureStackPlanner/bibliography.ts +12 -0
- package/src/tool/astrophotographyExposureStackPlanner/component.astro +149 -0
- package/src/tool/astrophotographyExposureStackPlanner/controller.ts +125 -0
- package/src/tool/astrophotographyExposureStackPlanner/dom-views.ts +41 -0
- package/src/tool/astrophotographyExposureStackPlanner/entry.ts +29 -0
- package/src/tool/astrophotographyExposureStackPlanner/evaluator.ts +44 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/de.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/en.ts +232 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/es.ts +52 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/fr.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/id.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/it.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/ja.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/ko.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/nl.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/pl.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/pt.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/ru.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/sv.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/tr.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/i18n/zh.ts +40 -0
- package/src/tool/astrophotographyExposureStackPlanner/index.ts +11 -0
- package/src/tool/astrophotographyExposureStackPlanner/localized.ts +59 -0
- package/src/tool/astrophotographyExposureStackPlanner/logic.test.ts +74 -0
- package/src/tool/astrophotographyExposureStackPlanner/logic.ts +95 -0
- package/src/tool/astrophotographyExposureStackPlanner/seo.astro +14 -0
- package/src/tool/astrophotographyExposureStackPlanner/storage.ts +22 -0
- package/src/tool/astrophotographyExposureStackPlanner/ui.ts +102 -0
- package/src/tool/meteorShowerObservingPlanner/dom-views.ts +38 -16
- package/src/tool/meteorShowerObservingPlanner/i18n/de.ts +219 -219
- package/src/tool/meteorShowerObservingPlanner/i18n/en.ts +219 -219
- package/src/tool/meteorShowerObservingPlanner/i18n/es.ts +219 -219
- package/src/tool/meteorShowerObservingPlanner/i18n/fr.ts +219 -219
- package/src/tool/meteorShowerObservingPlanner/i18n/id.ts +223 -223
- package/src/tool/meteorShowerObservingPlanner/i18n/it.ts +223 -223
- package/src/tool/meteorShowerObservingPlanner/i18n/ja.ts +219 -219
- package/src/tool/meteorShowerObservingPlanner/i18n/ko.ts +219 -219
- package/src/tool/meteorShowerObservingPlanner/i18n/nl.ts +223 -223
- package/src/tool/meteorShowerObservingPlanner/i18n/pl.ts +223 -223
- package/src/tool/meteorShowerObservingPlanner/i18n/pt.ts +223 -223
- package/src/tool/meteorShowerObservingPlanner/i18n/ru.ts +219 -219
- package/src/tool/meteorShowerObservingPlanner/i18n/sv.ts +223 -223
- package/src/tool/meteorShowerObservingPlanner/i18n/tr.ts +223 -223
- package/src/tool/meteorShowerObservingPlanner/i18n/zh.ts +219 -219
- package/src/tool/meteorShowerObservingPlanner/meteor-shower-observing-planner.css +16 -9
- package/src/tool/meteorShowerObservingPlanner/ui.ts +87 -87
- package/src/tools.ts +3 -1
- package/src/types.ts +2 -2
- package/src/worker.ts +27 -0
- package/src/pages/[locale]/[slug].astro +0 -161
- package/src/pages/[locale].astro +0 -251
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ExposurePlannerInput,
|
|
3
|
+
ExposurePlannerResult,
|
|
4
|
+
SensorPreset,
|
|
5
|
+
TimelineSlice,
|
|
6
|
+
} from './ui';
|
|
7
|
+
|
|
8
|
+
export const SENSOR_CROP_FACTORS: Record<SensorPreset, number> = {
|
|
9
|
+
fullFrame: 1,
|
|
10
|
+
apsC: 1.5,
|
|
11
|
+
microFourThirds: 2,
|
|
12
|
+
phone: 2.7,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_INPUT: ExposurePlannerInput = {
|
|
16
|
+
exposureRule: 'npf',
|
|
17
|
+
sensor: 'fullFrame',
|
|
18
|
+
focalLength: 24,
|
|
19
|
+
aperture: 2.8,
|
|
20
|
+
pixelPitch: 5.9,
|
|
21
|
+
declination: 0,
|
|
22
|
+
tracking: 'untracked',
|
|
23
|
+
trackingExposureSeconds: 120,
|
|
24
|
+
deadTimeSeconds: 2,
|
|
25
|
+
totalIntegrationMinutes: 120,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function finiteOr(value: number, fallback: number): number {
|
|
29
|
+
return Number.isFinite(value) ? value : fallback;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function normalizeInput(input: ExposurePlannerInput): ExposurePlannerInput {
|
|
33
|
+
return {
|
|
34
|
+
...input,
|
|
35
|
+
focalLength: Math.min(600, Math.max(5, finiteOr(input.focalLength, DEFAULT_INPUT.focalLength))),
|
|
36
|
+
aperture: Math.min(32, Math.max(1, finiteOr(input.aperture, DEFAULT_INPUT.aperture))),
|
|
37
|
+
pixelPitch: Math.min(30, Math.max(0.5, finiteOr(input.pixelPitch, DEFAULT_INPUT.pixelPitch))),
|
|
38
|
+
declination: Math.min(89, Math.max(0, finiteOr(input.declination, DEFAULT_INPUT.declination))),
|
|
39
|
+
trackingExposureSeconds: Math.min(900, Math.max(5, finiteOr(input.trackingExposureSeconds, DEFAULT_INPUT.trackingExposureSeconds))),
|
|
40
|
+
deadTimeSeconds: Math.min(120, Math.max(0, finiteOr(input.deadTimeSeconds, DEFAULT_INPUT.deadTimeSeconds))),
|
|
41
|
+
totalIntegrationMinutes: Math.min(1440, Math.max(1, finiteOr(input.totalIntegrationMinutes, DEFAULT_INPUT.totalIntegrationMinutes))),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function calculateRule500(focalLength: number, sensor: SensorPreset): number {
|
|
46
|
+
const cropFactor = SENSOR_CROP_FACTORS[sensor];
|
|
47
|
+
return 500 / (focalLength * cropFactor);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function calculateNpfSeconds(
|
|
51
|
+
focalLength: number,
|
|
52
|
+
aperture: number,
|
|
53
|
+
pixelPitch: number,
|
|
54
|
+
declination: number,
|
|
55
|
+
): number {
|
|
56
|
+
const declinationRadians = declination * (Math.PI / 180);
|
|
57
|
+
const cosine = Math.max(0.1, Math.cos(declinationRadians));
|
|
58
|
+
return (16.9 * aperture + 0.1 * focalLength + 13.7 * pixelPitch) / (focalLength * cosine);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function calculateTimeline(frameCount: number): TimelineSlice[] {
|
|
62
|
+
const visibleSlices = Math.min(12, Math.max(1, frameCount));
|
|
63
|
+
return Array.from({ length: visibleSlices }, (_, index) => ({
|
|
64
|
+
index: index + 1,
|
|
65
|
+
width: 100 / visibleSlices,
|
|
66
|
+
isFinal: index === visibleSlices - 1,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function calculateExposurePlan(rawInput: ExposurePlannerInput): ExposurePlannerResult {
|
|
71
|
+
const input = normalizeInput(rawInput);
|
|
72
|
+
const npfSeconds = calculateNpfSeconds(input.focalLength, input.aperture, input.pixelPitch, input.declination);
|
|
73
|
+
const rule500Seconds = calculateRule500(input.focalLength, input.sensor);
|
|
74
|
+
const untrackedCeilingSeconds = input.exposureRule === 'npf' ? npfSeconds : rule500Seconds;
|
|
75
|
+
const selectedCeilingSeconds = input.tracking === 'tracked' ? input.trackingExposureSeconds : untrackedCeilingSeconds;
|
|
76
|
+
const totalSeconds = input.totalIntegrationMinutes * 60;
|
|
77
|
+
const frameCount = Math.ceil(totalSeconds / selectedCeilingSeconds);
|
|
78
|
+
const plannedIntegrationMinutes = (frameCount * selectedCeilingSeconds) / 60;
|
|
79
|
+
const deadTimeMinutes = (frameCount * input.deadTimeSeconds) / 60;
|
|
80
|
+
const stackingGainStops = Math.log2(frameCount) / 2;
|
|
81
|
+
const npfDifferencePercent = ((npfSeconds - rule500Seconds) / rule500Seconds) * 100;
|
|
82
|
+
return {
|
|
83
|
+
npfSeconds,
|
|
84
|
+
rule500Seconds,
|
|
85
|
+
selectedCeilingSeconds,
|
|
86
|
+
frameCount,
|
|
87
|
+
plannedIntegrationMinutes,
|
|
88
|
+
deadTimePerFrameSeconds: input.deadTimeSeconds,
|
|
89
|
+
deadTimeMinutes,
|
|
90
|
+
sessionDurationMinutes: plannedIntegrationMinutes + deadTimeMinutes,
|
|
91
|
+
stackingGainStops,
|
|
92
|
+
npfDifferencePercent,
|
|
93
|
+
timeline: calculateTimeline(frameCount),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { astrophotographyExposureStackPlanner } 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 astrophotographyExposureStackPlanner.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ExposurePlannerInput } from './ui';
|
|
2
|
+
|
|
3
|
+
const STORAGE_KEY = 'jjlmoya-astronomy-exposure-stack-planner';
|
|
4
|
+
|
|
5
|
+
export function loadExposurePlannerInput(fallback: ExposurePlannerInput): ExposurePlannerInput {
|
|
6
|
+
try {
|
|
7
|
+
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
8
|
+
if (!raw) return fallback;
|
|
9
|
+
const parsed = JSON.parse(raw) as Partial<ExposurePlannerInput>;
|
|
10
|
+
return { ...fallback, ...parsed };
|
|
11
|
+
} catch {
|
|
12
|
+
return fallback;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function saveExposurePlannerInput(input: ExposurePlannerInput): void {
|
|
17
|
+
try {
|
|
18
|
+
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(input));
|
|
19
|
+
} catch {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { BibliographyEntry, ToolLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export type ExposureRule = 'npf' | 'rule500';
|
|
4
|
+
export type SensorPreset = 'fullFrame' | 'apsC' | 'microFourThirds' | 'phone';
|
|
5
|
+
export type TrackingMode = 'untracked' | 'tracked';
|
|
6
|
+
|
|
7
|
+
export interface ExposurePlannerInput {
|
|
8
|
+
exposureRule: ExposureRule;
|
|
9
|
+
sensor: SensorPreset;
|
|
10
|
+
focalLength: number;
|
|
11
|
+
aperture: number;
|
|
12
|
+
pixelPitch: number;
|
|
13
|
+
declination: number;
|
|
14
|
+
tracking: TrackingMode;
|
|
15
|
+
trackingExposureSeconds: number;
|
|
16
|
+
deadTimeSeconds: number;
|
|
17
|
+
totalIntegrationMinutes: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TimelineSlice {
|
|
21
|
+
index: number;
|
|
22
|
+
width: number;
|
|
23
|
+
isFinal: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ExposurePlannerResult {
|
|
27
|
+
npfSeconds: number;
|
|
28
|
+
rule500Seconds: number;
|
|
29
|
+
selectedCeilingSeconds: number;
|
|
30
|
+
frameCount: number;
|
|
31
|
+
plannedIntegrationMinutes: number;
|
|
32
|
+
deadTimePerFrameSeconds: number;
|
|
33
|
+
deadTimeMinutes: number;
|
|
34
|
+
sessionDurationMinutes: number;
|
|
35
|
+
stackingGainStops: number;
|
|
36
|
+
npfDifferencePercent: number;
|
|
37
|
+
timeline: TimelineSlice[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ExposurePlannerUI {
|
|
41
|
+
cameraLegend: string;
|
|
42
|
+
focalLabel: string;
|
|
43
|
+
focalHelp: string;
|
|
44
|
+
apertureLabel: string;
|
|
45
|
+
apertureHelp: string;
|
|
46
|
+
pixelPitchLabel: string;
|
|
47
|
+
pixelPitchHelp: string;
|
|
48
|
+
declinationLabel: string;
|
|
49
|
+
declinationHelp: string;
|
|
50
|
+
sensorLabel: string;
|
|
51
|
+
sensorOptions: Record<SensorPreset, string>;
|
|
52
|
+
ruleLabel: string;
|
|
53
|
+
ruleNpf: string;
|
|
54
|
+
rule500: string;
|
|
55
|
+
trackingLabel: string;
|
|
56
|
+
untracked: string;
|
|
57
|
+
tracked: string;
|
|
58
|
+
trackingExposureLabel: string;
|
|
59
|
+
trackingExposureHelp: string;
|
|
60
|
+
deadTimeLabel: string;
|
|
61
|
+
deadTimeHelp: string;
|
|
62
|
+
totalIntegrationLabel: string;
|
|
63
|
+
totalIntegrationHelp: string;
|
|
64
|
+
resultTitle: string;
|
|
65
|
+
resultKicker: string;
|
|
66
|
+
secondsUnit: string;
|
|
67
|
+
framesLabel: string;
|
|
68
|
+
totalTimeLabel: string;
|
|
69
|
+
sessionTimeLabel: string;
|
|
70
|
+
gainLabel: string;
|
|
71
|
+
sessionDetail: string;
|
|
72
|
+
workloadText: string;
|
|
73
|
+
highWorkloadText: string;
|
|
74
|
+
sensorPixelWarning: string;
|
|
75
|
+
comparisonTitle: string;
|
|
76
|
+
npfLabel: string;
|
|
77
|
+
rule500Label: string;
|
|
78
|
+
deltaLabel: string;
|
|
79
|
+
timelineTitle: string;
|
|
80
|
+
timelineStart: string;
|
|
81
|
+
timelineEnd: string;
|
|
82
|
+
trackingNote: string;
|
|
83
|
+
untrackedNote: string;
|
|
84
|
+
methodTitle: string;
|
|
85
|
+
methodText: string;
|
|
86
|
+
limitsTitle: string;
|
|
87
|
+
limitsText: string;
|
|
88
|
+
startingStatus: string;
|
|
89
|
+
formulaLabel: string;
|
|
90
|
+
formulaNpf: string;
|
|
91
|
+
formula500: string;
|
|
92
|
+
presetsLabel: string;
|
|
93
|
+
presets: Record<string, string>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export type ExposurePlannerLocaleContent = ToolLocaleContent<ExposurePlannerUI>;
|
|
97
|
+
|
|
98
|
+
export interface ExposurePlannerBibliographyEntry extends BibliographyEntry {
|
|
99
|
+
country: string;
|
|
100
|
+
language: string;
|
|
101
|
+
supports: string;
|
|
102
|
+
}
|
|
@@ -129,39 +129,61 @@ function calculateDomeCoords(input: ObservingPlannerInput, evaluation: Evaluatio
|
|
|
129
129
|
return { peakHour, elev, isAbove, radiantX, radiantY, moonX, moonY };
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
function getConstellationName(constKey: string, ui?: MeteorShowerObservingPlannerUI): string {
|
|
133
|
-
if (!ui || !ui.constellations) return constKey;
|
|
134
|
-
return ui.constellations[constKey] || constKey;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
132
|
function getStatusText(isAbove: boolean, elev: number, ui?: MeteorShowerObservingPlannerUI): string {
|
|
138
133
|
const alt = ui && ui.altLabel ? ui.altLabel : 'Alt';
|
|
139
134
|
const below = ui && ui.belowHorizonLabel ? ui.belowHorizonLabel : 'Below Horizon';
|
|
140
135
|
return isAbove ? `+${elev}° ${alt}` : below;
|
|
141
136
|
}
|
|
142
137
|
|
|
143
|
-
|
|
138
|
+
function getConstellationName(constKey: string, ui?: MeteorShowerObservingPlannerUI): string {
|
|
139
|
+
if (!ui?.constellations) return constKey;
|
|
140
|
+
return ui.constellations[constKey] || constKey;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
interface DomePresentation {
|
|
144
|
+
coords: DomeCoords;
|
|
145
|
+
peakZhr: number;
|
|
146
|
+
constName: string;
|
|
147
|
+
statusTxt: string;
|
|
148
|
+
moonLabelTxt: string;
|
|
149
|
+
belowHorizonTxt: string;
|
|
150
|
+
telemetryTitle: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function createDomePresentation(
|
|
154
|
+
input: ObservingPlannerInput,
|
|
155
|
+
evaluation: EvaluationResult,
|
|
156
|
+
ui?: MeteorShowerObservingPlannerUI,
|
|
157
|
+
): DomePresentation {
|
|
144
158
|
const shower = getShowerById(input.showerId);
|
|
145
159
|
const coords = calculateDomeCoords(input, evaluation);
|
|
146
160
|
const peakItem = evaluation.hourlyBreakdown.find((h) => h.isPeakHour) || evaluation.hourlyBreakdown[0];
|
|
161
|
+
const labels = ui ?? {};
|
|
162
|
+
return {
|
|
163
|
+
coords,
|
|
164
|
+
peakZhr: peakItem?.effectiveZhr ?? 0,
|
|
165
|
+
constName: getConstellationName(shower.radiantConstellation, ui),
|
|
166
|
+
statusTxt: getStatusText(coords.isAbove, coords.elev, ui),
|
|
167
|
+
moonLabelTxt: labels.moonLabel ?? 'Moon',
|
|
168
|
+
belowHorizonTxt: labels.belowHorizonLabel ?? 'Below Horizon',
|
|
169
|
+
telemetryTitle: labels.radiantTelemetryLabel ?? 'RADIANT TELEMETRY',
|
|
170
|
+
};
|
|
171
|
+
}
|
|
147
172
|
|
|
173
|
+
export function renderSkyDomeSvg(input: ObservingPlannerInput, evaluation: EvaluationResult, ui?: MeteorShowerObservingPlannerUI): string {
|
|
174
|
+
const presentation = createDomePresentation(input, evaluation, ui);
|
|
148
175
|
const moonOpacity = (input.moonPhase * 0.5).toFixed(2);
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
const moonLabelTxt = ui && ui.moonLabel ? ui.moonLabel : 'Moon';
|
|
152
|
-
const belowHorizonTxt = ui && ui.belowHorizonLabel ? ui.belowHorizonLabel : 'Below Horizon';
|
|
153
|
-
const telemetryTitle = ui && ui.radiantTelemetryLabel ? ui.radiantTelemetryLabel : 'RADIANT TELEMETRY';
|
|
154
|
-
|
|
155
|
-
const moonParams: MoonGraphicParams = { moonPhase: input.moonPhase, x: coords.moonX, y: coords.moonY, moonTxt: moonLabelTxt };
|
|
176
|
+
const { coords } = presentation;
|
|
177
|
+
const moonParams: MoonGraphicParams = { moonPhase: input.moonPhase, x: coords.moonX, y: coords.moonY, moonTxt: presentation.moonLabelTxt };
|
|
156
178
|
|
|
157
179
|
return `
|
|
158
180
|
<svg class="sky-dome-svg" viewBox="0 0 400 230" role="img" aria-label="Celestial Sky Dome">
|
|
159
181
|
${renderSkyDomeBackground(moonOpacity)}
|
|
160
182
|
${renderSkyDomeGrid()}
|
|
161
|
-
${renderTelemetryCard(telemetryTitle, constName, statusTxt, coords.isAbove ? '#10b981' : '#f43f5e')}
|
|
183
|
+
${renderTelemetryCard(presentation.telemetryTitle, presentation.constName, presentation.statusTxt, coords.isAbove ? '#10b981' : '#f43f5e')}
|
|
162
184
|
${renderMoonGraphic(moonParams)}
|
|
163
|
-
${renderMeteorStreaks(coords.isAbove,
|
|
164
|
-
${renderRadiantMarker(coords.isAbove, coords.radiantX, coords.radiantY, belowHorizonTxt)}
|
|
185
|
+
${renderMeteorStreaks(coords.isAbove, presentation.peakZhr, coords.radiantX, coords.radiantY)}
|
|
186
|
+
${renderRadiantMarker(coords.isAbove, coords.radiantX, coords.radiantY, presentation.belowHorizonTxt)}
|
|
165
187
|
</svg>
|
|
166
188
|
`;
|
|
167
189
|
}
|