Review fixes for gekh's #1113 round:
- a link runs show_street_level with origin 'programmatic', so its source
switch and layer are not saved as the recipient's preferences;
- with no place named, the action looks under the screen centre
(aim 'screen-centre', armed false), keeps the sky refusal and leaves a
link's armed point alone;
- an armed OPEN aimed at the sky opens nothing instead of the far link point;
- an imagery-stage failure is spoken as an error, missing imagery as missing
(street.nearestOutcome); the card says "the view centre" in the interface
language;
- every photo the action opens reveals its panel, and revealPanel scrolls the
panel into view in the phone sheet;
- embed: EXPAND survives embed's own clean view, the lifted photo hides in
cockpit, recording and scene playback, and a failed photo step closes the
earlier photo before the view flies;
- links pasted into an open tab run their street-level step after the
restore settles; a superseded start link does not;
- Mapillary's nearest search gives up after 12 s, body included.
TranStar's Content Usage terms prohibit copying, distribution and
external framing of its images without TxDOT's prior written
permission. The opt-in text only said the pack was off unless
CCTV_HOUSTON_ENABLED=1, without saying what an operator needs before
setting it. Say "enable only if you hold TxDOT's written permission" in
.env.example, the README, DATA_SOURCES.md, CURRENT-STATE and the
changelog, matching the NYC and Florida packs, and fix the column of the
.env.example comment.
The houston-cctv credit now reads "(Houston, when enabled)" and links
the Content Usage terms, like the Florida credit. Where the docs said
frames are "never stored or redistributed", they now say what happens:
frames are relayed on demand, buffered only per request, sent with
no-store and not archived.
TranStar's `dir` field is the side of the freeway a camera sits on, not
the way it faces. TranStar's own map script (translateCameraDirection)
shows it as "Traffic closest to the camera is traveling": South is
eastbound, North westbound, West southbound and anything else
northbound. The live catalog agrees: I-10 cameras are North/South and
I-45 cameras East/West.
Treating `dir` as a high-confidence facing pointed most Houston cameras
across or away from the road they watch, and dropped the "estimated"
marking. Map it to the travel direction instead (S 90, N 270, W 180,
E 0) as a low-confidence prior with the low-confidence pose, and keep
the id-hash fallback for None or unknown values.
The changelog fragment named share-link token 6 (sea marks); the layer
reserves 8. CURRENT-STATE said voice could toggle and query the layer and
listed it among the analyst record sources, but it is in VOICE_OFF_LAYERS.
Say voice is off for now, and that the layer covers FAA prohibited and
restricted special-use airspace only, not the UAS Facility Map / LAANC grids
or TFRs. A test pins these claims to the token ledger and the voice manifest.
Typed chat sessions no longer end on the voice inactivity timeout, and a
failed or stopped reply keeps the conversation: Stop cancels only the
reply in progress, and map actions that already ran stay in the model's
context. Each upstream request and map action gets its own deadline, old
tool results shrink instead of the history overflowing, and step limits,
a missing or rejected key, the OAuth lock and throttling each get their
own message.
- Server: OpenAI chat is off under GEV_PREFER_CODEX_OAUTH; chat throttles
default to the provider's throttle; replies carry API_KEY_MISSING,
API_KEY_REJECTED, CODEX_OAUTH_REQUIRED and RATE_LIMITED codes; upstream
Retry-After passes through, and a rejected key (Gemini's invalid-key 400
included) cools down for a minute
- Pinokio forwards the chat throttle settings
- UI: the reply reveal publishes only the message list; the border orbit
runs only while busy; layers attach with a button or Enter; the log is
aria-busy while a reply types out; auto-scroll respects a reader who
scrolled up; Escape closes the attach menu; the chat gear says Settings;
a note says chat bills the OpenAI key when voice uses ChatGPT sign-in
- Layout does less work while panel height animates; unused blurs removed
- Gemini voice start ends chat; the voice preview clears on every new
session
- Tests for the route gates, client limits, attachments and the fixes
Clear cancelled lookup loading over an existing photo, release caller abort listeners on internal retirement, surface embedded errors, and permit embedded photos over the clean globe. Add seven regression cases and update runtime documentation. Preserve the published localization and other review fixes.
P1: on a phone the Street Level panel sits in the Cameras tab sheet, so the
action and sl= links opened photos nobody could see and still reported
success. A photo that opens, and a link that arms, now open that sheet
(revealPanel). In cockpit, clean view, recording or scene playback the action
stops at a new `view` stage before anything switches or loads.
P2s:
- Mapillary's nearest search asks for 100 photos, the API's most. It cuts
the radius's photos at the limit in no particular order, so 8 left the
distance sort and the imagery filter a few arbitrary photos.
- The MCP globe panel opens a billed photo only for a result issued in the
last five minutes (show_in_gods_eye_view stamps issued_at); a host that
mounts the panel again hands back the old result, which only arms.
- OPEN uses an armed link's point only while the view centre is within 1 km
of it; a user who moved on opens what the view shows.
- show_in_gods_eye_view measures a camera's altitude from the terrain under
it before taking its look-at point, so high cities no longer put the
photos kilometres too far ahead.
- The armed-link toast is translated, and the new view-stage message too,
in all 11 catalogs.
- Tests: a turn superseded during the geocode, flight or map switch changes
nothing; an unknown key state lets a cold sl= link through; a wide tilted
area still opens photos at its centre.
Keeps main's locale catalogs and re-adds this PR's eight Street Level strings; re-pins the Realtime payload digests for main's two new overlay layers, where the unchanged tools match main's pin, so show_street_level is still the whole difference.
show_street_level (voice and typed) flies to a place and opens its nearest
street-level photo from Google Street View, Mapillary, KartaView or
Panoramax; Street View switches to Google 3D first. The view model gains a
street_level field, share links carry sl=<source>,<lat>,<lon>, and
show_in_gods_eye_view can open a photo. A plain sl=google link only arms the
point and waits for OPEN, so it never bills a key without a click.
Embed pages show an open photo in an overlay that Esc closes. On-screen
strings are translated into es, ru, zh-CN and zh-Hant. Covered by unit tests
and three new steps in the Street Level browser suite.
Review follow-up for the sonar pulses.
Bounded repaint: the pulses used to ask for a frame every 80 ms from a
setInterval for as long as a subject was shown, so a parked view
repainted at 12.5 fps indefinitely. They now run for 9 s (three ring
flashes) after a selection or a camera move, fading in and out over
1.2 s, then rest: the ring stands on screen at full strength, the pulse
lines and gradient disc are hidden, echoes stop, and nothing asks for
frames. A moving subject does not wake them, since a flying subject
moves on every refresh. The lifecycle's existing camera pose signature
wakes them.
No beat with the Cyber sonar: the setInterval is replaced by a
postRender-armed setTimeout on CYBER_SONAR_RENDER_INTERVAL_MS, as in
cyberSonarScene. Every drawn frame, whoever asked for it, re-arms the
request 80 ms later, so requests never land inside the detection
lane's 80 ms gate.
Reduced motion mid-session: a change listener on the media query
rebuilds the visual, and the same-subject refresh path rebuilds when
the preference no longer matches. The listener is removed in
clearVisual.
Readable edge: restOpacity goes from 0 to 0.4, so the 250 km ring stays
visible between flashes, in screenshots and for low-vision viewers.
The echo field takes an optional level so echoes rest with the pulses.
CURRENT-STATE and the changelog fragment describe the bounded run and
the dimmed ring.
Tests: render requests stop after the bound and stay stopped for a
minute; a camera move seen by the lifecycle wakes them for another
bounded run; a foreign frame postpones the next request to 80 ms after
it; reduced motion flipped by event and by a later refresh; the ring
never drops below restOpacity; echoes scale with the level; the level
fades in, holds, fades out and keeps its value when woken.
The maintainer workflow asks runtime changes for a changelog.d/<pr>-<slug>.md
fragment and no edit to CHANGELOG.md. The three entries move unchanged, with
their credit, into changelog.d/846-voice-geometry.md.