125 lines
4.1 KiB
Markdown
125 lines
4.1 KiB
Markdown
# Calendink Coding Guidelines
|
||
|
||
These rules apply to all code in this workspace.
|
||
|
||
---
|
||
|
||
## Backend — C++ / ESP-IDF
|
||
|
||
### Philosophy: C-with-Utilities
|
||
Write **C-style code** using C++ convenience features. No classes, no methods, no RAII, no exceptions.
|
||
|
||
**Use freely:** `template`, `auto`, `constexpr`, `enum class`, type aliases from `types.hpp`
|
||
**Avoid:** classes, constructors/destructors, `std::` containers, inheritance, virtual functions, RAII wrappers
|
||
|
||
`goto` for cleanup/shutdown paths in `app_main` is acceptable.
|
||
|
||
### Unity Build
|
||
All `.cpp` files are `#include`-ed into `main.cpp` as a single translation unit. **Do not register new source files in CMakeLists.txt.**
|
||
|
||
- Use `unity.cpp` aggregators per API group (e.g., `api/tasks/unity.cpp`)
|
||
- Mark all file-scoped symbols with `internal` (defined as `static` in `types.hpp`)
|
||
- Use `.hpp` for declarations shared across included files only
|
||
|
||
### API Handler Pattern
|
||
```cpp
|
||
// METHOD /api/path — What it does
|
||
// Body: { "field": value } (if applicable)
|
||
|
||
internal esp_err_t api_foo_post_handler(httpd_req_t *req) {
|
||
httpd_resp_set_type(req, "application/json");
|
||
httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
|
||
// 1. Parse request
|
||
// 2. Call store function
|
||
// 3. Build cJSON response
|
||
// Always cJSON_Delete() objects and free() printed strings — no leaks
|
||
}
|
||
|
||
internal const httpd_uri_t api_foo_post_uri = { .uri = "/api/foo", .method = HTTP_POST, .handler = api_foo_post_handler, .user_ctx = NULL };
|
||
```
|
||
|
||
### Store / Handler Separation
|
||
Data operations in `store.cpp` / `store.hpp`. HTTP parsing in endpoint files. Never mix them.
|
||
|
||
### Logging
|
||
**Always use `ESP_LOGI` / `ESP_LOGW` / `ESP_LOGE`** — never `printf()`.
|
||
|
||
Since this is a Unity Build (single translation unit), the log tag must be unique per file to avoid redefinition. Name it after the module, not a generic `TAG`:
|
||
```cpp
|
||
// In each file, use a unique local name:
|
||
internal const char *kTagHttpServer = "HTTP_SERVER";
|
||
internal const char *kTagMain = "MAIN";
|
||
internal const char *kTagMDNS = "MDNS";
|
||
```
|
||
|
||
### Type Aliases
|
||
Use `types.hpp` aliases: `uint8`, `uint16`, `uint32`, `uint64`, `int8`–`int64`, `internal`
|
||
|
||
### Seed Data
|
||
`seed_users()` and `seed_tasks()` must be guarded:
|
||
```cpp
|
||
#ifndef NDEBUG
|
||
seed_users();
|
||
seed_tasks();
|
||
#endif
|
||
```
|
||
|
||
### Data Persistence
|
||
All task/user data is currently **in-RAM only** (intentional — NVS/LittleFS persistence is a future milestone). Do not add persistence without a TDD.
|
||
|
||
---
|
||
|
||
## Frontend — Svelte 5 + TailwindCSS v4
|
||
|
||
### Styling: Tailwind Only
|
||
Use TailwindCSS exclusively. **No `<style>` blocks in components.**
|
||
|
||
- All design tokens are in `app.css` via `@theme`: `bg-bg-card`, `text-text-primary`, `border-border`, `text-accent`, `text-success`, `text-danger`, etc.
|
||
- If a utility is missing, add a token to `@theme` — don't add inline CSS
|
||
|
||
### Reactivity: Svelte 5 Runes Only
|
||
| Do ✅ | Don't ❌ |
|
||
|---|---|
|
||
| `$state()` | `writable()`, `readable()` |
|
||
| `$derived()` / `$derived.by()` | `$:` reactive statements |
|
||
| `$effect()` | `onMount()`, `onDestroy()`, `.subscribe()` |
|
||
| `$props()` / `$bindable()` | `export let` |
|
||
|
||
### `$state()` vs Plain `let`
|
||
- **`$state()`** — values the template reads, or that changes should cause a re-render
|
||
- **Plain `let`** — script-only internals (mutex flags, interval handles, etc.) the template never reads
|
||
|
||
### `$effect()` for Initialization
|
||
When an effect has no reactive dependencies and runs once on mount, add a comment:
|
||
```js
|
||
// Load initial data on mount
|
||
$effect(() => { fetchData(); });
|
||
```
|
||
|
||
### Shared Utilities
|
||
- Date/time helpers, formatters → `lib/utils.js`
|
||
- Cross-component reactive state → `lib/stores.js`
|
||
- API calls → `lib/api.js` (always via `trackedFetch()`)
|
||
|
||
**Never duplicate functions across components.**
|
||
|
||
### Component Structure
|
||
```svelte
|
||
<script>
|
||
// 1. Imports
|
||
// 2. $props()
|
||
// 3. $state()
|
||
// 4. $derived()
|
||
// 5. Functions
|
||
// 6. $effect()
|
||
</script>
|
||
<!-- Template — Tailwind classes only -->
|
||
```
|
||
|
||
---
|
||
|
||
## General
|
||
|
||
- **Don't commit build artifacts**: `dist/`, `bundles/`, `temp_*`, `*.gz` — update `.gitignore` accordingly
|
||
- **Version** is managed through `version.json`, injected as `__APP_VERSION__` at build time
|