npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@karibukit/planner-ui

v0.3.0

Published

The RafikiGo Planner UI (trip-concierge plan Phase 3): `PlannerApp` — chat + animated route map + itinerary day cards, mobile-first, themable per tenant. Consumed by rafikigo-web (Astro island, `/planner`) and marahill-fe (Next route, `/plan`).

Downloads

771

Readme

@karibukit/planner-ui

The RafikiGo Planner UI (trip-concierge plan Phase 3): PlannerApp — chat + animated route map + itinerary day cards, mobile-first, themable per tenant. Consumed by rafikigo-web (Astro island, /planner) and marahill-fe (Next route, /plan).

Usage

import { PlannerApp } from '@karibukit/planner-ui'
import '@karibukit/planner-ui/styles.css'
import 'maplibre-gl/dist/maplibre-gl.css'

<PlannerApp
  apiBaseUrl="https://api.karibukit.com"
  tenantSlug="rafikigo"
  publicToken={PLANNER_PUBLIC_TOKEN}      // the tenant's public token
  whatsappNumber="+254 114 505977"
  mapStyle={mapStyleUrlOrSpec}            // host picks the tile source
  theme={{ headingFont: "'Fraunces', serif" }}  // tenant themeTokens
  utm={utmParamsFromUrl}                  // lands in session.utm
  prefillMessage={prefillFromQueryParams} // ?d=&m=&p=&i= → seeds the composer
  autoSendPrefill={goFlagFromUrl}         // &go=1 (host forms only) → sends it instead
/>
  • Layout: the component fills its container (height: 100%) — give it a full-height parent. Desktop ≥1024px: chat left, map + day cards right. Mobile: full-screen chat, sticky trip bar, slide-over sheet.
  • Sessions are created lazily on the first send. Retries reuse the clientMsgId, so the server's idempotent replay applies.
  • Standalone map: import { RouteMap } from '@karibukit/planner-ui/map' — deliberately a separate entry so maplibre-gl stays out of the first-paint chunk (PlannerApp lazy-loads it with the first route).
  • Theming: every color/typeface routes through --kkp-* CSS variables; pass theme (see PlannerTheme) built from the tenant's themeTokens.

Demo

pnpm --filter @karibukit/planner-ui dev   # http://localhost:5199

Runs against a scripted mock client by default (full conversation offline: try a starter, then "is it available?", then "book it").

The mock is a rendering fixture, not the agent — say so before demoing it. It knows exactly two itineraries (6-day and 8-day, picked by regex on 8 days|two weeks|2 weeks|beach) and its price band is a hard-coded literal that the 8-day branch never touches. So "make it 3 days" silently returns the 6-day trip, and the price never moves however the trip changes. Both read as product bugs to anyone walking through it. Set the two env vars below to test real planning.

Demo URLs:

| | | |---|---| | / | the planner | | /?frame=phone | the planner inside a real 375×812 viewport | | /?view=share | the public trip page, demo fixture | | /?view=share&trip=<shareId> | the public trip page for a REAL trip, fetched from the configured API |

That last one exists because a trip's share URL is ${tenant.publicBaseUrl}/trip/<shareId> — production, by construction, since it's what you hand another human. Nothing in this monorepo serves that path: SharePage is a component and the host site mounts it. So the itinerary's "View" button on a locally-planned trip opens the live domain. As of 2026-08-25 rafikigo.com/planner is 200 but /planner/trip/* is 404 — the host route was never built, so every share link the planner has produced (the WhatsApp button and get_share_link included) is dead. demo/SharePreview.tsx is the reference implementation for that missing route.

Env in packages/planner-ui/.env:

  • VITE_MAPTILER_KEY — vector tiles (falls back to OSM raster, dev only)
  • VITE_PLANNER_API_BASE + VITE_PLANNER_PUBLIC_TOKEN — hit a real API instead of the mock

Release contract (how this package ships)

The ranger-widget npm channel exists but is manual and version-drifted — this package fixes the process instead of inheriting it:

  1. Build produces compiled ESM + bundled .d.ts (chat-protocol's types are inlined — it is private TS source no consumer can install) + dist/styles.css.
  2. CI runs pack:smoke on every push: entry files present in the tarball, no workspace: ranges in installable fields, block types bundled, maplibre-gl absent from the main chunk.
  3. Publish is explicit and versioned:
    cd packages/planner-ui
    pnpm typecheck && pnpm test && pnpm build && pnpm pack:smoke
    npm version <patch|minor>       # bump + git tag planner-ui-vX.Y.Z
    npm publish --access public
    Public, matching @karibukit/ranger-widget (already public on npm under the same scope). Nothing in the tarball is secret — files ships dist only, and the tenant token is a host-supplied prop. Access control lives in the API: a stranger's install fails verifyToken (401), and a valid token from an unlisted domain fails verifyOrigin (403). Restricted would need a paid npm org plus an auth token in every host's CI, to protect UI code that isn't sensitive.
  4. Hosts pin exact versions ("@karibukit/planner-ui": "0.1.0", no ^) and bump deliberately. A host upgrade PR states the version delta and what changed. Never point a host at a git branch or a local tarball in prod.

Map data

src/map/legs/*.geojson — committed OSRM road polylines, one per pair in the operator road network (trip/destinations.ts DRIVE_HOURS), keyed by the API's real destination slugs. 27 legs, ~23KB gz (budget 150KB). One geometry serves both directions; unknown pairs fall back to a straight line (logged) so the map never breaks the page.

| | | |---|---| | Source | OSRM demo server, road data © OpenStreetMap contributors (ODbL) | | Generated | 2026-08-24 via pnpm generate:legs | | Simplification | turf tolerance 0.0005 (~55m), 5-decimal coords |

src/map/legs/anchors.json — generated destination anchors (display name, prose name, coord per slug); the same script writes an identical copy to apps/api/src/ai/planner/anchors.json for the OG card (an og-image unit test keeps them matching). legIndex, prefill and og-image import it — never hand-edit the JSON.

Regeneration is rare and manual: add the destination to PLACES and the pair to LEGS in scripts/generate-leg-polylines.ts, run pnpm generate:legs (polite to the shared OSRM server: sequential, 1s apart; enforces the size budget). Name/coord edits alone: pnpm generate:legs --anchors-only — no OSRM traffic.

Tile hosting

The host passes mapStyle; nothing in this package assumes a tile vendor. Current lean (spike decision, unchanged): MapTiler free tier with an origin-restricted key; revisit self-hosted Protomaps only if tile spend becomes real money. Deploy-time call with NJ.

Deferred (with triggers)

  • Curated-trips starter row — needs an engine endpoint that lists curated trips; add when the engine exposes one.
  • ~~Chat transcript restore on reload~~ — shipped. session-store.ts keeps the session JWT in sessionStorage and getHistory replays the turns through the same reducer as live ones, so the card, map and cards all come back (QA 2026-08-24). The waiting state for it landed after QA 2026-08-25 — a reload used to paint the cold-start screen until the fetch returned.
  • Per-day drive-hours chip — day cards carry hours in prose; a structured chip needs driveHours on the itinerary block's day shape (API change). The LONG_DRIVE warning renders amber in "Worth knowing" instead.