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.
192 lines
17 KiB
Markdown
192 lines
17 KiB
Markdown
# 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
|