From 8e635c9a3ab2d925b102d035fab786a632ad772c Mon Sep 17 00:00:00 2001 From: Daniel Slay Date: Mon, 19 Oct 2026 10:50:22 -0400 Subject: [PATCH] test(cctv): pin the camera icon colors; say video feed, not live --- changelog.d/1200-cctv-live-icon-color.md | 10 +++---- docs/CURRENT-STATE.md | 2 +- src/data/cctv.test.mjs | 36 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/changelog.d/1200-cctv-live-icon-color.md b/changelog.d/1200-cctv-live-icon-color.md index 5f39fad1f..c17c1760d 100644 --- a/changelog.d/1200-cctv-live-icon-color.md +++ b/changelog.d/1200-cctv-live-icon-color.md @@ -1,5 +1,5 @@ -- Color CCTV cameras with live video violet. Cameras with an HLS, MP4 or WebM - feed were the same cyan as stills, so a live DelDOT or Statens vegvesen - camera could only be found by opening it. Stills stay cyan and the active - camera stays amber, and focus de-emphasis fades the violet the same way - (Danielslay86, #1200). +- Color CCTV cameras with a video feed violet. Cameras with an HLS, MP4 or + WebM feed were the same cyan as stills, so a DelDOT or Statens vegvesen + video camera could only be found by opening it. Stills stay cyan and the + active camera stays amber, and focus de-emphasis fades the violet the same + way (Danielslay86, #1200). \ No newline at end of file diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 734e722d7..fb3291080 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -4359,7 +4359,7 @@ silently demoting every later lookup for the session. ground clamp now lifts the cap _center_ only so the wireframe stays a true pyramid welded to the plane (was a flattened fan / the ~47.5 m divergence — RESOLVED), re-selecting the active camera is a no-op (killed a click-flash), and texture swaps gate on canvas content (killed a - periodic white flash). Camera icons are cyan for stills, violet for cameras with a live video feed, and amber for the active camera. Coverage is now **metro-wide: 250 cameras** (`CCTV_AUSTIN_MAX_SOURCES` + periodic white flash). Camera icons are cyan for stills, violet for cameras with a video feed, and amber for the active camera. Coverage is now **metro-wide: 250 cameras** (`CCTV_AUSTIN_MAX_SOURCES` default 36 → 250, hard bound 300), filtered to `camera_status === TURNED_ON` (~815 live of 1,003 rows). City packs (2026-07-04): Caltrans (districts 4/7/11/3 — SF, LA, San Diego, Sacramento; cap 300) and TfL London JamCams (cap 250) join Austin (cap 250) as keyless default diff --git a/src/data/cctv.test.mjs b/src/data/cctv.test.mjs index ca6a3c11a..e102b081d 100644 --- a/src/data/cctv.test.mjs +++ b/src/data/cctv.test.mjs @@ -77,6 +77,11 @@ import { CCTV_FOCUS_REQUEST_EVENT, activateCctvCameraFromWorldClick, } from '../cctvFocusRequest.js'; +import { + ACTIVE_CAMERA_COLOR, + IDLE_CAMERA_COLOR, + LIVE_CAMERA_COLOR, +} from '../layers/cctv/policy.js' const CCTV_PRESENTATION_SOURCE = fs.readFileSync( path.join( @@ -360,6 +365,37 @@ test('default COVERAGE refresh materializes the active and visible camera frustu } }); +test('camera icons are violet for video feeds, cyan for stills, amber when active', () => { + const record = (id, feedType) => ({ + camera: { ...UNCLAMPED_CAMERA, id, feedType, groundElevationM: 0 }, + billboard: {}, + coverageEntities: [], + projection: null, + }); + const idleVideo = record('idle-hls', 'hls'); + const idleStill = record('idle-image', 'image'); + const activeVideo = record('active-hls', 'hls'); + const viewer = { entities: { add: (entity) => entity } }; + + try { + // Disabled keeps the active video camera from opening a real projection; + // icon colors are assigned either way. + _setCctvCoverageStateForTest({ + viewer, + records: [idleVideo, idleStill, activeVideo], + activeCameraId: activeVideo.camera.id, + enabled: false, + coverageMode: 'off', + }); + refreshCoverageStyles(); + assert.equal(idleVideo.billboard.color, LIVE_CAMERA_COLOR); + assert.equal(idleStill.billboard.color, IDLE_CAMERA_COLOR); + assert.equal(activeVideo.billboard.color, ACTIVE_CAMERA_COLOR); + } finally { + _setCctvCoverageStateForTest({ enabled: false }); + } +}); + test('geometry drain pacing yields to tracked and cockpit camera ownership', () => { assert.deepEqual(cctvGeometryDrainPacing(), { batchSize: 4, delayMs: 120 }); assert.deepEqual(