Files
2026-07-28 21:31:38 +08:00

14 KiB
Raw Permalink Blame History

AGENTS.md

Commands

  • npm run dev — Vite dev server with HMR
  • npm run check:styles — validates shared style contracts and blocks deprecated palettes/classes
  • npm run build — vue-tsc -b && vite build (typecheck must pass or build aborts)
  • npx vue-tsc -b — standalone typecheck (no script in package.json)
  • npm run preview — previews the production build on strict port 5173
  • No general linter, formatter, or test runner exists; check:styles is the project-specific visual-system check

Architecture

  • Vue 3 + Vite + Tailwind CSS + Vue Router + Pinia + FastAPI + AMap (高德地图) + Naive UI
  • Path alias @/ → src/ (configured in both vite.config.ts and tsconfig.app.json)
  • FastAPI client at src/lib/api.ts; API base URL comes from VITE_API_URL
  • AMap loaded lazily via src/lib/amap.ts with type declarations in src/types/amap.d.ts
  • UI language is Chinese (zh-CN)
  • All persistence, storage, authorization, and admin operations go through the FastAPI backend

Directory Map

Path Purpose
src/lib/ Singletons and utilities: FastAPI client, 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 multipart upload, EXIF extraction, badge result handling)
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/style.css Global stylesheet assembly point; imports Tailwind, tokens, base rules, and shared components
src/styles/ Visual system source: semantic tokens, global square-corner baseline, shared oc-* component contracts
src/lib/theme.ts Naive UI theme overrides aligned with the semantic tokens
src/views/ Route-level page components (see Routes below)
src/types/ TypeScript types: domain models (database.ts), API DTOs (api.ts), AMap declarations (amap.d.ts), router meta (router.d.ts)
docs/style-system.md Concise implementation and naming guide for the visual system
scripts/check-style-system.mjs Automated guard against deprecated palettes and malformed shared classes

Routes

Path View Auth Required
/ MapView No
/login, /register, /forgot-password, /auth/confirm, /auth/reset-password Auth views No
/upload UploadView Yes
/encyclopedia EncyclopediaView Yes
/encyclopedia/:id CloudTypeView Yes
/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
/401 AuthRequiredView No
/:pathMatch(.*)* NotFoundView No
  • Route guards in router/index.ts: requiresAuth redirects to /401 with the intended URL in the redirect query; 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() exchanges the HttpOnly refresh cookie for an access token. The app only mounts after this initial check.
  • Access tokens live in memory only. Refresh cookies are managed by the backend and sent with credentials: 'include'.
  • src/lib/api.ts performs one automatic refresh-and-retry after an authenticated request receives 401.
  • API auth state changes are synchronized back to the Pinia store through opencloud:auth-updated and opencloud:auth-expired window events.
  • Email confirmation and password reset pages consume the opaque token query parameter generated by the backend.
  • Passwords must contain at least 8 characters in registration, settings, and reset flows.
  • Registration, email confirmation, login, password reset, profile creation, and uniqueness checks are backend responsibilities.

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 returned by GET /cloud-types. Fetched once and 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; backend deletion also removes stored files.

FastAPI Backend

  • Backend project lives in the sibling directory ../opencloud-backend.
  • Default development API URL is http://localhost:8000/api/v1; set VITE_API_URL for deployed environments.
  • Frontend route guards are for navigation UX only. The backend enforces ownership, authentication, admin roles, and disabled-account rules.
  • Images are uploaded as multipart form data. The backend stores originals, creates thumbnails, blurs coordinates, writes database records, and atomically unlocks badges.

API Conventions

  • All requests must go through src/lib/api.ts; do not call fetch directly from views, stores, or composables.
  • API paths passed to apiRequest() are relative to VITE_API_URL, for example /clouds rather than /api/v1/clouds.
  • Public requests explicitly use { auth: false }. Authenticated requests use the default and receive a Bearer access token.
  • JSON request bodies are plain objects. Image uploads use FormData; do not set the multipart Content-Type header manually.
  • FastAPI errors use detail; ApiError converts string and validation-array details into a user-facing message.
  • Backend DTO fields use snake_case. Page/store adapters may expose display-only camelCase fields such as cloudTypeName.
  • Paginated endpoints return items, page, page_size, total, and total_pages.
  • Cloud batch mutation endpoints accept at most 100 IDs. Profile and admin code split larger selections into chunks of 100.
  • Page-based navigation (50 items/page), not infinite scroll.
  • The backend resolves cloud-type/custom-type searches and @username searches.
  • Each response includes items, total, page, page_size, and total_pages.
  • 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 handles file selection, previews, EXIF date extraction, validation, sequential multipart uploads, progress, and badge results. Thumbnail generation, coordinate blurring, storage, database insertion, and badge unlocking run on the backend.
  • Batch items upload sequentially. If a later item fails, earlier successful uploads remain saved; there is no transactional rollback or resumable upload in the frontend.
  • 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.
  • Vite dev and preview servers both use strict port 5173, matching the backend's default CORS_ORIGINS and FRONTEND_URL.
  • SEO plugin in vite.config.ts: generates robots.txt and sitemap.xml at build time. Auth-only routes, including /encyclopedia, are excluded from the sitemap and disallowed in robots.txt.
  • 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 for map features:

  • VITE_AMAP_KEY

Optional:

  • VITE_API_URL — FastAPI base URL including /api/v1 (defaults to http://localhost:8000/api/v1)
  • VITE_SITE_URL — canonical frontend URL used by route SEO and generated sitemap; set it explicitly in deployed environments

Backend-only variables such as database credentials, JWT secrets, SMTP settings, cookie policy, CORS origins, and upload paths belong in ../opencloud-backend/.env, never in the Vite environment.

Visual System

  • The design language is a clean sky atlas: airy sky-blue surfaces, fresh eucalyptus green, square editorial panels, pixel-cloud identity, and hard offset shadows.
  • Read .agents/skills/opencloud-style/SKILL.md before material visual changes. Treat src/styles/tokens.css, src/styles/base.css, src/styles/components.css, and src/lib/theme.ts as the implementation sources of truth.
  • Shared/stable decisions belong in layers:
    • tokens and palette remapping → src/styles/tokens.css
    • global baseline and square-corner policy → src/styles/base.css
    • cross-page semantic classes → src/styles/components.css
    • feature-specific map/slider/media details → the component's scoped style
    • one-off layout and responsive composition → Tailwind in the template
  • The global interface is square by default. Ordinary cards, inputs, dropdowns, buttons, modals, and map controls use 0px radius. Circular marker dots, media overlay controls, AMap-generated bubbles/cards, and the media-focused image-detail shell are explicit exceptions.
  • Use hard zero-blur offset shadows (--oc-shadow-xs through --oc-shadow-xl) for ordinary UI depth. Interactive controls may lift by translate(-1px, -1px) on hover.
  • Brand/interaction green is fresh eucalyptus: main #34745d, dark #274c40, light surface #f2faf6.
  • Success green is a distinct leaf green: main #4d7f3b, dark #35522c, light surface #f4faef.
  • Existing teal-* and emerald-* Tailwind class names are compatibility aliases remapped in tokens.css; do not assume Tailwind's default values.
  • Do not introduce Tailwind green-*/lime-*, vivid default teal, purple, indigo, or generic gray palettes. Use slate, sky, remapped teal/emerald, amber, and rose according to semantic purpose.
  • The header brand mark is the pixel cloud/sun SVG inside a square 40px sky-100 surface with a sky-200 border and neutral 4px hard shadow. Do not replace it with emoji or give it a green background.
  • Map floating buttons use oc-map-button: square 40px × 40px, white background, slate border, 0px radius, and a hard shadow even when disabled.
  • For material visual changes, inspect representative desktop and real 375px mobile screenshots and verify there is no horizontal overflow.

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.
  • Global overrides are defined in src/lib/theme.ts and mounted by App.vue; keep brand/success colors aligned with src/styles/tokens.css.
  • Do not rely on default Naive UI primary/secondary colors for page CTAs or panel actions. Use the shared classes in src/styles/components.css.
  • Auth buttons use oc-primary-button with semantic variants:
    • oc-primary-button--teal — login / account-access actions
    • oc-primary-button--sky — register / create / forward actions
  • Panel and utility buttons use oc-panel-button with semantic variants:
    • oc-panel-button--neutral — white card-style utility actions
    • oc-panel-button--sky — primary panel actions
    • oc-panel-button--teal — active toggles / confirm actions
    • oc-panel-button--danger — destructive actions
    • oc-panel-button--amber — admin/state-toggle actions where warning emphasis fits better than danger
  • Card-like containers should prefer shared shadow classes instead of ad-hoc shadows:
    • oc-panel-card
    • oc-panel-card-soft
    • oc-empty-card
  • When styling NButton, prefer type="default" plus the shared class when a custom panel button variant is intended. Otherwise Naive UI theme variables may override white backgrounds or semantic colors.
  • Primary and panel actions use dark text on light semantic surfaces; do not assume primary buttons should use white text.

Visual Notes

  • The site icon and header brand mark are a matching pixel-style cloud motif on a light sky-blue square; avoid emoji or unrelated icon styles for primary brand surfaces.
  • Header action buttons use slight hover lift (translate(-1px, -1px)) with hard-offset shadow growth; new header-like actions should follow that interaction pattern.
  • Heatmap view toggles in ContributionHeatmap intentionally use separate buttons with gap spacing instead of NButtonGroup, because grouped buttons visually collide once hard shadows and hover transforms are applied.
  • Reuse existing oc-* classes before duplicating Tailwind bundles. New shared modifiers must always appear with their base class.
  • Validate style work with npm run check:styles, git diff --check, and npm run build.

MVP Constraints

  • No realtime updates — 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