WKWebView runs no service worker for a self-hosted server, so on iOS nothing
kept a map tile once it scrolled away. Binaries with the tileCache feature
answer a trek-tile:// scheme from a store on disk: a tile viewed once is
served from there for up to 30 days (Mapbox's limit for a device cache,
applied to every provider), then fetched anew; the store keeps under 250 MB,
dropping the least recently used tiles first, sends a unique app User-Agent,
and never prefetches. Mapbox's own resources (style, tiles, glyphs, sprites)
and Leaflet's raster tiles go through it, telemetry never does.
Offline, a Mapbox map in such an app now stays Mapbox where it was viewed,
and the pill offers the stored trip region instead. The offline settings
show what the store takes against its limit, with a way to empty it.
The UI ships with the server and the binary through the stores, so the two
drift. TrekShell.getCapabilities now reports the binary's shell API, version
and native features by name (iOS and Android), and the UI turns each native
feature on only where the binary has it; a binary from before the handshake
counts as shell API 1 with none. The other way round, the server names the
oldest binary its UI works with (minNativeShellApi), and an app below it is
asked to update with a link to its store.
In the editor the months of the leave year page like the trip's days, the
neighbour sliding in with the finger; in the overview the plan's years do
the same (only between years the plan has, the arrows still create one).
The day pager now pages anything with an id and can sit in a page's flow.
The row of person and legend chips is two compact pills now: People opens a
sheet to pick the member and show or hide shared calendars, Legend opens one
that explains the company days and holiday calendars, which were never a
choice.
The error and new-version fallback was a bordered box with a warning
triangle and the raw error message on show. It is now one centred card with
a round symbol (a friendly one for an update), a short title and line, and a
full-width reload button; the technical message stays for issue reports,
folded under Details. Phone, PWA and desktop alike.
The day plan offered Google Maps and CoMaps as two tiles. One Maps button now
opens the day in whichever app the traveller picks: Google Maps, Apple Maps
with the stops between as waypoints, CoMaps, Waze and on Android any other
map app, the last two to the first stop, which they say. The preferred map
app from the settings opens straight away.
The day's actions sit together on one glass surface as round icons over
their labels, and the up-next card wears the same surface as the timeline
card under it.
Offline, a Mapbox map, another OpenFreeMap style or a raster template the
device cannot serve now switches to the stored OpenFreeMap basemap, light or
dark with the colour scheme, keeping every marker, route and track, with a
small Offline map pill. It switches back once the connection returns.
"Prepare for offline" stores that basemap's region for every trip, whatever
the provider, and both styles' sprites and glyphs, including the scripts a
trip in East or South Asia labels its places in. The vector prefetch keeps to
OpenFreeMap's download rules (four requests at a time, 50 MB per run), raster
tiles are no longer prefetched from OpenStreetMap's own servers, which their
policy forbids, nor inside the iOS app, which has no service worker to serve
them.
The plan picker no longer shows a yearly badge, so its string reached nothing
and failed the i18n key check; the shell's start also initialised a value it
always overwrote.
The welcome headline and subtext, the demo button, the have-a-server link,
the unlimited-plan line and the claim title changed in en and de with the
redesign. All other locales now say the same, and the English headline
reads Your Trips to match the German plural.
* chore(capacitor): base files for capacitor
* chore(capacitor): base plugins
* feat(auth): hand a browser sign-in over to the native app
The native apps show the server's own UI in a WebView, where neither SSO nor
passkeys can run. They sign in through the system browser instead and need a
way to carry that session back.
The app keeps a random verifier and sends only its sha256 into the browser.
Once signed in there, the browser trades that challenge for a one-time code
(cookie session only, so an API or MCP token cannot mint itself a browser
session), and the WebView redeems the code together with the verifier. The
code rides through a URL scheme any app could claim, so on its own it is
worthless; it is single use and good for a minute.
Also: /api/health/features reports nativeApp so the apps can refuse servers
that predate this, and hashed files under assets/ are served immutable,
which the iOS app needs to start offline without a service worker.
* feat(native): the apps load the server's own UI
The apps ship only an address screen now. Once a server is picked, the
native side points the WebView at it: TrekBridgeViewController on iOS and a
config copy with server.url on Android. The UI then comes from that server,
in the version it runs and on its own origin, so the session cookie,
uploads, /ws and plugins work exactly as in a browser and no app release
has to keep pace with the servers out there.
The address screen (native-shell/, built into native-dist/) probes
/api/health/features through native HTTP, tries https before http when no
scheme was typed, and refuses servers without the nativeApp flag. It also
serves as the error page when the remembered server does not load: retry,
change server, or on iOS the stored copy of the server's start page.
TrekShell is the app's own plugin for all of that: remember, change and
reload the server, sign in through ASWebAuthenticationSession or a Custom
Tab, and on iOS keep and open the offline copy of the start page, because
WKWebView runs no service worker for a domain the app does not list.
Plugins the frame does not use are gone (push, camera, geolocation,
calendar, file transfer, in-app browser and more): the WebView covers the
camera and location itself, and push needs a relay before it is worth
shipping. With them go the push entitlement, the calendar usage strings
and the Firebase bits. The iOS scheme falls back to capacitor://, since
Capacitor drops an iosScheme of https, and the arm64 capability replaces
armv7.
* feat(native): the UI knows when it runs inside the app
Everything the server's UI does differently inside the iOS and Android app
lives under src/native and is inert in a browser.
Sign-in: SSO and the passkey button go through the system browser
(signInThroughBrowser), because identity providers refuse embedded
browsers and WebAuthn needs a domain association no build can have for
every server. The browser end is the new /native-handoff page: it signs
the user in if needed and hands the session over only after an explicit
tap, so a link carrying somebody else's challenge cannot pass a session
on silently. The verifier is hashed with @noble/hashes because
crypto.subtle is missing on the plain-http servers many home networks
use. An OIDC-only install no longer bounces to the IdP inside the
WebView, and passkey management is hidden in the app.
Downloads: the WebView cannot save a blob, so downloadBlob, downloadFile
and openFile write to the app cache and open the share sheet. The eight
places that built their own download anchor now use downloadBlob, which
they should have anyway.
The rest: "Change server" on the login page and in the account settings,
the splash hides after the first frame, Android's back button walks
history before it leaves the app, the status bar follows the scheme, and
on iOS the start page is refreshed and every chunk of the build (listed
in the new asset-manifest.json) is pulled into WebKit's cache so the app
can start without network.
* ci(native): build the Android and iOS shells on every change to them
Compiles the Swift side on a macOS runner so nobody needs a Mac to know it
still builds, and leaves a debug APK on every run to install on a phone.
Unsigned: signing and store uploads need secrets fork PRs do not get.
* feat(native): offline vector maps in the iOS app
In a browser the service worker answers MapLibre from the gl-map-offline
cache that glPrefetcher fills for trips marked for offline. The iOS app has
no service worker, so nothing read that cache there and the map went blank
without network.
A trek-offline:// protocol takes the service worker's place. Inside the iOS
app the MapLibre style URL is rewritten to it, the handler rewrites every
https URL inside the style and its TileJSON the same way, and then serves
documents network first and tiles, glyphs and sprites cache first. Outside
the iOS app nothing changes. The default OpenFreeMap basemap is covered;
Mapbox GL has no protocol hook and stays online-only in the app, as does a
custom raster template, whose opaque responses only a service worker can
store.
* fix(native): keep the iOS offline copy inside the app
loadSimulatedRequest went to the server URL without its trailing slash, and
Capacitor keeps a navigation in the WebView only when it starts with the
bridge's serverURL, slash included. The offline start page would have
opened in Safari instead. Also covers the platform helpers and the
hand-off API module.
* feat(native): the small things that give a WebView away
- A short haptic tap on success, warning and error toasts, which is where
every save and delete already reports. Info toasts stay silent.
- No "‹ › Done" strip above the iOS keyboard.
- No Safari preview when a link is long-pressed.
- The swipe from the left edge goes back on iOS, through the same history
Android's back button walks.
* test(mobile): the backup-code download revokes its URL after the shared delay
The mobile twin of the AccountTab test still expected the object URL to be
revoked in the same tick. Since both screens download through downloadBlob,
the revoke happens after its 100 ms grace period.
* fix(native): remember the login by default inside the app
With the switch off the app got a session cookie, which dies with the app
process, so every time the phone unloaded TREK the user had to sign in
again. Found while running the Android build in the emulator.
* fix(native): no overscroll stretch on Android
The UI asks for no overscroll effect with overscroll-behavior: none on html,
and Chrome honours it. The Android WebView draws the stretch on its root
scroller natively, though, and it scales the whole view, so the top bar and
the bottom dock were pulled along with the page. Found in the emulator.
* fix(native): Android shows "server not reachable" instead of a blank page
Capacitor injects its bridge on Android only into the server's origin once
server.url is set, so the local error page it switches to when the server
does not load had no bridge. It could neither retry nor change the server
and stayed blank.
TrekWebViewClient now catches a failed page load (a network error, or a 5xx
from a proxy while TREK restarts) and the app starts over on the bundled
address screen, where the bridge is present and the screen offers exactly
those two choices. The server config no longer carries errorPath.
Two traps on the way, both found in the emulator: a restart during launch
has to be a fresh task, since recreate() leaves the system splash lying over
the new page; and a restart shows no launch splash at all, because the
splash plugin cannot take it down again when the page hides it early.
* feat(native): TREK's own app icon and launch screen, named just TREK
The shells still carried Capacitor's placeholder icon and splash, the blue
X on a grid. They now show the white TREK mark on a dark, slightly glassy
tile: a soft light from the top left and a sheen over the upper half, in
the app's launch colour #09090b. Android 12+ draws its launch screen on the
same colour instead of light grey.
scripts/generate-app-icons.mjs renders the sources into assets/ from
public/icons/icon-white.svg, and `npm run icons:app` turns them into every
size both platforms ask for through @capacitor/assets. The Android
foreground mark is sized for the adaptive icon's round mask.
The home screen label is TREK: the old "TREK - Plan your trips!" was cut
off under the icon, and the longer title belongs to the store listings.
* fix(native): new lint warnings and the MapLibre protocol type after the dev merge
Awaits that sat in loops in the native-auth tests run together, the hand-off hook marks its navigations as ignored, the native tests drop their non-null assertions, and the offline protocol answers with the data type MapLibre 6.15 expects.
* fix(native): strict types in the address screen, Prettier on the native-auth e2e suite, file-local exports
The address screen's element helper returns the plain element instead of Object.assign's intersection, which strict mode could not represent; the haptics test reads an optional argument as optional; three exports only their own file used are local now.
* fix(native): Prettier on the native client files, rate-limit e2e requests in sequence
Formats the files the client formatting gate listed, and NATIVE-E2E-005 sends its twenty exchange attempts one after another: all at once they reset the test server's connections.
* fix(native): hide passkeys in the app through usePasskeys
The flag in useAccountSettings grew the identical destructuring in both account views past the duplication threshold; the shared passkeys hook already decides when its card is hidden, so the app check lives there now.
* feat(native): everything the App Store and Google Play submissions need
- iOS: microphone usage text for "Take Video", no-export-compliance flag,
ExportOptions.plist for App Store Connect uploads, the unused push
forwarders gone; CI builds with Xcode 26 and lints the property lists.
- Android: version and signing from Gradle properties (unsigned without
them), R8 with keep rules for TREK's and Capacitor's reflective classes,
no Auto Backup of the session cookie, no external file provider path,
template and Firebase leftovers removed, keystores and service accounts
ignored, full-size adaptive icon layers with a monochrome layer; CI also
builds the release APK.
- native-release.yml: signed bundle to a Play testing track and archive to
TestFlight, by hand or on a v* tag, behind the store environment.
- Version handshake: the server reports nativeApi 1, the app refuses a
server below its minimum and tolerates an app older than the offline copy.
- Address screen: privacy policy link, "Try the demo" and "What is a TREK
server?"; the privacy link also in the app's server settings. Three new
native strings in all 27 locales.
- Donation links and the notices asking for one are hidden in the app.
- Store listings for "TREK Travel Planner" in client/fastlane: texts, review
notes with placeholders, privacy and account deletion page drafts, the
feature graphic and icon, a length and image check in CI, and a Playwright
store project with the scripts that turn its captures into screenshots.
- Docs: wiki/Native-Apps.md, README no longer says there is no store app.
* fix(native): read a notice button's href without a weak type
An action button carries no href, so a parameter typed as only an optional href refused the notice DTO; the button type now names its kind as well.
* feat(native): a welcoming onboarding for the address screen
The app's own screen now opens on a welcome: the TREK mark, a line about what
TREK is, and two clear paths — connect the TREK you already open in the browser,
or get TREK Cloud. Both lead to polished screens: the address field with live
validation, a shake and haptic on a bad address and a success tick before the
server takes over; and a TREK Cloud intro with what it offers and a way in.
Screens slide and stagger in, buttons give a tap, and it all respects the safe
areas, light and dark, and reduced motion. Self-hosters are unaffected: cloud is
its own path, never in the way. New native strings are added across all locales
(English placeholders outside en/de, to translate).
* feat(native): a plan picker for TREK Cloud, with live prices
The Get TREK Cloud path now leads to a plan picker that reads the live plans
from the site (/api/plans), so the prices and quotas always match the website.
Monthly and yearly in a segmented toggle, the year marked with its saving, each
plan a card with its price and what it includes. Subscribe is wired to finish on
the web order for now; it becomes an Apple/Google in-app purchase once the store
products exist. New strings across all locales (en and de translated).
* feat(native): light status bar and a Go key on the address screen
The address screen sits on the near-black surface, so set the status bar to draw
its clock and icons in light while it is up; the server's own UI takes over the
style once it loads. The address field's on-screen return key now reads Go. Both
fail quietly where there is no native status bar (web, the simulator).
* i18n(native): translate the onboarding into es, fr, it, nl and pl
The welcome, TREK Cloud and plan-picker strings were English placeholders in
every locale but en and de. The five largest remaining European languages are
now translated in full; the others still carry the English text and are a
follow-up for native speakers.
* feat(native): the in-app TREK Cloud subscription flow
After picking a plan, the app now carries the whole purchase itself when the build
can take one: choose your address (checked for free as you type, against the same
endpoint the website uses), your email, and subscribe. The receipt goes to
/api/iap/redeem, and a Setting up your TREK screen waits on the new address and
loads it the moment it answers.
The purchase sits behind an IapClient seam (src/native/iap.ts): a fake drives the
whole flow in development and preview, the native client reports itself
unavailable until the store plugin and products exist, and until then the plan
picker still finishes on the web, so nothing half-wired faces a customer. New
claim and provisioning strings across all locales (en, de and the five largest
European languages translated).
* i18n(native): translate the app into the remaining 20 languages
The onboarding, plan picker, claim and provisioning strings were English
placeholders outside en, de and the five largest European languages. They are
now translated in full across the other 20 locales (ar, az, br, ca, cs, et, gr,
hu, id, ja, ko, ru, sk, sv, th, tr, uk, vi, zh, zh-TW), placeholders and brand
kept. The app now speaks every language TREK does.
* fix(i18n): add the Hebrew native strings, and correct the LoginPage size baseline
The he locale had every domain but native.ts, which broke i18n parity, the shared
coverage specs and the client parity test once the native strings existed. Add
he/native.ts with all native keys translated. Also correct the LoginPage size
baseline to its current line count.
* i18n(native): excuse the TREK Cloud brand, mark Email same-as-en, exempt the quota counts
The strict i18n checks, which the feature-branch CI does not run, catch three
things now that the native strings exist in every locale: TREK Cloud read as an
untranslated English phrase (it is a product name, like Vacay and Atlas, so add
it to the brand list); Email left identical in Italian, Indonesian and
Vietnamese, where it is the local word too (mark same-as-en) and in Greek (give
it a Greek term); and the GB and AI-document quota strings carrying {n} without
being plural groups (both are figures that never show a singular, like the other
unit-symbol counts in NOT_PLURAL).
* fix(native): typecheck, lint and knip on the native code; wire he's native bundle
The full suite, which the feature-branch CI does not run, catches four things the
native work still had: a non-narrowed union in the claim flow (tsc), a helper
named useFake that the react-hooks rule read as a hook (eslint), two iap types
exported but used only inside the file (knip), and the Hebrew native strings
present as a file but never spread into he's merged bundle (the aggregated key
parity test). All four fixed.
* fix(native): a flat redeem result, so the claim flow typechecks
The discriminated union did not narrow under the native-shell tsconfig, so the
failure code read as absent on the whole type. Use a flat result with optional
instanceUrl and code instead: both are always present on the type, no narrowing
needed, and the ok plus instanceUrl guard still routes success from refusal.
* style: prettier-format the native and i18n changes
The feature-branch CI does not run the formatting check, so the conflict-resolved
files (AboutTab, useAccountSettings, useTripExport), the native shell and iap
module, and the i18n files touched by the translation scripts were never run
through Prettier. Format them all; no behaviour changes.
* chore(native): re-trigger CI on the formatted tree
---------
Co-authored-by: Maurice <mauriceboe@icloud.com>
The row's badges said who brings an item, who it is shared with and who
takes care of it in the browser's own tooltip. They now use TREK's, and
the bringers one lists everyone with their picture on a line of their
own. The others show as a +N circle tucked under the first picture.
Tooltip takes an optional content for tips that hold more than words.
The waypoints switch showed in every Tours import dialog; it now shows in
the trip's import only. Confirming it when every point was already in the
trip closed the dialog without a word.
With a room kept for the whole trip and a second stay for a night away, the day
after the side trip took the first stay in the list as the hotel it woke up in.
The Days view then drew a drive from the kept room, often hundreds of kilometres
away, to the side trip's first stop or airport, next to the flight back. The
morning now belongs to the stay checked into last, and the evening to the stay
that runs on past the day. The map also passes a booking's own point at the edge
of the day, as the sidebar already did, so a taxi leaving from the hotel adds no
zero-length road from the hotel to itself.
The Tour booking type read like the Tours tab, and a booked food tour left people
looking at an empty Tours tab. It is now called Guided tour in every language,
the empty Tours tab says that booked tours go under Bookings, and the help
center and wiki use the new name. The stored type stays 'tour'.
Every date in TREK was written in the interface language's format, and
English is en-US, so an English interface always showed and read
mm/dd/yyyy with no way to change it. A few native date inputs followed
the browser instead, so one screen could mix two formats.
date_format is now a user setting with an admin default, next to the
time format: automatic (the language's format, as before), YYYY-MM-DD,
YYYY/MM/DD, DD.MM.YYYY, DD/MM/YYYY or MM/DD/YYYY. The date picker shows,
pre-fills, hints and reads typed dates in it, and every date written in
numbers across the app (lists, cards, admin panels, CSV and PDF exports)
goes through one formatter. Dates written with a month name keep the
language's wording. The remaining native date inputs are the shared
picker now. The display settings MCP tool reads and writes the key.
Closes#2971
The places toast is a plural group in every language, the points import goes through tourRepo instead of src/api, the hook and dialog keep their complexity, and the tests that checked the old confirm call or the GPX left-out count follow the new behaviour.
With the Tours addon on, a file picked or dropped on the places column goes
into the Tours import as soon as it holds one line, and that import reads
lines only. A Sygic or Trip-o-matic export (a route plus its stops) came in
as one tour and every place was dropped without a word.
The preview now counts a GPX file's waypoints as it already counted a KML
file's pins, and the dialog offers to add those points as places of the
trip, on by default. Confirming runs the Tours import for the lines and the
places import (points only) for the same files. A file whose tracks the
trip already holds can still bring its points.
Closes#2969
An iPhone or iPad running TREK from the Home Screen ignores print(), in
the page and in an iframe alike, so "Save as PDF" in the preview did
nothing there. Mobile Safari prints the same preview fine.
In the installed iOS app the preview now shows "Open in Safari" in
place of the dead button, with a line saying why. The link opens the
trip with ?export=pdf, and both shells open their export dialog or
sheet for it, so the PDF is one tap away once Safari is up. The
parameter is dropped once read and survives the login redirect.
Everywhere else the preview prints as before.
The preview overlay moves out of TripPDF.tsx into pdfPreview.ts, which
TripPDF had no room left for under lint:size, and its backdrop and
stacking now use the --overlay and --z-modal tokens.
An installation now asks for its TREK services key a few minutes after
it starts, and the daily check registers a missing key as well as
renewing a due one. Until now nothing happened before someone searched
for a place or opened the admin settings, so "Share place details"
stayed unavailable on a fresh or updated instance.
When an admin agrees to share place details, what users typed into
places since TREK 5.1.0 goes out too, not only what they type from then
on. The origin of every field is recorded since 5.1.0 whatever the
decision, so a catch-up walks those places page by page before each
send run: only values that are still the ones a user typed, only for
fields switched on, only for places the service can key, and only
while the queue holds fewer than 200 values. It goes on where it
stopped after a restart, never replaces a newer value a save queued,
and ends on switching off, declining or withdrawing everything. A field
switched on later catches up as well, under an agreement given with the
new prompt sentence that says so; an agreement from 5.1.0 does not.
Queuing moves into PlaceContribQueue so a save and the catch-up take
the same path.
Confirm sends the count the admin saw and waits for its answer; a stale
count reloads the state. A running backup is named as a skip reason, a
missing installation id and a stale count as errors, and the switch's
hint no longer promises removal right away: deleted files go a day
later, once no undo can bring them back. Strings in all 28 languages.
The panels follow the reworked reclaim state: Check now starts a check
in the background and the panel polls until it is done, a category with
an unusually large share of unused files offers Confirm, Freed comes
from the last run that deleted, a stopped check says why and no longer
reads as nothing found, the switch waits for its answer and a poll that
was in flight cannot flip it back, and refusals name their reason in
the operator's language. Strings updated in all 28 languages.
Desktop and phone show what the last check found (can be freed, unused for
less than a week, freed), why a category was skipped, the switch that lets
the nightly run delete and a report-only check on demand, through the
shared storage panel hook. All 28 languages, and the Admin-Storage wiki
page explains the check, the week's wait and when to leave it off.
The release popup now shows 5.1.0: place search, shared place details
and faster routing as three cards side by side, each with its own
drawing. The support band is set as a card like the promise above it
(optional supportLabelKey; a release without it keeps the plain line),
and the two support buttons get the same glass look in their colours.
Settings > About gets a full-width panel for dataforest cloud, who
sponsor the servers behind place search, address search and routing.
Both logos ship in client/public/brands, so opening Settings reaches no
third party. Wherever a line names dataforest cloud it links to
https://cloud.dataforest.net/?mtm_campaign=trek, the link dataforest
gave us; the README moves to it too.
All 28 locales carry the new copy, and the old 5.0 card keys are gone.
Editing a saved place looks it up again. Where the lookup has a website,
phone, e-mail or opening hours that differ from the place, a New place details
pill in the form's head band shows how many, and opens what the place holds
beside what the lookup has. Empty fields are ticked, filled ones are left to
the person and say they would replace; Take over writes only the ticked
fields, with the provider as their origin, and nothing the lookup does not
know is ever emptied.
The head band's pills move to PlaceFormHeaderPills.tsx, which brings the form
from 1157 to 1002 lines.
The TREK API, the transit provider, the place search provider and the search
log move out of API Keys into a card of their own, which keeps them together
and saves as they change. API Keys holds only Google Maps, Unsplash and Amap,
and is not shown on a managed install, where the keys are the operator's.
Allowed File Types moves to the left column, which had room for it.
The first time an admin opens the settings on an installation that can share,
a dialog asks for the whole installation, with every field switched on and a
plain list of what never goes along. Nothing is sent before Agree; Not now
asks again in 14 days, No keeps the question away until the terms change, and
closing it decides nothing.
Inside the TREK Places API card, Share place details then holds the switch for
the installation and one per field, the figures of the last 30 days, the
transmission log and the way to withdraw everything sent. The place form
reports for each field whether the user typed it or a provider filled it.
The card names place search, address search and routing with what each
answers and what is left of today's allowance. Each service is a tile with
what is left as its figure, the day's use as a bar and used and limit as
badges; the state, the kind of installation, a key from the environment
and the time the count starts again sit as badges in the header. The
sources add the terrain tiles the routes take their heights from.
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
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 hint under Routing service now explains that the TREK services and an
own Valhalla calculate every route, that OSRM calculates a day they cannot
answer, and that FOSSGIS keeps Valhalla to what OSRM cannot do. In all 27
languages.
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.
Zod schemas for what the key issuer answers (challenge, key grant, quota) and for GET /api/admin/service-identity. The card no longer claims there is no quota: every locale now says there is no key to enter, a fair daily allowance per installation, and that only the number of requests is counted.
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
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
Auto layout gathers journal entries with no photo and no text, one after the
other, onto one list page instead of giving each an empty page. Its heading
said "Stations" (German "Stationen"), which read as train stations and gave
no hint why those places were on it. It now says "Along the way" in every
language; each stop still carries its date.
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.
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.
A token whose account has no local ActivityPub author was reported as
invalid_response, which the settings show as 'The answer did not come from
wanderer', although wanderer had answered fine. It now has its own code,
no_author, with a message in every language that says what is missing.
The update dialog now shows the 5.0.0 headliners, Tours, TREK Studio, the assistant and help on every screen in 27 languages, each with a drawing of its own, translated into every language TREK ships. The 4.3.3 cards and their drawings are gone.
A Photo credits link in the dialog's footer opens a second view with every photo: its title linking to its page on Wikimedia Commons, its author and its license linking to the deed. The list is its own chunk, loaded when a visitor opens it. The countdown test reads the clock the interval moved.
The demo account can look at its Account tab, API keys, MCP configuration,
own AI connection and language, but not change them, and the server already
refuses each of those writes. DemoLock renders such a section dimmed and out
of reach under diagonal stripes in theme tokens, with a small Demo mode badge
and one line saying why; useDemoAccountLock decides, on the desktop settings
and the phone screens alike (a new pair in feature-pairs.json).
Locked: the whole Account tab (profile, password, 2FA, passkeys, deletion),
API keys, the MCP card with its tokens and connected apps, connecting an own
AI model and the language picker, which shows English. The admin of a demo
instance sees everything unlocked, 2FA included, so the old instance wide
"Not available in demo mode" line under 2FA and its key are gone from all
locales.
The old popup was a narrow white card with its own copy of the texts in
eight languages, hex colours and a minute counter on the browser clock. The
new DemoWelcome is a wide dialog on DialogShell with theme tokens only: what
TREK is in one line, the hourly reset with a live countdown in the server's
time zone, the 5.0 highlights to try right away (trip planner with bookings
and costs, Tours with 3D terrain, TREK Studio photo books, live
collaboration, Journey, Atlas, Vacay, Collections, offline use), what the
demo leaves out and what the full version adds, and the link to the source.
Its strings are real i18n keys in a new demo domain file, translated into
all 27 locales. Both dashboards open it instead of the old DemoBanner, which
leaves the theme and format baselines with its file and the test non-null
assertions it held.
MapLibre 6 and Mapbox GL 3 both throw out of their Map constructor without
WebGL2. Every place that builds a GL map now asks one helper first,
components/Map/glSupport.ts, and a browser without WebGL2 gets the Leaflet
map with the user's tiles instead of a map that fails to start:
- the trip map (MapViewAuto) and the journey maps (JourneyMapAuto) pick
their renderer through useGlRenderer, which folds in the Mapbox token
rule they each had;
- the settings preview, desktop and phone, shows the Leaflet preview
through useMapSettingsForm and says why under it;
- the tour planner keeps the 2D map when 3D is asked for and says that
3D needs WebGL2;
- the PDF route map and the Leaflet vector basemap already fell back and
now report a GPU failure too.
Behind the probe, buildGlMap wraps each constructor (MapViewGL,
JourneyMapGL, the settings preview, the terrain map, the PDF map). A GPU
that claims WebGL2 and still refuses the context is reported through
utils/webgl, which turns the answer to no and tells every mounted map,
so they switch to Leaflet without a reload; any other error is still
thrown. Two new strings, map.webgl2.fallback and map.webgl2.terrain, in
all 27 locales.
The four new photo link tools join the journal's tools: the journey chat
offers them to editors, the trip chat loads them with the journey group and
the home chat keeps none. A journeyPhotos card shows the photos as
thumbnails with the entries they leave and reach, the first one marked as
the cover in a new order, and a caption old to new; a library add shows the
photos once they landed. A linked photo comes off again, an unlinked one
goes back, moved photos go back and a caption is written back, each while
nothing changed since. A new order of an entry's photos and a library add
have no way back yet. The journal prompt names the photo tools.
The update card silently left out the tags and the pros and cons it was
about to write, so the user approved a change the card did not show. An
entry now shows its first tags and how its verdict leans, and an update
names the tags, each side of the verdict and the coordinates it changes.
A new order of the entries is drawn as the entries that move, in their new
places. A changed entry is written back from what the tool answered it
replaced, and a reorder is taken back to the order before, each while
nothing changed since. A Dawarich stay taken in as an entry gets the
entry card; it has no way back, since deleting the entry would leave the
stay marked as taken in.