Files
opencloud/AGENTS.md
T
Mplan 1e0da1fe36 docs: update AGENTS.md with current architecture and fix timeline slider reset
- 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
2026-05-24 17:26:48 +08:00

6.8 KiB

AGENTS.md

Commands

  • npm run dev — Vite dev server with HMR
  • npm run buildvue-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 both vite.config.ts and tsconfig.app.json)
  • Supabase client singleton at src/lib/supabase.ts — throws at import if env vars missing
  • AMap loaded lazily via src/lib/amap.ts with type declarations in src/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: requiresAuth redirects to /login, requiresAdmin redirects to /403
  • SEO meta tags applied per-route via lib/seo.ts in router.afterEach

Auth Flow

  • main.ts initializes auth before mounting: authStore.initialize() is called, which calls getSession() and subscribes to onAuthStateChange. The app only mounts after the initial session check completes.
  • Login sets user.value explicitly: login() extracts data.user from signInWithPassword and assigns it to the store directly, rather than relying on onAuthStateChange.
  • Profile is auto-created by DB trigger (handle_new_user on auth.users), not by the frontend.
  • Auth error messages are translated to Chinese in the store.
  • register() checks username uniqueness before calling signUp() — username is passed via options.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_types table. Fetched once, shared across views.
  • encyclopedia — Cloud types + user's collection (unlock state). Tracks unlockPercent for progress display. Depends on authStore.
  • profile — User profile pages. Fetches profile + cloud list per-user. Supports update/delete/visibility-toggle with optimistic cache patching. deleteClouds also removes from Supabase Storage.

Supabase

  • Env var is VITE_SUPABASE_PUBLISHABLE_KEY (not VITE_SUPABASE_ANON_KEY), using Supabase's sb_publishable_ key format.
  • All tables have RLS enabled. Check plan.md section 10 for the full schema and RLS policies.
  • Storage bucket clouds is public read, authenticated upload.
  • Profile role field (user/admin) controls admin access — checked in route guard, not in JWT metadata.
  • user_collections tracks encyclopedia unlocks with first_cloud_id FK to clouds.
  • 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 a count query and a data query that run in parallel via Promise.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

  • useUpload composable 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 with status: 'pending', badge unlock detection via user_collections upsert.
  • UploadView is the full-page batch uploader. QuickUploadModal is 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: generates robots.txt and sitemap.xml at build time. Disallows /admin, /auth/, /login, /register, /upload, /profile/settings.
  • lib/canvas.ts: patches HTMLCanvasElement.getContext('2d') to always pass willReadFrequently: true — needed for the badge card renderer in lib/cloudBadges.ts.

Environment Variables

Required (app won't start without):

  • VITE_SUPABASE_URL
  • VITE_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