Meir Michanie's Spanish translation from #602 (9d69e11, 4ba5d69), converted
to the English-keyed format: one entry per English message in
src/i18n/locales/es.json, translations unchanged. Also carries the es-only
CCTV control-row wrap rule.
Co-Authored-By: German Khokhlov <[email protected]>
Replace the semantic keys with the English source text, so code reads as the
UI does and English needs no catalog: t('Save keys'), tn(count, one, other)
for plurals, msg() for text stored in data, and bare data-i18n* markup that
translates the element's own English. Other languages are one flat JSON each.
- Simplify src/i18n: drop namespaces, the configurable locale pair
(GEV_DEFAULT_LOCALE / GEV_SECONDARY_LOCALE) and its build defines. Locale
comes from ?lang=, the stored choice, then browser languages.
- Add scripts/i18n.mjs (npm run i18n): extracts every message from t/tn/msg
and markup, and fails on missing, unused, placeholder-mismatched or
wrongly-shaped plural entries. npm test runs it via catalog.test.mjs.
- Add Russian (src/i18n/locales/ru.json, 833 messages), started from the
community catalog in #312 and reviewed against the current English.
- Fix data-i18n placed on parents that would wipe their children (cockpit
LEVEL and local weather cells, first-run EXPLORE MANUALLY tile), and a
doubled degree sign in the TRACK ACQUIRED signal.
- Rewrite docs/TRANSLATORS.md for the new workflow; drop sections the earlier
merge resurrected in docs/CURRENT-STATE.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A KMZ could name one large packed image in thousands of <href>s, and each
one made a fresh Blob copy for Cesium to decode. kmzImageResolver now keeps
one blob URL per archive entry, and countKmlGeometry refuses a document
with more than 2,000 <href> and <Style> elements (MAX_KML_LINKS) before
Cesium reads it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both systems publish keyless GBFS 2.3 feeds on publicbikesystem.net,
the same platform as the existing Austin, Honolulu and Chattanooga
entries (listed in MobilityData's systems.csv).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
OpenSky, the default civil feed, reports no operator or type code, so a
breakdown by either put every aircraft under unknown while the summary said
nothing. The summary now says "no operator reported" in that case, and
otherwise appends the unknown count after the largest groups.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
aircraft_in_area and vessels_in_area return at most 200 rows, so a client
asked which operators dominate an area, or the average altitude there, could
only estimate from a truncated sample. Both tools now take an optional
group_by (aircraft: operator, origin_country, type_code; ships: type,
destination) and stats (airborne altitude and speed for aircraft, speed for
ships). Each is computed over every match before rows are capped. Breakdowns
list the ten largest groups plus other and unknown counts, and the summary
names the three largest.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- keySetup.unlocks.openai follows the registry's new wording
('API key or ChatGPT OAuth')
- add keySetup.unlocks.mapillary for the new Mapillary client-token key
- add vision.styleNameAnime/styleNameSnow for the new anime/snow cockpit
vision modes
- add layers.meta.dataSourceUnavailable for the unavailable-layer tooltip
fallback introduced upstream
Two findings from PoBruno's Docker Engine on Linux run (#208), and one from
re-running the suite in the container after rebasing:
- The launcher pinned its own allowedHosts, including '.local'. Vite reads a
leading dot as a suffix match, so `Host: anything.local` reached the page and
the data proxies. Main has since moved the policy into build/allowedHosts.js,
which drops suffix entries on purpose and applies before the app's routes, so
the launcher's override is deleted rather than patched. As a side effect
GEV_ALLOWED_HOSTS now also works from `.env`, as it does under `npm run dev`.
- A checkout the container cannot write - typically a clone made as root on a
NAS or Proxmox host - made Save Keys answer 500 and never created .gev-cache,
with nothing logged. The launcher now warns at start, naming the owner, the
ids it runs as, and the two fixes.
- On Docker Desktop the bind-mounted repo always shows an owner other than the
container user, so git refuses it as "dubious ownership" and the tests that
shell out to git fail (src/data/bundledJson.test.mjs, new on main). The image
now marks /app, and only /app, as a safe directory.
Verified in a container on this base: `anything.local` 403 on the page and on
/api routes, localhost 200, GEV_ALLOWED_HOSTS honoured from the environment and
from .env, the warning on a root-owned /app run as 1000:1000, and
bundledJson.test.mjs 6/6.
Reported-by: PoBruno
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Docker delivers the host's own browser to the container from the container
network's gateway, never from 127.0.0.1, so the loopback-only admission gate
answered every POWER UP request with 403 and no key could be saved. The README
and CHANGELOG claimed otherwise; both are corrected here.
- keySetupCore: GEV_KEY_SETUP_TRUSTED_PEERS, an exact-address list (no
wildcards, prefixes or hostnames) admitted alongside loopback. Unset trusts
nothing, and the sharing, Host, Origin, Content-Type and proxy-header checks
all still apply to a listed address. Twelve assertions pin each refusal.
- scripts/docker-start.mjs: the container entry point, shaped after
scripts/pinokio-start.mjs. It reads the default gateway from /proc/net/route,
trusts exactly that address, and starts Vite. No gateway (--network none)
trusts nothing. The pure helpers are unit-tested and a contract test pins the
Dockerfile CMD and the loopback-only publish.
- Dockerfile: install git. `npm test` builds fixtures with `git init`, so the
suite failed 71 tests inside the image and the README's claim that it runs in
the container was wrong.
- SECURITY.md and .env.example document the setting and why it is safe only
behind a 127.0.0.1 publish; README gains the Linux/NAS id note and .dockerignore
drops macOS AppleDouble sidecars, both reported on the PR.
Verified in Docker on a clean build: saving a key from the panel writes the
bind-mounted .env (0600 in the container), it survives `compose down`/`up`, and
REMOVE comments it back out. A foreign Origin, a foreign Host, a proxy header
and a sibling container on the same network are each refused. `npm run doctor`,
`npm test` (5067 passed, 1 skipped, 0 failed) and `npm run build` are green
inside the container.
Reported-by: @jklaze
Co-Authored-By: Bogdan Petrut <[email protected]>
Co-Authored-By: Claude Opus 5 <[email protected]>
Review of #966: with a Google Maps key but no Mapillary token, switching
Street Level on read KEY REQUIRED / "Needs MAPILLARY_CLIENT_TOKEN" with
nothing about Street View, which stayed off (billing), so the layer looked
broken.
- While the switched-on providers lack their keys, the layer reports the
switched-off ones that would draw with a key already set
(keyAlternatives: Street View on Google 3D with a Google key). The
panel's KEY REQUIRED tooltip, its coverage line and the layer row's
tooltip then read "Needs MAPILLARY_CLIENT_TOKEN — add it in Provider
Settings, or light STREET VIEW to use your Google Maps key"
(keySetupWithAlternatives). Off Google 3D the plain KEY REQUIRED stays.
Street View stays off by default.
- OPEN STREET VIEW moves beside the coverage hint: at the default panel
width the PROVIDERS row has no room for it, so it wrapped onto a line of
its own.
- The panel gate's corner drag names what lay under the pointer when it
fails.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Doctor and dev-fresh told every user without an OpenAI key that voice could
use ChatGPT OAuth, whether or not Codex was installed. Say voice is off
until a key is added or a Codex sign-in is selected, and mark that path
experimental.
The login child inherited the whole server environment, including
OPENAI_API_KEY and every other provider credential. Pass only what Codex
needs to find its home, open a browser and reach OpenAI through a proxy.
Voice-card captions from main priced transcription usage into the API-key
dollar meter. In a ChatGPT OAuth session that could trip the $2 warning or
$5 cap while the readout said COST UNKNOWN. Count them with the session's
other OAuth usage instead.
Maintainer follow-up to #966 review:
- A failed panorama-details lookup now waits DETAILS_RETRY_COOLDOWN_MS
(5 s) per panorama before a view event can retry it, so dragging the
view after a failure no longer issues a lookup on every pov change. A
success clears the pause; one panorama's failure does not delay another.
- SECURITY.md notes that allowing maps.googleapis.com in script-src also
trusts its JSONP endpoints, an accepted limitation alongside the
existing 'unsafe-eval' allowance.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Follow-up to #686 requested on #923. The route's per-client limiter keys on the socket address, which for a cross-site <img> is the visitor's own browser, so any web page could fill the bounded cache with junk keys and spend the visitor's 90/min quota, leaving the app's own terrain lookups on 429. Gate the route with admitSameSite before the limiter, like the Places routes.
Co-authored-by: daikaginza <[email protected]>
/api/tomtom/flow was the only keyed, billed proxy left outside the cross-site gate added in #900. Every uncached tile is an upstream attempt counted against the persistent daily budget, so any web page the user visits could fire <img> requests at distinct z8-z16 tiles, spend the TomTom quota and leave the traffic layer on 429 budget for the rest of the UTC day. Gate both routes with admitSameSite, like the Places routes.
Resolve the source-pack conflicts with Statens vegvesen (keep both packs).
The Norway pack's 900 default pushed the per-pack defaults to 5,204, past
the 4,500 catalog cap this PR enforces, so raise DEFAULT_CCTV_MAX_SOURCES
to 5,500 and CCTV_MAX_SOURCES_CEILING to 6,000.
- OPEN STREET VIEW in the PROVIDERS row opens Street View's nearest
panorama at the ground under the middle of the screen, without moving
the camera: Street View no longer needs a pointer. It is disabled (and
says so) until the camera is at street zoom.
- Off the Google 3D map Street View does not appear at all: no chip,
legend swatch, globe credit, layer source or button, and Google's script
is not loaded (the viewer has no prewarm; the script loads on first use).
The share option keeps the user's choice for when Google 3D returns.
- A ground click measures the camera height at the click rather than
reading the cached flag.
- A chip without a usable key explains it instead of lighting; it darkens
beside a lit chip, and the other chips keep switching, so a refused
Street View key cannot lock Mapillary out.
- The gate checks that nothing of Street View shows off Google 3D even when
a share link switches it on, presses OPEN STREET VIEW on Google 3D, and
asserts that each chip it switches really switched.
Co-Authored-By: Claude Opus 5.5 <[email protected]>