The marker effect reads the card handler through the component's own ref
again, so exhaustive-deps sees a ref it knows, and the card hook takes it
alongside the map's other refs. The pin event assertion types the mock
calls it maps under strict mode.
The phone card logic moves out of JourneyMap into useEntryCard, which brings
the component back under the line limit. The new card tests reach the
card element through a guard instead of non-null assertions, the public
hook test reads the hook through renderHook, the GL card tests use
mock.lastCall where Array#at is outside the client's lib, and the
complexity baseline drops to the count the branch now holds.
ConnectionStop, EntryConnections and JourneyMapStop are only used in the
signatures of their own modules, and knip counts an export nothing
imports. The connection tests take the FE- prefix and header the other
journey tests carry.
The card a phone host opens its entry from was still a tooltip bound to
the pin, so Leaflet opened and closed it on the pin's hover and focus. A
tap on the card reaches a phone browser as the pointer leaving the pin
first: the pin's mouseout closed the card between the tap and its click,
and on iOS a content change in that phase can drop the click entirely.
Where a host wires onMarkerCardClick, the pins now bind no tooltip. One
card per map stands over the active pin, the way the GL popup does: it
follows highlightMarker, leaves when no entry is active or the entry
pins are switched off, and only a change of entry moves it. A press on
it never reaches the map. Desktop hosts keep the hover label on each pin.
The card tests run against the handlers the pins really get and the
card the map really adds, and the file parses again: two test names held
an unescaped apostrophe.
On the phone journey screen and the public share page on a phone, a tap on
a numbered pin brings up the entry's card above it, but the card could not
be tapped: the GL popup and the Leaflet tooltip both let the pointer fall
through to the map, and no phone host gave the card anything to do.
The map now takes onMarkerCardClick, and both renderers make the card open
that entry once a host wires it. The GL popup handles it in its delegated
listener next to the photo strip, which keeps opening the photos where the
desktop asks for that. Leaflet handles it on the tooltip element itself:
a press that reached the map container let Leaflet focus the map, which
blurred the pin and closed the card before its click landed. The phone
screen and the timeline open the entry through the carousel hook, the
same way a tap on the active card does. The desktop asides keep the card
as a hover label.
The unused hideMarkerTooltip prop goes, together with the docs that still
said the phone hides the card.
Closes#2885
The share link has counted its map stops through the whole journey since
#1962, while the owner's journal still restarted the count every day, so
the same stop read 66 on the link and 6 in the journal.
Both views now take their stops from one helper, journeyMapStops: located,
non-skeleton entries in the server's reading order (date, sort_order, id),
numbered across the journey. A draft keeps its pin in the journal but gets
no number, so the published stops after it carry exactly the numbers the
share link shows. The public entry query gains the id tiebreak the owner
query already had, so entries sharing a sort_order come back in the same
order on both sides.
Closes#2886
The journey map joins every located entry with a faint dashed line in the
order of its pins. It drew that line through everything, also across days
whose real route is on the map as a kept recording or the live Dawarich
trail, so straight guesses fanned out over the recorded track. The phone
has no layer switches, so the line could not even be hidden there.
A recorded track now carries the day it belongs to (an optional `date` on
the track contract, set by every builder of recorded lines: kept days, the
shared page's recordings, the live Dawarich trail and the day import
preview). A pure model splits the connection into the stretches still open
and those whose days were all recorded, the night between two recorded days
included. Both renderers draw only the open stretches with the entries; the
covered ones sit on a layer of their own that shows while the reader hides
what was recorded, so the guess comes back exactly where nothing else
explains the way.
The live trail is now matched to a kept day by that date instead of reading
the track name as one.
Closes#2888
The journey map grouped its photo thumbnails and its plan and stay marks
on a grid laid over container pixels. Every pan shifted that grid against
the map, so photos near a cell edge changed groups after each move: the
count badges jumped and the thumbnail switched to another photo, although
the zoom never changed.
Both renderers now bucket in world pixels at the current zoom, Leaflet
through its own CRS (so GCJ-02 basemaps stay right) and GL through a
plain Web Mercator projection that ignores centre, bearing and pitch.
Panning keeps every group as it is; only a zoom regroups.
Closes#2889
The booking and transport dialogs each spelled out the notes and the link
as four value and change props, and with the saving flag the two calls
became one copied block. The shared lower part now takes the form and its
setter, as the dialogs hold them, and writes the two fields itself.
The editor follow-up always hands the planner an afterSave, and its type now
says so, so the hook test calls it under strictNullChecks. The travel stats
test reads a country without a status as visited, as the standing helper
does for the hand-marked list. The client baseline holds the one non-null
assertion the count missed.
The booking save helper answers a Reservation the way the dialogs' onSave
does, and takes the payload the sheets read back. The export tests resolve
their routing promise without non-null assertions, the autocomplete test
reads its own console spy, and the Photon tally test asks its five dropped
questions together instead of awaiting in a loop. The Photon address line
falls back to empty when a feature has none.
The repair test now expects the dashboard to place the cleared beach in
Germany through the borders, as the Atlas map does since #2875. The client
lint baseline follows the counts this branch lowered.
A PDF of a long trip routes for minutes, and only the skip button could
stop it. Closing the export dialog, hiding the phone sheet or leaving the
trip left the legs going out and opened the document later on whatever
page the user was on. Closing now stops the routing and the document never
opens; skipping still prints without the remaining legs.
The plan filter moves from TripPDF to pdfScope beside the other plan
predicate, which keeps TripPDF under its size entry. The routing test name
gets its quotes back, and the export dialog and sheet tests cover the leg
count, the skip button and closing while routing.
The follow-up kept the booking a save stored until the editor session
changed, and on the phone the session was only the open flag. The import
review swaps the draft without closing the sheet, so the second booking of
the same kind went out as an update of the first and lost its linked cost.
The stored booking now lives only as long as a failed try needs it, and
the phone sheets name their session the way the desktop dialogs do, by the
booking or draft they hold. The sheets also seed their form from that
session, so the next draft of the review shows its own fields.
The follow-up hook gets its own test, the notes block test passes the
saving flag the component now takes, and the swallowed-catch baseline
follows the one catch the save merge removed.
The PDF export routed the whole trip under an 8 s deadline while the router
is asked one leg at a time, a second apart. A cold month-long trip got about
seven legs routed per export; the rest printed as straight lines at "0 m"
under a total that looked final, and every further export routed a few more
days from the device cache.
The export now waits for every leg, bounded by the request pacing, the
rate-limit retries and a per-leg timeout. The export dialog and the phone
sheet count the legs while they route and offer to print without the rest.
Any leg left without a road is marked in the document: the day reads "not
routed" or "at least ...", the total says it is a floor, and a note names
how many legs are missing. The planner overview shares the same wording.
The PDF also routes what the planner map routes now: the road trip's vias,
the multi-day tour stages and the reader's distance unit.
Closes#2876
The opening day is seeded while the loading splash still covers the shell,
so the rail's auto-scroll ran before any chip existed and never ran again:
its dependencies did not change when the rail first mounted. A trip opened
mid-way showed today's day below a rail stuck at day 1.
The rail element is now a dependency of the alignment, so the active chip
is brought into view when the rail appears, including after re-entering
the trip. The first alignment jumps instead of gliding across a rail
nobody has seen yet, and runs before paint; later day changes still glide.
Closes#2880
Saving a booking or a transport closed the editor and reported success
before the travellers and picked files were written. A failed upload left
a booking without some of its PDFs, no list to retry from, and a create
form that would add a duplicate when saved again. On desktop a file picked
on an existing booking went up at once and vanished when that failed.
The planner's save now takes the editor's follow-up as a step it waits
for: it stores the booking, runs the traveller write and the uploads, and
only then says so and closes. Each file that lands leaves the list, so a
failure leaves exactly the files still to send, and the editor keeps the
stored booking so the retry updates it instead of creating another one,
without a second linked cost or stay. All four editors (desktop booking
and transport dialogs, both phone sheets) share this through
useBookingFollowUp, an import review only moves on once an item's files
are up, and desktop files picked on a saved booking wait for Update like
on the phone.
Closes#2883
The built-in profile check returned the open profile key and the run request
returned an unnarrowed answer, both of which the client typecheck refused. The
new road trip tests asserted with non-null assertions where optional calls do.
The graceful stop step piped docker logs into grep -q under pipefail, so grep
leaving early after its match could fail the step with SIGPIPE although the
server had logged SIGTERM.
The route cache key followed the relay's rest, so after one failed relay request
(always the case offline) every day the device held only from Valhalla missed
the cache for ten minutes. The key now follows the configured provider, and a
resting relay only stops the question from going out.
A route now names the engine that drew it. A day OSRM drew because Valhalla
could not answer is OSRM's on the rail, and its other ways are asked of OSRM,
instead of being labelled Valhalla's and compared with Valhalla offers.
An own Valhalla gets the relay's twelve second deadline, and a day that runs it
out goes to OSRM without a second wait. A day that avoids something keeps its
old cache key, so the days a device already holds stay found. The leg segment
and the offer types move to modules both engines import.
With the TREK services or an own Valhalla, the day plan, the road trip and
the other ways of a leg are routed by Valhalla on foot, by bike and by car,
twenty locations a request. A day it cannot answer (relay refusal, time out,
no way for a leg) goes to OSRM whole and without a word; it is never handed
to FOSSGIS unless it avoids something only Valhalla can avoid. With FOSSGIS
nothing changes: OSRM routes the days and Valhalla the avoidances.
A Valhalla answer is cached under its own key beside the OSRM one, so
switching engines never serves a day from the other engine. The Valhalla
day routing moves out of RouteCalculator into valhallaDayRoute.ts.
A route or height question through the relay now gives up after 12 s. A 400 without Valhalla's error_code and a 404 or 405 count as the relay unable to answer instead of no way or no Valhalla, and a Retry-After sets how long the tab leaves the relay alone.
GET /api/admin/service-identity lists places, photon and valhalla, each with
today's allowance read fresh from /v1/quota of its own host (cached for 20
seconds per host) and whether this instance asks it right now: Photon only
beside the public Nominatim, the TREK Valhalla only where the routing choice
comes to it. Photon and Valhalla are asked only with a key and only while
TREK_SERVICES_HOSTS lists them. The last allowance an answer stated is kept
per host for when a host does not answer.
The card lists the three services under each other in the look it had,
a service this instance does not ask dimmed and named as such.
routing_valhalla_provider (fossgis, trek or custom) sits beside the routing
URLs under Admin, Default user settings, admin only and locked on a managed
install. Without a choice an own Valhalla is custom, an own OSRM alone asks
no Valhalla, and otherwise the TREK services answer.
The planner asks them through the same-origin relay, so no connect-src
changes. A relay that answers 401 (a shared page without a sign-in), 403,
413, 429 or 5xx sends the question to FOSSGIS without a word and is left
alone for ten minutes. A long line to describe goes as a polyline.
The proof of work and the bounded read of a 403 body recurse instead of looping, the background-work checks compare against null instead of testing a promise, and the tests repeat their requests through oneByOne. The renewal test loads the stored key before its first request, and the panel test picks the last confirm button without Array.prototype.at.
A calm panel under the card's facts: a dot with the state, the tier, a bar of today's use against the limit, what is left and the local time the count starts again. It reads when the card comes into view and every 30 seconds while it stays there and the tab is in front, says why when the services are off, revoked or cannot be read, and offers a new identity behind a confirmation unless the key comes from the environment.
Without an Access Key the image search goes through Unsplash's web endpoint,
which refuses datacenter addresses with a 401 or 403 or answers with a
challenge page. The search then said only "Unsplash search unavailable",
although an admin can already enter a free key in the settings. Such a
refusal now answers 502 with that hint and the code unsplash_key_needed, and
the cover search shows it in the user's language.
Closes#1449
A tour whose walking time and breaks came to more than 24 hours a stage
only greyed out Save, with nothing saying why. The planner now marks the
planned total and says what to do: make it a multi-day tour by ending a
stage at a stop, or enter a shorter planned total. The details section
opens by itself so the reason is in sight.
Closes#2869
A book held at most 150 spreads. The auto layout cut everything past that
without a word, so a long journey's book ended about three quarters of the
way through, with its last page and back cover gone, and adding, copying or
applying a designed page to a full book was refused.
The spread cap is gone from the book schema, the page operations, the
design composer and its apply, the assistant's book card and the design tool
contracts. A design's own page budget (pages.max) still bounds what it lays
out. The save route's 8 MB body still bounds a book's size, which at two to
five kilobytes an auto laid page is a few thousand pages.
The templated design digests move because their counter ids shift by two;
the pages, their order and their elements are unchanged.
A wider gutter left the button floating in the corner over the first card and the scrollbar. It now sits in its own row above the scrolling content, level with the Help title, and the content keeps symmetric padding.
The close button floats over the scrolling content, which only kept a 40px gutter on the end side while the button reaches 52px in, so the first card ran right up to it. The content now keeps a wider gutter on that side.
The cluster source, its layers, the expansion zoom and the DOM bubbles
now live in placeClusterLayers.ts with their own tests, which brings
MapViewGL back under its size baseline instead of growing it. Lowers the
size and eslint baselines that this change made smaller.
The MapLibre and Mapbox maps painted their place clusters as a canvas
circle plus a symbol layer, so the count used whatever glyphs the map
style ships and the bubble had no hover. They are now DOM pins built by
the same markup and CSS as the Leaflet bubbles, with the same sizes,
font and hover scale. The canvas layers stay, invisible, as the hit
area the track and route layers check a click against.
The date picker of the external photo picker hung absolutely under its
trigger, inside a dialog that clips its content. The last week of a
six-week month fell below the dialog, and for the end date in the
right-hand column the calendar opened past the dialog's right edge, so
the last weekday and the next-month arrow could not be reached.
The calendar now opens in a portal, pinned to its trigger through
useAnchoredPosition: it flips above the trigger when below is too short,
scrolls when neither side holds it, and is kept inside the viewport
sideways.
Closes#2858
With Google as the transit provider, the result list still credited
Transitous, and a route added from the panel or through
create_transit_journey was saved with provider 'transitous'. The panel
now credits "Google Maps" (untranslated, as Google's terms ask) or
Transitous after the backend that answered and saves that one; the MCP
journey records the backend that plans routes for the caller, and
search_transit_routes reports it.
With connected days, every join between two days kept its one-request-a-second
slot even when the route came back from the device's cache, so a long road
trip still waited about a second per join on every open. A join answered from
the cache now gives its slot back; requests that go out stay spaced as before.
Refs #2794
Once a store was connected for a trip, its address and login could only be
entered again after a sync run had been refused, and the stored connection
could not be removed at all: the server's DELETE route had no way in from the
app. A binding's settings now open the connection at any time, the folder
picker leads back to it before a folder is chosen, and the connection form
removes a stored connection after a confirmation, on the desktop panel and the
phone sheet alike. Removing it drops the trip's bindings through it; documents
stay on both sides.
Closes#2842
create_day_note and update_day_note described `time` as a clock label and capped
it at 250 characters, while the planner shows it as the Markdown body under the
title and allows 2000. The assistant put long text in the title and had to cut
notes it was asked to edit. Both tools now take text and time from the REST
contract with descriptions naming title and body, and an update still refuses an
empty title. The assistant's change card labels the two fields title and content
instead of text and time.
Closes#2840
A new list starts with a '...' placeholder row. Typing the first item there
and pressing Enter saved it but left the cursor nowhere, while the "Add item"
field puts it straight back for the next item. Enter on a filled placeholder
row now opens the category's add field and focuses it. A blur still just
saves, so clicking elsewhere does not pull the focus back.
Closes#2825
A GPX or KML import skipped every row whose name was already a place of the
trip. A watch names a hike after where it went, so "Hokitika Gorge" was
refused as already in the trip because the gorge itself was a planned
place, with no track to show for it (#2810). A track now matches only the
tracks of that name; a pin still matches every place of its name.
The Places filter also hid its "Tracks" choice whenever Tours was on, on the
grounds that tracks are tours then. A track imported before Tours is a
plain place and stays in the list, so the choice now counts the tracks that
are no tour.
A GPX track imported in 4.x is a plain place: a line, no tours row. It never
showed under Tours, and importing the same file there was refused as a
duplicate by its name, so there was no way to turn it into a tour (#2810).
The tour import now takes over a track of the trip that has the same name
and is no tour yet: the place keeps its name and line and gets the tours
row, with the recorded times the file adds. The preview says so instead of
"Already in the trip". New and taken-over tracks commit in one transaction.
Connecting an own server asked Ollama's /api/tags and gave up on any answer
that was not a 404, and every failure, a refused key included, read as "not
responding". The model list is now looked for at /api/tags, at the address as
entered and at /v1/models, an answer only counts when it is a list, and a
refused key or a server that answered without a list say so (#2808).
AI parsing through an own server always called Ollama's /api/chat, which an
OpenAI-compatible server such as Groq answers with 404. A server without it
now gets the same extraction over /chat/completions at the address as
entered (#2806).
TREK 5.0 left most registry plugins blocked because their authors still declare
"<5.0.0", and the only way past that was TREK_PLUGINS_IGNORE_TREK_RANGE for every
plugin at once. An admin can now install or switch on one such plugin after a
warning; the consent is kept on its row for that plugin version and TREK major,
so an update or a new major asks again. The environment switch works as before.
Discover marks a plugin not confirmed for this TREK on its card, and Install opens
the warning first. Admin > Plugins reads cleaner: one row of alike facts per plugin
with each state that needs attention on its own line, type, status and order behind
one Filter button, and the containment notes in a dialog behind the shield in the
header. The row, the filter menu and the dialog moved into their own files.
The return address already carries a query, so a code appended with ? instead
of & ended up inside the ai marker and the page did nothing, without a word.
An imported hotel was looked up once, by its name and its address together.
A booking site's romanized address (Agoda for a stay in Japan) often matches
nothing and sank the hotel's name with it, so the place was created without
coordinates and silently never showed on the map.
The review now finds the stay right away (name and address, then the name,
then the address) and says under the address whether it is on the map and
where, or that it is not yet, with a way to search again. Picking an address
suggestion sets the spot by hand. The save uses that spot, falls back the same
way when there is none, and a stay saved without a spot says so in a toast.
Desktop dialog and phone sheet share the lookup through useReservationForm.