# Lotus Chat — Technical Implementation Field Guide
**Date:** June 2026

This document provides exhaustive, low-level implementation details for the remaining items in `LOTUS_TODO.md`. Follow these patterns to ensure code is "Lotus-perfect" (idiomatic, performant, and TDS-compliant).

---

## 🧵 Priority 3 — Higher Complexity

### P3-8 · Thread Panel (Full Side Drawer)
**Architecture:** Mirror the `MembersDrawer` pattern but with a specialized timeline.

*   **1. State (src/app/state/room/thread.ts):**
    ```typescript
    export const activeThreadIdAtom = atom<string | null>(null);
    ```
*   **2. Layout (src/app/features/room/Room.tsx):**
    Insert the `ThreadPanel` conditionally alongside the `RoomTimeline`.
    ```tsx
    {activeThreadId && (
      <>
        <Line variant="Background" direction="Vertical" size="300" />
        <ThreadPanel roomId={roomId} threadId={activeThreadId} />
      </>
    )}
    ```
*   **3. Component (src/app/features/room/thread/ThreadPanel.tsx):**
    *   Use `room.getThread(threadId)` from the SDK.
    *   Render a `Header` with a "Close" button that sets `activeThreadIdAtom` to `null`.
    *   Reuse `RoomTimeline` but pass a filtered `EventTimelineSet`.
    *   **Pro Tip:** Use `thread.timelineSet` directly for the most accurate thread view.

---

## 🎨 Priority 5 — Gamer / Aesthetic / Customization

### P5-1 · Custom Accent Color Picker (Non-TDS only)
**Mechanism:** Dynamic CSS variable injection.

*   **1. Setting (src/app/state/settings.ts):**
    Add `customAccentColor: string` (hex).
*   **2. Manager (src/app/pages/ThemeManager.tsx):**
    Inside the `useEffect` that monitors theme changes:
    ```typescript
    if (!lotusTerminal && customAccentColor) {
      document.documentElement.style.setProperty('--lt-accent-orange', customAccentColor);
      // Also derive a 'glow' version (e.g. 50% opacity)
      document.documentElement.style.setProperty('--lt-accent-orange-glow', `${customAccentColor}80`);
    }
    ```
*   **3. UI (src/app/features/settings/general/General.tsx):**
    Use a `<Input type="color">` component. Hide this section if `lotusTerminal` is `true`.

### P5-14 · Animated Avatar Overlay
**Mechanism:** CSS Pseudo-element wrapping.

*   **1. Wrapper (src/app/components/user-avatar/UserAvatar.tsx):**
    ```tsx
    const avatar = (
      <AvatarImage className={classNames(css.UserAvatar, className)} ... />
    );
    if (!frame) return avatar;
    return (
      <div className={css.AvatarFrameContainer} data-frame={frame}>
        {avatar}
        <div className={css.AvatarFrameEffect} />
      </div>
    );
    ```
*   **2. CSS (src/app/components/user-avatar/UserAvatar.css.ts):**
    Define animations like `rotate` or `pulse`. Use `position: absolute; inset: -4px` on the effect div to create a glowing ring around the avatar.

---

## 🛠️ Priority 4 — Specialized Features

### P4-4 · Math / LaTeX Rendering
**Mechanism:** KaTeX injection into the HTML parser.

*   **1. Sanitizer (src/app/utils/sanitize.ts):**
    You must allow KaTeX-specific tags and classes (e.g., `span`, `annotation`, `math`). Use a specialized allowed list for math blocks.
*   **2. Parser (src/app/plugins/react-custom-html-parser.tsx):**
    Detect `$ ... $` and `$$ ... $$` patterns in text nodes.
    ```tsx
    if (node.type === 'text') {
      const parts = node.data.split(/(\$\$.*?\$\$|\$.*?\$)/g);
      return parts.map(p => {
        if (p.startsWith('$')) return <KaTeX math={p.replace(/\$/g, '')} />;
        return p;
      });
    }
    ```
*   **3. CSS (src/app/styles/CustomHtml.css.ts):**
    Import `katex/dist/katex.min.css` only when a math block is rendered to save initial bundle size.

### P4-6 · OIDC / SSO Next-Gen Auth (MSC3861)
**Mechanism:** Matrix Authentication Service (MAS) Integration.

*   **Architecture:** Shift from password-based `/login` to OAuth2 `authorization_code` flow.
*   **Key Files:** `src/app/pages/auth/Login.tsx` and `src/app/hooks/useAuth.ts`.
*   **Implementation:**
    1.  Use `oidc-client-ts` or a similar lightweight OIDC library.
    2.  Check for `m.authentication` in `/.well-known/matrix/client`.
    3.  Redirect to the MAS authorization endpoint.
    4.  Handle the callback in a new `OidcCallback` route and store the OIDC `refresh_token`.

---

## 🎨 Priority 5 — Gamer / Aesthetic / Customization

### P5-12 · Seasonal / Event Themes
**Mechanism:** Date-aware global overlays.

*   **1. Component (src/app/components/seasonal/SeasonalEffect.tsx):**
    ```tsx
    const isHalloween = now.month === 9 && now.day >= 15; // etc.
    if (!isHalloween) return null;
    return <div className={css.HalloweenSpiderWeb} />;
    ```
*   **2. Styles (src/app/styles/Animations.css.ts):**
    Use `repeating-linear-gradient` for spider webs or `keyframes` for falling snow.
*   **3. Mounting:** Place at the very end of `App.tsx` (above the `LotusToastContainer`) with `pointer-events: none`.

### P5-13 · Avatar Frame / Border Decorations
**Mechanism:** Static variant of P5-14.

*   **Implementation:** Use a simple `border` or `box-shadow` in `UserAvatar.tsx` based on the `io.lotus.avatar_frame` account data. Unlike the animated version, this should avoid `keyframes` to save CPU on long member lists.

### P5-2 · Additional Color Theme Presets
**Mechanism:** Standard vanilla-extract theme multiplication.

*   **Implementation:** Replicate the `lotusTerminalTheme` object in `src/colors.css.ts` for each new theme (Cyberpunk, Ocean, etc.). Ensure each theme defines all 50+ tokens required by the `folds` library and TDS.

### P5-15 · In-Call Soundboard
**Mechanism:** Local-to-Global Audio Bridge.

*   **Architecture:** Use the `Web Audio API` to mix sounds into the `MediaStream` before it enters the Element Call widget.
*   **Implementation:**
    1.  Create an `AudioContext`.
    2.  Create a `MediaStreamDestinationNode`.
    3.  Create an `AudioBufferSourceNode` for the clip.
    4.  Route the mic `MediaStream` and the clip source to the destination.
    5.  Pass the destination's `.stream` to the call bridge.

### P5-20 · Quick Reply from Browser Notification
**Mechanism:** Service Worker `notificationclick` Action.

*   **1. Registration (src/sw.ts):**
    ```typescript
    self.addEventListener('notificationclick', (event) => {
      if (event.action === 'reply' && event.reply) {
        const { roomId, threadId } = event.notification.data;
        const session = sessions.get(event.clientId); // Uses existing session mapping
        // Send via direct fetch to bypass SDK loading
        fetch(`${session.baseUrl}/_matrix/client/v3/rooms/${roomId}/send/m.room.message`, {
          method: 'POST',
          headers: { Authorization: `Bearer ${session.accessToken}` },
          body: JSON.stringify({
            msgtype: 'm.text',
            body: event.reply,
            'm.relates_to': threadId ? { rel_type: 'm.thread', event_id: threadId } : undefined
          })
        });
      }
    });
    ```

---

## 🔬 Extreme Complexity Projects

### P5-30 · Advanced ML Noise Suppression (Krisp-style)
**Mechanism:** RNNoise WASM + Web Audio Worklet Pipeline.

*   **Objective:** Filter non-vocal noise from the microphone stream in real-time.
*   **Architecture:**
    1.  **Engine:** Use `RNNoise` (Recurrent Neural Network for noise suppression). It is lightweight and highly effective for speech.
    2.  **Pipeline:** `Mic Stream` -> `AudioWorkletNode` (Processing) -> `MediaStreamDestination` -> `Element Call`.
*   **Implementation Steps:**
    1.  **WASM Wrapper:** Compile the `RNNoise` C library to WebAssembly. Use a library like `rnnoise-wasm` or `noise-suppression-js`.
    2.  **Audio Worklet:** Create `src/app/utils/audio/RnnoiseWorklet.ts`. This must handle 480-sample chunks (10ms of audio at 48kHz), which is the standard frame size for RNNoise.
    3.  **Client Integration:** 
        *   In `CallControl.ts`, intercept the `localStream`.
        *   Pass the stream through the Worklet.
        *   Crucially, you must ensure that the processed stream is used by the `RTCPeerConnection` within the Element Call iframe.
### P5-31 · Granular Voice & Screenshare Quality Controls
**Mechanism:** WebRTC Encoding Parameters + Backend Quality Guard.

*   **Objective:** Per-room and per-user control over audio fidelity and screenshare smoothness.
*   **Architecture:**
    1.  **State Event:** `io.lotus.room_quality` (state key `""`) containing:
        ```json
        {
          "audio_bitrate": 128000,
          "screen_max_res": "1080p",
          "screen_max_fps": 60
        }
        ```
    2.  **Client-Side (RoomInput / CallControl):**
        *   **Screenshare:** In `src/app/plugins/call/CallControl.ts`, when initiating screenshare, map the "Quality" setting to `getDisplayMedia` constraints:
          ```typescript
          const constraints = {
            video: {
              width: { ideal: 1920 }, // 1080p
              frameRate: { ideal: 60 }
            }
          };
          ```
        *   **Audio Bitrate:** After the call joins, find the `RTCRtpSender` for the audio track and update parameters:
          ```typescript
          const sender = peerConnection.getSenders().find(s => s.track?.kind === 'audio');
          const params = sender.getParameters();
          params.encodings[0].maxBitrate = roomBitrate || 128000;
          await sender.setParameters(params);
          ```
    3.  **Backend Sidecar (The "Quality Guard"):**
        *   **Pattern:** Extend the `voice-limit-guard.py` (on LXC 151) to handle quality metadata.
        *   **Mechanism:** When a user requests a LiveKit JWT to join a room, the Guard fetches the `io.lotus.room_quality` event for that room via the Synapse Admin API.
        *   **Enforcement:** The Guard injects these limits into the LiveKit token claims (if supported) or simply returns them to the client as an authorized "config" packet that the client must respect.
*   **Challenges:**
    *   **LiveKit Compatibility:** Ensuring the SFU doesn't over-compress a high-bitrate stream from a "Pro" user.
    *   **Network Stability:** High bitrates (512kbps audio + 60fps 1080p video) require significant upstream bandwidth. Implement a "Network Warning" UI if packets are dropped.

### P5-34 · User-to-User Private Notes
**Mechanism:** Encrypted account data map.

*   **Objective:** Private, cross-session notes about other users.
*   **Key Files:**
    *   `src/app/hooks/useUserNotes.ts`: New hook for CRUD operations.
    *   `src/app/components/user-profile/UserRoomProfile.tsx`: UI site.
*   **Implementation:**
    1.  Store as a single map in `io.lotus.user_notes` account data: `{ "@alice:server": "Cool dev", "@bob:server": "Needs moderation" }`.
    2.  Wrap the entire map in E2EE if the client supports local account data encryption, or rely on standard Matrix account data privacy.
    3.  Add a "Notes" tab or textarea to the user profile popout.
