Agents.md
This commit is contained in:
38
AGENTS.md
38
AGENTS.md
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
A collaborative wiki of math concepts. Articles are written in Markdown with LaTeX
|
A collaborative wiki of math concepts. Articles are written in Markdown with LaTeX
|
||||||
(KaTeX) and stored in MongoDB behind an Express API; a Nuxt 4 app renders and edits them.
|
(KaTeX) and stored in MongoDB behind an Express API; a Nuxt 4 app renders and edits them.
|
||||||
|
The interface itself speaks five languages (en, es, ca, fr, de), chosen per account.
|
||||||
|
|
||||||
```
|
```
|
||||||
mathew/
|
mathew/
|
||||||
@@ -46,7 +47,9 @@ Plain JavaScript, ESM (`"type": "module"`, `.js` files with relative `.js` impor
|
|||||||
`User` (username + `passwordHash`, `select: false`; `setPassword`/`checkPassword`
|
`User` (username + `passwordHash`, `select: false`; `setPassword`/`checkPassword`
|
||||||
via bcrypt, `toPublic()` for responses, and `credentialProblem()` as the single
|
via bcrypt, `toPublic()` for responses, and `credentialProblem()` as the single
|
||||||
place account rules are checked). The profile picture lives on `User.avatar` as a
|
place account rules are checked). The profile picture lives on `User.avatar` as a
|
||||||
small data URL — no file storage. `User.role` is `'admin'` or `'member'`
|
small data URL — no file storage. `User.locale` is the interface language the
|
||||||
|
person picked (`SUPPORTED_LOCALES`: `en es ca fr de`, default `'en'`).
|
||||||
|
`User.role` is `'admin'` or `'member'`
|
||||||
(`ROLES`); `User.hasNone()` and `User.ensureAnAdmin()` cover who gets the
|
(`ROLES`); `User.hasNone()` and `User.ensureAnAdmin()` cover who gets the
|
||||||
former — see the roles gotcha below.
|
former — see the roles gotcha below.
|
||||||
- `src/routes/` — one Express `Router` per resource, mounted in `index.js`.
|
- `src/routes/` — one Express `Router` per resource, mounted in `index.js`.
|
||||||
@@ -55,7 +58,8 @@ Plain JavaScript, ESM (`"type": "module"`, `.js` files with relative `.js` impor
|
|||||||
`POST /api/auth/login`, `GET /api/auth/me` (who a token belongs to).
|
`POST /api/auth/login`, `GET /api/auth/me` (who a token belongs to).
|
||||||
Login answers the same way to a wrong name and a wrong password. Account updates,
|
Login answers the same way to a wrong name and a wrong password. Account updates,
|
||||||
all behind `requireAuth`: `PUT /api/auth/me` (rename), `PUT /api/auth/password`
|
all behind `requireAuth`: `PUT /api/auth/me` (rename), `PUT /api/auth/password`
|
||||||
(needs the current one), `PUT /api/auth/avatar` (data URL, `""` clears).
|
(needs the current one), `PUT /api/auth/avatar` (data URL, `""` clears),
|
||||||
|
`PUT /api/auth/locale` (interface language, one of `SUPPORTED_LOCALES`).
|
||||||
- `src/routes/admin.js` — mounted at `/api/admin`, everything behind
|
- `src/routes/admin.js` — mounted at `/api/admin`, everything behind
|
||||||
`requireAuth, requireAdmin`: `GET /api/admin/users` (every account, newest
|
`requireAuth, requireAdmin`: `GET /api/admin/users` (every account, newest
|
||||||
first, with `createdAt`), `PUT /api/admin/users/:id/role` (`role` is `'admin'`
|
first, with `createdAt`), `PUT /api/admin/users/:id/role` (`role` is `'admin'`
|
||||||
@@ -91,6 +95,16 @@ the accent themes generate from a map in `_tokens.scss`. Colours stay CSS custom
|
|||||||
properties so themes swap at runtime via `data-theme`/`data-accent` attributes
|
properties so themes swap at runtime via `data-theme`/`data-accent` attributes
|
||||||
(set pre-paint by an inline script in `nuxt.config.ts`).
|
(set pre-paint by an inline script in `nuxt.config.ts`).
|
||||||
|
|
||||||
|
UI wording is localized with `@nuxtjs/i18n` (`frontend/i18n/`): `i18n.config.ts` plus
|
||||||
|
one JSON catalog per language in `i18n/locales/` (all 309 keys, kept in parity with
|
||||||
|
`en.json`). `strategy: 'no_prefix'` — the interface language never appears in a URL
|
||||||
|
(`/wiki/<lang>/<slug>` is the *article's* language) — with cookie detection
|
||||||
|
(`mathew-locale`, `fallbackLocale: 'en'`) so the server renders in the right language
|
||||||
|
from the first draw. Every UI string goes through `t()` in the catalogs; messages that
|
||||||
|
carry `<code>`/`<span>` markup are drawn with `v-html` through `th()`
|
||||||
|
(`useAppLocale`), which escapes interpolated values. Adding a language = add a catalog
|
||||||
|
copy + an `APP_LANGUAGES` entry + the code in the backend's `SUPPORTED_LOCALES`.
|
||||||
|
|
||||||
- `app/pages/` — `/` (search), `/wiki/[slug]` (article + TOC), `/wiki/[slug]/edit`, `/new`,
|
- `app/pages/` — `/` (search), `/wiki/[slug]` (article + TOC), `/wiki/[slug]/edit`, `/new`,
|
||||||
`/admin` (the dashboard: every account and its role, plus the buttons that hand the
|
`/admin` (the dashboard: every account and its role, plus the buttons that hand the
|
||||||
role on or take it back — an admin alone sees anything but a refusal here).
|
role on or take it back — an admin alone sees anything but a refusal here).
|
||||||
@@ -117,9 +131,13 @@ properties so themes swap at runtime via `data-theme`/`data-accent` attributes
|
|||||||
profile popup), `useArticleActionsMenu` (same, for an article's ⋮ menu), and
|
profile popup), `useArticleActionsMenu` (same, for an article's ⋮ menu), and
|
||||||
`useAccountActionsMenu` (same, for a dashboard row's ⋮ — it holds the **id** of
|
`useAccountActionsMenu` (same, for a dashboard row's ⋮ — it holds the **id** of
|
||||||
the row that is open, so one row's menu is open at a time rather than a
|
the row that is open, so one row's menu is open at a time rather than a
|
||||||
boolean per row).
|
boolean per row), and `useAppLocale` (the interface language: `APP_LANGUAGES` —
|
||||||
|
the five supported UI languages with their own names —, `choose(code)` to turn
|
||||||
|
the UI (`setLocale` also keeps the `mathew-locale` cookie in step), `applyAccount(code)`
|
||||||
|
to let an account overrule the device, and `th()` for messages drawn as HTML).
|
||||||
- `app/plugins/auth.ts` — runs `restoreSession()` once at startup on server and client,
|
- `app/plugins/auth.ts` — runs `restoreSession()` once at startup on server and client,
|
||||||
so the sidebar's account row and the editor's gate are right in the first render.
|
so the sidebar's account row and the editor's gate are right in the first render,
|
||||||
|
and applies the account's `locale` when it restores one.
|
||||||
- `app/utils/markdown.ts` — the single render pipeline: `markdown-it` (**html: false**,
|
- `app/utils/markdown.ts` — the single render pipeline: `markdown-it` (**html: false**,
|
||||||
raw HTML is escaped) + `markdown-it-texmath` + KaTeX (`$...$` inline, `$$...$$` display,
|
raw HTML is escaped) + `markdown-it-texmath` + KaTeX (`$...$` inline, `$$...$$` display,
|
||||||
macros `\R \N \Z \Q \C`) + highlight.js, sanitized with DOMPurify. It also builds the
|
macros `\R \N \Z \Q \C`) + highlight.js, sanitized with DOMPurify. It also builds the
|
||||||
@@ -128,6 +146,18 @@ properties so themes swap at runtime via `data-theme`/`data-accent` attributes
|
|||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
|
- The **interface language** is not the article language: `/wiki/fr/…` addresses the
|
||||||
|
French *article*, never the UI. UI precedence: the signed-in account's `locale` (applied
|
||||||
|
at startup and on signing in) > the `mathew-locale` cookie (written by `setLocale`, read
|
||||||
|
on the server pre-render) > browser > `en`. The settings popup always turns this device;
|
||||||
|
with a session it also saves to the account — an account that refuses (dead session)
|
||||||
|
doesn't undo the switch. Error messages that come back from the API stay English; only
|
||||||
|
the frontend's own fallbacks are translated.
|
||||||
|
- Never call `t()` in `withDefaults` static prop defaults — they don't react to locale
|
||||||
|
changes. Use `props.x ?? t(...)` in the template/computed instead (see `AuthGate`,
|
||||||
|
`ConfirmModal`).
|
||||||
|
- Message catalogs are compiled: literal `{`/`@` break the compiler, so the editor's LaTeX
|
||||||
|
seed lives as a JS constant in `ArticleForm.vue` and only its words are catalog keys.
|
||||||
- Reading is open; **writing takes an account**. `POST /api/articles` and `PUT`/`DELETE`
|
- Reading is open; **writing takes an account**. `POST /api/articles` and `PUT`/`DELETE`
|
||||||
by slug need `Authorization: Bearer <token>` from `/api/auth/login` or
|
by slug need `Authorization: Bearer <token>` from `/api/auth/login` or
|
||||||
`/api/auth/register`. The frontend keeps that token in the `mathew-session` cookie
|
`/api/auth/register`. The frontend keeps that token in the `mathew-session` cookie
|
||||||
|
|||||||
Reference in New Issue
Block a user