mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
Let a dayTintProvider send its own colour
Four tones cannot keep a twenty-stop trip's legs apart, which is most of the point of colour-coding one. A contribution can now carry `#rrggbb` alongside `tone`, per region or as a shorthand. The split of control is: the plugin picks the hue, the host picks the weight. A colour rides the same per-theme, per-region alphas as a tone, and its OKLCH lightness is clamped into a per-theme band first, so no contribution can hand a user a day card they can't read. The band brackets the built-in tones rather than tightening around them, so an ordinary colour passes through untouched and a hex equal to a tone renders exactly like that tone. Hue and chroma are never touched - it bounds a colour, it doesn't boost one, so a near-white grey still lands as a faint tint. Only `#rrggbb` survives normalization, and that is a security boundary rather than tidying: the value ends up inside a `color-mix()` in an inline `background`, a shorthand that takes a layer list, so a string closing the paren early could append a `url(...)` and beacon everyone viewing the trip. A region gets exactly one paint instruction - what it names beats the shorthand, a colour beats a tone at the same level - so the client never has a tie to break. A region naming a colour the host can't use degrades to `default` like a bogus tone already did.
This commit is contained in:
@@ -63,7 +63,7 @@ ungranted capability is physically unreachable**, not just disallowed. See
|
||||
| `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`. |
|
||||
| `hook:route-provider` | Offer **routing profiles** the planner's route toggle can route days with, via the `hooks.routeProvider` provider hook | Implement `RouteProvider` in `hooks` and declare the profiles in `capabilities.routeProfiles` (max 3; each `{id, label, icon?}`). TREK calls `getRoute({tripId, dayId, profile, waypoints})` for exactly the profile the user picked — this is **targeted**, not a fan-out — with a 20 s timeout so the plugin can call an external solver through its declared egress. The result (`coordinates`, `distance`, `duration`, `legs`, `viaPoints?`) is validated whole (leg count must equal `waypoints−1`, ≤10000 vertices, ≤40 vias); anything malformed is discarded and the planner falls back to straight lines. Exposed at `POST /api/plugin-routes/:pluginId/:profileId` (member-gated per trip). |
|
||||
| `hook:day-schedule-provider` | Attach **time contributions** to the day plan via the `hooks.dayScheduleProvider` provider hook | Implement `DayScheduleProvider` in `hooks` — returns `{id, dayId, assignmentId?/reservationId?/position?, minutes?, label, tone?}[]`. Rows render under their anchor (a place/booking row, or a day's start/end) on desktop and mobile, and `minutes` is **folded into the day's route-footer total** — the one hook whose output feeds displayed timing, which is why minutes are clamped (1–1440) and `dayId` is checked against the trip's own days. ≤60 items per plugin, labels sanitized + capped (≤120); a failing provider is skipped. Exposed at `GET /api/day-schedule/:tripId`. |
|
||||
| `hook:day-tint-provider` | Colour-code **whole days** in the day plan via the `hooks.dayTintProvider` provider hook | Implement `DayTintProvider` in `hooks` — returns `{dayId, tone?, badgeTone?, headerTone?, activityTone?, label?}[]`, one entry per day you want coloured. A day card has **three separately tintable regions** — the day-number badge (which is also the mobile day chip), the header row, and the expanded activity list — so you can mark a leg boldly on the badge and leave the dense activity list plain. `tone` is a shorthand for every region you do not name; a region you never name is not tinted at all and renders exactly as it does without your plugin. **Tone only, never a hex** — a fixed pastel would break the dark theme; the host chooses the alpha per theme and per region (a large surface behind dense text gets a fainter tint than a small badge), and a tinted header hovers to a deeper mix of its own tone instead of losing its colour. `dayId` is checked against the trip's own days, `label` (≤60) becomes the day's tooltip, the raw array is sliced at 2000. **A day takes at most one contribution, and it is resolved whole**: within one provider's list the first entry for a day wins, and across plugins the first granted provider wins — so a day can't flicker between colours, and a losing plugin can't fill in the winner's untinted regions. Unlike `hook:day-schedule-provider` there is no fixed item cap; the bound is the trip's day count, which is what makes this usable on a six-month itinerary. A failing provider is skipped. Exposed at `GET /api/day-tints/:tripId`. |
|
||||
| `hook:day-tint-provider` | Colour-code **whole days** in the day plan via the `hooks.dayTintProvider` provider hook | Implement `DayTintProvider` in `hooks` — returns `{dayId, tone?, color?, badgeTone?, badgeColor?, headerTone?, headerColor?, activityTone?, activityColor?, label?}[]`, one entry per day you want coloured. A day card has **three separately tintable regions** — the day-number badge (which is also the mobile day chip), the header row, and the expanded activity list — so you can mark a leg boldly on the badge and leave the dense activity list plain. `tone` / `color` are shorthands for every region you do not name; a region you never name is not tinted at all and renders exactly as it does without your plugin. Paint a region with a palette tone or with your own colour — **`#rrggbb` only**, since the value ends up inside a CSS colour the host builds; a colour beats a tone within a region, and a region's own value beats the shorthand. **You choose the hue, the host chooses the weight**: it sets the alpha per theme and per region (a large surface behind dense text gets a fainter tint than a small badge) and clamps your colour's lightness into a band that reads on both the light and the dark sidebar, so no contribution can hand a user a day card they cannot read. A tinted header hovers to a deeper mix of its own colour instead of losing it. `dayId` is checked against the trip's own days, `label` (≤60) becomes the day's tooltip, the raw array is sliced at 2000. **A day takes at most one contribution, and it is resolved whole**: within one provider's list the first entry for a day wins, and across plugins the first granted provider wins — so a day can't flicker between colours, and a losing plugin can't fill in the winner's untinted regions. Unlike `hook:day-schedule-provider` there is no fixed item cap; the bound is the trip's day count, which is what makes this usable on a six-month itinerary. A failing provider is skipped. Exposed at `GET /api/day-tints/:tripId`. |
|
||||
| `geolocation:read` | Ask the HOST for the browser's live position from the plugin's sandboxed frames (`window.trek.geolocation`) | A **bridge-level** permission — it unlocks no ctx RPC method. The sandbox itself never gains the geolocation API: the host page reads `navigator.geolocation` (the browser's own site permission prompt still applies) and posts plain `{lat, lng, accuracy, heading, speed, timestamp}` data into the frame over postMessage. `get()` resolves one fix; `watch(cb)` streams updates until unsubscribed, and the host force-stops the GPS watch when the frame closes. Nothing is sent to the server — a position only reaches the plugin's server code if its own client ships it through one of its routes (visible in that route's traffic like any other call). |
|
||||
| `hook:table-contributor` | Contribute host-rendered **columns/actions** into a native planner view via the `hooks.tableContributor` provider hook | Implement `TableContributor` in `hooks` — returns declarative column/action leaves keyed by `entityId` (never markup). The host normalizes + bounds every field (length caps, `http`/`https`/`mailto`-only urls, enum tone/target) and renders them in the reservations, places, day, costs, packing and files views; an action opens your sandboxed frame or calls one of your routes. Also exposed at `GET /api/view-contributions/:view/:tripId`. |
|
||||
| `hook:pdf-section-provider` | Append text-only **sections** to the trip PDF export via the `hooks.pdfSectionProvider` provider hook | Implement `PdfSectionProvider` in `hooks` — returns `{title, paragraphs?, table?}[]` of plain strings the export escapes and lays out itself; no markup ever reaches the document. The host caps the section/paragraph/header/row counts (≤5/≤20/≤8/≤50) and every string length, and clips table rows to the header width; a failing provider is skipped. Exposed at `GET /api/pdf-sections/:tripId`. |
|
||||
|
||||
Reference in New Issue
Block a user