diff --git a/CHANGELOG.md b/CHANGELOG.md index bbbd03d..14d052c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,56 @@ Maintained in the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) forma ## [Unreleased] +### Review fixes: Cesium scenes, basemap chain and navigation (2026-10-19) + +- **Military symbols**: unknown and hostile SIDCs were written as `UFA-…`/`HFA-…` (affiliation in the coding-scheme position); milsymbol rejected them and drew them as **friendly** blue frames, so the Situation page's hostile and unknown tracks contradicted their red/yellow labels and trails. Sea-surface function codes (`MS`) were invalid too. The table now uses valid codes (`symbols.test.ts` asserts validity and affiliation for every entry), and `militarySymbol` returns the real `width`/`height` plus an anchor `offset` — the labelled station symbol (95×41) used to be stretched into a square +- **Direction**: targets vanished after 20 minutes of the 12 h clock window (positions were sampled only for 0–1200 s with no extrapolation). Positions are now evaluated analytically per frame (`CallbackPositionProperty`, the same function as the margin panel). The polar chart follows the site theme instead of a hard-coded `theme="dark"` +- **GeoHash**: clicking a cell flies in again — the handler read the `viewer` state captured by `onReady` (always `null` on first mount, a destroyed instance after a basemap retry) +- **TimeDeck**: re-reads the clock range when a scene configures it after mount (Trajectory's slider was stuck on Cesium's default 24 h window) and updates at most per second / 10 Hz instead of re-rendering on every Cesium tick, paused or not +- **Situation**: the event feed recovers after the 12 h loop wraps or the user scrubs backwards (it used to stay empty while the alert KPI restarted) +- **Heatmap**: the click probe uses the current transmitter powers and re-computes when a slider moves; a worker that fails to load (chunk 404, no module-worker support) now falls back to the main-thread sampler instead of leaving the layer blank +- **Scene HUD**: the top-right action bar moved left of Cesium's toolbar, which it used to cover (Home and the 2D/3D picker kept on purpose); the toolbar sits above the glass panels so the picker can expand +- **Basemap failure detection**: the AMap fallback host is now observed (`TILE_HOSTS`); HTTP-level tile errors (429/403/5xx, invisible to the XHR `error` event) come from the current base layer's `imageryProvider.errorEvent`, re-attached on every layer swap (the old listener sat on `ImageryLayer.errorEvent`, which only reports provider-creation failures, and only incremented a counter); network failures count only for the current provider's host, so in-flight requests of a removed layer no longer push the new one over the threshold; the AMap probe no longer raises the banner once the carto upgrade has taken over (OSM and AMap blocked + carto reachable used to flag every Cesium page), and only one AMap probe runs at a time +- **Navigation**: clicking a group title goes to the first visible page the role can access (`firstAccessibleLeaf` in `src/utils/routeNav.ts`) — the static `/system` → `/system/users` redirect sent operator/analyst/viewer, who still see the group because `/system/access` has no access key, to a 403. The command palette excludes `hideInMenu` and wildcard routes (sign-in and the `*` 404 were listed) and ignores Enter/arrow keys while an IME is composing +- **CI**: the e2e basemap failure test also blocks `*.autonavi.com` (the AMap fallback answered 200 with `access-control-allow-origin: *`, so the banner never appeared); the e2e console whitelist includes `basemaps.cartocdn.com`; `size-budget.json` raises `publicMB` from 4 to 4.5 for the 586 KB glTF (public measured 3.82 MB, over the old 3.8 MB headroom line) +- **Tests**: new `symbols.test.ts` and `routeNav.test.ts`; palette tests cover hidden and wildcard routes; lifecycle tests cover the host filter and HTTP-level provider errors +- **Low-severity follow-ups**: Trajectory replays on the new viewer after a basemap retry and swaps in the glTF aircraft when its HEAD check resolves after the viewer is ready (unless the user started a custom route); the coverage corridor actually grows (it was re-sliced from its own prefix and stuck at two samples) and no longer runs ahead of the aircraft when speed ≠ 1. Situation's KPI/event heartbeat is throttled to ≤4 Hz by wall clock (it re-rendered every frame at ×120/×600). `useCesiumViewer` tears down the viewer, tile observer and rejection listener when a page's `onReady` throws, logs it and shows the error state. BoxSelect tools tolerate a null viewer; `polarPattern` returns `azimuthDeg`; the unused `meta.fl`, `landOn`, `followEntity` and `tileObserverInstalled` are gone; en-US labels fixed (Sample quality, Trails, East China Sea); the group-nav e2e matches "Audit log". New `fieldModel.test.ts`; situation and station-fleet tests cover enter/exit pairing, determinism, weight proportionality and tiny counts +- **Last four low-severity items**: the heatmap's propagation distance scales longitude by cos(latitude) (`planarDistanceKm`, within 0.5% of the haversine probe; east–west distances used to be ~15% too long, squashing the isolines against the coverage circles); GroupNav and the command palette share `canAccessRoute` with plugin-access semantics, so an access key missing from `access.ts` hides the page there too instead of showing a link into a 403; the palette input is a proper `combobox` (`aria-controls`, `aria-activedescendant`, empty state moved out of the listbox as a `status`); the Cesium scene bar scrolls horizontally on narrow screens and keeps the selected scene in view instead of truncating every label + +### Cesium situational scenes (2026-10-18) + +- **Situation**: rebuilt on a deterministic maritime/air model (`situationModel.ts`: core/warning/watch zones, six formation templates, 61 tracks with AIS/flight metadata, zone enter/exit events computed offline and streamed into a live feed during replay), MIL-STD-2525C symbols drawn with milsymbol (`components/CesiumViewer/symbols.ts`), a shared affiliation/accent palette (`palette.ts`) and a keyframe camera tour (`CameraDirector.ts`) +- **One clock for six scenes**: `TimeDeck` binds play/pause, speed and scrubbing to `viewer.clock`, replacing per-page `performance.now()` heartbeats +- **GeoHash**: a synthetic 8000-station fleet (`stationFleet.ts`, 34 provincial clusters, fixed seed) alongside the live 240 devices; extruded density cells grow in on precision/source changes +- **Heatmap**: the field is computed in a Web Worker (`heatWorker.ts`) and rendered through an ImageData colour LUT with d3-contour isolines (`heatRender.ts`); `fieldModel.ts` rewritten +- **Trajectory**: a glTF patrol aircraft (`public/assets/models/aircraft.glb`, the Apache-2.0 Cesium_Air sample) with a canvas billboard fallback when the model is unreachable +- **Basemap chain without an ion token**: a colour-graded ("night") OSM layer by default; a pixel-checked probe upgrades to CartoDB Dark Matter; when OSM is unreachable the AMap raster tiles are the last fallback (GCJ-02, basemap only — not used for measurements); the failure banner shows only when all of them fail. `https://*.basemaps.cartocdn.com` is allowed in img-src/connect-src in all four CSP copies +- **HUD for all six scenes**: compass and distance legend (cesium-navigation-es6), graticule toggle, MGRS cursor readout +- **Dependencies**: `milsymbol` 3.0.4, `mgrs` 2.2.0 + +### Two navigation fixes (2026-10-18) + +- **Clicking a top-level group now enters its default page** (`/system` → users; since 2026-10-19 the first page the role can access, see below). Implemented with `subMenuItemRender` wrapping submenu titles in a click handler: `postMenuData`/`onTitleClick` injection does not work here because pro-components 3.1.14's internal build mangles BaseMenu's prop to `n`, while `subMenuItemRender` is a plain-name prop passed verbatim through the ProLayout → Header → TopNavHeader → BaseMenu `{...props}` chain. Nested group titles in the dropdown (Cesium) navigate the same way; on phones (≤768px, where the header collapses to a drawer) the click handler defers to the drawer's native expand +- **Fixed the tab row jumping on page entry**. ProLayout's content `main` carries a default `padding: 32px 40px` and only drops it (`has-page-container` class) once a PageContainer mounts — on a route's first visit the lazy-chunk frame renders GroupNav inside that padding at (88, 40), then it snaps to (56, 0). A `:has([data-tour="groupnav"])` rule zeroes the content padding whenever the tab row is present (the steady state already is zero), and GroupNav now stays mounted with an empty selection through group-root redirect frames (`/system` → `/system/users`) instead of unmounting for one frame + +### Layout polish: hierarchy, dropdowns and the command palette (2026-10-18) + +- **Visual hierarchy**: the group tab row is now the single emphasis — 40px tall, `colorSplit` bottom hairline, left edge aligned to the same 40px reference line as page content (was 24px/misaligned). Group pages no longer repeat the page name in a large PageContainer title: DemoPage collapses to a compact row (14px secondary description + page actions), cutting the page header from ~80px to ~36px; standalone pages (dashboard) keep the full header. The group name rides the right end of the tab row as a small uppercase tertiary tag — scroll context without breaking the left reference line +- **Header dropdowns restored**: top-level groups carry their children again (hover/click popup, access-filtered), so any page is one click from anywhere instead of top-group + tab; on phones the hamburger drawer gets the full two-level tree back +- **Command palette (⌘K / Ctrl+K)**: `src/components/CommandPalette` searches every accessible leaf page by localized name or path (menu.* keys, same cumulative rules as titles), keyboard-first (↑↓ / Enter / Esc), with a header search button as the discoverable entry. Traverses umi's unnamed layout wrappers in clientRoutes — a plain name-based walk yields nothing +- **Cesium scene bar**: sticky second row (top: 96px) visually identical to the group tabs, each scene with a semantic icon (grid / aim / heatmap / route / select / picture) so the scene switcher reads as navigation while the in-page `Segmented` controls stay parameters +- **Interaction details**: switching tabs or palette entries resets scroll to top (long tables used to land at the old offset); the tab row scrolls horizontally instead of antd's «···» overflow (a collapsed selected tab loses the current position) and auto-scrolls the selection into view; route changes fade content in over 150ms, disabled under `prefers-reduced-motion` +- **Tests**: palette flatten/filter unit tests (including the unnamed-wrapper case), GroupNav tests updated for the no-selection guard + +### Layout refactor: top nav + in-content group tabs (2026-10-18) + +- **Navigation**: the `mix` layout (top-level groups in the header + a second menu column on the left) is replaced by a two-level scheme with a single navigation shell — `layout: 'top'` carries the eight first-level groups in the header (their dropdowns were briefly dropped here and restored the same day, see "Layout polish" above; no route sets `hideChildrenInMenu`), and the second level moved into `GroupNav`, a sticky horizontal menu at the top of the content area (`src/components/GroupNav`). The content region reclaims the former 208px sider width; page URLs, access keys and legacy redirects are untouched +- **GroupNav** filters items with the same access semantics as the ProLayout menu, reuses the `menu..` i18n keys, highlights the current page by longest-prefix match (`/map/cesium/geohash` → the Cesium tab), keeps antd Menu's keyboard navigation but scrolls horizontally (`disabledOverflow`) instead of collapsing into «···», and exposes a `