Commit Graph
2749 Commits
Author SHA1 Message Date
Sameh Khamis 690a6637e2 Show the timeline only while a layer with time to show is on
The bar now needs more than the world clock being away from live: a layer
that moves with the clock must be on with time to show. In Core that is
weather radar, satellite or lightning with frames; a host adds layers by
following them. Turning the last one off hides the bar; turning it back
on brings it back.

The world clock reports this as `supported` in its state. Weather products
register their frames as coverage one by one, suspended or not, so the way
back to live stays on screen with reduced motion. Weather refreshes and
satellite catalog changes tell the clock their coverage may have changed.
A coverage that throws counts as none, and the clock reads its state once
per notification round.
2026-11-01 22:36:45 -08:00
Sameh Khamis 54f12140cf Merge branch 'main' into fix/e-route-label-kilometre 2026-11-01 22:30:51 -08:00
Sameh Khamis 561141fe55 Merge pull request #1450 from Rohilalala/fix/e-route-distance-ten-km
fix(directions): show a 9.96 km route as 10 km, not 10.0 km
2026-11-01 22:30:48 -08:00
Sameh Khamis 941a7a392e Merge branch 'main' into fix/e-route-distance-ten-km 2026-11-01 22:22:22 -08:00
Sameh Khamis 27000cf6da Merge pull request #1454 from Rohilalala/fix/e-context-heading-north
fix(flights): read a 359.6° track as 0° in the voice context, not 360°
2026-11-01 22:22:14 -08:00
manjunath d9a460658d merge main 0c28dc7 into feat/voice-outlines 2026-11-02 11:39:21 +05:30
Sameh Khamis 639a7fb23a Merge branch 'main' into fix/e-context-heading-north 2026-11-01 21:58:41 -08:00
Sameh Khamis 0c28dc78a5 Merge pull request #1500 from bilawalsidhu/sameh/vue-loader-cache
Cache compiled components and load them on the test thread
2026-11-01 21:56:55 -08:00
Sameh Khamis 434fc9602d Show a basic timeline while the world time is away from live
A bar above the command dock, like a video player's: play and pause on
the left, the world time's date and time in UTC beside it, and Live to
return. It stays hidden while live, so Core looks as before until weather
history is scrubbed or replayed, a host moves the clock, or a link made
away from live is opened. Adds the pause and play_arrow glyphs and the
strings in every catalog.
2026-11-01 21:55:33 -08:00
Sameh Khamis edf8c0929d changelog: note the component cache 2026-11-01 21:50:25 -08:00
Sameh Khamis 7d236a1174 build: cache compiled components and load them on the test thread
Since the UI moved to single-file components, every unit-test process
compiled the views it imports from scratch, and the loader ran off-thread
(module.register), adding a round trip to every module a test imports,
including the ones that are not components. Core's unit tests took about
twice as long, and a host running Core's suite on smaller runners passed
its CI time limit.

register-vue now installs synchronous hooks on the loading thread
(module.registerHooks). Compiled components are kept in the system temp
directory, keyed by the file's path and text, the Vue version and the
loader's own code, so each one compiles once across test processes; the
compiler itself loads only on a cache miss. Cached modules run as code, so
the directory is used only when it is a real directory owned by the current
user and writable by no one else; on Windows it lives in the user's local
app data instead of a temp directory that may be shared. The cache never
sits inside the package tree, and GEV_VUE_CACHE=0 compiles every time.

On four cores the full suite went from 441 s to 240 s from a cold cache.
2026-11-01 21:50:25 -08:00
Sameh Khamis 32ec6ed436 Share links carry the world time when made away from live
Field 28, worldTime, holds the world time of a link made away from live:
zigzag seconds since 2026-01-01, like field 8, but it may lie ahead of the
link's creation (a forecast moment). Opening such a link pauses the world
clock at that time; a link without it leaves the clock alone, so a page
opens live and Back or Forward does not stop a replay. Times no JS Date
can hold are dropped.
2026-11-01 21:50:09 -08:00
Sameh Khamis 0c4da3cdb2 Keep one world clock that every timed layer reads
Core told time eight ways: Date.now() in most layers, JulianDate.now() for
flights and military, Cesium's clock frozen at page load for the sky, the
weather clock for observed imagery. They now read one clock,
src/time/worldClock.js, exported as ./time.

Live is a mode, not a timestamp: while live the clock is the wall clock,
so every layer draws as before. Away from live only the layers that follow
the clock move with it. The weather products follow it, as they followed
the weather transport; a host opts others in with follow(id).

- The weather clock takes its time from the world clock and drives its
  replay, stepping its frames as before.
- Satellites, launches, flights and military (still 30 s and 15 s behind),
  transit, GNSS, Recent Imagery, street level, the HUD and the celestial
  ring read worldTimeFor(id). Cadences stay on the wall clock.
- Cesium's sky (stars, sun, moon, atmosphere, lighting) draws at the world
  time each frame instead of the moment the page loaded.
- Weather frames and satellites declare coverage() for a host's timeline.
2026-11-01 21:47:32 -08:00
Sameh Khamis 0e90a03de7 Round every heading readout through one shared helper
The transit, ALPR, vessel, CCTV and flight/military context fixes each
wrapped 359.5° to north with an inline Math.round(x) % 360. They now call
roundHeadingDegrees in src/data/headingDegrees.js, which also wraps negative
and multi-turn bearings. normalizeHeading moves there with it, and
cockpitMath re-exports it so existing imports keep working. It now adds a
turn only to a negative remainder, so a bearing just under half a degree
keeps its precision.

The awareness, Local ADS-B, cockpit wind, cockpit heading and Street Level
readouts use the same helper. The Cockpit's TRACK ACQUIRED signal rounded
without wrapping and could read COURSE 360°; it now uses
formatHeadingReadout, which shows --- for a track that is not finite.
2026-11-01 21:41:49 -08:00
Sameh Khamis e200e46695 Merge branch 'main' into fix/e-context-heading-north 2026-11-01 21:41:48 -08:00
Sameh Khamis 13a04a8957 Merge pull request #1441 from Rohilalala/fix/e-cctv-heading-north
fix(cctv): show a camera facing 359.6° as HDG 0°, not HDG 360°
2026-11-01 21:41:39 -08:00
manjunath c5aa0d28c3 merge main 07094d9 into feat/voice-outlines 2026-11-02 11:05:08 +05:30
Sameh Khamis 0e942de38c Merge branch 'main' into fix/e-cctv-heading-north 2026-11-01 21:28:43 -08:00
Sameh Khamis ebb726c03c Merge pull request #1432 from Rohilalala/fix/b-vessel-course-north
fix(vessels): show a 359.7° course as 0°, not 360°
2026-11-01 21:28:31 -08:00
Sameh Khamis 87e2a2eaa8 Merge branch 'main' into fix/b-vessel-course-north 2026-11-01 21:16:27 -08:00
Sameh Khamis 87e5cf3f93 Merge pull request #1451 from Rohilalala/fix/e-alpr-direction-360
fix(alpr): show a 359.6° camera bearing as 0°, not 360°
2026-11-01 21:16:18 -08:00
Sameh Khamis e5fb496d90 Merge branch 'main' into fix/b-awareness-marker-km 2026-11-01 21:04:42 -08:00
Sameh Khamis 188dd4fd29 Merge branch 'main' into fix/e-alpr-direction-360 2026-11-01 21:04:29 -08:00
Sameh Khamis 07094d9be2 Merge pull request #1452 from Rohilalala/fix/e-transit-heading-north
fix(transit): show a reported 359.6° bearing as hdg 0°, not hdg 360°
2026-11-01 21:04:18 -08:00
Sameh Khamis 37d12ad731 Merge branch 'main' into fix/e-context-heading-north 2026-11-01 20:53:42 -08:00
Sameh Khamis 0e170e0515 test(vessels): read the north-rounding HUD heading from the published readout
The vessel HUD now publishes its text through services.readouts, so the
test harness exposes hud.readout rather than a DOM node's textContent.
2026-11-01 20:53:37 -08:00
Sameh Khamis af79691a19 test(cctv): check the north-rounding meta line through presentCctvPanel
The panel now renders from the CCTV store, so the meta line is no longer
written to a DOM node by _renderCctvState. Assert on presentCctvPanel's
meta text instead.
2026-11-01 20:53:32 -08:00
Sameh Khamis c62e580d7e Merge branch 'main' into fix/e-cctv-heading-north
# Conflicts:
#	src/ui/cctvPresentation.js
2026-11-01 20:52:40 -08:00
Sameh Khamis 08265d94d4 Merge branch 'main' into fix/b-vessel-course-north 2026-11-01 20:52:20 -08:00
Sameh Khamis 66d7a4ebce Merge branch 'main' into fix/e-transit-heading-north 2026-11-01 20:52:05 -08:00
Sameh Khamis 92bb1d6a89 Merge pull request #1479 from bilawalsidhu/german/hal-644-ar
i18n: Arabic (ar), the first right-to-left language
2026-11-01 20:44:13 -08:00
Sameh Khamis 1f9a453bc4 i18n(ar): own the Arabic catalog in the application package; mirror select chevrons
The application package boundary owns src/i18n/locales/ar.json beside the
other catalogs, and the style.css shim test checks that the shim holds only
import statements rather than counting its lines, so adding rtl.css to it
does not read as rules.

Selects pad the chevron's side with padding-inline-end, as do the Radio
filter, the SDR gain select and the voice timeout select, which set their
own padding; rtl.css moves the chevron to the left, where an Arabic field's
text ends. A rail legend's range keeps left-to-right order under its colour
ramp, which always runs low to high from left to right.
2026-11-01 20:22:51 -08:00
Sameh Khamis bc5d038afd Merge Core main into german/hal-644-ar: Arabic on the Vue UI
Main moved the UI to Vue views and Pinia stores, so the Arabic changes to
the old DOM controllers now live in their views: the rail legend range
comes from formatScaleRange (keeping units a source supplies as given),
the voice timeout options are worded by Intl unit formatting in
VoiceControl, and the scene editor's JSON fields stay left to right in
SceneDialog. Main's CCTV status line, launch panel and calibration values
keep the Arabic isolates and Western-digit formatting.

isolate() and keepCodesWithValues() read the locale through getLocale(),
which follows the page's vue-i18n instance, and the Intl formatters take
intlLocale(getLocale()). A language switch now applies in place, and
applyDocumentLanguage sets <html dir> on every switch, so the page mirrors
without a reload.

The icon rails and their tips are placed with logical properties, so they
stay on the outer side of the mirrored stacks; Cyber's new stack offsets,
padding and edge lines are logical too, and rtl.css moves Cyber's right
stack to its new rail clearance. Main removed the Context mini radio, so its
right-to-left rules and test go.

The Arabic catalog gains main's new strings and drops the ones main no
longer uses; the other catalogs keep main's new keys and the branch's
landing labels.
2026-11-01 20:15:02 -08:00
Sameh Khamis accfa1fa85 Merge pull request #1499 from bilawalsidhu/sameh/core-vue
Move the application UI to Vue and Pinia
2026-11-01 20:04:46 -08:00
Sameh Khamis 5582ff288d docs: describe the component kit on its own terms
The kit's comments and docs describe its props and classes directly instead
of by reference to another kit.
2026-11-01 19:40:46 -08:00
Sameh Khamis 859ceac8b4 docs: name the Vue and Pinia changelog entry after its pull request 2026-11-01 19:30:00 -08:00
Sameh Khamis 2dda92c9e6 docs: record that a map source's unavailableReason may be a function
The default map sources word their reason when it is shown, so it follows
the locale; hosts that read it directly need to know the new form.
2026-11-01 18:57:30 -08:00
Sameh Khamis 5605fdc60a Merge Core main into sameh/core-vue
railCardBlocks.js is the Vue wrapper on this branch, so main's verbatim
legend units move into RailCardBlocks.vue.
2026-11-01 17:37:56 -08:00
Sameh Khamis 183e651c4c Show Cyber's edge lines beside open panels, on both sides
With the panel rails, the left stack's red edge line stood alone beside
the rail whenever no panel was open. It now shows only while a left panel
is open, and the right stack gains the mirrored line for its open panel.
The right stack takes the line and its gap without moving its panels, so
its rail sits outside the line like the left one.
2026-11-01 17:05:35 -08:00
Sameh Khamis 06994e0d0e Keep the panel rails still when a tall panel opens
A tall panel moves its stack to the focus or tail layout, and the rail
inside the stack went with it, so the Data Layers and Authoring buttons
jumped up and the right rail followed. Each layout pass now offsets the
rail back to the resting top, with the same transition as the stack, and
the right rail lines up with the left rail's buttons. Cyber pins the left
stack's top, so it flags that and the rail takes no offset.
2026-11-01 16:58:37 -08:00
Sameh Khamis b20c80e590 Switch language in place, without a reload
Choosing a language in the picker now applies it at once. The choice is
stored and ?lang is kept in step through replaceState, and the picker keeps
focus.

Code that writes translated text itself re-presents it on a switch:
onLocaleChange() lets an owner listen, removed at teardown, and status
writers hand over a function of the locale (voice cards, scene director,
SDR, draw hints, imagery split, map sources, Bhote Koshi media) so the
words are rebuilt rather than re-sent. The HUD, cockpit, radio, CCTV,
launch, awareness and credit texts follow the same hook, and
applyDocumentTranslations can be applied again without undoing text code
wrote since.

The unsaved-work registry and persistLocaleAndReload existed only to guard
the reload, so they go, with their catalog entries.
2026-11-01 16:47:39 -08:00
Sameh Khamis ba20bc13d8 i18n: translate the application views with t() as they render
Every data-i18n, data-i18n-title, data-i18n-aria-label and
data-i18n-placeholder marker in the Vue templates becomes a t() call with
the key the boot pass used to look up, contexts included. The three labels
the shell rewrote at boot (Visual Presets, Location and the standby
description) render through t() too, and that rewrite is gone. With the
locale in vue-i18n, view text now follows a locale change, and markup
under v-if or v-for no longer comes back in English when it re-renders.

Rendered text and attributes are unchanged in every locale. The boot pass
still translates static page markup such as the loading screen and a host's
own markup, and npm run i18n rejects data-i18n* in a Vue template.
2026-11-01 15:38:23 -08:00
Sameh Khamis 2c72a038d5 i18n: keep the page's locale in a vue-i18n instance
The application views install one vue-i18n instance that holds the active
locale and the catalogs. src/i18n/index.js stays free of Vue: bindLocale()
keeps its locale in the instance's ref, so t(), tn() and the Intl formatters
read it from there and a view that translates while rendering renders again
when the locale changes. A tag written straight to the ref is normalized.
Message formatting, catalogs and call sites are unchanged, and switching
language still reloads the page.

The instance outlives the views: unmounting one no longer disposes it. The
browser build defines vue-i18n's feature flags and drops its message
compiler, which Core does not use.
2026-11-01 15:25:55 -08:00
Sameh Khamis 22d4cf8ee7 docs: move the branch's changelog entries into a changelog.d fragment
The Vue and Pinia migration's entries move out of CHANGELOG.md into
changelog.d/0000-vue-and-pinia.md, newest first, as the contributing guide
asks. 0000 stands in for the pull request number until it is opened. The
migration plan's cleanup step now records where it stands: the template
expansion is gone, the pattern is required for new UI, the page-level
markup tests render the real views, the attribute translation pass waits
for vue-i18n, and a host panel registration API waits for its first host.
2026-11-01 15:10:46 -08:00
Sameh Khamis 19ca67a6e5 docs: require Vue views, Pinia stores and the kit for new UI
CONTRIBUTING gains a Building UI section: new UX elements are Vue
components rendering a Pinia store their owner publishes to, portable
owners stay framework-free behind a publish option, controls come from
src/ui/kit first, controllers don't bind the DOM by hand (Teleport where a
view renders elsewhere; body classes, focus, same-task measured positions
and painted surfaces stay imperative), text goes through t()/tn() with
msg() for stored English, and tests mount components. The README and the
architecture summary drop the stale vanilla-JS claim, the UI ownership doc
points to the guide, and the migration doc's UiPanel note names
v-panel-state as what draws a panel's collapse.
2026-11-01 15:06:05 -08:00
Sameh Khamis fd4b69ff52 feat(cockpit): teleport the shared Display groups into Cockpit's popover
The Cockpit display portal publishes the Display popover's slots to the
gev-display store while Cockpit is on, and the Display panel teleports its
HUD, Detection, Parameters and 3D groups into them and back, each only
while its own slot is published. The portal no longer moves the groups:
it keeps each surface's scroll position, puts back focus the move
dropped, and lays the surfaces out once Vue has moved them. The popover's
uses-shared-display-controls class renders from the store, and the right
rail no longer re-homes the parameters panel the markup already places.
2026-11-01 15:01:41 -08:00
Sameh Khamis 9ed9098593 refactor(panels): read panel state from the panel chrome store
PanelChrome, its collapse-preference restore and the phone shell read a
panel's disclosure state back from the gev-panel-chrome store
(_shownPanel), falling back to the markup's classes before the panel is
first presented, instead of reading the classes v-panel-state draws. The
share link and the phone shell share one rule for a panel the person keeps
closed (prefersCollapsed). The rails, the accordion and the hover
disclosure still read the classes, which the directive draws in the same
task as their layout.
2026-11-01 14:48:31 -08:00
Sameh Khamis 4c92f517db feat(cockpit): render the utility strip's measured layout from the store
The Cockpit layout pass publishes the utility strip's fit to the
gev-cockpit store: the HUD's --cockpit-utility-* lengths, whether only the
expanded control fits, and whether a collapsed control beside an expanded
one is hidden from assistive technology. CockpitView renders the lengths
on the HUD and CockpitUtilityControls the strip's class and each control's
aria-hidden, which now also follows a popover opened between passes. Focus
leaves a control before the strip hides it. Exit publishes no layout,
which also clears the expanded height, class and aria-hidden the old exit
left behind. The briefing and Contact cards' positions stay imperative:
the same pass measures them straight after writing them.
2026-11-01 14:36:51 -08:00
Sameh Khamis c3c69a91cd feat(display): render the style parameters from the display store
VisualSettings publishes the active style's parameter rows to the display
store, and the Display panel renders them with the shared row component;
a moved slider goes back through the store's setStyleParameter action.
The readout snaps a value to its step as the browser does, half-steps up
and an off-grid maximum down. A preset that resets the shown style's
uniforms republishes its rows, input for another style's parameters is
ignored, and a stopped owner takes its rows with it unless a newer owner
has published since. createStyleParameters is deprecated and builds its
rows the same way.
2026-11-01 14:28:02 -08:00
Sameh Khamis 3a750c9021 feat(recording): render the safe frame from a recording store
RecordingControls publishes the safe frame a recording composes in, 16:9
or 9:16 (null while none records), to the new recording store, and the
scene chrome renders the overlay's active and ratio classes from it
instead of the controls writing them. Any frame but 9:16, even none named,
records in 16:9 as before.
2026-11-01 14:15:36 -08:00