Addresses per person and per trip (random base32 token, stored hashed and
encrypted), an intake every transport shares, and a job that reads each
mail once under a lease: filters without AI, machine-readable data always,
one AI request with the person's trips, the server's checks G1 to G9, then
one transaction per mail that adds what passed, keeps the rest in the inbox
with a reason, and notifies the forwarder and co-travellers. Undo within
7 days, manual apply, filing as documents, a prefilled form and a nightly
retention complete it; on a managed install the operator's switch decides.
mail_booking_added tells the forwarder (with keep or undo) and co-travellers
of a shared trip; mail_booking_pending points at the inbox. Both are listed
only while the mail import is on, with texts in all 28 locales.
registerResponseAction lets a domain name what a yes/no tap does without
this module importing it.
MailReadService sends one mail as one document: its subject, body and up to
three attachments inside data tags the prompt calls untrusted, with the
person's trips under references made up for the call. Answers keep only
offered trip references and known field names. BookingImportService reads
a mail's machine-readable parts and maps an AI reading to booking items.
Five tables carry the mail import: forwarding addresses (token hash plus the
encrypted token), a person's switches, the readers' cursor and lease, every
mail as it arrived, and each booking read from it with its checks and fate.
Repositories read the trips a booking may go to by column, so the import
weighs a booking without reaching into five other domains.
The Ollama router catches a failed model call and answered an empty list, which
the outcome read as a document without bookings. It now returns the outcome with
the list; failureOutcome and foundOutcome move to llm-outcome.ts so the router and
the service share them without importing each other.
The self-hosted import reads its catch-all mailbox over IMAP; imapflow is the
client the transport is written against. The lockfile follows with the install.
walkMail() reads a forwarded mail for the import: the envelope apart, a body
without From, To, Cc or forwarding header blocks, message/rfc822 and quoted
forwards unwrapped up to depth 10, and the PDF, pkpass, ics, JPEG, PNG and WebP
attachments whose bytes match their type. Nothing is rendered or fetched.
Zod contracts for the forwarding address, the person's switches, messages,
the bookings read from them, the inbox and every action on an entry, the upload,
the webhook and the admin status card. One reason enum names why a mail or booking
waits, mapped from every AI outcome; mailInbox:changed and mailInbox:settings are
registered as user-scoped events.
LlmParseService.parse() now says how a reading ended (ok, empty, no_text,
quota_exhausted, ai_disabled, user_cap, provider_error, too_large). The clients throw
LlmRequestError with status and body code, so the TREK Cloud gate's 429 QUOTA_SPENT
and HARD_CAP read as a spent allowance. The preview reports the outcome per file and
the assistant reads its empty reason from it rather than matching warning strings.
confirm() now answers { created, failed: [{ index, reason }] } instead of logging
and dropping the items it could not write, and checks the trip and reservation_edit
itself so the import route and the assistant get one answer. An item with a booking
reference records it as its origin (sync off) and a repeat import is a duplicate.
CreateReservationData and insertReservation take an optional external_source,
external_id and sync_enabled, so an importer can record where a booking came from;
without them every write keeps the column defaults. Client bodies on REST and the
plugin RPC have the origin stripped, so no caller can pose as an import or a link.
getReservationWithJoins now takes the trip and reads nothing outside it.
Reading the bridge's MaplibreGL export threw under the test mocks, which
define only the factory, so every mocked basemap counted as an unloaded
chunk. The guard now patches the class of the layer being attached, and
only a class that has _transitionEnd itself, so stand-in layers are left
alone.
The resize guard reached for the bridge's MaplibreGL class, which the test
mocks of the bridge do not provide, so every mocked basemap counted as an
unloaded chunk. The guard now only applies where the class exists. The
mini map hides place names through VectorBasemap's own hideLabelLayers
(a hideLabels prop) instead of a second copy of that logic, and the route
test no longer uses Array.prototype.at, which the client's lib target lacks.
The live flight card shows a small real map behind the pass: the great
circle between the airports, solid where flown and dotted ahead, the
aircraft on the line turned along the route. The route is framed in the
free middle, the map fades out under the airports and times laid over it,
and its place names are hidden. Data and map credits sit on the map as its
hover text instead of a footer line.
The card head now follows the booking dialog: a tinted band with pills for
the state, what comes next and the delay; the numbers of the moment use the
dialog's fields on the desktop and the sheet's stat boxes on the phone; both
times sit on equal tiles in one line. Without live data the card, the strips
and the plan line show nothing, and the pulsing dots are gone.
Also fixes the failed dev run: unit symbols and the leg position are listed
as NOT_PLURAL, the aircraft label uses logical sides, the flight store is in
the test reset registry, the sync loop stops once no flight lies ahead (no
endless timers under fake timers), and timeCell and widgetFlight drop below
the max-params and complexity limits. Guards the GL bridge's resize frame
against a layer removed before it runs, and keeps the booking code pill as
tall as its neighbours.
The transport page explains what TREK follows, where it shows and what open
data cannot give; the environment page documents the switch and what leaves
the server.
Android lint refuses a composition reading the Configuration through
LocalContext. The arc's mirroring now follows the locale's layout direction,
which is what the widget's language decides anyway.
A new Flight widget on iOS (small to large and the lock screen) and Android
shows the trip's next flight or the one in the air: both airports, the arc
with the aircraft, a countdown the system keeps running, and the delay. It
follows the feed's phase, so a late departure is not flown along the arc.
Around a flight the travel day's Live Activity becomes a boarding pass with
a running bar and countdown, in the Dynamic Island too.
The booking dialog and the phone's transport sheet show a flight's moment,
both airports with planned and expected times, the route as an arc with the
aircraft on it and the numbers of the flight; booking cards and day plan
rows get a one-line state with a progress bar. The trip map draws the
aircraft in the air on Leaflet and GL, and the admin settings carry the
switch. Translated into every language.
A new flight-live domain polls the open feeds adsb.lol and adsb.fi (no key,
no cost; adsb.fi only on self-hosted installs, its terms are non-commercial)
for every booked flight leg in its watch window, derives phase, take-off,
landing, delay and an arrival estimate, and pushes changes as flight:live.
GET /api/trips/:tripId/flights/live, the MCP tool get_trip_flight_status,
notifications for take-off, landing and delays, and the admin switch
flight_live_enabled (FLIGHT_LIVE_ENABLED wins) come with it.
"Vorhandene verknüpfen" broke onto two lines in the narrow half of the
booking dialog. The labels now truncate instead of wrapping, and the German
label is the shorter "Datei verknüpfen", matching "Datei anhängen".
The whole-list sharing runs on usePackingCategorySharing in the packing panel
and the phone tab, its sheet is markup fed by the tab. The trips service reads
a stored costs_mode as one of TripsCostsMode, which the export check wants used.
feeds and trips import the calendar query DTO, so it lives in
calendar.contract.ts rather than as calendar's internal dto file. The places
controller tests pass the KML service in every call, and the revisit pill
test builds a whole list.
Inside the trip settings, Escape and the backdrop no longer close the dialog
while the date question is up, and the embedded form does not load the
members it does not show. Every trip open is timed afresh for the splash.
The tests follow the cover image card, the danger button, the delayed splash
and the open and download mocks.
oidc_auto_redirect is managed-locked in the registry too, costs_mode is read
as the mode it is, the days of a dated trip are read through the factories,
and the dashboard's country order moves out of the atlas service into a
module of its own, so the service keeps its size.
New warnings fixed at their source (refs written in effects, typed tile
options, no stray imports, smaller functions and components), the payer and
split heads hold their switch instead of letting it overhang, the baselines
lowered where the round removed warnings, and the tests updated to the
solo trip, the shares view and the revisit status.
The trip settings sheet opens on a list of its areas, each a page of its own
with a way back, and the journal there is drawn in the phone's own cards. A
new packing list is typed into a card with its add and cancel, selecting
places keeps its way out in view with the actions as icons below, and the
journey's satellite switch sits in its bar beside the menu.
Edit trip is an area of the settings dialog, beside People, Sharing and
Journal, instead of a second dialog. The side column names the areas only, the
dialog keeps one height, and every area uses the same cards, fields and
buttons; the trip form on its own takes the same head, cards and buttons. A
trip that loads quickly opens without the splash on the desktop too.
The trips entity takes costs_mode as the generator writes it, the plugin
output contract publishes it, and the hand-built test schemas carry it. The
new MCP fields come from @trek/shared, the link tool tests join the collab
file that already mocks the database, oidc_auto_redirect is the operator's
on a managed install, and the tests follow the KML service, the dated trip
days and the zod pipe.
The transport sheet's row wrapped once a ticket and the wallet were there.
Its actions are icons now, named by their labels, and the attachments sit in
their own rows with a download, like the transports list. The wallet is a
black circle, beside the close button in the booking editor. A failed
download on the phone now says so.
The Google link carried no mode, so a hike opened as a drive, and a stop on
the equator or the prime meridian was dropped. CoMaps routes a start and an
end only; a tour with stops between would reach it as unrouted pins.
With a category left out the total card still counted every expense, and in
the table view the only way back was to switch views; the chip now brings
everything back. The phone says what is left out once, under the total.
Linking a receipt offers pictures and PDFs only, like the upload.
The footer wrapped once a place had a day, a website and a collection. Now
only adding to the day, navigation and edit carry words, website and
collection are icons, and removing from the day and delete sit behind one
"…". The words drop out when the panel is narrow (a tablet) instead of the
row wrapping. Folding is kept for the place on show only (#3039).
DialogIconButton is the square footer button DeleteButton already was, for
any action named by its tooltip. A "…" menu near the bottom of the window
used to be pushed up over its own button; it now opens above it.
Safari before 16 has no AbortSignal.timeout, which cost the whole export; a
controller and a timer replace it. Photos decode at twice the thumbnail
height and three at a time, so a long trip no longer ends the web view.
The list menu on desktop and phone gets "Sharing for Whole List": pick
Shared, Personal or Shared with people once and every item of yours in
that list moves there through the existing per-item sharing write. Items
other people added stay as they are, and the dialog says so up front.
Each attached file row now has a download icon next to its name, on the
desktop booking cards and detail dialog and on the phone's bookings and
transports cards. On the phone the file rows move out of the card body
button so open and download are two real sibling buttons.
The place and endpoint reads behind the passport tile now carry the last day
of each started trip, and getTravelStats orders the countries by their latest
visit, with countries only marked by hand after them. The country set is
unchanged; repository, parity and service tests pin the order.
A new app setting oidc_auto_redirect (default on) is part of the admin form
and the public app config. Off, an instance without password login no longer
sends visitors straight to the IdP: the login page shows its SSO button,
also while the cached config is read. Admin toggle on desktop and phone.
Both feed routes and the one-off export.ics take ?days=0, validated by a
shared query contract, and then skip the all-day "Day N" event per day; the
default is unchanged. The subscribe dialog gets a Day overview events switch
that adds the parameter to its links, export_trip_ics takes dayEvents and the
feed tools explain the parameter.
list_collab_links, create_collab_link, update_collab_link and
delete_collab_link run the Links panel's REST routes as tools: the shared
body contracts, trip access, collab_edit on writes and the collab🔗*
broadcasts, behind the links sub-feature flag. Unit, e2e and broadcast
parity cases cover them; the wiki lists the tools.
dashboard_timezones joins the display-preference allow-list as a list of
IANA zone ids, each checked with Intl like the widget does, without repeats
and capped at 24. The write goes through saveMany, so open tabs refresh, and
both tool descriptions name the widget's fallback zones.
persistence.data.accessModes and persistence.uploads.accessModes default to
ReadWriteOnce, so existing installs render the same claims. ReadWriteMany lets a
backup pod mount the volumes; values.yaml and the docs warn that SQLite allows
only one writer of travel.db. CI renders the override too.
The tour detail gets a Maps button beside Navigation that hands the whole route
to the apps that follow one, in the tour's own mode (walking or cycling). The
stops are spread evenly along the line and capped at ten, the most a Google
Maps directions link opens. Ski, paddle and horse tours are not offered, since
a map app would route them by road; the GPX export stays the way for those.
On the road a stop's picture is often what decides whether it is worth the
detour. The phone's stop sheet shows the place's picture full width under its
name and the desktop stop dialog shows it in place of the kind tile; both open
the place's photos on tap. The picture is the place's own image, else the
provider photo for its Google or OSM id through the shared photo cache.
The journey map gets the planner's round satellite switch in its top corner,
on the desktop aside, the phone views and the public page. Leaflet takes the
vector basemap off while the imagery shows and reattaches it on the way back;
the GL renderer slides the imagery under the journey's own layers. The choice
is per view and opens on the map, so no setting is read or written.