Files
cinny/LOTUS_REFERENCE.md
T

116 lines
14 KiB
Markdown
Raw Normal View History

# Lotus Chat — Engineering Reference
**Repo:** `lotus` branch at `https://code.lotusguild.org/LotusGuild/cinny`
**Deploy:** push to `lotus` → CI → auto-deploy to `chat.lotusguild.org` (~11 min)
> **There is no backlog in this file.** All open work lives in Gitea issues — [cinny](https://code.lotusguild.org/LotusGuild/cinny/issues), [element-call](https://code.lotusguild.org/LotusGuild/element-call/issues), [cinny-desktop](https://code.lotusguild.org/LotusGuild/cinny-desktop/issues), [matrix](https://code.lotusguild.org/LotusGuild/matrix/issues) (infra/CI) — grouped by milestone (`Features 2026-Q4`, `Desktop 2026-Q4`, `Manual QA backlog`, `Desktop QA backlog`, the `Audit 2026-09 · *` set). Shipped features are documented in [LOTUS_FEATURES.md](./LOTUS_FEATURES.md); how to run the automated tests is in [LOTUS_TESTING.md](./LOTUS_TESTING.md). The former `LOTUS_TODO.md` backlog was migrated to issues on 2026-09-17 (full history in git).
This file keeps only what a contributor needs to have open while working: the two design laws, decisions already made, what the server blocks, and the operational reference.
---
## ⚠️ TDS DESIGN LAW — READ BEFORE TOUCHING ANY UI
> **ALL Lotus Terminal Design System (TDS) styling — colors, animations, glows, borders, fonts, spacing — MUST come exclusively from `/root/code/web_template/base.css` CSS variables.**
> Do NOT hardcode hex values. Do NOT invent new variable names. Canonical tokens: `--lt-accent-orange`, `--lt-accent-cyan`, `--lt-accent-green`, `--lt-glow-*`, `--lt-box-glow-*`, `--lt-border-color`, `--lt-font-mono`. Syntax-highlight token classes: `.tok-kw .tok-str .tok-num .tok-cmt .tok-fn`.
> Reference patterns: `/root/code/tinker_tickets/` (markdown.js, base.js, ticket.css). Applies to every task without exception.
> New components must respect both TDS dark (`LotusTerminalTheme`) and TDS light (`LotusTerminalLightTheme`); non-TDS theme work uses vanilla-extract (match `src/lotus-terminal.css.ts`).
## 🧩 NATIVE-CINNY LAW — EVERY FEATURE MUST FEEL LIKE STOCK CINNY
> **Every feature must feel native to upstream Cinny — indistinguishable from what the Cinny team would ship.** Reference: <https://github.com/cinnyapp/cinny>.
>
> - **Use the `folds` design system, not bespoke UI** (`Button`, `Chip`, `IconButton`, `Menu`, `MenuItem`, `Dialog`, `Modal`, `Input`, `Switch`, `Badge`, `SettingTile`, `SequenceCard`, …) and folds tokens (`color.*`, `config.space.*`, `config.radii.*`). **Use folds `Icon`/`Icons`, never literal emoji, in UI chrome.** No hardcoded hex/`rgba()`, no invented CSS variables.
> - **Match Cinny's existing patterns** — find the closest existing component/flow and mirror it before adding UI.
> - **The ONE exception:** explicit **TDS** features, which follow the TDS Design Law above (opt-in, only in Lotus Terminal mode).
---
## Deferred / dropped (decided — kept for context)
- **[DEFERRED] P5-51** Federated "Identity Contexts" (session isolation) — multi-sprint, touches auth/crypto/storage core; smaller intermediate step = plain multi-account switch. **[DROPPED] P5-52** per-room sync governor — js-sdk can't truly per-room filter `/sync`; only a cosmetic hide. **[DEFERRED] P5-53** local scripting plugin — prefer a declarative automation-rules feature (no arbitrary code). **[DEFERRED] Audit-3** profile banner — MSC4427 open/unmerged; revisit on merge. **[WON'T FIX] P5-50** Windows HW media pipeline (WebRTC decode lives in WebView2; not injectable). **[MOVED] P5-9** LFG → LotusBot `!lfg`.
---
## 🚫 Blocked Features (server / upstream gated)
Re-run `/_matrix/client/versions` + `unstable_features` after each Synapse upgrade. **Re-checked on 1.157.1 (2026-07-23): no change — all four below are still `false`.** The 1.156.0→1.157.1 delta unblocked nothing (it's a bugfix release; the only feature-bearing release in the gap was 1.156.0, which we were already running).
- **[BLOCKED] Live Location Sharing** (MSC3489 + MSC3672 both `false`) — real-time GPS beacons over the existing static share.
- **[BLOCKED] Reaction/Relation Redaction** (MSC3892 `false`) — remove a reaction without redacting the parent; current full-redaction fallback is acceptable.
- **[DONE 2026-07] Room Preview before joining** (MSC3266) — the client was always built (`JoinBeforeNavigate``RoomCard` via `mx.getRoomSummary`). The earlier "blocked" flag was a **misdiagnosis**: it tested `/v1/rooms/{id}/summary` (404), but the SDK calls the _unstable_ `im.nheko.summary/summary/{id}` path, which returns **200** with name/topic/members/join_rule. Verified live after the 1.156 upgrade; also added a join-rule/encryption chip + Request-to-join for knock rooms to the preview card.
- **[BLOCKED] Thread Subscriptions** (MSC4306 `false`) — "Follow thread" button (depends on the shipped Thread Panel).
---
## 📖 Reference
### Server Capabilities (as of 2026-07)
- **Homeserver** `matrix.lotusguild.org` · **Synapse** `1.157.1+trixie1` (upgraded 2026-07-23 from **1.156.0** — note the host was found on 1.156.0 while the docs claimed 1.155.0, so **always verify with `dpkg-query -W matrix-synapse-py3`**, don't trust the docs; apt package on Debian 13, LXC 151) · **Matrix spec** up to `v1.12` (Synapse still advertises v1.12; MSC features via `unstable_features`).
- **MSC ON** (re-dumped live from `/_matrix/client/versions` on 1.157.1): `msc4140` · `msc3771` · `msc3440.stable` · `msc4133.stable` · `simplified_msc3575` · `msc4222` · `msc3266` (room summary live at unstable `im.nheko.summary/summary/{id}` — 200; the `/v1/rooms/{id}/summary` path is still 404) · `msc3401_matrix_rtc` · `msc2285.stable` · `msc3827.stable` · `msc3981` · `msc4380.stable` · `msc4445` · `msc2659.stable` · `msc2666` · `msc2432` · `e2e_cross_signing` · `label_based_filtering`. **OFF/blocked:** `msc4306` · `msc3882` · `msc3912` · `msc4155` · `msc3489`/`msc3672` · `msc3892` · `msc4028` · `msc4069` · `msc4108` · `msc3391` · `msc4354` (sticky events — **deliberately off**, see the Matrix 2.0 section above) · `msc4143` (RTC foci — **not a gap**: LiveKit is discovered via `.well-known` `org.matrix.msc4143.rtc_foci`, confirmed live, not this flag).
- **Dead client code:** Synapse 1.157.0 **removed** `msc3861` (MAS auth delegation) entirely — the ~6 `msc3861`/`msc2965` references in `src/` can never activate against this homeserver (we auth via Authelia `oidc_providers`). Harmless, but cleanup material.
- **Live endpoints:** Report User (MSC4260) **200** ✅ · Report Room (MSC4151) ✅.
- **Homeserver access (audits):** Synapse = LXC 151 (`pct exec 151 -- bash`), config `/etc/matrix-synapse/homeserver.yaml`. Web deploy = LXC 106. Voice guard = `voice-limit-guard.py` on LXC 151.
- **SDK notes:** no arbitrary profile-field methods (use `mx.http.authedRequest()` for MSC4133); js-sdk can't per-room filter `/sync`; sanitizer strips `<math>`/MathML; SW exists at `src/sw.ts`; `getMatrixToRoom()` builds invite URLs; EC audio-inject unblocked via the fork's `io.lotus.inject_audio`.
### Key File Reference
| What | File | Lines |
| ------------------------------ | ------------------------------------------------------------------- | ------------------- |
| Global keydown / room nav | `hooks/useKeyDown.ts` · `hooks/useRoomNavigate.ts` | whole / 19-72 |
| Room unread counts atom | `state/room/roomToUnread.ts` | `roomToUnreadAtom` |
| Overlay portal provider | `pages/App.tsx` · `index.html` | 65 / 101 |
| Room settings tabs | `features/room-settings/RoomSettings.tsx` | 27-56 |
| State event read/write pattern | `features/common-settings/general/RoomEncryption.tsx` | 42-52 |
| Power levels | `hooks/usePowerLevels.ts` | whole |
| Slash commands | `hooks/useCommands.ts` | 140-537 |
| Chat background picker/defs | `features/settings/general/General.tsx` · `lotus/chatBackground.ts` | 945-981 / whole |
| Matrix.to URL builder | `plugins/matrix-to.ts` | `getMatrixToRoom()` |
| Media URL conversion | `utils/matrix.ts` | `mxcUrlToHttp()` |
| Search pagination / virtual | `features/message-search/{useMessageSearch,MessageSearch}.tsx` | 74-121 / 234-365 |
| Call mic control | `plugins/call/CallControl.ts` | 206-212 |
| Knock support check | `utils/matrix.ts` | 376-391 |
| Notification mute push rules | `hooks/useRoomsNotificationPreferences.ts` | 110-150 |
### Element Call fork — operational reference
Fork = `LotusGuild/element-call` (branch `lotus`, upstream base **v0.25.0** since the 2026-09 sync — was v0.20.1); cinny consumes the npm package `@lotusguild/element-call-embedded` (built bundle copied into `public/element-call/`).
**Toolchain (upstream-driven, accepted 2026-09):** Node ≥ 22.13 (`.node-version` = 24) and **pnpm 11**, installed directly (`npm i -g pnpm@<packageManager version>`, currently 11.21.0) — **not** via `corepack enable`: `matrix-js-sdk` is a git dependency pnpm builds from source, and its own devEngines pins pnpm 11.9.0; a corepack-shimmed pnpm refuses to switch for that nested install and `pnpm install` fails (fork CI run #1854). pnpm 10 rejects the lockfile and Node 20 cannot build. Lint is **oxlint + oxfmt** (upstream dropped eslint/prettier in v0.25.0): `pnpm lint` (tsc + oxlint + knip) and `pnpm format:check` / `pnpm format`. `matrix-js-sdk` is pinned to a `matrix-org/matrix-js-sdk#develop` commit in the lockfile, as upstream ships it. Fork CI (`.gitea/workflows/ci.yml`) hard-gates lint + format + `pnpm test:unit` before build, with `concurrency: cancel-in-progress`.
**Publish a new version (CI on tag push; needs the `NPM_PUBLISH_TOKEN` org secret):** the published version is derived from the git tag — bump `embedded/web/package.json` (currently `0.25.0-lotus.4`, published by CI; the secret is `NPM_PUBLISH_TOKEN`, names starting `GITEA_` are reserved), push `lotus`, then `git push lotus v0.25.0-lotus.1`; the `publish` job builds and publishes to the Gitea registry. Always push (never delete) the annotated `vX.Y.Z-lotus.N` tag for every published version. Then in cinny bump the `@lotusguild/element-call-embedded` pin (currently `0.25.0-lotus.4`) → `npm install` → build. Manual fallback: `pnpm run build:embedded && cd embedded/web && npm version <ver> --no-git-tag-version && npm publish`.
**`io.lotus.*` widget actions** (add new toWidget actions to the enum + `LOTUS_TO_WIDGET_ACTIONS` in `src/lotus/lotusActions.ts`; only send AFTER call-join or a 10s timeout fires):
| Action | Dir | Purpose | Module |
| :--------------------------- | :------ | :----------------------------------------------------- | :-------------------- |
| `io.lotus.call_state` | EC→host | speaker/mute/camera stream (`lotusCallState=1`) | `lotusCallState.ts` |
| `io.lotus.focus_participant` | host→EC | spotlight (works during screenshare) | `lotusFocus.ts` |
| `io.lotus.inject_audio` | host→EC | soundboard clip mixed into call (`lotusAudioInject=1`) | `lotusAudioInject.ts` |
| `io.lotus.set_quality` | host→EC | audio/screenshare bitrate/fps caps | `lotusQuality.ts` |
| `io.lotus.decorations` | host→EC | in-call avatar decorations | `lotusDecorations.ts` |
| `io.lotus.set_deafen` | host→EC | LiveKit-source deafen (P6-2) | `lotusDeafen.ts` |
Also flag-gated: `lotusTransparent`/`lotusTheme`, `lotusDenoiseSource=1` (in-source ML denoise).
### CI/CD + per-feature checklist
```
edit → commit → git push origin lotus
→ Gitea Actions (.gitea/workflows/ci.yml): npm ci → build + npm test + tsc + eslint + prettier (ALL hard gates) → audit + bundle-size (informational)
→ lotus_deploy.sh on LXC 106 polls the "Build & Quality Checks" status → npm ci && npm run build → rsync → live (~11 min)
(a push that lands while a deploy is mid-build is queued and deployed right after — matrix@b6ea4a3; before that it was dropped)
```
Before marking a feature complete: `npx tsc --noEmit` (0 errors) · `npx eslint src/` (0 new) · `npx prettier --check src/` · `npm test` (Node runner via tsx, hard CI gate — colocated `*.test.ts`) · update `README.md`/`landing/index.html` for Lotus-custom features · visually verify on `chat.lotusguild.org`.
**CI hardening (2026-07, reviewed):**
- [x] **Concurrency**`cancel-in-progress` on cinny `ci.yml` and cinny-desktop `release.yml` (`386a2979` / `c5461ce`): a superseded lotus push cancels its in-flight web CI and collapses queued ~30-min Tauri desktop builds to just the newest. Safe for deploys because `lotus_deploy.sh` now **follows origin/lotus HEAD** each poll iteration + resets to the gated SHA (`matrix` `c15a489`) — closes the latched-SHA freeze race.
- [x] **Hard quality gates** — typecheck/eslint/prettier promoted from `continue-on-error` to blocking (tree held clean). eslint gates on errors only; `no-explicit-any` warnings stay informational.
**CI follow-ups (open):**
**CI follow-ups** are tracked in the `matrix` repo: [#8](https://code.lotusguild.org/LotusGuild/matrix/issues/8) dedicated `desktop-linux` runner, [#9](https://code.lotusguild.org/LotusGuild/matrix/issues/9) debounce the desktop trigger, [#10](https://code.lotusguild.org/LotusGuild/matrix/issues/10) verify Gitea `concurrency`. Build-once/deploy-the-artifact was considered and deferred (noted on #8).