mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
Merge remote-tracking branch 'origin/dev' into feat/trip-route-overview
# Conflicts: # client/src/components/Map/MapView.tsx # client/src/components/Map/MapViewGL.tsx # client/src/pages/TripPlannerPage.tsx # client/src/pages/tripPlanner/useTripPlanner.ts
This commit is contained in:
@@ -72,6 +72,7 @@ A journey can also be laid out as a printable photo book. Open a journey and cli
|
||||

|
||||
|
||||
- **Page format** — Square 21 × 21 cm (the default), Square 30 × 30 cm, A4 landscape, A4 portrait, A5 landscape, or a custom width × height in millimetres. Everything is drawn as a spread (two pages side by side) with 3 mm bleed and a 5 mm safe margin.
|
||||
- **The shape of the book** follows a bound one: a cover, a single first page on the right (page 1, where a title page or a dedication goes), the spreads, a single last page on the left, and a back cover. The first and last pages take the cover layouts and, like the covers, stay where they are; new spreads always go between them. Page numbers, when switched on, count from the first page, and the number on screen is the number that prints. Books made before this had no single pages and keep their numbering as it was.
|
||||
- **Auto layout** has two entries. **This spread** builds the spread on screen again from the journal entry it came from, and is only offered on a spread that came from one. **The whole book** replaces every page, keeping your title and page setup. Both are ordinary undo steps, so you can press one, look at it, and undo.
|
||||
- **Pages, Content, Elements, Travel and Layouts** are the sections of the left rail. Content holds the journey's own photos and entries; Layouts has thirteen spread layouts (Hero and story, Four up, Strip and text, Mosaic and so on) plus a separate set of five for the cover and back; Elements has text styles, shapes, lines, grids, empty frames with their frame styles, and a searchable icon library; Travel builds figures out of the journey itself — route maps, country outlines and lists, flags, date, day and distance marks, and a trip summary.
|
||||
- **Properties** on the right edits whatever is selected: position and size, crop and focal point, fill or fit, a look filter, corner radius, frame style, stacking order, lock. An element that auto layout tied to a journal entry follows that entry until you edit it here, which breaks the link.
|
||||
|
||||
@@ -94,6 +94,49 @@ module.exports = {
|
||||
}
|
||||
```
|
||||
|
||||
## Answer place searches from your own index
|
||||
|
||||
**Needs:** `hook:search-provider` (+ `http:outbound:<host>` and a matching `egress` when
|
||||
the index is somebody else's server)
|
||||
|
||||
TREK searches its own place index and OpenStreetMap. This hook adds a third one, and
|
||||
its hits are drawn into the same list the user is already looking at.
|
||||
|
||||
`rating` is the reason to reach for it: open data carries no ratings at all, so a
|
||||
question like "the best rated hotel around here" cannot be answered from OpenStreetMap.
|
||||
Whatever your index knows, this is where it reaches the search list.
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
hooks: {
|
||||
searchProvider: {
|
||||
async search({ query, limit, lang, near }, ctx) {
|
||||
const res = await fetch(
|
||||
`https://your-index.example/search?q=${encodeURIComponent(query)}&n=${limit}`,
|
||||
)
|
||||
const { results } = await res.json()
|
||||
return results.map(r => ({
|
||||
id: r.ref, // namespaced host-side to plugin:<yourId>:<ref>
|
||||
name: r.title,
|
||||
lat: r.lat,
|
||||
lng: r.lon,
|
||||
address: r.address,
|
||||
rating: r.stars, // 0..5, clamped host-side
|
||||
website: r.url, // http/https only
|
||||
}))
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Called for an explicit search, never per keystroke, so an index with rate limits is
|
||||
safe here. You get two seconds to answer and at most 20 of your places are kept: a
|
||||
person is waiting on the list, and the app stops waiting at two and a half seconds
|
||||
whatever happens, so a provider that misses that window is left out of the list rather
|
||||
than holding it up. `near` is the coordinate the search is biased toward,
|
||||
and it is what tells the four places called "Hase-dera" apart — use it when you have it.
|
||||
|
||||
## Raise validation warnings on a trip
|
||||
|
||||
**Needs:** `hook:trip-warning-provider`
|
||||
|
||||
@@ -140,8 +140,9 @@ permissions still requires explicit re-consent).
|
||||
## The plugin types
|
||||
|
||||
- **integration** — background logic (jobs, routes) with no UI of its own. Every
|
||||
provider hook is **live** — from placeDetailProvider and warningProvider through
|
||||
photoProvider (Memories) and calendarSource — see [Provider hooks](#provider-hooks).
|
||||
provider hook is **live** — from placeDetailProvider, searchProvider and
|
||||
warningProvider through photoProvider (Memories) and calendarSource — see
|
||||
[Provider hooks](#provider-hooks).
|
||||
- **page** — adds a nav entry that opens a full-page sandboxed iframe.
|
||||
- **widget** — adds a card to the dashboard (`sidebar` slot), a hero-bar overlay
|
||||
(`hero` slot), a panel inside the trip planner's **place-detail** view
|
||||
@@ -805,6 +806,7 @@ module.exports = definePlugin({
|
||||
| Hook | Permission | Status |
|
||||
|---|---|---|
|
||||
| `placeDetailProvider.getDetails(placeId, ctx)` → `{ label, value?, url? }[]` | `hook:place-detail-provider` | **live** — shown in the place-detail panel; also `GET /api/place-details/:placeId` |
|
||||
| `searchProvider.search(request, ctx)` → `SearchResultPlace[]` | `hook:search-provider` | **live** — answers place searches from an index TREK does not ship, drawn into the app's own search list beside the core results (#2221). `request` is `{query, limit, lang?, near?}`; each place is `{id?, name, lat, lng, address?, rating?, website?, phone?, category?, description?}`. `rating` is the field open data cannot answer — OpenStreetMap carries none — so it is what makes "the best rated one around here" answerable at all; it is clamped to 0..5. Coordinates are range-checked, strings capped, `website` must be http/https, and ids are namespaced to `plugin:<yourId>:<id>` so they can never collide with an OSM one. Called for an explicit search, **not** per keystroke. Also `GET /api/plugin-search`, and to a connected assistant as the `search_places_via_plugins` MCP tool |
|
||||
| `warningProvider.getWarnings(tripId, ctx)` → `{ level, message, dayId?, placeId? }[]` | `hook:trip-warning-provider` | **live** — validation warnings shown as a non-blocking banner in the trip planner; also `GET /api/trip-warnings/:tripId`, and to a connected assistant as the `get_trip_warnings` MCP tool (≤20 warnings per provider, message ≤300 chars) — that path needs only the trips read scope, not `plugins:use` |
|
||||
| `tableContributor.getContributions(view, tripId, ctx)` → `TableContribution[]` | `hook:table-contributor` | **live** — host-rendered **columns/actions** keyed by `entityId` in the reservations, transports, places, day, costs, packing, files and todos views. A `column` is `{kind:'column', entityId, id, label, value?, url?, icon?, tone?}` (url is http/https/mailto only); an `action` is `{kind:'action', entityId, id, label, icon?, target}` where `target` opens your sandboxed frame (`{kind:'frame', sub}`) or calls a route (`{kind:'route', method, sub}`). All fields are bounded + normalized host-side; also `GET /api/view-contributions/:view/:tripId` |
|
||||
| `mapMarkerProvider.getMarkers(tripId, ctx)` → `MapMarkerContribution[]` | `hook:map-marker-provider` | **live** — bounded markers overlaid on the trip map (#587). Each is `{id, lat, lng, label?, popupText?, url?, icon?, tone?}`; coordinates are range-checked (−90..90 / −180..180), text length-capped, url http/https/mailto-only, count capped (≤200/plugin). Declarative only — plugin JS never runs on the map canvas. Also `GET /api/map-markers/:tripId` |
|
||||
@@ -1476,6 +1478,7 @@ install (and by `trek-plugin validate` and registry CI, which check against the
|
||||
| `ws:broadcast:user` | `ctx.ws.broadcastToUser` |
|
||||
| `http:outbound` or `http:outbound:<host>` | outbound HTTP to `egress[]` hosts |
|
||||
| `hook:place-detail-provider` | `hooks.placeDetailProvider` — extra place rows TREK renders (see [Provider hooks](#provider-hooks)) |
|
||||
| `hook:search-provider` | `hooks.searchProvider` — answers place searches from your own index (see [Provider hooks](#provider-hooks)) |
|
||||
| `hook:trip-warning-provider` | `hooks.warningProvider` — validation warnings in the planner (see [Provider hooks](#provider-hooks)) |
|
||||
| `hook:table-contributor` | `hooks.tableContributor` — host-rendered columns/actions in the reservations, transports, places, day, costs, packing, files and todos views (see [Provider hooks](#provider-hooks)) |
|
||||
| `hook:map-marker-provider` | `hooks.mapMarkerProvider` — bounded markers on the trip map |
|
||||
|
||||
@@ -58,6 +58,7 @@ ungranted capability is physically unreachable**, not just disallowed. See
|
||||
| `hook:photo-provider` | Register as a photo provider in Memories | Implement the `PhotoProvider` interface. |
|
||||
| `hook:calendar-source` | Register as a calendar source | Implement the `CalendarSource` interface. |
|
||||
| `hook:place-detail-provider` | Contribute extra details (reviews, ratings, links) to a place via the `hooks.placeDetailProvider` provider hook | Implement `PlaceDetailProvider` in `hooks` on the plugin definition (not on `ctx`) — shown in the place-detail panel; also exposed at `GET /api/place-details/:placeId`. |
|
||||
| `hook:search-provider` | Answer place searches from an index TREK does not ship, beside its own results | Implement `SearchProvider` in `hooks` on the plugin definition — hits are drawn into the app’s search list; also exposed at `GET /api/plugin-search` and as the `search_places_via_plugins` MCP tool. Only these results can carry a `rating`: open data has none. |
|
||||
| `hook:trip-warning-provider` | Raise validation warnings on a trip via the `hooks.warningProvider` provider hook | Implement `WarningProvider` in `hooks` on the plugin definition (not on `ctx`) — shown as a non-blocking banner in the planner; also exposed at `GET /api/trip-warnings/:tripId`, and to a connected assistant as the `get_trip_warnings` MCP tool (≤20 warnings per provider, message ≤300 chars) — that path needs only the trips read scope, not `plugins:use`. |
|
||||
| `hook:map-marker-provider` | Overlay bounded **markers** on the trip map via the `hooks.mapMarkerProvider` provider hook | Implement `MapMarkerProvider` in `hooks` — returns `{id, lat, lng, label?, popupText?, url?, icon?, tone?}[]` (#587 "show bookings on map"). **Declarative only** — plugin JS never runs on the map canvas. The host range-checks coordinates (−90..90 / −180..180), String-coerces + length-caps text, allowlists the popup url (http/https/mailto), and caps the marker count (≤200) per plugin; a failing provider is skipped. Exposed at `GET /api/map-markers/:tripId`. |
|
||||
| `hook:map-layer-provider` | Overlay bounded **vector layers** (polylines, polygons, metric circles) on the trip map via the `hooks.mapLayerProvider` provider hook | Implement `MapLayerProvider` in `hooks` — returns `{id, name?, features}[]` where a feature is `{type, points?/center?+radiusM?, tone?, width?, dash?, opacity?, fill?, label?}`. **Declarative only** — the host draws everything; styling is the tone palette plus clamped numerics (width 1–8, opacity 0.05–1, radius ≤2000 km) and a dash enum, so a layer can't impersonate core UI. Budgets per plugin: ≤4 layers / ≤150 features / ≤8000 vertices / ≤2000 vertices per shape; invalid or oversized shapes are dropped whole (never truncated); a failing provider is skipped. Drawn beneath TREK's own routes. Exposed at `GET /api/map-layers/:tripId`. |
|
||||
|
||||
Reference in New Issue
Block a user