mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-13 15:51:00 +00:00
cae04e83dd035892dabc7bb65f6bf892fa0162ac
117 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d7c4324afc |
chore(capacitor): base files for capacitor (#2020)
* 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>
|
||
|
|
50ea9a74c9 |
fix(mcp): keep budget categories to the Costs keys, and say what a journal entry's visibility does
An assistant writing expenses through the MCP was told "e.g. Accommodation, Food, Transport" and stored exactly that, so "Food" and "food" sat side by side in the MCP totals while the Costs tab already read both as one. A typed category in any case, or a common name like Hotel or Flight, is now stored as its fixed key on every path (REST, MCP, linked booking costs), the totals the MCP reads group by that key, and the tools name the real keys. The client reads categories through the same shared mapping. Any other text is still kept as given. The journal tools said that a private entry stays off the share link. It never did: the share link shows every entry that is not a draft, whatever its visibility, and the web editor has no visibility at all. The tools now say so and point to is_draft, which is what keeps an entry to the contributors. The wiki explains why the location button needs HTTPS and how to run TREK behind an HTTPS tunnel while still using it over plain HTTP at home, and that Android's photo picker drops a photo's location while Immich and Synology keep it. |
||
|
|
51af575c9a |
fix(map): keep places and routes on one world, and start new installs with Journey and Collections
A right-click, click or drag on a repeated world copy reported the longitude it has on that copy (274.9 for -85.1). The place was saved there, the geocoder found nothing, and a route to a place on the real world ran across the whole map (#2976). Longitudes are now brought back onto [-180, 180] where the map hands them out (Leaflet and GL), where they are stored (places, booking endpoints, collection places, so REST, MCP, plugin RPC and importers alike) and before reverse geocoding. A migration moves the ones already stored. Leaflet draws a layer at its literal longitude only, so a trip from Los Angeles to Auckland had its pins at opposite ends of the world while the flight ran off the edge to the next copy (#2975). A trip that crosses the antimeridian is now drawn in one longitude frame, seamed in the widest stretch it leaves empty, so pins, routes, flights and every overlay lie on one copy and the map opens across the Pacific. Every other trip has no frame and draws exactly as before. The GL day fit also takes the short way. A new install now starts with Journey and Collections switched on, next to the trip addons (all but Road trip), Atlas, Vacay and the Naver import that already were. Existing installs keep their addons as they are. Closes #2975 Closes #2976 |
||
|
|
182ed56fa4 |
places: let an admin share the details users typed with the TREK Places API
Every shareable field of a place now records where its value came from: typed by a user, filled by a provider, written by the assistant, a plugin or an import. Only values a user typed are ever sent, and only after an admin agreed to the current terms for the whole installation, field by field. The new place-contrib domain keeps the decision in app settings, queues values in an outbox a cron job sends from, logs what the service made of each one and can withdraw everything this installation sent. Development, test and demo instances, an own TREK_PLACES_URL, TREK_SERVICES=off and TREK_PLACES_CONTRIBUTE=off never send anything. Index results carry the community version, so the details caches refresh when it changes. |
||
|
|
80183bc986 |
feat(server): relay Valhalla routing to the TREK services with the installation key
POST /api/routing/valhalla/route, /trace_attributes and /height ask the Valhalla at TREK_VALHALLA_URL (default https://valhalla.liketrek.com) for a signed-in user. The body is held to the shared contract (64 KB, at most 20 locations), every question counts against 120 a minute per user, and the installation key goes along only to a host TREK_SERVICES_HOSTS lists. Valhalla's answer is passed on as it came, refusals included. The road trip router asks the same relay when the routing choice lands on the TREK services, so the MCP road trip, city walk and hike tools share its limits, and hands a question it cannot answer to FOSSGIS. |
||
|
|
f19f0a33b2 |
feat(shared): add the service identity contract and the allowance texts of the TREK API card
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. |
||
|
|
cad7d54e83 |
feat(shared): the demo account's addresses live in one place for both sides
The client is about to lock settings sections for the shared demo account, and it has to ask the same question the server does. DEMO_ACCOUNT_EMAILS and isDemoAccountEmail now sit in @trek/shared, and the server's DEMO_EMAILS is built from them instead of spelling the two addresses a second time. |
||
|
|
6a77496438 |
feat(assistant): an attached file stays with its conversation
A file attached in the trip chat lived only as a buffer for the one request that read it, so the assistant could not look at it again and nothing could keep it in Files. Now its bytes go to storage before the turn writes anything (under the trip files' category, which is never served by path) and a row in the new assistant_attachments table keeps its name, type, size, hash and the text TREK read out of it, written in the turn's opening transaction. The message's attachment part carries the row's id; old messages without one still read. The text is read the way the AI import reads it, through a new public LlmParseService.readText, and a file whose text cannot be read is kept all the same. A storage that refuses the bytes refuses the turn before any row exists. Deleting a thread takes its files along, and the nightly retention sweep drops files older than seven days and bytes no row names any more. |
||
|
|
73fdbdc0c3 |
feat(studio): Auto layout lays the book on the designed pages
"The whole book" now lays the book on the designed templates through autoDesignedBook, in the book's own page format and theme (a recoloured one of its own included), its title kept and every page holding a locked element kept; each press takes a new seed, so a second press gives another book, one undo away as before. "This spread" lays the page on another design than the one on screen and its neighbours, its id kept, and now works on the cover, the summary and the countries too, so the menu offers it wherever the auto layout knows the page. Loose gallery photos reach the layout with the day they were taken, which it shares them out by. The menu's two hints say what the button now does, in every language. Nothing here places the classic summary, countries or marks any more. |
||
|
|
0879b5aecc |
feat(shared): the Studio's auto layout entry points
auto/auto-designed.ts holds what the Studio's Auto layout menu calls. autoDesignedBook lays the whole book on the designed templates in the book's own theme object (a recoloured one of its own included, the calm fabric-classic theme for a book without one), over the stored book, its page format kept and every page holding a locked element kept; another seed lays another book. autoRelayoutSpread lays one page again around its locks, its id kept: an entry's page on the best template other than the one on screen and its neighbours', avoiding one that would cut its story where it can; the cover on the next cover template of its archetype and then of its kin; the summary and the countries in their next look, an old classic summary included. Pages the engine did not make are left alone, as before. autoRelayoutKind says which pages those are. designBook's templated path is split so the relayout reads the same setting (templatedSetting) as the whole book, and the page layer is shared (layEntryPage); the parity digests are unchanged by it. buildBook and relayoutSpread stay exported and unchanged. |
||
|
|
6254510e0f |
refactor(shared): the travel looks are built in shared
The factories behind the Travel panel's cards (a summary as a poster, a ticket or a sign, a figure on a medal or an odometer, a map of the journey or of one country, a mark as a stamp or a tag, a country as a stamp, a visa or a postcard, each dressed in the book's theme) move to book/travel/book-travel-looks.ts, so the auto layout can build its framing pages from the same looks. Where an element lands stays the caller's: the panel still centres it on the spread on screen, through the same wrapper and the same API, and a designed page names its frame. A mark can now also take an entry's value rather than the journey's. |
||
|
|
84e18e4954 |
fix(studio): paste style, the page background, layouts and fill page keep the engine's rules
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. |
||
|
|
090e754077 |
fix(studio): a restore brings in no photo from outside the journey's gallery, letter fills counted
restore_book_layout took any layout the caller sent, so a layout naming a photo of another journey was kept, and laying a page out on it put that photo into this book while place_photo refused the same id. The layout is now held to the rule a page restore states: every photo it names, in a frame, on a map marker or in the letters of a text, is in the gallery or on a page of the book already, or the call is refused with photo_not_in_gallery. restore_book_page and the design apply read the photos of a page in one place now (book-photo-refs.ts), which counts the photo a text is filled with. A page put back with letters filled with a photo from outside the gallery is refused as a frame of it would be, and a designed page whose letter fill photo left the gallery since is skipped as photo_gone; on a new book's cover the letters keep their colour instead, as its frame is emptied. A saved layout keeps no photo of the page it was taken from in its letters, as it keeps none in its frames, so laying another page out on it no longer carries that photo along. A layout kept before this fills such letters with the first photo of the page it is laid on, or leaves them in their colour. The style operation checks a letter fill photo only when the change sets the fill, so a title whose photo left the gallery can still be resized or recoloured, as the text operation already allowed. |
||
|
|
5651c11803 |
feat(assistant): the book designer works from a brief, with archetype covers, lively spreads and stickers
- design_book takes a brief (energy, decoration, collages, theme), a cover archetype and a cover lockup, and pages.overrides that give single pages of the plan another beat or layout. Directives written before parse as they did, and a design asked for none of it is composed exactly as before. - The engine composes the seven cover archetypes from the book's own photos, words, theme faces and colours, lockups, shapes and stickers, varied by the seed: spine band, poster pop, cut-out, illustrated postcard, grid and badge, minimal serif and stamp collage. A title the directives give a display tier is held to it, and the notes say when a band, a badge or a stamp holds it smaller or a cover leaves a layout out. - A lively or loud book turns two in five hero and grid spreads into collages, script overlays, badge grids, stamp rows or two-tone splits, the first of them always, turns and frames the prints of its other pages, sets its cover title larger and takes one or two, or three to six, stickers of its theme's motifs a spread, never over words, a photo's detail or the fold. A calm book asked for collages gets them and nothing else. - Every turn the engine gives a print or a sticker stays within what the brief's energy takes, a calm book's within its theme's own, so a design is never judged against its own brief. - New checks name a flat cover, a page without a lead, colours or faces off the theme and, for a design made with a brief, a book livelier or calmer than it asked, on the pages the design made and on a page revised in the draft. Page edits answer with the art checks too, and the assistant's cards name them in every language. - list_book_assets lists the shapes, faces, lockups, themes and patterns beside the stickers, and the Studio's shape groups and photo frames are one list the assistant reads too. - The Studio prompt gains an index of themes, archetypes and faces and JSON recipes of the elements to add and style for each cover and three lively spreads, and a turn that designs the book may preview sixteen pages, still three images a request; a viewer is told of none of it. |
||
|
|
49f7a447d3 |
feat(studio): decorated templates, 21 covers and 16 spreads laid on any page in the book theme
A decorated template is a whole designed page kept as data: full document elements in fractions of the page they were drawn on, with roles for the photos and words a page brings, links to the theme for its colours and faces, groups for its lockups (a title with its date line, a badge with its words, a stamp with its postmark) and an archetype with tags. Seven cover archetypes in three variants each and sixteen double spreads ship in shared/src/book/templates/data. Laying a page out on one pours its photos into the photo roles in order and its words into the title, subtitle, date, story and caption roles, takes the words it has none for from the book or the journal entry, sets the template in the book theme, scales it to the page format with its bleeds on the bleed of that page, and parks what has no room, a date line the date mark was not made of included. A text set on a colour is made to read there at the size it was fitted to, on a band of ink or paper when neither reads. A mark set as text, such as a large quotation mark, is design to every layout and never taken for the words of a page. A fitted text keeps the size it was drawn at as its ceiling, so a shorter title grows back, and every fitted text is now solved with eight percent of headroom on letter width: set in the browser, the bold condensed faces and several scripts run that much wider than the letter measure, and a fit at exactly the measure wrapped its last word out of the frame. The layout operation of edit_book_page and revise_book_design takes the new ids as a checked string and keeps the name of a locked group, list_book_layouts lists them with archetype, tags and roles, and the design prompt names them. A sibling extract script reads a page drawn in the Studio back into the format, roles, tints and groups kept. |
||
|
|
ea5447056b | feat(studio): the seven cover archetypes a decorated book starts from | ||
|
|
75d0593198 |
feat(studio): text effects, fit to frame and 24 lockups in a Text styles shelf
- A text can be cased (capitals, lower case, small caps), placed at the top, middle or foot of its frame, outlined, given a hard shadow or a marker band, bent on an arc or round a circle, filled with a gallery photo or left hollow, and shrunk to fit its frame. Every effect prints as vector: copies and strokes for HTML type, textPath and a clip over an <image> for SVG. - Fitting is solved when the text is edited, in the Studio and in the edit engine, and stored; the chosen size is kept as the ceiling. - Text sizes reach 400 pt and tracking 1.5 em. - The Elements panel opens on a Text tab: the plain styles and 24 lockups (titles, badges and stamps, chapter marks, quotes, captions, places), each drawn with the book's own title and placed as one group. - The assistant sets every effect through style and add, places a lockup with add_lockup, reads the effects in describe_book_page and gets two new findings (curve_overflow, effect_heavy) beside photo-fill and band checks. - Book format revision 6. |
||
|
|
82b8eb1067 | feat(studio): book themes with palette, font pairing and motif set | ||
|
|
ce9c94a77b |
feat(studio): a second shelf of shapes, cut edges and a drawn line with heads and 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. |
||
|
|
7a7876b9f4 |
feat(studio): photo look with border, hard shadow, zoom, mirror, adjustments, duotone and cutout
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. |
||
|
|
cd60d4d859 |
feat(tours): assistant takes found places into saved tours and route files from the chat
P6: insert_tour_stop and remove_tour_stop take a place into a saved hike or city walk as one stop and out again, each the other's undo, with change cards in the chat. The planner's insertion rule (poiInsertion) moved to shared so the planner and the server place a stop the same way. "+ Tour" on a found place holds it as a stop of the saved tour open in the planner. P7: GPX, KML and KMZ files attached in the chat are read into their tracks and each is held as a change that takes it in as a hike through import_tour_gpx, undoable per track; the GPX text stays on the held call. |
||
|
|
454fc1a5f4 |
feat(tours): round hikes, other ways of a leg and wanderer trails through the assistant
The assistant can now search a round hike of about a wished length from one point and the router's other ways for one leg of a hike, with the planner's own search, and propose each one as a hike card to compare and take. It can also search the user's wanderer trails, preview one as a card, import it and check a wanderer tour against its source. The round search, its loop geometry, the test whether two lines are one way, the other ways among a router's offers and the pins that hold a chosen way move to @trek/shared; the planner and the road trip use them from there. |
||
|
|
767d91146a |
feat(tours): the assistant edits stations, reverses and closes tours, sets breaks, colour and copies, and writes ski and paddle plans
City walk changes take edit, reverse and close_loop; edits alone keep the saved way and never ask the router. update_tour turns a hike round or closes its loop with reshape. New MCP tools mirror the Tours tab's REST writes and reads: update_tour_details, set_tour_route_color, copy_tour, classify_ski_track, set_paddle_plan, decide_paddle_obstacles, restore_recorded_track, get_tour_trail_details and find_horse_routes, with change cards, undo where the answer keeps the values before, and broadcast parity cases. |
||
|
|
d8213bfa42 |
feat(studio): groups, a layers panel, marquee, nudge, multi-resize, align and paste style
Elements can be grouped on a page and picked, moved, resized, aligned and restacked as one. A layers panel lists the page front to back with lock, visibility, rename and drag to restack. The sheet gains marquee selection, arrow-key nudge, one box that resizes a multi-selection with Shift holding the proportions, and the design-tool shortcuts. The inspector lines a selection up and spaces it out, and copies and pastes a style. The assistant gets the same through the group, ungroup, name and hide operations, and describe_book_page lists the groups. The format gains the optional group, name and hidden fields and a spread's group names, so BOOK_SCHEMA_REV goes to 2. |
||
|
|
8c6f5deab2 |
feat(studio): a sticker library of 148 hand-drawn illustrations, placed, recoloured and searched by people and the assistant
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. |
||
|
|
23c446b7e9 |
feat(studio): paint shapes and pages with gradients, alpha and twelve patterns
- 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. |
||
|
|
fabefed39f |
feat(studio): 34 typefaces with real weights and italics, a categorised font picker
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. |
||
|
|
6e43490ba0 |
feat(assistant): plan and save every tour type, bike tours, runs, paddle and ski tours included
The assistant can now plan and save every type the Tours tab plans. A bike tour or a run is proposed and created through the hike's own plan and save (propose_tour, create_tour), routed on the server with the costing of its profile, now kept once in shared and read by the planner as well. A paddle or ski tour planned with plan_paddle_route or plan_ski_route is saved with create_paddle_tour or create_ski_tour, and "Plan this" takes those plans, bike tours and runs too. Every create has its card and its way back. |
||
|
|
30382dc366 |
feat(studio): a book saved by a newer Studio opens read-only instead of losing what it cannot read
- 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. |
||
|
|
4570ace78c |
feat(journey): the shared book engine lays out, edits and designs whole books
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. |
||
|
|
984ee67517 | feat(assistant): journal entry cards, and markdown stripped in shared | ||
|
|
3967ca2790 | feat(assistant): a card framework every addon package plugs into | ||
|
|
3c0f324266 |
feat(tours): plan, create and change hikes on the server, for the assistant too
The hiking costing moves to shared, so the planner and the server ask the router the same way. The road trip router learns to plan a hike on foot with that costing (Valhalla only: OSRM knows no trail difficulty), to read heights along a line in chunks and to read the OSM ways a line follows, all through its one paced queue. A hike router builds on it: the way, its heights, distance, climb and DIN walking time, and the SAC grades of its stretches from the trail grade service, each part best effort. New MCP tools in HikePlanMcp, in the assistant's tours group: - propose_hike plans a hike without saving it. - create_hike saves a confirmed hike through the planner's own creation; a router that does not answer leaves it pending for the planner, a way that cannot be planned is refused and nothing is saved. - update_tour changes a saved hike against its revision, keeping linked stations and refusing what would silently drop drawn legs or stages. - import_tour_gpx takes a GPX file's text in as tours, like the import. Same permission checks as REST, broadcasts from the services. In the assistant a new hike or city walk is an "Add tour" card with its route and difficulty, undone through delete_place while the tour is untouched, and a hike being planned shows as a live step. |
||
|
|
b64da26651 |
feat(assistant): trip assistant chat with your own AI connection
Adds an in-app assistant that reads and plans the open trip. Users connect their own provider (Sign in with ChatGPT, or an Anthropic, OpenAI, Gemini or OpenRouter key) or use the one the admin set up, including a custom OpenAI-compatible server. It is an addon and only shows while a connection is usable. The chat runs on the existing MCP tools, pinned to the trip. Every change is held as a change card the user applies or discards, and applied changes can be undone. On the desktop it is a floating window that can be moved and resized; on the phone it is fullscreen. Search results show as place cards that can go onto a day, weather lookups as a strip, proposed places as ghost markers on the map with a before/after switch, and an answer can be shared into the trip chat. Booking files can be dropped in and are read into change cards. |
||
|
|
31859651d7 |
feat(help): rewrite the help center for v5 with a Tours section, new pictures in every language, and fix the tour planner bugs it surfaced
Every help screen checked against the v5 redesign. Tours is documented for the first time, as a screen of its own with twelve screens under it, and notifications get help of their own. All pictures are taken again on a realistic demo trip, with short loops where a still cannot show the motion. The English is plain and every language is written naturally rather than word for word. The switcher shows two levels at most, a screen's overview lists its sub-screens as cards above its guides, each wiki page is linked once, and the help sidebar is wider. Fixes found along the way: Save to Collection keeps a tour's route, a track dropped on the places column opens the Tours import, tour Share on desktop, the unsaved tour draft lives on the account, a route click no longer adds an end point, Trip places leaves the trip's tours out, Escape closes a stage's day menu, confirms before destructive deletes, DWD warnings sharing one id kept apart, a booking's date moves with it, and smaller fixes across notifications, bookings, the day plan, stays, Atlas, Collections, Vacay and the admin screens. |
||
|
|
9b71948497 |
feat(journey): read what a journey map shows beside its entries, and share its plan and recorded days on purpose
GET /api/journeys/:id/map-layers answers the planned places of the linked trips (one per place, with every day the plan puts it on and the entry written about it), the trips' GPX routes while the track switch is on, and the located stays entries were taken over from. It goes through journey_trips, so a trip nobody wrote about yet shows its plan too. Every source is read on its own: one that fails says unavailable and the others still draw. A position that is not one is left out, never moved. GET /api/public/journey/:token/map-layers cuts the same on the server by the link: nothing while the map is not shared, the plan and its routes only with share_plan, the recorded days only with share_recordings and only beside a published entry, stays only of published entries. Migration 046190 adds both flags to journey_share_tokens, off by default, so a journey that is public already shows what it showed before. The share link REST route and the MCP share tool take them alike; get_journey_map_layers reads the layers over MCP on the same access, a route by its size only. |
||
|
|
94c369b913 |
feat(journey): prepare the recorded days of a journey's trips in the background, switched on per journey, with each day's state
Journey Settings get "Prepare recorded days automatically" under GPX Tracks, off by default and set by the owner. With it on, the Dawarich background check also reads the linked trips of the journey for the owner and every editor with a connection of their own, within the same 400 day reach and windows as the trip sync, and only for new and changed stays: a trip the person travels on is not asked twice, and the stays of a trip they are not on are filed under no trip. Nothing is written into the journey; the stays wait at their day, and a stay taken over or left out is never offered again. Under the switch the caller sees when their stays were last fetched, or why not (no connection, no dated trip, background check off, the reason of the last failure beside the state still shown), with a "Fetch now" that the switch also runs when it is turned on. A day of the trips that is not final yet carries a dot on its take-over button, on the phone in the stay sheet, and the tooltip says whether it is still running, was fetched before it ended, was not fetched yet or shows an older state after a failed refresh. The day review says so for today too. Server: migration 046180 adds journeys.auto_prepare_recordings and journey_recording_prep (the last attempt, last success and last error per journey and person). JourneyRecordingPrepService in the journey domain lists the prepared trips and keeps the status; DawarichSyncService walks those trips in the same loop as the person's own and records each journey's outcome. GET /api/journeys/:id/recordings/prep answers the caller's status under a @trek/shared contract, the MCP tool get_journey_recording_prep reads the same, and update_journey takes auto_prepare_recordings like the PUT contract. The column stays out of the plugin output. All 27 locales carry the strings; the Journey, Dawarich and MCP tool wiki pages describe it. |
||
|
|
7c1de6ed82 |
feat(journey): take a recorded day over with its stays, chosen, renamed and joined into drafts or one day report
A journey day now has a list button in its header beside the route, with the number of stays still waiting, and the row of the day's Dawarich stays gets a "Take over day" button; on a phone the journey's More options offers it for the day of the card shown. The dialog reads the day again from the caller's own Dawarich and shows the route and the stays on one map, numbered in the order they were lived and pinned in the colour of the day, beside a list with times, durations, the stays Dawarich only guessed or is unsure about, the ones without a position, the entries the journal has on the day and how many people will see the drafts. When Dawarich does not answer, it says why and shows the stays of the last sync. Open stays start chosen, the same place twice in a row starts joined as one entry under a title and a chosen map position, any two neighbours can be joined or kept apart, a name can be corrected and a stay left out. "Day report" turns every chosen stay into one entry that lists them with their times, at a chosen stay or off the map. The day's route can go along when the journey keeps none yet. Nothing is written before "Take over N drafts", and a blank name or title holds it back with the reason. Stays left out are dismissed and can be restored in the stay list. The take-over writes drafts only, never touches what the journal holds, and is one transaction: every stay is checked to be the caller's and of that day, the entries, the stays they hold and the marked suggestions commit together, and the entries are announced after the commit. A stay the journey holds already is reported instead of written twice, so a retry after a lost answer is harmless; a failed attempt reads the day again and keeps the choices, and a route that fails after the drafts can be kept again alone. Server: migration 046170 adds journey_entry_stays (one row per stay of an entry with its own name, place and times, unique per journey, source, person and visit, gone with its entry), owned by the journey domain through JourneyEntryStaysService. The single stay accept records its stay there too, over REST and MCP alike. DawarichSyncService.refreshDay reads one day's visits and upserts them without flagging anything missing; DawarichDayStaysService lists and settles a day's stays. JourneyDayImportService in journey-recordings serves POST /api/journeys/:id/recordings/:date/stays/review and /import with @trek/shared contracts, and the MCP tools review_journey_day_stays and take_over_journey_day_stays run on the same service and checks. createEntry can write without announcing for a caller that announces after its commit. The desktop dialog and the phone sheet of the route (JRN-01) and of the take-over share one frame each. All 27 locales carry the strings; the Journey and Dawarich wiki pages and the MCP tool list describe it. |
||
|
|
65bb2d95eb |
feat(journey): keep the route recorded on a journey day from Dawarich, previewed first, with its gaps
A day header in the journey timeline now carries the Dawarich mark for an editor whose Dawarich answers (dimmed with the reason while offline). It opens a dialog that names the journey and the day and reads that one local day from the caller's own connection: a sketch of the route, distance, first and last time, pieces, gaps without recording, modes and whether tracks or thinned points answered. The day is cut in the zone Dawarich records in where its answer shows one, else in this device's, and a picker cuts it in any other. Nothing is kept before "Keep route". On a phone the same sheet opens from the journey's More options for the day of the card shown. What is kept is the preview that was seen: the save reads the day again and refuses with recording_changed when its hash moved, and the dialog shows the new version. The same day taken over twice keeps one version and announces nothing; a different version is replaced only on "Replace", never touching an entry or text. A failed read throws before any write, so the kept version stays. Gaps stay gaps: points are split where silence ends elsewhere, and no day is joined to the one before. The dialog says how many other people will see the route; editors may keep their own recording. Kept days show in the day header (distance, whose recording, since when, still running) and on the desktop and phone journey maps in the colour of their day's pins, winning over the live trail of their date, and stay drawn without Dawarich. A trip's GPX track is now drawn dashed as the plan, recorded routes solid. Shared links do not show recorded days. Remove route asks first and works without a connection. Server: migration 046160 adds journey_recorded_days (one row per journey, local day and source, segments as JSON, zone, window, fetch time, hash, importer kept past their account). The journey domain owns the rows through JourneyRecordedDaysService; the new journey-recordings domain takes a day over through DawarichTracksService.fetchDay, which reads past the cache, asks a window wide enough for every zone and refuses with DomainErrors. REST under /api/journeys/:id/recordings (list, preview, PUT and DELETE /:date) with @trek/shared contracts, and MCP list_journey_recorded_days, preview_journey_recorded_day, save_journey_recorded_day and delete_journey_recorded_day on the same service and access. A new user event journey:recording:updated keeps every open map current. JourneyTrack gains an optional kind. All 27 locales carry the strings; the Journey and Dawarich wiki pages and the MCP tool list say what it does. |
||
|
|
6e13f7e655 | fix(tours): keep a city walk on foot across the water, mark a crossing by boat apart from the walking time and save it only once taken | ||
|
|
43353b9394 | feat(tours): read the walking time left of a city walk on a day over MCP, apart from its stays and the whole walk | ||
|
|
c8eb510bd4 |
feat(ski-routing): plan ski tours on the piste graphs of TREK Places
A new Nest domain plans the legs of a ski and winter tour on the runs, lifts, cross-country trails, ski touring and winter paths the Places host builds from OpenStreetMap, one SGR1 graph per ski area. TREK fetches the index once, downloads an area by its id when a planner first plans in it, checks it against the index's SHA-256 and keeps it in data/ski; no clicked point ever leaves the server. A leg whose area is slower than two seconds answers loading, and an older copy stands in while the host is down. The reader decodes the writer's golden file byte for byte, including the two edge flags it added (area_has_run, type_unknown). The search is A* in a worker thread over a profile per discipline (alpine, snowboard, cross-country by technique, touring, snowshoe, winter hike) with the times and penalties of the cost model, up to five snap candidates per point, lift stations as entry points, legs that go on from each other, pieces of a split network stitched by their boundary nodes, other ways by the penalty method, and a failed leg explained by relaxing one rule at a time (difficulty, lift class, technique, riding down, uphill) without ever handing out the relaxed line. POST /api/trips/:tripId/ski/route, GET .../ski/area-near and GET .../ski/network/:areaId sit behind Tours and the trip, with plan_ski_route as their MCP twin. A daily job looks for a new build and sweeps areas unused for 90 days. The admin switch ski_routing_enabled is on wherever TREK Places is. |
||
|
|
c38eb7ebf0 |
feat(tours): estimate the walking time of imported tours
A hike imported from GPX, KML/KMZ or wanderer had no time at all, so several hikes on one day could not be planned against each other. It now gets the walking time the planner times a hike by (DIN 33466), worked out from its own distance, ascent and descent; a run gets its distance at the pace entered. Without heights in the track the time is unknown rather than the time on the flat, and types timed by a router, by a speed of their own or by nothing get no estimate. The estimate is derived on every read and never stored, so tours imported before get it too and no stored or entered time is touched. The rule moved to @trek/shared, where the planner, the client and the server read it. The tour list, the day rows on desktop and phone and the tour detail say it as an estimate, apart from a planned total; list_tours and get_tour_plan carry it as time_estimate and name it as derived. |
||
|
|
2e1dca9fe9 |
feat(tours): let a background check hint at changes of your own wanderer trails
Settings, Integrations, wanderer gains an optional background check of the trails you imported, off until you switch it on. While on, TREK reads the short record of each such trail at most once a day (never its GPX file) and leaves a hint when the trail may have moved on: "may have changed" when only wanderer's change time moved, "changed" when its name or description proves it, and "not checkable" when the trail is gone, no longer yours or carries no change time. Nothing is ever said to be unchanged. A new hint arrives as a notification that opens the tour (a new ?place= link in the planner); the tour's provenance shows it with the source check right beside it, and the card lists all open hints in a dialog, each leading to its tour. The check reads the source in full as before and spends the hint; taking a change in stays the confirmed reimport. No tour is ever changed or deleted. The check only asks through your own connection, only about trails your account imported into trips you can still open, and stops when you switch it off, remove the connection, wanderer refuses the token (until a test passes or a new token is saved) or the admin switches wanderer off; hints and snapshots stay. A run asks at most 25 connections for at most 10 records each, one after another, and an instance that fails (an outage, a rate limit) is left alone for an hour, doubling up to two days. Server: migration 046120 adds the switch and the run bookkeeping to wanderer_connections and the tour_source_hints table (one row per snapshot, for its importer, gone with it). WandererWatchService and an hourly wanderer-watch job on the CronRegistrar live in the tours domain and read through WandererService; GET and PUT /api/integrations/wanderer/watch and GET /api/trips/:tripId/tours/wanderer/sources/:placeId/hint (place_edit, like the check) answer with @trek/shared contracts. A new user notification event, wanderer_source_hint, can be muted per channel. Saving a new token now clears what the last check said about the old one. There is no MCP tool, as for the rest of the personal wanderer connection. All 27 locales carry the strings, and the admin wiki page says what the check does. |
||
|
|
f833a05461 |
feat(tours): point a tour at its trip's packing list
A tour can now point at the packing list of its trip, as a whole or at one of its categories (say "Hiking gear" for a hike), and shows it in its detail. The new Packing list section reads the list live as the user may see it: the backpack tile with the item count on its corner and the category's own colour, how many are packed with a thin progress bar, the first items ticked off or not with their quantity, how many more there are, and a button that opens the trip's Lists tab. Who may change the tour links one, changes it or lets go through a dialog that lists the whole list and every category with its colour and item count, and writes only on its confirm. It is a reference, never a copy: the items stay in Packing, are ticked off and edited there, and each traveller sees in the tour only the items Packing shows them, so an item kept to oneself never shows through a tour. A category that is renamed, cleared or only holds items of others reads as missing, an empty one as empty, a link that could not be read says so with a retry, and while Lists are switched off the link stays, dimmed, and can still be let go. Removing the link, or the tour, leaves the list and every item as they are. Nothing suggests or changes an item by itself. On the server the link is a row of its own in the tours domain (tour_packing_links, migration 046110), one per tour and gone with it. GET, PUT and DELETE /api/trips/:tripId/tours/:placeId/packing-link read it with the trip and change it with place_edit; linking needs the Lists addon and, for a category, an item of it the user can see. The writes answer the tour with its link and tell the trip through tours:changed, so the offline client queues them as writes of the tour and replays them like one. A copied trip keeps its tours' links, a tour saved as new or taken over does not. The MCP tools get_tour_packing, link_tour_packing and unlink_tour_packing run over the same service and checks; the read also hands the tour's facts as context for suggesting what to pack, which only becomes an item through the packing tools. The empty category placeholder now lives in @trek/shared so both sides leave it out alike. All 27 locales carry the new strings, and the wiki describes the link and the tools. |
||
|
|
3fe34061fd |
feat(tours): keep whole tours in collections and take them into trips
A saved tour can now be kept in a collection, apart from any trip, and later made into a tour of another trip. The bookmark button in a tour's footer and in the planner's head (waiting for the save while there are unsaved changes) opens a dialog with every list of the user; a list they may only read stays in it, dimmed with a lock and a tooltip. The list keeps an independent copy: the line, the type, every measure and setting, the waypoints in order with their roles, the origin, and the trip and tour it was saved from as they were called then. Its days, its links to places of the trip and its obstacle decisions stay in the trip, and later changes to the tour do not reach the copy. A collection shows its kept tours as cards above its places: the grade tile with the source badge, measures and the waypoint count as badges, the trip it came from, and who kept it. The owner or an admin of the list can remove one after a question; tours made from it stay. "Add to a trip" shows the kept tour on a map with its facts and numbered waypoints, lists the user's trips (the ones they may not add tours to dimmed), and asks outright whether the waypoints come along. "Line only" keeps the line as saved and adds no waypoints; a tour kept without a line can only bring them. The new tour has an id of its own, no days and no new places, and names the trip and tour it was saved from; the dialog then offers to open that trip. Neither the collection nor the original tour changes. On the server the kept tours are a table of their own in the tours domain (collection_tours, migration 046100), each row written whole once. Every copy, the in-trip "save as new tour", the take-over from another trip and both directions here, now goes through one copy model (tour-copy.helpers) instead of a second one. Reading and removing follow the collection's roles, keeping one also needs access to the source trip, and taking one over needs place_edit on the target trip; the kept tour is read again inside the write, so one removed meanwhile leaves no half-made tour. The MCP tools list_collection_tours, get_collection_tour, save_tour_to_collection, take_over_collection_tour and delete_collection_tour run over the same service and checks. All 27 locales carry the new strings, and the wiki describes the flow. |
||
|
|
2449616ee9 |
feat(tours): plan a multi-day tour as one route split into stages
A hut-to-hut hike or a paddle trip over several days is now planned in the Tours planner as one tour: the whole route is planned as before, and stops along it are marked as stage ends (MDT-01). The stages are read off that one route in its order, never kept as copies, so a change inside a stage changes the whole tour and every figure on it. - A Day tour / Multi-day tour switch beside the tour type; a stop's menu ends a stage there or lets it go on. Back to a day tour removes every stage end after a confirmation, as one step undo brings back. - The list of points is headed per stage with its distance and moving time; a stop that ends a stage carries a moon at its number, on the map too. - The overview lists the stages from stop to stop with distance, climb, descent and time (hiking rule per stage, paddle estimate per stage, the router's time shared by distance otherwise), marks where each ends across the elevation profile, and a stage of more than a day on the move warns. - A stage end a change cannot keep (its stop removed, moved to an end or made a shaping point) is dropped and said so, with undo, never moved to another point. - A multi-day tour may take a day a stage, so its time limit grows with its stages; the tour keeps its one type, portages included. - The list and the MCP tools show the stage count and where stages end. Stage ends are a flag on the waypoint (migration 046000); a tour without one is a day tour, as every tour saved before. |
||
|
|
bde2ad2ca8 |
feat(atlas): sum up the tours you have done, by period and type (STA-02)
With the Tours addon on, the Atlas panel gets a third tab, Tours, beside
Stats and Bucket List: what you have done on your tours across all your
trips. A period (all time, this year, the last 12 months or an earlier year)
and a tour type narrow it; the tab shows the distance large with the number
of tours at its corner, the ascent and descent, and the types with their
distance. "Show tours" opens every tour of the period in a dialog with the
same filters, the sums, the split by type and one row per tour.
What counts is said, never guessed. A tour counts once the day it is on is
over, with the values of the planning, badged "Planned" and explained as
not measured. A linked actual track (ACT-01) replaces those values for its
execution, badged "Recorded" and named, and is never added on top nor
counted as a tour of its own. A tour on several days counts once, a whole
route does not count beside the stages planned from it, a paddle tour sums
no heights, and a value nobody knows stays out of the sum, which then reads
"at least". Only the user's own part counts: a day names them among its
participants, or names nobody on a trip of their own; being able to open
somebody else's trip makes none of its tours theirs. Tours still ahead, on
no day or only on undated days are named as not counting. A tour that fell
through can be left out of the sums ("Leave out"): it stays in the plan
and in the list, dimmed, and "Count again" brings it back. Only the user's
own numbers change.
Server: GET /api/tours/activity-stats answers the user's activity entries
across every trip they can open, and PUT /api/tours/activity-stats/exclusions
leaves a tour out or counts it again (the tour must be in such a trip, else
404). A tour_activity_exclusions table keeps the exclusions per user and
tour. The sums are added up by a shared summary in @trek/shared, so the
Atlas and the assistant say the same numbers. MCP: get_tour_activity_stats
and set_tour_activity_excluded over the same service. The tab reads live
and needs a connection, like the rest of the Atlas statistics.
|
||
|
|
d0cc6fc2f4 |
feat(tours): link the track a tour was actually done with, and show its own values (ACT-01)
A planned tour can now be given the track it was actually done with. In the tour's detail, "Add actual track" opens a dialog that asks for the day the tour was done on (or none) and a recording of the trip: an import nobody planned over, or a new one through the existing GPX import, its preview preset to the tour's type. What the choice measures is shown before anything is written, and only "Link" writes. The linked track keeps its own name and source; the detail shows its distance, heights, profile and, only where the file carried time stamps, the time it took, marked as actual values next to the untouched plan. A track can be changed or unlinked again. Server: a tour_activities table points a tour's execution (a day assignment, or none) at a recording of the same trip, and the tours GPX import keeps the first and last time of a track whose every point carries one. The values are read from the recording's own line on every read, so a changed recording updates them while the plan's line, measures and revision stay as they were. Every check runs inside the write: the recording must be an import of this trip not planned over, documenting no other tour; the day must be one of this tour; a refusal names its reason and changes nothing. PUT and DELETE /api/trips/:tripId/tours/:placeId/activities answer the detail, which now carries the actual tracks; the list names them per tour and per recording. MCP: assign_tour_track and unassign_tour_track over the same service and checks, get_tour_plan reads the values without the line. A recording linked as a tour's actual track is no longer counted as a tour of its own in the trip's tour summary, and the GPX preview imports a city walk as one instead of as a hike. |
||
|
|
1969ca57d9 |
feat(tours): prepare_tour_change prepares a change to a saved city walk for review
An assistant can now prepare a change to a saved city walk and show the traveller what it would do before anything is saved. Preparing writes nothing: no tour, station, visit or day plan changes, and turning the proposal down keeps the plan as it was. - The changes apply in order and name the saved stations by their keys: add a station after another (a spontaneous stop is an add after the station the traveller is at), remove, replace and move. Stops put after the same station keep the order they were given in. - What the travellers did stays as it is: a station visited or skipped on any day of the walk is never removed, replaced or moved, nothing goes before the last station already done, a protected station keeps its place and a leg drawn by hand stays as drawn. A proposal breaking a rule is refused whole, naming every change and why. Other tour types, and a walk that is a recorded track, are refused too: so far only a city walk takes prepared changes, since the others plan their way with routing of their own the server cannot run. - The answer sets the walk after the change against the saved one: each station with what changed it and where it stood, what was removed, the way on foot before and after planned by the same router (the stored distance and walking time given apart, never mixed in), the stays as far as known, every leg with the new ones marked, the gaps that could not be worked out, a warning for a must station removed or replaced, and each new station as create_place takes it, to keep it for later as a place of the trip instead. - The proposal names the revision it was prepared on, so a save of it is refused once the tour has moved on, and says whether the traveller may change the trip's places at all. The way on foot of a new walk, its creation and a prepared change now come from one CityWalkFootWayService instead of a copy in the briefing service. |