Files
opencloud/AGENTS.md
T
Mplan 8ba0212eec refactor(api)!: align client with canonical HTTP contract
Route all feature requests through business API modules, sync generated DTOs, and add communication tests and architecture documentation.

BREAKING CHANGE: migrate the client to plural resource paths, canonical snake_case fields, ISO timestamps, and explicit paginated responses.
2026-09-30 12:04:58 +08:00

192 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md
## Commands
- `npm run dev` — Vite dev server with HMR
- `npm run lint` / `npm run lint:fix` — ESLint checks / automatic fixes for JS, TS, and Vue; warnings fail the check
- `npm run format:check` / `npm run format` — Prettier check / formatting for source, config, and docs
- `npm run check` — lint, formatting, style contracts, typecheck, and communication tests
- `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)
- `npm run typecheck` — standalone Vue / TypeScript typecheck
- `npm run preview` — previews the production build on strict port `5173`
- ESLint uses `eslint.config.js`; Prettier uses `.prettierrc.json`. Keep formatting rules in Prettier. Communication tests use Node’s built-in runner (`npm test`, Node 22.18+); `check:styles` checks the visual system.
## Architecture
- **Vue 3 + Vite + Tailwind CSS + Vue Router + Pinia + Hono + Better Auth + AMap (高德地图) + Naive UI**
- Path alias `@/` → `src/` (configured in both `vite.config.ts` and `tsconfig.app.json`)
- HTTP transport at `src/lib/api.ts`; business calls in `features/{clouds,profile,admin}/api.ts`; API base URL comes from `VITE_API_URL`
- AMap loaded lazily via `src/shared/map/amap.ts` with type declarations in `src/shared/map/types/amap.d.ts`
- UI language is Chinese (zh-CN)
- All persistence, storage, authorization, and admin operations go through the Hono backend
## Directory Map
| Path | Purpose |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `src/features/` | Business modules; each owns its route views, components, stores, and composables as needed |
| `src/features/auth/` | Auth views, auth store, and Better Auth client |
| `src/features/clouds/` | Shared cloud detail/edit/like components, cloud-type and likes stores, cloud-type constants |
| `src/features/upload/` | UploadView, QuickUploadModal, and useUpload |
| `src/features/profile/` | Profile views, profile store, and ContributionHeatmap |
| `src/features/encyclopedia/` | Encyclopedia views and encyclopedia store |
| `src/features/{map,gallery,community,admin,system}/views/` | Remaining route views grouped by feature |
| `src/shared/map/` | Reusable location picker and mini map, AMap loader, and AMap declarations |
| `src/lib/` | Shared infrastructure: API client and adapters, SEO meta builder, Naive UI theme |
| `src/components/layout/` | AppHeader (top nav bar with auth state) |
| `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/router/` | Route registration, lazy view imports, guards, and per-route SEO |
| `src/types/` | Frontend models (`models.ts`, `view-models.ts`), generated HTTP DTOs (`api-contract.ts`), and router meta (`router.d.ts`) |
| `docs/project-structure.md` | File ownership, dependency conventions, and examples for placing new code |
| `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 |
- When adding or moving source files, follow `docs/project-structure.md`: colocate business code under its owning feature; keep reusable location primitives in `shared/map` and application layout in `components/layout`.
- Import concrete files directly; route views remain lazy imports in `src/router/index.ts`.
## 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` calls `authStore.initialize()` before mounting: Better Auth checks the session cookie and then loads `/profiles/me`.
- The backend manages HttpOnly session cookies; browser requests use `credentials: 'include'`.
- `features/auth/authClient.ts` owns Better Auth SDK configuration and error adaptation. No custom access-token refresh or automatic mutation retry.
- Required business requests receiving 401 dispatch `opencloud:auth-expired`, which clears Pinia auth state. Public requests use `{ auth: false }` to suppress that event.
- Email confirmation and password reset consume the backend’s opaque `token` query parameter. Passwords require at least 8 characters.
## 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-type catalog cache for encyclopedia pages; numeric rarity comes from the backend.
- **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.
## Backend and API Conventions
- Current backend: sibling `../hono-api` (Hono + Better Auth + PostgreSQL + object storage), default `http://localhost:3000`.
- When changing requests, DTOs, pagination, or authentication, read `docs/api-architecture.md`; the backend `API.md` lists endpoints and migration details.
- Views/stores call their feature API module; only feature API modules call `lib/api.ts`. Better Auth SDK calls stay in the auth module.
- Backend `src/schemas/` generates `contracts/api.ts`; run frontend `npm run api:sync` to update `src/types/api-contract.ts`. For coordinated changes, run backend `contract:check` and frontend `api:check`; do not hand-edit generated DTOs.
- Use `query` objects, plain JSON bodies, and typed upload input. The API module handles URL encoding and FormData construction.
- Business JSON uses snake_case and ISO UTC date strings. Pagination returns `items/page/page_size/has_more`; use the explicit flag for navigation.
- Business errors use `{message, issues?}`; Better Auth keeps its SDK protocol. `ApiError` preserves HTTP status and error details.
- Batch mutations accept at most 100 UUID strings; feature API modules split larger selections. Completed batches remain applied after later failures.
- Backend permissions enforce ownership, verified sessions, roles, and disabled accounts; route guards provide navigation UX only.
## Gallery Pagination
- Page-based navigation, 50 items/page, with a 250ms search debounce.
- Backend resolves cloud-type names and `@username` searches. Next-page availability comes from `has_more`.
## 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 selection, previews, EXIF dates, validation, sequential uploads and progress. The cloud API module encodes multipart data; the backend creates previews, stores variants and inserts records. Coordinates are blurred by the current frontend forms.
- 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 `CORS_ORIGIN` development setting.
- **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`.
## Environment Variables
Required for map features:
- `VITE_AMAP_KEY`
Optional:
- `VITE_API_URL` — Hono root URL (defaults to `http://localhost:3000`, without `/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, Better Auth secrets, email-provider settings, cookie policy, CORS origins, and object-storage settings belong in `../hono-api/.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