1e0da1fe36
- Rewrite AGENTS.md with directory map, route table, store docs, and build/deploy information reflecting current project state - Fix map timeline slider not resetting to current time when reopening the controls panel
6.8 KiB
6.8 KiB
AGENTS.md
Commands
npm run dev— Vite dev server with HMRnpm run build—vue-tsc -b && vite build(typecheck must pass or build aborts)npx vue-tsc -b— standalone typecheck (no script in package.json)- No linter, formatter, or test runner exists
Architecture
- Vue 3 + Vite + Tailwind CSS + Vue Router + Pinia + Supabase + AMap (高德地图) + Naive UI
- Path alias
@/→src/(configured in bothvite.config.tsandtsconfig.app.json) - Supabase client singleton at
src/lib/supabase.ts— throws at import if env vars missing - AMap loaded lazily via
src/lib/amap.tswith type declarations insrc/types/amap.d.ts - UI language is Chinese (zh-CN)
- All DB operations are direct
supabase.from(...)calls from the browser — security depends on Supabase RLS policies
Directory Map
| Path | Purpose |
|---|---|
src/lib/ |
Singletons and utilities: supabase, amap, canvas patch, cloudTypes constants, cloudBadges canvas renderer, SEO meta builder |
src/stores/ |
Pinia stores: auth, clouds (cloud_types cache), encyclopedia (collection + unlock tracking), profile (user pages + cloud CRUD) |
src/composables/ |
Vue composables: useUpload (batch upload with thumbnail generation, EXIF extraction, badge unlocking) |
src/components/cloud/ |
Cloud-related modals and widgets: ImageDetailModal, CloudEditModal, MapPickerModal, QuickUploadModal, MiniLocationMap |
src/components/layout/ |
AppHeader (top nav bar with auth state) |
src/components/profile/ |
ContributionHeatmap |
src/views/ |
Route-level page components (see Routes below) |
src/types/ |
TypeScript types: database models (database.ts), AMap declarations (amap.d.ts), router meta (router.d.ts) |
Routes
| Path | View | Auth Required |
|---|---|---|
/ |
MapView | No |
/login, /register, /auth/confirm, /auth/reset-password |
Auth views | No |
/upload |
UploadView | Yes |
/encyclopedia |
EncyclopediaView | No |
/encyclopedia/:id |
CloudTypeView | No |
/gallery |
GalleryView | No |
/community |
CommunityView | No |
/profile |
ProfileView (own) | Yes |
/profile/settings |
ProfileSettingsView | Yes |
/profile/:id |
ProfileView (public) | No |
/admin |
AdminView | Yes (admin) |
/403 |
ForbiddenView | No |
/:pathMatch(.*)* |
NotFoundView | No |
- Route guards in
router/index.ts:requiresAuthredirects to/login,requiresAdminredirects to/403 - SEO meta tags applied per-route via
lib/seo.tsinrouter.afterEach
Auth Flow
main.tsinitializes auth before mounting:authStore.initialize()is called, which callsgetSession()and subscribes toonAuthStateChange. The app only mounts after the initial session check completes.- Login sets
user.valueexplicitly:login()extractsdata.userfromsignInWithPasswordand assigns it to the store directly, rather than relying ononAuthStateChange. - Profile is auto-created by DB trigger (
handle_new_useronauth.users), not by the frontend. - Auth error messages are translated to Chinese in the store.
register()checks username uniqueness before callingsignUp()— username is passed viaoptions.data.username.
Stores
- auth — User session, profile, login/register/logout, username/password update, password reset.
initialize()must be called before app mounts. - clouds — Simple cache of
cloud_typestable. Fetched once, shared across views. - encyclopedia — Cloud types + user's collection (unlock state). Tracks
unlockPercentfor progress display. Depends onauthStore. - profile — User profile pages. Fetches profile + cloud list per-user. Supports update/delete/visibility-toggle with optimistic cache patching.
deleteCloudsalso removes from Supabase Storage.
Supabase
- Env var is
VITE_SUPABASE_PUBLISHABLE_KEY(notVITE_SUPABASE_ANON_KEY), using Supabase'ssb_publishable_key format. - All tables have RLS enabled. Check
plan.mdsection 10 for the full schema and RLS policies. - Storage bucket
cloudsis public read, authenticated upload. - Profile
rolefield (user/admin) controls admin access — checked in route guard, not in JWT metadata. user_collectionstracks encyclopedia unlocks withfirst_cloud_idFK toclouds.
Gallery Pagination
- Page-based navigation (50 items/page), not infinite scroll.
resolveSearchFilters()pre-fetches user IDs or cloud type IDs before the main query.buildFilteredQuery()returns a chainable query builder;loadPage()forks it into acountquery and adataquery that run in parallel viaPromise.all.- Search debounced at 250ms.
Map Timeline
- Realtime mode: slider selects a minute of today, shows clouds captured within 2 hours before that time. Marker opacity decays with age.
- Archive mode: browse clouds by day or month. Toggling timeline controls closed auto-returns to realtime.
- Slider resets to current time each time the controls panel is opened.
Upload Flow
useUploadcomposable handles: file selection (drag/drop/click), client-side thumbnail generation (JPEG, max 640px edge, 0.72 quality), EXIF date extraction, coordinate blurring (2 decimal places), Supabase Storage upload (original + thumbnail), DB insert withstatus: 'pending', badge unlock detection viauser_collectionsupsert.UploadViewis the full-page batch uploader.QuickUploadModalis a single-image shortcut from the map page.
Build & Deploy
- Vercel with
vercel.json: SPA rewrites, security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy). No CSP or HSTS configured. - SEO plugin in
vite.config.ts: generatesrobots.txtandsitemap.xmlat build time. Disallows/admin,/auth/,/login,/register,/upload,/profile/settings. lib/canvas.ts: patchesHTMLCanvasElement.getContext('2d')to always passwillReadFrequently: true— needed for the badge card renderer inlib/cloudBadges.ts.
Environment Variables
Required (app won't start without):
VITE_SUPABASE_URLVITE_SUPABASE_PUBLISHABLE_KEY
Required for map features:
VITE_AMAP_KEY
Optional:
VITE_SITE_URL— canonical URL for SEO meta and sitemap (falls back to Vercel env or hardcoded default)
Future (not in .env):
OPENAI_API_KEY,OPENWEATHERMAP_API_KEY
Naive UI
- Used for modals, buttons, inputs, tags, progress bars, alerts, dropdowns, empty states, skeletons, and message toasts.
- No SSR — pure client-side rendering.
- Custom CSS overrides via scoped
<style>blocks for slider styling, transitions, and component tweaks.
MVP Constraints (from plan.md)
- No Supabase Realtime — refresh-based loading
- No OAuth — email/password only, email confirmation required
- No AI cloud identification — manual type selection
- AMap only (China-focused), no Mapbox fallback yet