# 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
