- 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.
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.
Brings the native apps branch up to dev (NestJS-only server, MikroORM
repositories, tracked sessions, per-domain locales, the lint ratchets) and
ports its new code onto that architecture:
- server: /api/auth/native moves onto injected services. The session is
minted by the auth domain's new LoginSessionService (login bookkeeping and
SessionsService.issue in one transaction), the one-time codes live behind a
NativeHandoffStore port with a capped in-memory implementation that drops
expired codes on write instead of running a timer, both routes declare
response contracts, and the exchange answers { success: true }. Unit and
e2e suites run on createTestModule and the ORM factories.
- shared: native strings for az, et, sk and th, native wired into every
locale, the address placeholder as a full URL.
- client: the account and export changes go into dev's shared hooks
(useAccountSettings, useTripExport), the native server card is a
desktop/phone pair on useNativeServer, the hand-off call moves out of the
page hook into native/, best-effort native failures go through
reportError instead of the console, and the address screen is known to
knip and lint:i18n-keys.
- package-lock.json regenerated on Linux.
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.
GHSA-jrc7-96c5-q579 covers every maplibre-gl up to 6.4.0 and has no 5.x
release, so npm audit kept flagging the patched 5.24.0. Moving to 6.15.0
retires the local backport: patch-maplibre.mjs and its postinstall,
predev and prebuild hooks go, along with the Dockerfile copy and its
format-baseline entry. 6.x sanitizes attributions against an allow list.
What the major changes for TREK:
- ESM only, no default export: the engine module imports the namespace
and hands that out as its default, so every caller stays as it was.
- The worker is no longer inlined, and inside a bundle MapLibre cannot
find it next to its own module. The engine sets it from Vite's
?worker&url build before any map, the Leaflet bridge's included.
- WebGL2 only: the probe asks for webgl2 alone, so a WebGL1-only browser
gets the raster basemap instead of a GPUInitializationError.
- 6.9 shapes right-to-left text itself and deprecated setRTLTextPlugin;
the old plugin would replace that and could only run through eval in
the module worker, so MapLibre no longer registers it. Mapbox does.
The lockfile moves maplibre-gl and its own dependencies only; the
Leaflet bridge already allows ^6.0.0 as its peer.
Every style now has four covers and five double spreads, 108 in all, and the
spreads of each style take different photo counts, from a words-only page to
eight photos, so a book can be laid on them without an empty frame. The five
new styles come with their own themes: Riso (overprinted spot inks), Magazine
(a travel magazine), Retro travel (vintage posters), Midnight (a dark film
look) and Field notes (a traveller's notebook). Each new style has a cover
composer for the design engine and a recipe for the assistant, and its chip,
theme and template names are translated into every language.
The travel tab is four tabs now, figures, maps, countries and marks, and
offers only the newer looks, each dressed in the book's theme and linked to
it. The looks books already hold still draw on their pages exactly as
before; they are just no longer offered.
- Figures: a summary as a poster, a ticket, tiles, a road sign, an
odometer, a handwritten note, a pencil sketch or a band, and each figure
on its own as a medal, on odometer drums or ringed by hand.
- Maps: the route with its photographs, its stops numbered, its two ends
marked or on a dark ground, over satellite or relief imagery, a map per
trip, and a map of each country with its stops alone, cut to its shape.
- Countries: each country as a passport stamp, a visa, an airmail
postcard, its flag in cloth, a map pin, a luggage label or a sketch.
- Marks: the date, the day, the distance, the coordinates and the flag as
stamps, tags, pills, ribbons, die-cut stickers, embroidered patches and
sketches.
The schema gains a summary's and a country's look, seven mark styles and
the numbered and ends stops of a map, all additive, as revision 11.
Also in Studio: the save state is a tile beside who is here, a book a newer
Studio saved is said by an overlay that holds the whole Studio until it
reloads, and the page rail draws the first and the last page beside the
blank inside of their cover. The notifications page and the bell's panel
take the dialogs' look: a head with a tile, cards with an unread dot, and
tools that come forward on hover.
- Dashboard: a browser that names no time zone (Chrome on Windows for a zone
it cannot map) crashed the world clocks and the whole dashboard. One helper
now reads the browser zone and every reader picks its fallback; a stored
zone the browser does not know is left out instead of throwing (#2694).
- Phone: a leg of a multi-leg train or flight showed only its route, and its
sheet showed the first leg's seat. The day plan subtitle is one helper for
desktop and phone now, so a leg names its own train or flight, platform and
seat, translated instead of the hardcoded "Gl."/"Sitz"; the sheet opened
from a leg shows that leg's number, seat, platform and times (#2701). The
PDF export translates platform and seat too.
- Journey list: the year badge named the year a journey was written, not the
years of its trips; a trip over New Year shows both (#2685).
- A place's description and notes are markdown in the planner but printed as
raw syntax in the PDF export and on a shared trip. Both render it now through
the app's sanitized markdown pipeline, raw HTML stays text (#2692).
Closes#2694Closes#2701Closes#2685Closes#2692
Every add in the Studio (a sticker, a shape, an icon, a text, a frame, a line,
a travel element, a photo from the content panel or a drop), a text style and
a grid of frames appended without looking at the page's cap of 90 elements,
which paste and duplicate already kept. The save then failed with a 400 and
every later save of the book failed the same way. Each of them now leaves a
full page as it is and selects nothing, as a paste does.
The opacity slider committed on every key let go on it, so the Tab that moved
the focus there wrote the value it was last moved to, an empty undo step at
best and another colour's opacity at worst. It commits once after it moved,
on the key or the pointer let go or when the focus leaves.
The Theme panel threw away colours, faces and motifs being tried whenever the
book came back from a save by another editor or the assistant, because the
same theme read anew is a new object. The draft now follows the book's theme
only when the theme itself changed.
Paste style wrote the copied fields straight onto the element, although a
pasted look is meant to be the style operation the assistant sends. It now
runs the engine's own restyle (bookElementStyled): a colour or face pasted by
hand takes the element off the theme, so the next theme applied no longer
brings the old colour back; a plain fill replaces the paint of the shape it
lands on instead of staying hidden under it; and a line takes another line's
look without its points, which are its course, not its look.
A background picked by hand in the inspector kept the page's link to the
theme, so the next theme applied set the page back to the theme's colour. The
picker and the background operation now paint through one helper that takes
the link away (withSpreadPaintByHand). Giving one page's background to every
page carries that page's link along, or takes the others' away when it was
painted by hand.
A built-in layout, a book's own layout and laying a page out again from its
entry ignored locks in the Studio: a locked shape was dropped and a locked
photo poured into a slot unlocked, while the assistant's layout operation left
both where they were. All four now lay a page out around its locked elements
through one function (layoutAroundLocks), which the engine's layout operation
uses too.
Fill page and fill spread kept the photo in its group, so the background
picture was selected and dragged with its caption and listed under the group
at the front. It leaves its group as the engine's fill does, and a group left
with one member goes.
The rail lists its sections in the order a book is made: Pages, Content,
Layouts, Elements and Travel, then Theme and Layers set apart below a rule.
Stickers are a tab of Elements beside the icons instead of a rail section of
their own, and Elements has six tabs (text, shapes, lines, frames with the
grids, icons, stickers). The quotation mark moved to the text styles, the
outline rectangle to the basic shapes and the plain rule to the lines. The rail
and every tab row are tab lists the arrow keys walk through, and a long name
wraps instead of being cut. The icon and sticker searches say in the field how
many match, which the sticker library's own head used to.
Every panel on both sides opens on the same head, and every labelled control
uses one row that moves the control under its label when the two do not fit,
so no label sits in a fixed column any more. Number fields, chips, sliders,
tab labels, theme names and travel cards wrap the same way, a country beside
a stop wraps under itself, and an upload cell holds its words to two lines
with the whole label in its tooltip. The heads, shelves and groups are real
headings, so a screen reader can walk the panels by them.
The inspector shows every kind in one order: what the element is and says,
its position and size, its look, its effects, then Arrange. Align and the
stacking buttons are one Arrange group with a button that opens the page in
Layers; its rows sit under their labels, each in two sets that move to a
second line whole. Rarely used groups start folded and open on an element
that already uses them (a turned element, a map held at a zoom, a summary set
in another face), and every group mounts afresh for the next selection. A
sticker's duplicate angle and opacity are gone in favour of the shared
position group, which now has the turn buttons for every kind. With several
elements selected it keeps their angle, opacity and turn, and the mirror when
all of them are stickers, and leaves the frame's numbers to one element. A
photo's fill page and fill spread sit under its crop. With nothing selected
the inspector shows the page background, the book's theme with the way to the
Theme panel, and the page numbers.
Seventeen translation keys nothing has used since before the rebuild (the old
inspector title, the empty spread note, the summary and country list names,
the roads buttons and others) are gone from every language. The broad key
template the old panels used had counted them as reached; the format names,
the empty photo grid messages and the map source hints are now named where the
key check can read them.
Also fixed on the way: the travel stops list shared its class with the
gradient stops and was laid out as sixteen pixel handles; the alignment
buttons were named in raw English; the map's print resolution note joined its
two halves with a dash.
The full screen chat is a modal dialog but left the focus on the trip behind
it. It now takes the focus when shown, keeps Tab inside and hands the focus
back when set aside or closed, through the helper MSheet now shares. The
dropdown panels take the focus onto their first entry, close on Escape and
give the focus back to their button; the history and menu buttons say
whether their panel is open, and the open chat is announced as current.
- The element, spread, document, page operation and card unions carry a
name of their own, so the declarations of every schema that holds them
refer to them instead of spelling them out again. The shared declarations
shrink from 9 to 5.5 MB and the server's typecheck from 4.4 to 2.9 GB, so
the build fits the image's 4 GB again.
- edit_book_page and revise_book_design take add by what the element is,
with an id of its own; its look follows with style by that id in the same
call, every look field described once. A change an assistant card kept
with its look on add still reads and runs.
- restore_book_page states what every element has once, then each kind's
own fields and the type the five travel kinds share, without defaults; the
page is read as a spread again before it goes back.
- The theme links are explained once in the page edit's description, and
style points a text's face, a shape's paint and outline and a photo's crop
look at their one full description.
- The lockup roles live in a module of their own, so the operation schemas
no longer import the lockup engine, whose cycle left the operations unset
in the shared tests.
- The theme switches no longer fall through a comment, the newer document
test follows the current revision, the line inspector names its stroke
keys, a text's anchor reads no physical side, two shapes record the
proportions they are drawn at, and the photo look and line tests follow
the slider and the folded ornaments.
- Fifty more shapes: arches, paper cut-outs (leaves, coral, weed, organic
circles, a wonky star and sun), stamps, a postmark, a rosette, tags and
ticket stubs, sunbursts, ribbons and bands, speech bubbles, drawn arrows,
hearts, stars, photo frames and ornaments (scroll, laurel, fleuron, corner).
The broad ones are photo frames too, and each tile is drawn at its shape's
own proportions, which a new shape is also placed at.
- A cut edge on rectangles, circles, arches and the other broad shapes, painted
or not, and on a photo's cut: zigzag, scallop, a postage stamp's perforation,
wavy, torn and deckle, with a depth in millimetres. Cut in millimetres, so
holes stay round on a wide frame.
- A line element: two to 32 points dragged on the canvas (a handle pulled from
the middle adds one, a double-click removes it), straight or curved, solid,
dashed or dotted, a wave or a zigzag, a hand-drawn wobble, a head on either
end and an ornament in the middle. A shelf of lines and dividers places them.
- The assistant reaches all of it: add and style take the line (its points on
the spread in mm) and the edges, describe_book_page says them, and two new
findings say when a line is too thin to print and when an edge is set on a
shape that takes none. A photo's mask is checked against the shape set
rather than listed again, which keeps the page edit's schema smaller.
- BOOK_SCHEMA_REV goes to 6.
Photos get a look beyond crop, mask and frame style, in Studio and for the
assistant alike:
- border (width and colour, following the mask or the corner radius), a hard
offset shadow and a die-cut cutout edge, all flat vector shapes in print
- zoom 1 to 4 about the focal point and a left to right mirror
- brightness, contrast, saturation and warmth, and a duotone; these are
filters, labelled raster in print in the inspector
- the print resolution of the selected photo in the inspector, with a warning
under 200 dpi, measured from the original
PhotoView moves out of SpreadView into its own file and uses per instance
SVG ids, so a spread drawn twice keeps its clips and filters apart. The style
operation takes the look, describe-spread lists it, the dpi and crop findings
account for zoom and mirror, and a new raster_effect finding notes filters on
a large photo. BOOK_SCHEMA_REV goes to 6.
The demo gate test allows wanderer as the parity harness does, the open tour
store is listed in the store registry, the trip chat card's test reads the
dates and not the question's hidden label, the lightbox leaves the rtl
baseline, and the tours module is formatted.
A third scope, home, next to trip and journey: threads that name neither a
trip nor a journey (the CHECK now only forbids both), reached under
/api/assistant by their owner only. A home turn reads the user's trips
through list_trips into a short card (under way, next, later, behind,
undated), offers every read (a trip's on the id the model names, checked by
the tool with the user's rights) and only the writes that take no trip or
journey (atlas, vacation planner, collections, tour library, settings), all
held as change cards. open_trip_chat answers with a card that goes on in
that trip's own chat with the question waiting in its composer.
On the desktop the window opens on the home chat outside a trip; its head
lists the trips to switch into and a switched trip chat has the way back.
On the phone the home chat opens full screen from the dashboard head and the
dock's More list, and the card opens the trip with its chat. Both run on
useAssistantHome.
The ids of long catalogues (fonts, shapes, patterns) read as a checked string
in the book tools' schemas, so restore_book_page and the page edit stay under
their ceilings. The lightbox takes its reading direction from the language.
Baselines follow the removed avatar upload and the lower counts, the doubled
NEWER constants and three unused imports go, the locale indexes are sorted,
and the place gallery, picture, sheet, inspector weight and paddle tests
follow the merged behaviour.
- The opacity and paint sliders remember the value they were last moved to and commit that when let go, instead of reading the controlled input, which still shows the old value until the parent renders.
- The render test's old-fade shape is star-5; star is not a shape, so the whole fixture failed to parse.
A new element kind, sticker, draws layered vector illustrations from a library in twelve themes, behind its own entry point @trek/shared/stickers. Studio gets a Stickers rail section (themes, localized search, recently used, click or drag to place) and an inspector for colour slots, mirroring, turning and opacity. The assistant gets list_book_assets, add and style of stickers in edit_book_page, stickers in describe_book_page and an overlap warning for a sticker over words. BOOK_SCHEMA_REV goes to 2.
- A shape and a page take a paint: one colour with its own alpha, a linear or
radial gradient of two to six stops, or one of twelve patterns (checks,
gingham, stripes, pinstripes, dots, polka dots, zigzag, waves, grid, scales,
sunburst, confetti). fill, gradient and background keep the colour that
stands for the paint, so an older reader still draws something close, and a
plain colour or the old cover fade is stored in the old fields alone.
- Gradients render as SVG in user space with ids per mounted copy, so the two
halves of a spread in page mode never share a defs id; patterns are drawn
out as geometry clipped to the outline and capped at 2000 primitives.
- The new paint picker has none, solid, gradient and pattern tabs, a stop bar
to click, drag and pull stops off, an angle dial, a radial centre pad and
pattern tiles drawn by the page's own generator.
- Every swatch row gains the book's own colours, colours read from the
page's photos, recent colours and, where it applies, an opacity slider.
- With nothing selected the inspector sets the page background, gives it to
every page of its kind, and lays a photo under the whole page.
- The assistant's background operation takes a paint as well as a colour,
style and add set a shape's paint, and describe_book_page reads both.
- BOOK_SCHEMA_REV goes to 2.
The book catalogue grows from 7 families to 34, all self-hosted through
@fontsource under the SIL OFL 1.1 or the Apache License 2.0: condensed and
poster faces, heavy retro display, elegant display serifs, Montserrat, twelve
scripts and DM Mono. Weights run from 100 to 900, each family offering only
the weights it ships, and the serifs gain real italics.
The catalogue lives in @trek/shared (book-fonts.data.ts) with each family's
roles (display, heading, body, script, mono), weights, italics and letter
width, so the Studio, the findings, the design engine and the assistant read
one list. Page operations and the design engine set a weight a family lacks
at its nearest one, describe_book_page names the role and the weight, and
font_mix no longer counts scripts and mono faces, warning above three.
The Studio gets StudioFontPicker: the current family set in itself, a
searchable list grouped by kind with every family in its own face and the
book's own families first, and a weight slider over the weights the family
has. Faces load only when used; the assistant's page snapshot embeds only the
faces a page draws, the service worker installs only the latin files of the
original seven families, and the faces of an opened book are cached for
offline use. BOOK_SCHEMA_REV goes to 2.
Tests for the picker, the shared image field, staging and saving the picture,
the gallery hook and trigger, the lightbox's dialog, focus and reading
direction, the credit and the enrichment cache; the inspector, phone sheet,
details column, form and wiring tests follow the picture into the editor.
The picker's upload tile and credit line are components of their own, the
heading goes with an empty picker, the gallery's state names its place, and
'No picture' only shows when a picture is being removed.
- Web search: ChatGPT (Responses web_search), Anthropic (web_search server
tool) and OpenRouter (web plugin) search the web themselves when the admin
switch allows it (on as it ships). A search shows as a step ("Searching the
web for ..."), the cited pages as source chips under the answer. Anthropic
answers that searched are replayed with their blocks (new provider state
adapter); the search step never goes back to the model as a call. The
prompt says when to search and that chosen places still go through
search_place.
- Tour writes: assign_tour_track (undo: take that assignment off),
unassign_tour_track, set_tour_activity_excluded (undo: the other switch),
link_tour_packing and unlink_tour_packing (undo from the link before, which
both answers now name) get change cards and their ways back.
FE-COMP-I18NCTX-011a starts all 27 locale chunks at once and does not wait
for them, so the Russian chunk of FE-COMP-I18NCTX-018 queued behind the rest
and the test failed in the whole file (and on CI), though it passed alone.
- A book carries schemaRev. The reader that meets a newer one opens the book
read-only with a reload banner and saves nothing, where it used to drop the
elements it could not read on the next save.
- The server refuses a save, a restore or a tool edit that would write an older
revision over a newer book or one newer than itself (409 book_schema_newer),
on REST and through every book tool alike.
- normalizeBookDocument moves to book-normalize.ts.
- Print waits for photos eagerly and up to 15 s for an export of more than 40
photos, and skips images without a source.
The day word of every language now says where the number goes ("DAY {day}",
"{day}日目", "第{day}天", "{day}. GÜN"), and one helper builds the mark for the
engine, the auto layout, a template and the Travel panel, so Japanese,
Korean and Chinese no longer read "日目 3". A word stored without the
placeholder is followed by the number as before. The panel's own day word is
gone with its only reader.
A country mark carries the name as its words beside the outline and a flag
mark sets it small beside the flag, the same for the engine's value, the
panel's tile, the assistant's add and a template's country mark; a flag in a
template keeps the place its name was drawn in. A flag or country mark asked
for by its own code is named after that country without the journey having
to name one.
Figures, countries, maps and marks placed from the panel are the elements the
assistant adds, built by the shared factories; the panel keeps only their
placement. A trip map no longer carries the whole journey's trail, switching a
map's scope re-cuts it the way the engine does and clears the roads of the old
legs, and switching back to the whole journey restores the trail.
A chat column in the Studio with its own privacy notice, a live page snapshot
through modern-screenshot that leaves out photos of other people and foreign
map tiles, and flushes before every turn and apply.
Every element kind and Studio tool gets an op, the auto layout and its
templates move from the client to shared with a parity fixture, and a pure
design engine turns directives into a finished book with design findings.