tempest-react-sdk 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -569,6 +569,17 @@ export declare interface BannerProps extends Omit<HTMLAttributes<HTMLDivElement>
569
569
 
570
570
  export declare type BannerVariant = "info" | "success" | "warning" | "danger";
571
571
 
572
+ /**
573
+ * Initial bearing (forward azimuth) from `origin` to `destination`, in degrees
574
+ * clockwise from true north, normalized to `[0, 360)`. Useful for orienting a
575
+ * heading arrow on a trajectory.
576
+ *
577
+ * @param origin - Start coordinate.
578
+ * @param destination - End coordinate.
579
+ * @returns Bearing in degrees, `[0, 360)`.
580
+ */
581
+ export declare function bearingDeg(origin: Coordinate, destination: Coordinate): number;
582
+
572
583
  /**
573
584
  * Fixed-bottom mobile tab bar. 3–5 items recommended. Pair with
574
585
  * `<Show below="md">` to render only on mobile.
@@ -638,6 +649,18 @@ export declare interface BottomSheetProps extends Omit<HTMLAttributes<HTMLDivEle
638
649
  children?: ReactNode;
639
650
  }
640
651
 
652
+ /**
653
+ * Compute the axis-aligned bounding box that contains every point. Returns
654
+ * `null` for an empty list (there is nothing to bound).
655
+ *
656
+ * @param points - Coordinates to enclose.
657
+ * @returns The bounds, or `null` when `points` is empty.
658
+ */
659
+ export declare function boundingBox(points: readonly Coordinate[]): GeoBounds | null;
660
+
661
+ /** Geometric center of a bounding box. */
662
+ export declare function boundsCenter(bounds: GeoBounds): Coordinate;
663
+
641
664
  export declare interface BreadcrumbItem {
642
665
  label: ReactNode;
643
666
  href?: string;
@@ -978,6 +1001,9 @@ export declare function chunk<T>(items: T[], size: number): T[][];
978
1001
  */
979
1002
  export declare function clamp(value: number, min: number, max: number): number;
980
1003
 
1004
+ /** Clamp a latitude into the valid `[-90, 90]` range. */
1005
+ export declare function clampLatitude(latitude: number): number;
1006
+
981
1007
  declare type ClassValue = string | number | bigint | boolean | null | undefined | ClassValue[];
982
1008
 
983
1009
  /**
@@ -1218,6 +1244,18 @@ export { Control }
1218
1244
 
1219
1245
  export { Controller }
1220
1246
 
1247
+ /**
1248
+ * WGS84 geographic coordinate. Mirrors the `Coordinate` schema from
1249
+ * `tempest-fastapi-sdk` (`geo/schemas.py`) — `latitude` in `[-90, 90]`,
1250
+ * `longitude` in `[-180, 180]`, serialized snake_case on the wire.
1251
+ */
1252
+ export declare interface Coordinate {
1253
+ /** WGS84 latitude in degrees, `[-90, 90]`. E.g. `-23.5505`. */
1254
+ latitude: number;
1255
+ /** WGS84 longitude in degrees, `[-180, 180]`. E.g. `-46.6333`. */
1256
+ longitude: number;
1257
+ }
1258
+
1221
1259
  /**
1222
1260
  * Button that copies a string to the clipboard and shows a transient "copied"
1223
1261
  * state for `timeout` milliseconds.
@@ -1493,6 +1531,26 @@ export declare interface CreateLoggerOptions {
1493
1531
  */
1494
1532
  export declare function createOfflineStore<TItem, TKey extends string | number = string>(config: OfflineStoreConfig<TItem>): OfflineStore<TItem, TKey>;
1495
1533
 
1534
+ /**
1535
+ * Build a {@link RoutingBackend} backed by an OSRM server **you host**. OSRM
1536
+ * only serves a driving profile, so motorcycle/bus durations are derived by
1537
+ * scaling the car duration through {@link durationFactor} — same approach as
1538
+ * the FastAPI SDK's `OSRMBackend`.
1539
+ *
1540
+ * !!! warning
1541
+ * This makes a network request to `baseUrl`. It is opt-in: nothing in the
1542
+ * SDK calls it unless you construct it and pass your own server URL. For a
1543
+ * zero-network estimate, use {@link estimateTravel} instead.
1544
+ *
1545
+ * @param options - Server URL, optional `fetch`, per-mode factors.
1546
+ * @returns A backend whose `route()` queries OSRM.
1547
+ *
1548
+ * @example
1549
+ * const backend = createOSRMBackend({ baseUrl: "https://osrm.internal" });
1550
+ * const estimate = await backend.route(origin, destination, { mode: "car" });
1551
+ */
1552
+ export declare function createOSRMBackend(options: OSRMBackendOptions): RoutingBackend;
1553
+
1496
1554
  /**
1497
1555
  * Build a `206 Partial Content` response from a full one for an HTTP `Range`
1498
1556
  * request. Supports `bytes=start-end`, open-ended `bytes=start-` and suffix
@@ -1504,6 +1562,52 @@ export declare function createOfflineStore<TItem, TKey extends string | number =
1504
1562
  */
1505
1563
  export declare function createPartialResponse(request: Request, response: Response): Promise<Response>;
1506
1564
 
1565
+ /**
1566
+ * Record a live GPS trajectory from `navigator.geolocation.watchPosition`.
1567
+ * Framework-free — the {@link usePositionTracker} hook wraps this for React.
1568
+ *
1569
+ * Jitter while stationary is filtered by `minDistanceKm`; distance is
1570
+ * accumulated across the *full* run even when `maxPoints` trims the retained
1571
+ * array. 100% browser-side: no network, no external service.
1572
+ *
1573
+ * @param options - Filtering, retention and callbacks.
1574
+ * @returns A controller with `start`/`stop`/`clear` and live getters.
1575
+ *
1576
+ * @example
1577
+ * const tracker = createPositionTracker({
1578
+ * minDistanceKm: 0.01,
1579
+ * onUpdate: (pts) => console.log(pts.length, "points"),
1580
+ * });
1581
+ * tracker.start();
1582
+ * // …later
1583
+ * tracker.stop();
1584
+ * console.log(tracker.distanceKm);
1585
+ */
1586
+ export declare function createPositionTracker(options?: CreatePositionTrackerOptions): PositionTracker;
1587
+
1588
+ /** Options for {@link createPositionTracker}. */
1589
+ export declare interface CreatePositionTrackerOptions {
1590
+ /**
1591
+ * Discard a new sample when it is closer than this (in km) to the last
1592
+ * kept point — filters GPS jitter while standing still. Default: `0.005`
1593
+ * (5 meters). Pass `0` to keep every sample.
1594
+ */
1595
+ minDistanceKm?: number;
1596
+ /**
1597
+ * Cap the retained trajectory to the most recent N points (older points are
1598
+ * dropped, but their distance stays counted). Default: `Infinity`.
1599
+ */
1600
+ maxPoints?: number;
1601
+ /** Forwarded to `navigator.geolocation.watchPosition`. */
1602
+ positionOptions?: PositionOptions;
1603
+ /** Called with the full trajectory each time a point is added. */
1604
+ onUpdate?: (points: readonly TrackPoint[]) => void;
1605
+ /** Called on a `GeolocationPositionError`. */
1606
+ onError?: (error: GeolocationPositionError) => void;
1607
+ /** Called whenever the status changes. */
1608
+ onStatusChange?: (status: TrackerStatus) => void;
1609
+ }
1610
+
1507
1611
  /**
1508
1612
  * Build a [[TelemetryAdapter]] backed by [`posthog-js`](https://posthog.com/docs/libraries/js).
1509
1613
  * The PostHog client is supplied by the caller (not bundled).
@@ -2096,6 +2200,25 @@ export declare function decodeJWT(token: string): DecodedJWT;
2096
2200
  */
2097
2201
  export declare function deepMerge<T>(target: T, source: Partial<T>): T;
2098
2202
 
2203
+ /**
2204
+ * Default average car speed in km/h used to derive duration from distance.
2205
+ * Mirrors `DEFAULT_CAR_SPEED_KMH` from `tempest-fastapi-sdk`.
2206
+ */
2207
+ export declare const DEFAULT_CAR_SPEED_KMH = 50;
2208
+
2209
+ /**
2210
+ * Multiplier applied to the great-circle distance to approximate real road
2211
+ * distance. Mirrors `DEFAULT_CIRCUITY_FACTOR` from `tempest-fastapi-sdk`.
2212
+ */
2213
+ export declare const DEFAULT_CIRCUITY_FACTOR = 1.3;
2214
+
2215
+ /**
2216
+ * Per-mode multipliers applied to the car duration. Mirrors
2217
+ * `DEFAULT_MODE_DURATION_FACTORS` from `tempest-fastapi-sdk` — motorcycles are
2218
+ * a touch faster, buses considerably slower (stops + dedicated lanes).
2219
+ */
2220
+ export declare const DEFAULT_MODE_DURATION_FACTORS: Record<TravelMode, number>;
2221
+
2099
2222
  /**
2100
2223
  * Identity helper that types a declarative route tree. Use it so editors give
2101
2224
  * autocomplete and type-checking on every node; the array is returned as-is.
@@ -2261,6 +2384,22 @@ export declare interface DropzoneProps {
2261
2384
  className?: string;
2262
2385
  }
2263
2386
 
2387
+ /**
2388
+ * Look up the duration multiplier for a travel mode, falling back to `1.0`
2389
+ * for unknown modes. Mirrors `duration_factor` from `tempest-fastapi-sdk`.
2390
+ *
2391
+ * @param mode - Travel mode.
2392
+ * @param factors - Optional override map (partial merges over the defaults).
2393
+ * @returns The multiplier for `mode`.
2394
+ */
2395
+ export declare function durationFactor(mode: TravelMode, factors?: Partial<Record<TravelMode, number>>): number;
2396
+
2397
+ /**
2398
+ * Mean Earth radius in kilometers. Same value used by `tempest-fastapi-sdk`
2399
+ * (`geo/distance.py`) so client and server distances agree.
2400
+ */
2401
+ export declare const EARTH_RADIUS_KM = 6371.0088;
2402
+
2264
2403
  export declare interface ElementSize {
2265
2404
  width: number;
2266
2405
  height: number;
@@ -2365,6 +2504,37 @@ export declare interface ErrorTextProps extends HTMLAttributes<HTMLParagraphElem
2365
2504
 
2366
2505
  export declare function estimatePasswordStrength(value: string): PasswordStrength;
2367
2506
 
2507
+ /**
2508
+ * Offline travel estimate between two coordinates. No network — distance comes
2509
+ * from {@link haversineKm} scaled by circuity, duration from a mode-adjusted
2510
+ * average speed. Mirrors `estimate_travel` from `tempest-fastapi-sdk`.
2511
+ *
2512
+ * @param origin - Start coordinate.
2513
+ * @param destination - End coordinate.
2514
+ * @param mode - Travel mode. Default: `"car"`.
2515
+ * @param options - Tuning knobs (circuity, speed, per-mode factors).
2516
+ * @returns A `"heuristic"` {@link TravelEstimate}.
2517
+ * @throws {RangeError} If `carSpeedKmh <= 0` or `circuityFactor <= 0`.
2518
+ *
2519
+ * @example
2520
+ * estimateTravel(
2521
+ * { latitude: -23.5505, longitude: -46.6333 },
2522
+ * { latitude: -23.5629, longitude: -46.6544 },
2523
+ * "car",
2524
+ * ); // { mode: "car", distance_km: …, duration_minutes: …, source: "heuristic" }
2525
+ */
2526
+ export declare function estimateTravel(origin: Coordinate, destination: Coordinate, mode?: TravelMode, options?: EstimateTravelOptions): TravelEstimate;
2527
+
2528
+ /** Options for {@link estimateTravel}. */
2529
+ export declare interface EstimateTravelOptions {
2530
+ /** Distance multiplier over the great-circle line. Default: `1.3`. */
2531
+ circuityFactor?: number;
2532
+ /** Average car speed in km/h. Default: `50`. */
2533
+ carSpeedKmh?: number;
2534
+ /** Per-mode duration multipliers (merged over the defaults). */
2535
+ modeDurationFactors?: Partial<Record<TravelMode, number>>;
2536
+ }
2537
+
2368
2538
  export declare interface EventStreamController {
2369
2539
  close: () => void;
2370
2540
  /** Force an immediate reconnect, resetting the retry counter. */
@@ -2386,6 +2556,18 @@ export declare interface EventStreamMessage<T> {
2386
2556
 
2387
2557
  export declare type EventStreamStatus = "idle" | "connecting" | "open" | "closed" | "error";
2388
2558
 
2559
+ /**
2560
+ * Grow a bounding box outward by `ratio` of its own span on every side, with a
2561
+ * small absolute floor so a single-point (zero-span) box still gets padding.
2562
+ * A `ratio` of `0.1` adds 10% margin — handy so a plotted trajectory does not
2563
+ * hug the edge of the viewport.
2564
+ *
2565
+ * @param bounds - The box to expand.
2566
+ * @param ratio - Fraction of the span to add per side. Default: `0.1`.
2567
+ * @returns A new, larger {@link GeoBounds}.
2568
+ */
2569
+ export declare function expandBounds(bounds: GeoBounds, ratio?: number): GeoBounds;
2570
+
2389
2571
  export declare interface FeatureFlagsAdapter {
2390
2572
  /** Synchronous lookup of a flag with a default. */
2391
2573
  isEnabled: (key: string, defaultValue?: boolean) => boolean;
@@ -2446,6 +2628,38 @@ export declare interface FileUploadProps {
2446
2628
 
2447
2629
  export declare type FilterPredicate<T> = (item: T, search: string) => boolean;
2448
2630
 
2631
+ /**
2632
+ * Build a projection that fits `bounds` into a `width × height` viewport while
2633
+ * preserving aspect ratio (uniform scale, centered). This is what powers the
2634
+ * tile-free trajectory plot: project the bounds, scale to the SVG box, keep
2635
+ * shapes undistorted.
2636
+ *
2637
+ * @param bounds - Geographic extent to fit.
2638
+ * @param width - Viewport width in pixels.
2639
+ * @param height - Viewport height in pixels.
2640
+ * @param options - Padding tuning.
2641
+ * @returns A {@link FittedProjection} with a `project(coord)` mapper.
2642
+ */
2643
+ export declare function fitProjection(bounds: GeoBounds, width: number, height: number, options?: FitProjectionOptions): FittedProjection;
2644
+
2645
+ /** Options for {@link fitProjection}. */
2646
+ export declare interface FitProjectionOptions {
2647
+ /** Inner padding in pixels kept clear on every edge. Default: `16`. */
2648
+ padding?: number;
2649
+ }
2650
+
2651
+ /** A ready-to-use mapping from coordinates to viewport pixels. */
2652
+ export declare interface FittedProjection {
2653
+ /** Project a coordinate to a pixel inside the viewport. */
2654
+ project: (coord: Coordinate) => PixelPoint;
2655
+ /** Uniform scale (unit-plane → pixels) actually used, after aspect fit. */
2656
+ scale: number;
2657
+ /** Viewport width in pixels. */
2658
+ width: number;
2659
+ /** Viewport height in pixels. */
2660
+ height: number;
2661
+ }
2662
+
2449
2663
  export declare type FlagValue = boolean | string | number | null;
2450
2664
 
2451
2665
  /**
@@ -2739,6 +2953,17 @@ export declare interface ForProps<T> {
2739
2953
  */
2740
2954
  export declare function generateIdempotencyKey(): string;
2741
2955
 
2956
+ /**
2957
+ * Axis-aligned geographic bounding box. `min`/`max` follow the same degree
2958
+ * ranges as {@link Coordinate}.
2959
+ */
2960
+ export declare interface GeoBounds {
2961
+ minLatitude: number;
2962
+ maxLatitude: number;
2963
+ minLongitude: number;
2964
+ maxLongitude: number;
2965
+ }
2966
+
2742
2967
  export declare interface GeolocationState {
2743
2968
  loading: boolean;
2744
2969
  error: GeolocationPositionError | null;
@@ -2880,6 +3105,22 @@ export declare interface GrowthBookLike {
2880
3105
 
2881
3106
  export { HashRouter }
2882
3107
 
3108
+ /**
3109
+ * Great-circle distance between two coordinates using the haversine formula
3110
+ * (spherical Earth). Mirrors `haversine_km` from `tempest-fastapi-sdk`.
3111
+ *
3112
+ * @param origin - Start coordinate.
3113
+ * @param destination - End coordinate.
3114
+ * @returns Distance in kilometers (`>= 0`).
3115
+ *
3116
+ * @example
3117
+ * haversineKm(
3118
+ * { latitude: -23.5505, longitude: -46.6333 }, // São Paulo
3119
+ * { latitude: -22.9068, longitude: -43.1729 }, // Rio de Janeiro
3120
+ * ); // ≈ 360.9
3121
+ */
3122
+ export declare function haversineKm(origin: Coordinate, destination: Coordinate): number;
3123
+
2883
3124
  /** Inverse of `<Show>` — hides children when the condition matches. */
2884
3125
  export declare function Hide({ above, below, only, children }: HideProps): ReactNode;
2885
3126
 
@@ -3054,6 +3295,63 @@ export declare interface InstallBackgroundSyncOptions {
3054
3295
  maxRetentionMinutes?: number;
3055
3296
  }
3056
3297
 
3298
+ /**
3299
+ * Dismissible bottom banner that invites the user to install the PWA. Wired to
3300
+ * {@link useBeforeInstallPrompt}: it renders only when the browser captured an
3301
+ * install prompt and the app is not already running standalone — so on
3302
+ * platforms that never fire `beforeinstallprompt` (e.g. iOS Safari) it stays
3303
+ * hidden and you can surface manual instructions elsewhere.
3304
+ *
3305
+ * @example
3306
+ * <InstallBanner
3307
+ * title="Instale o FAMACHApp"
3308
+ * description="Acesso offline e atalho na tela inicial."
3309
+ * storageKey="famacha:install-dismissed"
3310
+ * />
3311
+ */
3312
+ export declare function InstallBanner({ title, description, installLabel, dismissLabel, icon, storageKey, onResult, className, }: InstallBannerProps): JSX.Element | null;
3313
+
3314
+ export declare interface InstallBannerProps {
3315
+ /** Headline. Default `"Instale o app"`. */
3316
+ title?: ReactNode;
3317
+ /** Supporting copy under the title. */
3318
+ description?: ReactNode;
3319
+ /** Install button label. Default `"Instalar"`. */
3320
+ installLabel?: string;
3321
+ /** Accessible label for the dismiss button. Default `"Dispensar"`. */
3322
+ dismissLabel?: string;
3323
+ /** Optional leading icon. */
3324
+ icon?: ReactNode;
3325
+ /**
3326
+ * `localStorage` key used to remember dismissal across reloads. Omit to
3327
+ * make dismissal last only for the current session (component state).
3328
+ */
3329
+ storageKey?: string;
3330
+ /** Called with the user's choice after the install prompt resolves. */
3331
+ onResult?: (outcome: InstallOutcome) => void;
3332
+ className?: string;
3333
+ }
3334
+
3335
+ /**
3336
+ * Button wired to the PWA install prompt ({@link useBeforeInstallPrompt}).
3337
+ * Renders nothing when the app can't be installed — no prompt captured yet,
3338
+ * already installed, or running standalone — so you can drop it anywhere
3339
+ * without guarding visibility yourself.
3340
+ *
3341
+ * Inherits every {@link Button} prop (`variant`, `size`, `leftIcon`, …).
3342
+ *
3343
+ * @example
3344
+ * <InstallButton variant="primary" leftIcon={<Download />} />
3345
+ */
3346
+ export declare function InstallButton({ label, onResult, ...props }: InstallButtonProps): JSX.Element | null;
3347
+
3348
+ export declare interface InstallButtonProps extends Omit<ButtonProps, "onClick" | "children"> {
3349
+ /** Button label. Default `"Instalar app"`. */
3350
+ label?: ReactNode;
3351
+ /** Called with the user's choice after the install prompt resolves. */
3352
+ onResult?: (outcome: InstallOutcome) => void;
3353
+ }
3354
+
3057
3355
  /**
3058
3356
  * Install a `notificationclick` handler that focuses an existing client when
3059
3357
  * possible and falls back to opening a new window.
@@ -3065,6 +3363,8 @@ export declare interface InstallNotificationClickHandlerOptions {
3065
3363
  resolveUrl?: (data: unknown) => string;
3066
3364
  }
3067
3365
 
3366
+ export declare type InstallOutcome = "accepted" | "dismissed" | "unsupported";
3367
+
3068
3368
  /**
3069
3369
  * Precache the app shell at `install` and serve it offline:
3070
3370
  * - reads `precache-manifest.json` (emitted by `tempestPwaManifest()`),
@@ -3140,6 +3440,12 @@ export declare type InterpolationValues = Record<string, string | number>;
3140
3440
  */
3141
3441
  export declare function isApiError(error: unknown): error is ApiError;
3142
3442
 
3443
+ /**
3444
+ * Type guard for {@link Coordinate}: an object with finite, in-range
3445
+ * `latitude` and `longitude`.
3446
+ */
3447
+ export declare function isCoordinate(value: unknown): value is Coordinate;
3448
+
3143
3449
  /**
3144
3450
  * Type guard for a {@link CursorPage} envelope.
3145
3451
  *
@@ -3237,6 +3543,12 @@ export declare function isShareSupported(): boolean;
3237
3543
  */
3238
3544
  export declare function isString(value: unknown): value is string;
3239
3545
 
3546
+ /** True when `value` is a finite latitude in `[-90, 90]`. */
3547
+ export declare function isValidLatitude(value: number): boolean;
3548
+
3549
+ /** True when `value` is a finite longitude in `[-180, 180]`. */
3550
+ export declare function isValidLongitude(value: number): boolean;
3551
+
3240
3552
  /**
3241
3553
  * Renders a `<kbd>` styled like a keyboard key — useful for shortcut hints.
3242
3554
  * Compose multiple keys by rendering siblings: `<Kbd>Ctrl</Kbd> + <Kbd>K</Kbd>`.
@@ -3499,6 +3811,21 @@ export declare interface MenubarProps extends HTMLAttributes<HTMLDivElement> {
3499
3811
  menus: MenubarMenu[];
3500
3812
  }
3501
3813
 
3814
+ /**
3815
+ * Web Mercator (EPSG:3857) latitude clamp. Latitudes beyond this diverge to
3816
+ * infinity in the projection, so tile maps cap here.
3817
+ */
3818
+ export declare const MERCATOR_MAX_LATITUDE = 85.05112878;
3819
+
3820
+ /**
3821
+ * A point projected onto the unit Web Mercator plane. Both axes are in `[0, 1]`
3822
+ * — `x` grows east, `y` grows south (screen convention).
3823
+ */
3824
+ export declare interface MercatorPoint {
3825
+ x: number;
3826
+ y: number;
3827
+ }
3828
+
3502
3829
  export declare type Messages = Record<string, string>;
3503
3830
 
3504
3831
  /**
@@ -3753,6 +4080,12 @@ export declare interface NavigationRailProps extends Omit<HTMLAttributes<HTMLEle
3753
4080
 
3754
4081
  export { NavLink }
3755
4082
 
4083
+ /**
4084
+ * Normalize a longitude into the `[-180, 180]` range, wrapping values that
4085
+ * cross the antimeridian (e.g. `190` → `-170`).
4086
+ */
4087
+ export declare function normalizeLongitude(longitude: number): number;
4088
+
3756
4089
  /**
3757
4090
  * Module-level singleton progress controller. Drive it imperatively from
3758
4091
  * anywhere (router transitions, fetch interceptors) and render the visual bar
@@ -3952,6 +4285,19 @@ export declare interface OpenModalOptions {
3952
4285
  onClose?: () => void;
3953
4286
  }
3954
4287
 
4288
+ /** Options for {@link createOSRMBackend}. */
4289
+ export declare interface OSRMBackendOptions {
4290
+ /**
4291
+ * Base URL of an **OSRM** HTTP server. There is no default on purpose — this
4292
+ * SDK ships no external endpoint. Point it at a routing engine **you host**.
4293
+ */
4294
+ baseUrl: string;
4295
+ /** `fetch` implementation. Default: the global `fetch`. */
4296
+ fetch?: typeof globalThis.fetch;
4297
+ /** Per-mode duration multipliers applied over the car profile. */
4298
+ modeDurationFactors?: Partial<Record<TravelMode, number>>;
4299
+ }
4300
+
3955
4301
  export { Outlet }
3956
4302
 
3957
4303
  /**
@@ -4054,6 +4400,15 @@ export declare type PasswordStrength = 0 | 1 | 2 | 3 | 4;
4054
4400
 
4055
4401
  export { Path }
4056
4402
 
4403
+ /**
4404
+ * Total length of a trajectory: the sum of haversine distances between each
4405
+ * consecutive pair of points. Returns `0` for an empty or single-point path.
4406
+ *
4407
+ * @param points - Ordered coordinates along the path.
4408
+ * @returns Cumulative distance in kilometers.
4409
+ */
4410
+ export declare function pathLengthKm(points: readonly Coordinate[]): number;
4411
+
4057
4412
  /**
4058
4413
  * Extract a permission list from a JWT.
4059
4414
  *
@@ -4134,6 +4489,12 @@ export declare type PinInputSize = "sm" | "md" | "lg";
4134
4489
 
4135
4490
  export declare type PinInputType = "numeric" | "alphanumeric";
4136
4491
 
4492
+ /** A pixel coordinate inside the plotting viewport. */
4493
+ export declare interface PixelPoint {
4494
+ x: number;
4495
+ y: number;
4496
+ }
4497
+
4137
4498
  /**
4138
4499
  * Convenience wrapper around a shared {@link AudioPlayer}. Use this for
4139
4500
  * one-off notification sounds. For more complex flows (e.g. several
@@ -4222,6 +4583,22 @@ export declare interface PortalProps {
4222
4583
  container?: Element | null;
4223
4584
  }
4224
4585
 
4586
+ /** Imperative controller returned by {@link createPositionTracker}. */
4587
+ export declare interface PositionTracker {
4588
+ /** Begin watching position. No-op if already tracking. */
4589
+ start: () => void;
4590
+ /** Stop watching. Retains the recorded trajectory. */
4591
+ stop: () => void;
4592
+ /** Stop and discard the recorded trajectory. */
4593
+ clear: () => void;
4594
+ /** Snapshot of the recorded trajectory (newest last). */
4595
+ readonly points: readonly TrackPoint[];
4596
+ /** Total distance of the recorded trajectory in kilometers. */
4597
+ readonly distanceKm: number;
4598
+ /** Current tracker status. */
4599
+ readonly status: TrackerStatus;
4600
+ }
4601
+
4225
4602
  /**
4226
4603
  * Minimal subset of `posthog-js` used by the adapter. Pass either the real
4227
4604
  * default export (`import posthog from "posthog-js"`) or a stubbed object
@@ -4254,6 +4631,16 @@ export declare interface ProgressProps {
4254
4631
 
4255
4632
  export declare type ProgressVariant = "primary" | "success" | "warning" | "danger";
4256
4633
 
4634
+ /**
4635
+ * Project a geographic coordinate onto the unit Web Mercator plane. This is the
4636
+ * same projection tile servers use, so a self-hosted tile layer and the
4637
+ * tile-free SVG plot line up pixel-for-pixel.
4638
+ *
4639
+ * @param coord - Coordinate to project.
4640
+ * @returns `{ x, y }` in `[0, 1]`.
4641
+ */
4642
+ export declare function projectMercator(coord: Coordinate): MercatorPoint;
4643
+
4257
4644
  /**
4258
4645
  * Service-worker context helpers for handling `push` and `notificationclick`
4259
4646
  * events. Import these inside your own `sw.ts` — they expect to run in the
@@ -4641,6 +5028,21 @@ export declare type RouterKind = "browser" | "hash" | "memory";
4641
5028
 
4642
5029
  export { Routes }
4643
5030
 
5031
+ /**
5032
+ * Pluggable routing backend. A backend turns two coordinates into a
5033
+ * {@link TravelEstimate}. Mirrors the `RoutingBackend` protocol from
5034
+ * `tempest-fastapi-sdk` so client and server share the same contract.
5035
+ *
5036
+ * Implement this to route against your own self-hosted engine (OSRM, Valhalla,
5037
+ * GraphHopper). The offline {@link estimateTravel} heuristic satisfies the same
5038
+ * shape without any network.
5039
+ */
5040
+ export declare interface RoutingBackend {
5041
+ route: (origin: Coordinate, destination: Coordinate, options?: {
5042
+ mode?: TravelMode;
5043
+ }) => Promise<TravelEstimate>;
5044
+ }
5045
+
4644
5046
  /** A single runtime-caching rule, matched against each `GET` request. */
4645
5047
  export declare interface RuntimeRoute {
4646
5048
  /** A `RegExp` tested against the full URL, or a predicate over the parsed URL. */
@@ -4993,13 +5395,33 @@ export declare interface SpacerProps extends HTMLAttributes<HTMLDivElement> {
4993
5395
  axis?: SpacerAxis;
4994
5396
  }
4995
5397
 
4996
- /** Loading spinner with preset sizes (xs..xl). Provide `label` for screen readers. */
4997
- export declare function Spinner({ size, className, label }: SpinnerProps): JSX.Element;
5398
+ /**
5399
+ * Loading spinner with preset sizes (xs..xl). Provide `label` for screen
5400
+ * readers, `caption` for a visible message, and `overlay` to center it inside a
5401
+ * full-area container (e.g. a Suspense / route fallback).
5402
+ *
5403
+ * @example
5404
+ * <Spinner />
5405
+ * <Spinner size="lg" caption="Carregando…" overlay />
5406
+ */
5407
+ export declare function Spinner({ size, className, label, caption, overlay, }: SpinnerProps): JSX.Element;
4998
5408
 
4999
5409
  export declare interface SpinnerProps {
5000
5410
  size?: SpinnerSize;
5001
5411
  className?: string;
5412
+ /** Accessible label announced by screen readers. */
5002
5413
  label?: string;
5414
+ /**
5415
+ * Visible caption rendered under the spinner. When set, the spinner and
5416
+ * caption are wrapped in a centered column.
5417
+ */
5418
+ caption?: ReactNode;
5419
+ /**
5420
+ * Center the spinner inside a full-area overlay (fills the nearest
5421
+ * positioned ancestor; pair with a relative container or a route fallback).
5422
+ * Implies the wrapped layout.
5423
+ */
5424
+ overlay?: boolean;
5003
5425
  }
5004
5426
 
5005
5427
  export declare type SpinnerSize = "xs" | "sm" | "md" | "lg" | "xl";
@@ -5411,7 +5833,7 @@ export declare type ThemeMode = "light" | "dark" | "system";
5411
5833
  * Pair with `themeInitScript()` in the HTML head to prevent the flash of
5412
5834
  * incorrect theme on first paint.
5413
5835
  */
5414
- export declare function ThemeProvider({ children, defaultTheme, storageKey, target, attribute, }: ThemeProviderProps): JSX.Element;
5836
+ export declare function ThemeProvider({ children, defaultTheme, storageKey, target, attribute, themeColor, }: ThemeProviderProps): JSX.Element;
5415
5837
 
5416
5838
  export declare interface ThemeProviderProps {
5417
5839
  children: ReactNode;
@@ -5420,12 +5842,30 @@ export declare interface ThemeProviderProps {
5420
5842
  /** localStorage key used to persist the preference. Pass `null` to disable persistence. Default: `"tempest-theme"`. */
5421
5843
  storageKey?: string | null;
5422
5844
  /**
5423
- * Element that receives the `data-tempest-theme` attribute. Defaults to
5845
+ * Element that receives the theme attribute(s). Defaults to
5424
5846
  * `document.documentElement`. Override when scoping the theme to a subtree.
5425
5847
  */
5426
5848
  target?: () => HTMLElement | null;
5427
- /** Attribute name written on the target. Default: `"data-tempest-theme"`. */
5428
- attribute?: string;
5849
+ /**
5850
+ * Attribute name(s) written on the target with the resolved theme
5851
+ * (`"light"` / `"dark"`). Default: `"data-tempest-theme"`.
5852
+ *
5853
+ * Pass an array to mirror the theme onto more than one attribute — handy
5854
+ * when the SDK components read `data-tempest-theme` but the host app's own
5855
+ * CSS keys off a different attribute (e.g. `["data-tempest-theme",
5856
+ * "data-theme"]`). Avoids a separate sync effect in the consumer.
5857
+ */
5858
+ attribute?: string | string[];
5859
+ /**
5860
+ * When set, keeps `<meta name="theme-color">` in sync with the resolved
5861
+ * theme — `content` becomes `themeColor.dark` in dark mode and
5862
+ * `themeColor.light` in light mode. The meta tag must already exist in the
5863
+ * document `<head>`. No-op when omitted.
5864
+ */
5865
+ themeColor?: {
5866
+ light: string;
5867
+ dark: string;
5868
+ };
5429
5869
  }
5430
5870
 
5431
5871
  /**
@@ -5644,6 +6084,89 @@ export declare interface TooltipProps {
5644
6084
  openDelay?: number;
5645
6085
  }
5646
6086
 
6087
+ /** Convert degrees to radians. */
6088
+ export declare function toRadians(degrees: number): number;
6089
+
6090
+ /** Lifecycle status of a {@link PositionTracker}. */
6091
+ export declare type TrackerStatus = "idle" | "tracking" | "error";
6092
+
6093
+ /**
6094
+ * A single sample in a recorded trajectory: a {@link Coordinate} stamped with
6095
+ * the epoch millisecond it was captured, plus the optional accuracy radius
6096
+ * reported by the Geolocation API.
6097
+ */
6098
+ export declare interface TrackPoint extends Coordinate {
6099
+ /** Capture time in epoch milliseconds (`GeolocationPosition.timestamp`). */
6100
+ timestamp: number;
6101
+ /** Horizontal accuracy radius in meters, if the device reported one. */
6102
+ accuracy?: number;
6103
+ }
6104
+
6105
+ /**
6106
+ * Plot a GPS trajectory. By default it renders a **tile-free SVG** map (Web
6107
+ * Mercator projection, auto-fit to the points, optional grid + scale bar) —
6108
+ * 100% self-contained, no external tiles or paid API. Pass `tileUrl` pointing
6109
+ * at a map server **you host** to upgrade to a real Leaflet tile layer.
6110
+ *
6111
+ * @example
6112
+ * // Zero-dependency SVG plot
6113
+ * <TrajectoryMap points={points} current={lastPoint} height={360} />
6114
+ *
6115
+ * @example
6116
+ * // Real tiles from your own server (requires `leaflet` installed)
6117
+ * <TrajectoryMap points={points} tileUrl="https://tiles.internal/{z}/{x}/{y}.png" />
6118
+ */
6119
+ export declare function TrajectoryMap({ points, current, height, padding, showGrid, showScale, strokeColor, tileUrl, tileAttribution, label, className, style, ...rest }: TrajectoryMapProps): JSX.Element;
6120
+
6121
+ export declare interface TrajectoryMapProps extends Omit<HTMLAttributes<HTMLDivElement>, "children"> {
6122
+ /** Ordered coordinates forming the trajectory. */
6123
+ points: readonly Coordinate[];
6124
+ /** Optional "current position" marker, drawn on top of the path. */
6125
+ current?: Coordinate | null;
6126
+ /** Viewport height in pixels. Default: `320`. */
6127
+ height?: number;
6128
+ /** Inner padding in pixels kept clear on every edge. Default: `24`. */
6129
+ padding?: number;
6130
+ /** Draw a light reference grid behind the path (SVG mode). Default: `true`. */
6131
+ showGrid?: boolean;
6132
+ /** Draw a distance scale bar (SVG mode). Default: `true`. */
6133
+ showScale?: boolean;
6134
+ /** Path stroke color. Default: the primary token. */
6135
+ strokeColor?: string;
6136
+ /**
6137
+ * Tile URL template (`{z}/{x}/{y}`) of a map server **you host**. When set,
6138
+ * the map upgrades to a real Leaflet tile layer (requires `leaflet` to be
6139
+ * installed). Omit for the zero-dependency, zero-network SVG plot.
6140
+ */
6141
+ tileUrl?: string;
6142
+ /** Attribution text shown over the tile layer. */
6143
+ tileAttribution?: string;
6144
+ /** Accessible label for the map region. Default: `"Trajetória"`. */
6145
+ label?: string;
6146
+ }
6147
+
6148
+ /**
6149
+ * Estimated travel between two coordinates. Mirrors the `TravelEstimate`
6150
+ * schema from `tempest-fastapi-sdk` (`geo/schemas.py`), snake_case preserved so
6151
+ * a response deserializes straight into this type.
6152
+ */
6153
+ export declare interface TravelEstimate {
6154
+ /** Travel mode the estimate was computed for. */
6155
+ mode: TravelMode;
6156
+ /** Great-circle distance scaled by circuity, in kilometers (`>= 0`). */
6157
+ distance_km: number;
6158
+ /** Estimated duration in minutes (`>= 0`). */
6159
+ duration_minutes: number;
6160
+ /** How the estimate was produced. `"heuristic"` (offline) or `"osrm"`. */
6161
+ source: "heuristic" | "osrm";
6162
+ }
6163
+
6164
+ /**
6165
+ * Travel mode. Mirrors the `TravelMode` string enum from `tempest-fastapi-sdk`
6166
+ * (`geo/enums.py`) — the on-the-wire value is the raw string.
6167
+ */
6168
+ export declare type TravelMode = "car" | "motorcycle" | "bus";
6169
+
5647
6170
  /**
5648
6171
  * Truncate a string to `max` characters, appending `suffix` when cut.
5649
6172
  * Returns the original when shorter than (or equal to) `max`.
@@ -5691,6 +6214,15 @@ export declare function uniqueBy<T>(items: T[], key: (item: T) => unknown): T[];
5691
6214
  /** Strip any masking and return only digits. */
5692
6215
  export declare function unmask(value: string): string;
5693
6216
 
6217
+ /**
6218
+ * Inverse of {@link projectMercator}: recover a coordinate from a unit-plane
6219
+ * point.
6220
+ *
6221
+ * @param point - `{ x, y }` in `[0, 1]`.
6222
+ * @returns The geographic coordinate.
6223
+ */
6224
+ export declare function unprojectMercator(point: MercatorPoint): Coordinate;
6225
+
5694
6226
  /**
5695
6227
  * Unregister all registered service workers for this origin.
5696
6228
  *
@@ -5826,6 +6358,12 @@ export declare interface UseBeforeInstallPromptResult {
5826
6358
  installable: boolean;
5827
6359
  /** True after the user accepts the install prompt. */
5828
6360
  installed: boolean;
6361
+ /**
6362
+ * True when the app is already running as an installed PWA (display-mode
6363
+ * `standalone`/`fullscreen`/`minimal-ui`, or iOS `navigator.standalone`).
6364
+ * Use it to hide install affordances for users who already installed.
6365
+ */
6366
+ isStandalone: boolean;
5829
6367
  /** Show the install prompt. Resolves with the user's choice. */
5830
6368
  prompt: () => Promise<"accepted" | "dismissed" | "unsupported">;
5831
6369
  }
@@ -6494,6 +7032,49 @@ export declare interface UsePollResult<T> {
6494
7032
  start: () => void;
6495
7033
  }
6496
7034
 
7035
+ /**
7036
+ * React hook wrapping {@link createPositionTracker}. Records a live GPS
7037
+ * trajectory tied to the component lifecycle — the watch is torn down on
7038
+ * unmount. Fully browser-side; no external service.
7039
+ *
7040
+ * @example
7041
+ * const { points, distanceKm, isTracking, start, stop } = usePositionTracker({
7042
+ * minDistanceKm: 0.01,
7043
+ * });
7044
+ * return (
7045
+ * <>
7046
+ * <button onClick={isTracking ? stop : start}>{isTracking ? "Parar" : "Rastrear"}</button>
7047
+ * <TrajectoryMap points={points} />
7048
+ * <span>{distanceKm.toFixed(2)} km</span>
7049
+ * </>
7050
+ * );
7051
+ */
7052
+ export declare function usePositionTracker(options?: UsePositionTrackerOptions): UsePositionTrackerResult;
7053
+
7054
+ export declare interface UsePositionTrackerOptions extends Omit<CreatePositionTrackerOptions, "onUpdate" | "onStatusChange"> {
7055
+ /** Start tracking automatically on mount. Default: `false`. */
7056
+ autoStart?: boolean;
7057
+ }
7058
+
7059
+ export declare interface UsePositionTrackerResult {
7060
+ /** Recorded trajectory (newest last). */
7061
+ points: readonly TrackPoint[];
7062
+ /** Most recent point, or `null` before the first fix. */
7063
+ lastPoint: TrackPoint | null;
7064
+ /** Total distance of the trajectory in kilometers. */
7065
+ distanceKm: number;
7066
+ /** Current tracker status. */
7067
+ status: TrackerStatus;
7068
+ /** True while `status === "tracking"`. */
7069
+ isTracking: boolean;
7070
+ /** Begin watching position. */
7071
+ start: () => void;
7072
+ /** Stop watching, keeping the trajectory. */
7073
+ stop: () => void;
7074
+ /** Stop and discard the trajectory. */
7075
+ clear: () => void;
7076
+ }
7077
+
6497
7078
  /**
6498
7079
  * Return the value from the previous render. `undefined` on the first render.
6499
7080
  *