Files
gods-eye-view/src/hudSummaryResponse.js
T
German KhokhlovandClaude Opus 5.5 54dce5b3bd fix(i18n): second screenshot review: remaining English, numbers and wraps
From the re-shot Russian screenshots:
- Translated: weather frame/imagery status errors, HUD summary provenance
  ("НЕДОСТУПНО: СУДА AIS ОНЛАЙН"), Bhote Koshi map labels, cockpit signal
  distances (КМ). The HUD summary fields are separated with " · " in every
  language, so the style name and view band no longer read as one phrase.
- Numbers follow the locale: cockpit altitude ("26 175"), precipitation.
- No breaks inside codes and dates: ADS-B and similar in layer rows,
  Sentinel-2, cyclone issue dates (word joiner; the UI font has no U+2011),
  no-break space after label colons in cyclone details.
- Layout: count lines under ALPR, short unavailable chip (context
  "status chip"), roomier draw segments, tighter CCTV calibration chips,
  two-line beat titles and media card titles, wrapping weather status.
- Wording: ru advisory is «сводка» (was «бюллетень»; "Advisory center" is
  «Центр циклона по сводке»), Street View is «Просмотр улиц», OPACITY
  «НЕПРОЗР.», synced «синхр.», Area «Зона»; the import dialog no longer says
  "this scene" for several scenes. Glossary updated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 19:13:12 +04:00

115 lines
4.2 KiB
JavaScript

import {
feedProvenanceEnvelope,
layerSnapshots,
} from './data/layerSnapshot.js';
export const HUD_SUMMARY_UNCONFIGURED_CODE = 'OPENAI_NOT_CONFIGURED';
/**
* Describe the optional HUD summary capability without turning a deliberately
* keyless boot into an HTTP failure.
*
* @param {unknown} apiKey - Candidate server-side OpenAI credential.
* @returns {{ statusCode: 200, payload: { configured: false, code: string, error: null, summary: null } }|null}
* A graceful unconfigured response, or null when the provider is configured.
*/
export function keylessHudSummaryResponse(apiKey) {
if (String(apiKey ?? '').trim()) return null;
return {
statusCode: 200,
payload: {
configured: false,
code: HUD_SUMMARY_UNCONFIGURED_CODE,
error: null,
summary: null,
},
};
}
/** Return true only for the deliberate, successful no-key capability response. */
export function isHudSummaryUnconfigured(status, data) {
const keys =
data !== null && typeof data === 'object' && !Array.isArray(data)
? Object.keys(data)
: [];
return (
keys.length === 4 &&
status === 200 &&
data?.configured === false &&
data?.code === HUD_SUMMARY_UNCONFIGURED_CODE &&
data?.error === null &&
data?.summary === null
);
}
/**
* HUD summary model instructions. The five-word line may only describe
* supplied labels, and must carry feed-state when any enabled layer is not
* nominal.
*/
export const HUD_SUMMARY_INSTRUCTIONS = [
"Write one concise intelligence-HUD summary for God's Eye View.",
'Use only the supplied place, street, nearby-place, enabled-layer labels, and feedProvenance.',
'Prefer the clearest named place and include a relevant enabled layer only when useful.',
'If any enabled layer is not nominal, the five words MUST include that feedState token (STALE, DEGRADED, FALLBACK, LOADING, or UNAVAILABLE) and must not present the view as live.',
'Do not infer from coordinates or invent a place or feed state.',
'Output exactly five words with no title, punctuation, markdown, or introductory phrase.',
].join(' ');
/**
* Stamp HUD summary context with the same layer snapshots voice tools use.
* @param {Array<object>} [layers] `DataLayerManager.getAll()` rows.
* @param {{ now?: number }} [options]
* @returns {{
* enabledLayerLabels: string[],
* enabledLayers: Array<{id: string|null, name: string|null, feedState: string, source: string|null, count: number}>,
* feedProvenance: object,
* }}
*/
export function hudSummaryLayerContext(layers = [], options) {
const snapshots = layerSnapshots(layers, options).filter((s) => s.enabled);
const envelope = feedProvenanceEnvelope(snapshots, options);
return {
enabledLayerLabels: snapshots.map((s) => s.name).filter(Boolean),
enabledLayers: snapshots.map((s) => ({
id: s.id,
name: s.name,
feedState: s.feedState,
source: s.source,
count: s.count,
})),
feedProvenance: envelope,
};
}
/**
* The feed state and up to two layer names for the HUD telemetry suffix when
* an enabled feed is not nominal. Values stay English (`state` is a feed-state
* id, `names` are `layer.name`); the HUD translates them for display.
* @param {Array<object>} [layers]
* @param {{ now?: number }} [options]
* @returns {{ state: string, names: string[] }|null}
*/
export function hudTelemetryProvenance(layers = [], options) {
const snapshots = layerSnapshots(layers, options).filter((s) => s.enabled);
const envelope = feedProvenanceEnvelope(snapshots, options);
if (!envelope.overall || envelope.overall === 'nominal') return null;
const names = envelope.layers
.filter(
(s) => s.feedState && s.feedState !== 'nominal' && s.feedState !== 'off',
)
.map((s) => String(s.name || s.id || 'LAYER'))
.slice(0, 2);
return { state: envelope.overall, names };
}
/** Require the supplied non-nominal state before showing an AI summary. */
export function hudSummaryMatchesProvenance(summary, provenance) {
const state = provenance?.overall;
if (!state || state === 'nominal' || state === 'off') return true;
return String(summary || '')
.toUpperCase()
.split(/\W+/)
.includes(String(state).toUpperCase());
}