尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证正在加载完整 README。 以下为已保存的摘录。
A fun, interactive way to track every state you've visited in North America —
mark states off on a live map, capture the memories and photos from each
trip, and relive them in a slideshow. Photo book printing is on the roadmap.
Deployed on Vercel (auto-deploys from main), backed
by Supabase for the database and photo storage.
Next.js 16 (App Router) + React 19 + TypeScript + Tailwind CSS 4, backed by
Supabase (Postgres + Storage). No local database
file, no persistent-volume requirement — the app is stateless and can
deploy to any Node host.查看完整 README ↗Live at trailmarks-lovat.vercel.app
A fun, interactive way to track every state you've visited in North America — mark states off on a live map, capture the memories and photos from each trip, and relive them in a slideshow. Photo book printing is on the roadmap.
Deployed on Vercel (auto-deploys from main), backed
by Supabase for the database and photo storage.
Next.js 16 (App Router) + React 19 + TypeScript + Tailwind CSS 4, backed by Supabase (Postgres + Storage). No local database file, no persistent-volume requirement — the app is stateless and can deploy to any Node host.
react-simple-maps +
us-atlas TopoJSON — a real,
accurate geographic map (not a hand-drawn approximation), rendered with
geoAlbersUsa so Alaska and Hawaii sit as insets like on a printed map.sharp on upload, then served through Next's image optimizer.maplibre-gl with free,
no-API-key tiles from OpenFreeMap (positron
style) — real streets/terrain, not a choropleth, so a recorded route draws
as an actual line over the map like Polarsteps. MapLibre's web worker
can't be found once Next bundles the library, so
scripts/copy-maplibre-worker.mjs (run by predev/prebuild) copies it
into public/vendor/maplibre/ and TripMap points setWorkerUrl() there."Glacier": modeled on the photo-first travel apps the owner picked as
references. Cool mist and white surfaces let the photos carry the color.
Petrol (deep teal) is for anything you act on. Aqua marks land and
routes you've claimed. Sun (amber) is for highlights and achievements,
and Coral is used only for recording and errors. There is a true dark mode.
Tokens live in src/app/globals.css; never hard-code hex values in components.
.brand-gradient. .glass is for stat chips on
photos, and .photo-scrim keeps white type legible on them.Panel) with a soft cool shadow and a 28px
radius. Buttons come in primary, secondary and quiet for the page,
plus light and glass for photos.src/lib/motion.ts holds the shared easings and springs. Respect
prefers-reduced-motion (handled globally via MotionConfig).src/components/map/UsMap.tsx) is hand-built on d3-geo +
us-atlas's pre-projected Albers file, so every state is its own animatable
path. It has a sunrise-sweep intro; tap to claim (the edge glows and the
color blooms outward from your finger, gold when it completes the family),
press and hold to unclaim (a ring fills under your finger); haptics; and an
amber outline trace on select.src/lib/supabase.ts — single getSupabase() singleton, a
@supabase/supabase-js client authenticated with the service_role
key. All data access goes through this app's own API routes on the
server; the browser never talks to Supabase directly, so no key is ever
exposed client-side and Row Level Security on every table is left "on,
no policies" as a second lock (the service role bypasses RLS regardless).SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY
(Supabase dashboard → your project → Settings → API). Put them in
.env.local for local dev (gitignored) and in your host's environment
variables for production.photos (resized
to max 2400px and re-encoded to WebP with sharp first) and are served
straight from Supabase's CDN — next.config.ts allow-lists
*.supabase.co/storage/v1/object/public/** for next/image.src/lib/statesData.ts — the 50 states + DC, keyed by USPS code, with a
FIPS code for matching against the TopoJSON id, region, land area (for
the "% of North America explored" stat), and a fun fact.families (name + invite code), profiles (one per person:
family, display name, map color), state_visits (one row per person per
state, visited flag + first-visited date), memories (title/body/date,
optionally linked to a state and/or a trip, with lat/lng for trip
"steps"), photos (file name + caption, linked to a memory), trips
(title/status/dates), trip_points (the raw GPS breadcrumb trail for a
trip). Trips, memories and photos carry user_id (author) and family_id./join starts a new family; /join?code=XXXXXXXX joins one
from its invite link (on /family). Accounts are created server-side as
already-confirmed users (src/lib/family.ts → createAccount), so no
confirmation email or Supabase email setup is needed.Campfire.tsx). The stats drawer adds a
family ranking and an "everyone's been" count./join also takes a typed code.src/lib/photoBadge.ts, unlimited) or, up to 5 times per person, an
AI illustration (/api/avatar/illustrate, AI Gateway, medium quality,
about 1 cent each). Every illustration is kept in adventurers to switch
between; a failed call doesn't use a try. Only finished avatars are stored,
never the selfie, in the private avatars bucket (/a/<file>)./family, anyone can make another
member a one-time, 48-hour link (src/lib/passwordReset.ts; only a hash is
stored). You can change your own password there too./p/<file>, resized via a custom next/image
loader), avatars and voice notes are all in private buckets, served only to
the owner's family.src/proxy.ts refreshes the session cookie on every request and sends
anyone signed out to /login (API routes get a 401). Pages call
requireViewer() and API routes apiViewer() (src/lib/auth.ts), then
scope every query by the viewer's family and check ownership on writes.src/lib/supabaseConfig.ts,
overridable with SUPABASE_PUBLISHABLE_KEY). It is public by design and
reads nothing on its own, since every table has RLS on with no policies;
data access stays on the server-side service role client.Starting a trip (/trips/new → trip page → "Start recording") begins a
navigator.geolocation.watchPosition session that streams points to
/api/trips/[id]/points, throttled to roughly one point per ~10 seconds or
~13 meters of movement. src/components/TripMap.tsx (MapLibre) draws that
breadcrumb trail as a live route line on a real map, and
src/lib/stateLookup.ts does a point-in-polygon check (via d3-geo's
geoContains against the same us-atlas state polygons the choropleth
map uses) to figure out which states a trip actually passed through.
Finishing a trip (src/lib/trips.ts → finishTrip) runs that detection
over every recorded point and merges the results into state_visits — so a
recorded road trip auto-marks the states/countries it touched, the same way
the manual tap-to-mark map does. Both stay available: manual marking for
quick logging or trips with no GPS data, auto-detection for anything
actually recorded.
Important limitation — read before assuming this behaves like Life360 or Polarsteps' native app: this is a website, not a phone app with background location permission. GPS recording only runs while that trip's page is open in the foreground (screen on, tab active) — it stops the moment the tab is closed or the phone is locked/backgrounded. The UI says "Recording — keep this tab open" for exactly this reason. True background tracking (recording a drive while the phone is in your pocket) needs a native app or a PWA with background geolocation, which is a real follow-up project, not a setting to flip. Points recorded so far are never lost when recording stops — resuming just continues appending to the same trip.
The trailmarks Supabase project (org: benjaminjakeallen-alt's Org) already
has this schema applied via migration:
families, profiles, state_visits, trips, trip_points,
memories, photos — the shapes described above. Migrations since
family accounts live in supabase/migrations/.photos.To point a fresh checkout at it (or a different Supabase project), set:
SUPABASE_URL=https://<project-ref>.supabase.co
SUPABASE_SERVICE_ROLE_KEY=<service_role secret, from Settings → API>
Setting up a brand-new Supabase project instead of reusing this one? The
migration SQL is straightforward to reconstruct from src/lib/supabase.ts,
src/lib/trips.ts, and src/lib/memories.ts's row shapes, or ask Claude to
regenerate it from this repo's history.
npm install
npm run dev
Open http://localhost:3000. The map starts empty — click any state to mark it visited (or start a trip under Trips to record a real route), which takes you to that page to add memories and photos.
Geolocation requires a "secure context" (HTTPS, or localhost — which
npm run dev already is) or the browser will refuse it outright.
On the map, a state's Open journal grows a slide-over panel out of the
button, docked on the right: that state's family memories plus the composer, without leaving the
map (the full /states/[code] page is a link away). Every composer has a
mic: speak and the words stream into the entry (browser speech recognition,
src/lib/useDictation.ts), while the recording itself is kept and plays back
on the memory (src/lib/useRecorder.ts). Without live speech recognition the
clip is transcribed on the server.
The map's USA / World switch turns to a globe (WorldGlobe.tsx): drag to
spin, tap a continent and it turns to face you, tap a country to claim it,
hold to unclaim, search for the small ones. See docs/BACKLOG.md item 8.
On a trip page, Import photos reads each photo's capture time and GPS in
the browser, joins photos to nearby same-day steps, groups the rest into new
stops, and shows the plan for review before uploading. See
docs/BACKLOG.md item 7 for the rules and the iPhone picker's limits.
Detailed feature ideas awaiting a go-ahead live in docs/BACKLOG.md.
statesData.ts and swapping in a North America TopoJSON.docs/BACKLOG.md items 9 and 10.