Files
opencloud/AGENTS.md
Mplan 55c90e8c60 style(ui): unify sky-atlas visual system
Centralize interactive component styles, adopt the fresh eucalyptus palette, and keep map controls square with hard shadows. Update project guidance and the OpenCloud style skill to match the implementation.
2026-07-25 02:41:27 +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