Compare commits

...

6 Commits

Author SHA1 Message Date
github-actions[bot] a0943f8226 chore: bump version to 5.0.1 [skip ci] 2026-10-28 10:43:52 +00:00
Maurice 6741e8ca0d fix(help): give the close button a row of its own
A wider gutter left the button floating in the corner over the first card and the scrollbar. It now sits in its own row above the scrolling content, level with the Help title, and the content keeps symmetric padding.
2026-10-28 10:24:08 +01:00
Maurice c14f179e39 fix(help): keep the help center's content clear of its close button
The close button floats over the scrolling content, which only kept a 40px gutter on the end side while the button reaches 52px in, so the first card ran right up to it. The content now keeps a wider gutter on that side.
2026-10-28 10:24:08 +01:00
Maurice 3bfef73a46 refactor(maps): move the place clusters out of MapViewGL
The cluster source, its layers, the expansion zoom and the DOM bubbles
now live in placeClusterLayers.ts with their own tests, which brings
MapViewGL back under its size baseline instead of growing it. Lowers the
size and eslint baselines that this change made smaller.
2026-10-28 10:11:56 +01:00
Maurice 7e790c3fb7 fix(maps): draw the GL cluster bubbles like the Leaflet ones
The MapLibre and Mapbox maps painted their place clusters as a canvas
circle plus a symbol layer, so the count used whatever glyphs the map
style ships and the bubble had no hover. They are now DOM pins built by
the same markup and CSS as the Leaflet bubbles, with the same sizes,
font and hover scale. The canvas layers stay, invisible, as the hit
area the track and route layers check a click against.
2026-10-28 10:11:56 +01:00
Maurice 228db8541c chore(notices): show 5.0.1 in the release popup
The release popup still named 5.0.0 and linked the 5.0.0 release notes. It now names 5.0.1 and links its tag.
2026-10-28 09:52:18 +01:00
14 changed files with 300 additions and 116 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
apiVersion: v2
name: trek
version: 5.0.0
version: 5.0.1
description: Minimal Helm chart for TREK app
appVersion: "5.0.0"
appVersion: "5.0.1"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trek/client",
"version": "5.0.0",
"version": "5.0.1",
"engines": {
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
},
+1 -1
View File
@@ -30,7 +30,7 @@
"no-useless-escape": 1
},
"suppressed": {
"@typescript-eslint/no-explicit-any": 40,
"@typescript-eslint/no-explicit-any": 38,
"react-hooks/exhaustive-deps": 46
},
"tests": {
+1 -1
View File
@@ -7,7 +7,7 @@
"src/components/Budget/CostsPanel.tsx": 1798,
"src/components/Map/MapView.tsx": 1385,
"src/components/Map/MapViewGL.test.tsx": 2919,
"src/components/Map/MapViewGL.tsx": 2290,
"src/components/Map/MapViewGL.tsx": 2218,
"src/components/Map/RouteCalculator.ts": 1063,
"src/components/PDF/TripPDF.tsx": 1082,
"src/components/Planner/DayDetailPanel.test.tsx": 2333,
+13 -9
View File
@@ -102,16 +102,20 @@ export default function HelpPanel(): React.ReactElement | null {
<HelpNav context={context} currentId={contextId} activeGuideId={!searching && guide ? guide.id : null} />
<div className="relative flex flex-col min-h-0 bg-surface" role="main">
<button
type="button"
onClick={closeHelp}
aria-label={t('help.center.close')}
className="absolute top-4 end-4 z-10 w-9 h-9 rounded-full flex items-center justify-center text-content-muted bg-surface-card border border-edge shadow-card hover:text-content hover:bg-surface-hover transition-colors"
>
<X className="w-4 h-4" />
</button>
{/* The close button sits in a row of its own above the scrolling content rather
than floating over it, so no card ever runs up against it. */}
<div className="flex shrink-0 justify-end px-4 pt-4">
<button
type="button"
onClick={closeHelp}
aria-label={t('help.center.close')}
className="w-9 h-9 rounded-full flex items-center justify-center text-content-muted bg-surface-card border border-edge shadow-card hover:text-content hover:bg-surface-hover transition-colors"
>
<X className="w-4 h-4" />
</button>
</div>
<div ref={scrollRef} className="trek-help-scroll flex-1 min-h-0 overflow-y-auto overscroll-contain">
<div className="max-w-[1080px] mx-auto px-6 sm:px-10 py-8">
<div className="max-w-[1080px] mx-auto px-6 sm:px-10 pt-4 pb-8">
{searching ? (
<HelpSearchResults key="search" />
) : guide ? (
+18 -91
View File
@@ -50,16 +50,10 @@ import { pluginsApi, type PluginMapMarker, type PluginMapLayer } from '../../api
import { DEFAULT_MAP_CENTER, DEFAULT_MAP_ZOOM } from '../../constants/mapDefaults'
import { alongShortestArc, computeMapViewport, TILE_SIZE_GL } from '../../utils/mapViewport'
import { selectedPlaceTarget } from './selectedPlaceTarget'
import { PLACE_CLUSTER_CIRCLE_LAYER_ID, PLACE_CLUSTER_COUNT_LAYER_ID } from './placeClusterLayers'
// Marker grouping for the GL map (#1385): MapLibre/Mapbox can't show the rich
// HTML photo markers *and* cluster them natively, so we feed the place points
// into a clustered GeoJSON source. The cluster bubbles render as GL circles +
// a count label; the individual rich HTML markers are then only drawn for the
// points the source reports as currently unclustered. Grouping is always on,
// matching the Leaflet map's MarkerClusterGroup.
const PLACE_CLUSTER_SOURCE_ID = 'trip-place-clusters'
const PLACE_UNCLUSTERED_LAYER_ID = 'trip-place-unclustered-hit'
import {
PLACE_CLUSTER_CIRCLE_LAYER_ID, PLACE_CLUSTER_COUNT_LAYER_ID, PLACE_CLUSTER_SOURCE_ID,
addPlaceClusterLayers, drawClusterBubbles, expandCluster,
} from './placeClusterLayers'
type PlaceWithCoords = Place & { lat: number; lng: number }
@@ -112,72 +106,6 @@ interface Props extends MapViewProps {
gl: any
}
/**
* How eagerly place markers merge into a cluster.
*
* The overview band the Leaflet map clusters in as well (`CLUSTER_UNTIL_ZOOM` there),
* kept as plain numbers rather than shared constants so the GL bundle does not have to
* carry Leaflet for them. Above the cutoff supercluster hands every point back on its
* own, and the stops that share a coordinate are folded together at the pin level
* instead — see the reconcile below.
*/
const CLUSTER_RADIUS = 20
const CLUSTER_MAX_ZOOM = 8
/** The cluster source and the three layers over it. Extracted so the rebuild on a mode
* change and the initial build cannot drift apart. */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function addPlaceClusterLayers(map: any): void {
map.addSource(PLACE_CLUSTER_SOURCE_ID, {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] },
cluster: true,
clusterRadius: CLUSTER_RADIUS,
clusterMaxZoom: CLUSTER_MAX_ZOOM,
})
map.addLayer({
id: PLACE_CLUSTER_CIRCLE_LAYER_ID,
type: 'circle',
source: PLACE_CLUSTER_SOURCE_ID,
filter: ['has', 'point_count'],
paint: {
'circle-color': '#111827',
'circle-opacity': 0.97,
'circle-radius': ['step', ['get', 'point_count'], 18, 10, 21, 50, 24],
'circle-stroke-width': 2.5,
'circle-stroke-color': 'rgba(255,255,255,0.9)',
},
})
map.addLayer({
id: PLACE_CLUSTER_COUNT_LAYER_ID,
type: 'symbol',
source: PLACE_CLUSTER_SOURCE_ID,
filter: ['has', 'point_count'],
layout: {
'text-field': ['get', 'point_count_abbreviated'],
'text-size': 12,
'text-allow-overlap': true,
},
paint: {
'text-color': '#ffffff',
'text-halo-color': 'rgba(17,24,39,0.35)',
'text-halo-width': 1,
},
})
map.addLayer({
id: PLACE_UNCLUSTERED_LAYER_ID,
type: 'circle',
source: PLACE_CLUSTER_SOURCE_ID,
filter: ['!', ['has', 'point_count']],
paint: {
'circle-radius': 24,
'circle-opacity': 0,
'circle-stroke-opacity': 0,
},
})
}
function createMarkerElement(place: Place & PlaceMarkerFlags & { category_color?: string; category_icon?: string }, photoUrl: string | null, orderNumbers: number[] | null, selected: boolean): HTMLDivElement {
// A stop that interrupts the drive gets its own small disc, decided before the photo
// branch: the brand logo a fuel search comes back with is exactly what this replaces.
@@ -612,6 +540,8 @@ export function MapViewGL({
useDawarichTrailGL(mapRef.current, mapReady, dawarichTrack, dawarichSelectedDate, 'trip-route-casing', dawarichHiddenDates)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const markersRef = useRef<Map<number, PlacePin>>(new Map())
/** The count bubbles, one per cluster the source currently reports. */
const clusterPinsRef = useRef<PlacePin[]>([])
// Own layer for the hand-positioned place pins (MapLibre path, see makePlacePin).
const pinLayerRef = useRef<HTMLDivElement | null>(null)
const locationMarkerRef = useRef<LocationMarkerHandle | null>(null)
@@ -642,6 +572,7 @@ export function MapViewGL({
// in the same frame the canvas draws, or the ones left out swim against the ones in.
const repositionPins = useCallback(() => {
markersRef.current.forEach(pin => pin.reposition())
clusterPinsRef.current.forEach(pin => pin.reposition())
poiMarkersRef.current.forEach(pin => pin.reposition())
pluginMarkersRef.current.forEach(pin => pin.reposition())
routeViaMarkersRef.current.forEach(pin => pin.reposition())
@@ -1304,21 +1235,7 @@ export function MapViewGL({
? map.queryRenderedFeatures(e.point, { layers: [PLACE_CLUSTER_CIRCLE_LAYER_ID, PLACE_CLUSTER_COUNT_LAYER_ID] })
: []
const feature = features?.[0]
const clusterId = feature?.properties?.cluster_id
const coordinates = feature?.geometry?.coordinates
if (clusterId == null || !Array.isArray(coordinates)) return
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const source = map.getSource(PLACE_CLUSTER_SOURCE_ID) as any
const easeToZoom = (nextZoom: number) => {
try { map.easeTo({ center: coordinates, zoom: nextZoom, duration: 350 }) } catch { /* noop */ }
}
try {
const maybeZoom = source?.getClusterExpansionZoom?.(clusterId, (err: Error | null, nextZoom: number) => {
if (!err && typeof nextZoom === 'number') easeToZoom(nextZoom)
})
if (typeof maybeZoom === 'number') easeToZoom(maybeZoom)
else if (maybeZoom && typeof maybeZoom.then === 'function') maybeZoom.then(easeToZoom).catch(() => {})
} catch { /* noop */ }
expandCluster(map, feature?.properties?.cluster_id, feature?.geometry?.coordinates)
}
const setClusterCursor = () => {
const canvas = typeof map.getCanvas === 'function' ? map.getCanvas() : null
@@ -1704,8 +1621,17 @@ export function MapViewGL({
source.setData(buildPlaceClusterData(places) as any)
const placesById = new Map<number, PlaceWithCoords>(validPlaces.map(place => [place.id, place]))
let raf: number | null = null
const dropClusterPins = () => {
clusterPinsRef.current.forEach(pin => pin.remove())
clusterPinsRef.current = []
}
const reconcileClusterPins = () => {
dropClusterPins()
clusterPinsRef.current = drawClusterBubbles(map, (el, lng, lat) => attachPin(map, gl, pinLayerRef.current, el, lng, lat))
}
const runReconcile = () => {
raf = null
reconcileClusterPins()
const features = map.querySourceFeatures(PLACE_CLUSTER_SOURCE_ID, { filter: ['!', ['has', 'point_count']] }) || []
const seen = new Set<number>()
const visiblePlaces: PlaceWithCoords[] = []
@@ -1752,6 +1678,7 @@ export function MapViewGL({
return () => {
if (raf !== null) cancelAnimationFrame(raf)
dropClusterPins()
map.off('moveend', scheduleReconcile)
map.off('zoomend', scheduleReconcile)
map.off('idle', scheduleReconcile)
+2 -1
View File
@@ -1,5 +1,6 @@
import L from 'leaflet'
import { STACK_RADIUS_PX } from './coincidentPlaces'
import { clusterBubbleSize } from './placeClusterLayers'
/** Up to here the map is an overview, where stops merge to keep the world readable. */
export const CLUSTER_UNTIL_ZOOM = 9
@@ -66,7 +67,7 @@ export interface ClusterLike {
/** A count bubble sized by its count, styled by `.marker-cluster-custom` in index.css. */
export function createClusterIcon(cluster: ClusterLike) {
const count = cluster.getChildCount()
const size = count < 10 ? 36 : count < 50 ? 42 : 48
const size = clusterBubbleSize(count)
return L.divIcon({
html: `<div class="marker-cluster-custom" style="width:${size}px;height:${size}px;"><span>${count}</span></div>`,
className: 'marker-cluster-wrapper',
@@ -0,0 +1,86 @@
import { describe, expect, it, vi } from 'vitest';
import {
type ClusterMap,
PLACE_CLUSTER_SOURCE_ID,
addPlaceClusterLayers,
clusterBubbleSize,
createClusterBubble,
drawClusterBubbles,
} from './placeClusterLayers';
function fakeMap(features: unknown[], expansionZoom: unknown = 11) {
const source = { getClusterExpansionZoom: vi.fn(() => expansionZoom) };
const map = {
addSource: vi.fn(),
addLayer: vi.fn(),
getSource: vi.fn((id: string) => (id === PLACE_CLUSTER_SOURCE_ID ? source : undefined)),
easeTo: vi.fn(),
querySourceFeatures: vi.fn((_id: string, query: { filter: unknown[] }) =>
query.filter[0] === 'has' ? features : []
),
};
return { map: map as unknown as ClusterMap & typeof map, source };
}
describe('cluster bubbles', () => {
it('FE-COMP-PLACECLUSTER-001: a bubble grows in three steps with its count', () => {
expect([1, 9, 10, 49, 50, 500].map(clusterBubbleSize)).toEqual([36, 36, 42, 42, 48, 48]);
});
it('FE-COMP-PLACECLUSTER-002: the GL bubble is the Leaflet markup inside a bare wrapper the pin can move', () => {
const wrapper = createClusterBubble(38);
expect(wrapper.className).toBe('');
const bubble = wrapper.firstElementChild as HTMLElement;
expect(bubble.className).toBe('marker-cluster-custom');
expect(bubble.style.width).toBe('42px');
expect(bubble.style.height).toBe('42px');
expect(bubble.textContent).toBe('38');
});
it('FE-COMP-PLACECLUSTER-003: the canvas circle and count stay as an invisible hit area', () => {
const { map } = fakeMap([]);
addPlaceClusterLayers(map);
expect(map.addSource).toHaveBeenCalledWith(PLACE_CLUSTER_SOURCE_ID, expect.objectContaining({ cluster: true }));
const layers = map.addLayer.mock.calls.map(([layer]) => layer as { id: string; paint: Record<string, unknown> });
expect(layers.find((l) => l.id === 'trip-place-clusters-circle')?.paint['circle-opacity']).toBe(0);
expect(layers.find((l) => l.id === 'trip-place-clusters-count')?.paint['text-opacity']).toBe(0);
});
it('FE-COMP-PLACECLUSTER-004: one bubble per cluster, placed at its centre, that zooms in on a click', () => {
const cluster = { properties: { cluster_id: 5, point_count: 12 }, geometry: { coordinates: [139.7, 35.6] } };
const { map, source } = fakeMap([
cluster,
cluster, // the same cluster crossing a tile edge
{ properties: { cluster_id: 6 }, geometry: { coordinates: [1, 1] } }, // no count
{ properties: { cluster_id: 7, point_count: 3 } }, // no geometry
]);
const placed: { el: HTMLElement; lng: number; lat: number }[] = [];
const pins = drawClusterBubbles(map, (el, lng, lat) => {
placed.push({ el, lng, lat });
return lng;
});
expect(pins).toEqual([139.7]);
expect(placed[0].el.textContent).toBe('12');
expect(placed[0].lat).toBe(35.6);
const onMapClick = vi.fn();
document.body.appendChild(placed[0].el);
document.body.addEventListener('click', onMapClick);
placed[0].el.dispatchEvent(new MouseEvent('click', { bubbles: true }));
document.body.removeEventListener('click', onMapClick);
placed[0].el.remove();
// The click is the bubble's: the map underneath never sees it.
expect(onMapClick).not.toHaveBeenCalled();
expect(source.getClusterExpansionZoom).toHaveBeenCalledWith(5, expect.any(Function));
expect(map.easeTo).toHaveBeenCalledWith({ center: [139.7, 35.6], zoom: 11, duration: 350 });
});
it('FE-COMP-PLACECLUSTER-005: a source that reports nothing draws nothing', () => {
const { map } = fakeMap([]);
map.querySourceFeatures.mockReturnValue(null as never);
expect(drawClusterBubbles(map, () => 1)).toEqual([]);
});
});
@@ -5,3 +5,169 @@
*/
export const PLACE_CLUSTER_CIRCLE_LAYER_ID = 'trip-place-clusters-circle';
export const PLACE_CLUSTER_COUNT_LAYER_ID = 'trip-place-clusters-count';
// Marker grouping for the GL map (#1385): MapLibre/Mapbox can't show the rich
// HTML photo markers *and* cluster them natively, so we feed the place points
// into a clustered GeoJSON source. The source decides what is clustered; the
// bubbles are drawn as DOM (`drawClusterBubbles`) and the individual rich HTML
// markers only for the points the source reports as currently unclustered.
// Grouping is always on, matching the Leaflet map's MarkerClusterGroup.
export const PLACE_CLUSTER_SOURCE_ID = 'trip-place-clusters';
const PLACE_UNCLUSTERED_LAYER_ID = 'trip-place-unclustered-hit';
/**
* How eagerly place markers merge into a cluster.
*
* The overview band the Leaflet map clusters in as well (`CLUSTER_UNTIL_ZOOM` there),
* kept as plain numbers rather than shared constants so the GL bundle does not have to
* carry Leaflet for them. Above the cutoff supercluster hands every point back on its
* own, and the stops that share a coordinate are folded together at the pin level
* instead — see the reconcile in MapViewGL.
*/
const CLUSTER_RADIUS = 20;
const CLUSTER_MAX_ZOOM = 8;
/** The part of either SDK's map the cluster code calls. */
export interface ClusterMap {
addSource: (id: string, source: object) => void;
addLayer: (layer: object) => void;
getSource: (id: string) => unknown;
easeTo: (options: { center: unknown[]; zoom: number; duration: number }) => void;
querySourceFeatures: (id: string, options: { filter: unknown[] }) => ClusterFeature[] | null | undefined;
}
interface ClusterFeature {
properties?: { cluster_id?: unknown; point_count?: unknown } | null;
geometry?: { coordinates?: unknown };
}
interface ClusterSource {
getClusterExpansionZoom?: (id: unknown, done: (err: Error | null, zoom: number) => void) => unknown;
}
/** The cluster source and the three layers over it. Extracted so the rebuild on a mode
* change and the initial build cannot drift apart. */
export function addPlaceClusterLayers(map: Pick<ClusterMap, 'addSource' | 'addLayer'>): void {
map.addSource(PLACE_CLUSTER_SOURCE_ID, {
type: 'geojson',
data: { type: 'FeatureCollection', features: [] },
cluster: true,
clusterRadius: CLUSTER_RADIUS,
clusterMaxZoom: CLUSTER_MAX_ZOOM,
});
// Invisible: the bubble the user sees is DOM (createClusterBubble), these two
// layers stay as the hit area the track and route layers check a click against.
map.addLayer({
id: PLACE_CLUSTER_CIRCLE_LAYER_ID,
type: 'circle',
source: PLACE_CLUSTER_SOURCE_ID,
filter: ['has', 'point_count'],
paint: {
'circle-opacity': 0,
'circle-stroke-opacity': 0,
'circle-radius': ['step', ['get', 'point_count'], 18, 10, 21, 50, 24],
},
});
map.addLayer({
id: PLACE_CLUSTER_COUNT_LAYER_ID,
type: 'symbol',
source: PLACE_CLUSTER_SOURCE_ID,
filter: ['has', 'point_count'],
layout: {
'text-field': ['get', 'point_count_abbreviated'],
'text-size': 12,
'text-allow-overlap': true,
},
paint: { 'text-opacity': 0 },
});
map.addLayer({
id: PLACE_UNCLUSTERED_LAYER_ID,
type: 'circle',
source: PLACE_CLUSTER_SOURCE_ID,
filter: ['!', ['has', 'point_count']],
paint: {
'circle-radius': 24,
'circle-opacity': 0,
'circle-stroke-opacity': 0,
},
});
}
/** Eases the map in to the zoom a cluster falls apart at. Both SDKs answer that
* question, one through a callback and one through a promise. */
export function expandCluster(
map: Pick<ClusterMap, 'getSource' | 'easeTo'>,
clusterId: unknown,
coordinates: unknown
): void {
if (clusterId == null || !Array.isArray(coordinates)) return;
const source = map.getSource(PLACE_CLUSTER_SOURCE_ID) as ClusterSource | undefined;
const easeToZoom = (nextZoom: number) => {
try {
map.easeTo({ center: coordinates, zoom: nextZoom, duration: 350 });
} catch {
/* noop */
}
};
try {
const maybeZoom = source?.getClusterExpansionZoom?.(clusterId, (err, nextZoom) => {
if (!err && typeof nextZoom === 'number') easeToZoom(nextZoom);
});
if (typeof maybeZoom === 'number') easeToZoom(maybeZoom);
else if (maybeZoom instanceof Promise) maybeZoom.then(easeToZoom).catch(() => {});
} catch {
/* noop */
}
}
/**
* Puts one count bubble on the map per cluster the source currently reports, through
* `place` (the caller's pin factory), and returns what `place` handed back. A cluster
* crossing a tile edge comes back once per tile, so bubbles are keyed by cluster id.
*/
export function drawClusterBubbles<T>(map: ClusterMap, place: (el: HTMLElement, lng: number, lat: number) => T): T[] {
const pins: T[] = [];
const seen = new Set<unknown>();
for (const feature of map.querySourceFeatures(PLACE_CLUSTER_SOURCE_ID, { filter: ['has', 'point_count'] }) ?? []) {
const clusterId = feature?.properties?.cluster_id;
const count = feature?.properties?.point_count;
const coordinates = feature?.geometry?.coordinates;
if (typeof count !== 'number' || !Array.isArray(coordinates) || seen.has(clusterId)) continue;
seen.add(clusterId);
const el = createClusterBubble(count);
el.addEventListener('click', (ev) => {
ev.stopPropagation();
expandCluster(map, clusterId, coordinates);
});
pins.push(place(el, coordinates[0], coordinates[1]));
}
return pins;
}
/** A count bubble's diameter in pixels: both renderers size their bubbles by it. */
export function clusterBubbleSize(count: number): number {
return count < 10 ? 36 : count < 50 ? 42 : 48;
}
/**
* A count bubble as DOM, styled by `.marker-cluster-custom` in index.css.
*
* The GL map draws its bubbles with this element rather than as canvas text, so they
* read in the app's own font and get the same hover the Leaflet map has. A canvas
* symbol layer can only use the glyphs the map style ships and knows no `:hover`.
* The bubble sits in a bare wrapper because the pin positions its element through
* `transform`, which would otherwise override the hover's scale.
*/
export function createClusterBubble(count: number): HTMLDivElement {
const size = clusterBubbleSize(count);
const bubble = document.createElement('div');
bubble.className = 'marker-cluster-custom';
bubble.style.width = `${size}px`;
bubble.style.height = `${size}px`;
const label = document.createElement('span');
label.textContent = String(count);
bubble.appendChild(label);
const wrapper = document.createElement('div');
wrapper.appendChild(bubble);
return wrapper;
}
+5 -5
View File
@@ -1,12 +1,12 @@
{
"name": "@trek/root",
"version": "5.0.0",
"version": "5.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@trek/root",
"version": "5.0.0",
"version": "5.0.1",
"workspaces": [
"client",
"server",
@@ -29,7 +29,7 @@
},
"client": {
"name": "@trek/client",
"version": "5.0.0",
"version": "5.0.1",
"dependencies": {
"@assistant-ui/react": "0.15.25",
"@fontsource/abril-fatface": "^5.3.0",
@@ -25005,7 +25005,7 @@
},
"server": {
"name": "@trek/server",
"version": "5.0.0",
"version": "5.0.1",
"dependencies": {
"@aws-lite/client": "^0.23.7",
"@aws-lite/s3": "^0.2.7",
@@ -25384,7 +25384,7 @@
},
"shared": {
"name": "@trek/shared",
"version": "5.0.0",
"version": "5.0.1",
"dependencies": {
"isomorphic-dompurify": "^3.23.0",
"zod": "^4.6.7"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@trek/root",
"private": true,
"version": "5.0.0",
"version": "5.0.1",
"engines": {
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trek/server",
"version": "5.0.0",
"version": "5.0.1",
"engines": {
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
},
+2 -2
View File
@@ -59,7 +59,7 @@ export const SYSTEM_NOTICES: SystemNotice[] = [
titleKey: 'system_notice.release_notes.headline',
bodyKey: 'system_notice.release_notes.intro',
release: {
version: '5.0.0',
version: '5.0.1',
eyebrowKey: 'system_notice.release_notes.eyebrow',
headlineKey: 'system_notice.release_notes.headline',
introKey: 'system_notice.release_notes.intro',
@@ -95,7 +95,7 @@ export const SYSTEM_NOTICES: SystemNotice[] = [
footnoteKey: 'system_notice.release_notes.footnote',
notes: {
labelKey: 'system_notice.release_notes.notes_label',
href: 'https://github.com/liketrek/TREK/releases/tag/v5.0.0',
href: 'https://github.com/liketrek/TREK/releases/tag/v5.0.1',
},
note: {
eyebrowKey: 'system_notice.release_notes.note_eyebrow',
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@trek/shared",
"version": "5.0.0",
"version": "5.0.1",
"engines": {
"node": "^22.22.2 || ^24.15.0 || >=26.0.0"
},