- Added AGENTS.md for AI agent documentation and maintainers, outlining project structure and conventions. - Updated CONTRIBUTING.md to specify that all project documentation must be in English, including README and docstrings. - Enhanced README.md to reference documentation guidelines and the new AGENTS.md file. - Cleaned up .gitignore by removing unnecessary entries for cursor-related files. - Introduced new .cursor rules for backend, frontend, project architecture, and testing to standardize development practices.
94 lines
4.3 KiB
Plaintext
94 lines
4.3 KiB
Plaintext
---
|
|
description: Rules for working with the Telegram Mini App frontend (webapp/)
|
|
globs:
|
|
- webapp/**
|
|
---
|
|
|
|
# Frontend — Telegram Mini App
|
|
|
|
## Module structure
|
|
|
|
All source lives in `webapp/js/`. Each module has a single responsibility:
|
|
|
|
| Module | Responsibility |
|
|
|--------|---------------|
|
|
| `main.js` | Entry point: theme init, auth gate, `loadMonth`, navigation, swipe gestures, sticky scroll |
|
|
| `dom.js` | Lazy DOM getters (`getCalendarEl()`, `getDutyListEl()`, etc.) and shared mutable `state` |
|
|
| `i18n.js` | `MESSAGES` dictionary (en/ru), `getLang()`, `t()`, `monthName()`, `weekdayLabels()` |
|
|
| `auth.js` | Telegram `initData` extraction (`getInitData`), `isLocalhost`, hash/query fallback |
|
|
| `theme.js` | Theme detection (`getTheme`), CSS variable injection (`applyThemeParamsToCss`), `initTheme` |
|
|
| `api.js` | `apiGet`, `fetchDuties`, `fetchCalendarEvents`; timeout, auth header, `Accept-Language` |
|
|
| `calendar.js` | `dutiesByDate`, `calendarEventsByDate`, `renderCalendar` (6-week grid with indicators) |
|
|
| `dutyList.js` | `dutyTimelineCardHtml`, `dutyItemHtml`, `renderDutyList` (monthly duty timeline cards) |
|
|
| `dayDetail.js` | Day detail panel — popover (desktop) or bottom sheet (mobile), `buildDayDetailContent` |
|
|
| `hints.js` | Tooltip positioning, duty marker hint content, info-button tooltips |
|
|
| `dateUtils.js` | Date helpers: `localDateString`, `dutyOverlapsLocalDay/Range`, `getMonday`, `formatHHMM`, etc. |
|
|
| `utils.js` | `escapeHtml` utility |
|
|
| `constants.js` | `FETCH_TIMEOUT_MS`, `RETRY_DELAY_MS`, `RETRY_AFTER_ACCESS_DENIED_MS` |
|
|
|
|
## State management
|
|
|
|
A single mutable `state` object is exported from `dom.js`:
|
|
|
|
```js
|
|
export const state = {
|
|
current: new Date(), // currently displayed month
|
|
lastDutiesForList: [], // duties array for the duty list
|
|
todayRefreshInterval: null, // interval handle
|
|
lang: "ru" // 'ru' | 'en'
|
|
};
|
|
```
|
|
|
|
- **No store / pub-sub / reactivity.** `main.js` mutates `state`, then calls
|
|
render functions (`renderCalendar`, `renderDutyList`) imperatively.
|
|
- Other modules read `state` but should not mutate it directly.
|
|
|
|
## HTML rendering
|
|
|
|
- Rendering functions build HTML strings (concatenation + `escapeHtml`) or use
|
|
`createElement` + `setAttribute`.
|
|
- Always escape user-controlled text with `escapeHtml()` before inserting via `innerHTML`.
|
|
- `setAttribute()` handles attribute quoting automatically — do not manually escape
|
|
quotes for data attributes.
|
|
|
|
## i18n
|
|
|
|
```js
|
|
t(lang, key, params?) // → translated string
|
|
```
|
|
|
|
- `lang` is `'ru'` or `'en'`, stored in `state.lang`.
|
|
- `getLang()` resolves language from: Telegram `initData` user → `navigator.language` → `"ru"`.
|
|
- Params use named placeholders: `t(lang, "duty.until", { time: "14:00" })`.
|
|
- Fallback chain: `MESSAGES[lang][key]` → `MESSAGES.en[key]` → raw key string.
|
|
- All user-visible text must go through `t()` — never hardcode Russian strings in JS.
|
|
|
|
## Telegram WebApp SDK
|
|
|
|
- Loaded via `<script>` in `index.html` from `https://telegram.org/js/telegram-web-app.js`.
|
|
- `main.js` calls `Telegram.WebApp.ready()` and `expand()` on startup.
|
|
- Auth: `initData` from SDK → hash param `tgWebAppData` → query string (fallback chain in `auth.js`).
|
|
- The `X-Telegram-Init-Data` header carries initData to the backend API.
|
|
- `requireTelegramOrLocalhost` gates access; localhost is allowed for dev.
|
|
- Theme changes subscribed via `Telegram.WebApp.onEvent("theme_changed", applyTheme)`.
|
|
|
|
## Theme system
|
|
|
|
- CSS variables in `:root`: `--bg`, `--surface`, `--text`, `--muted`, `--accent`,
|
|
`--duty`, `--today`, `--unavailable`, `--vacation`, `--error`, etc.
|
|
- `[data-theme="light"]` and `[data-theme="dark"]` override base variables.
|
|
- `theme.js` resolves scheme via:
|
|
1. `Telegram.WebApp.colorScheme`
|
|
2. CSS `--tg-color-scheme`
|
|
3. `prefers-color-scheme` media query
|
|
- `applyThemeParamsToCss()` maps Telegram `themeParams` to `--tg-theme-*` CSS variables.
|
|
- Prefer `var(--token)` over hardcoded colors; use `color-mix()` for alpha variants.
|
|
|
|
## Testing (frontend)
|
|
|
|
- **Runner:** Vitest (`webapp/vitest.config.js`), environment: `happy-dom`.
|
|
- **Test files:** `webapp/js/**/*.test.js`.
|
|
- **DOM setup:** Element refs are resolved lazily (getters). Set `document.body.innerHTML`
|
|
with required DOM structure in `beforeAll`/`beforeEach`; import order of modules using `dom.js` is flexible.
|
|
- **Run:** `npm run test` (in `webapp/`).
|