2026-06-14 00:33:04 -04:00
# Lotus Chat — Technical Implementation Field Guide
2026-06-09 18:35:59 -04:00
**Date:** June 2026
2026-06-14 00:33:04 -04:00
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).
2026-06-09 18:35:59 -04:00
---
## 🧵 Priority 3 — Higher Complexity
### P3-8 · Thread Panel (Full Side Drawer)
2026-06-14 00:33:04 -04:00
**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.
---
2026-06-10 22:55:32 -04:00
2026-06-14 00:33:04 -04:00
## 🎨 Priority 5 — Gamer / Aesthetic / Customization
2026-06-09 18:35:59 -04:00
2026-06-14 00:33:04 -04:00
### 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.
2026-06-09 18:35:59 -04:00
---
## 🛠️ Priority 4 — Specialized Features
### P4-4 · Math / LaTeX Rendering
2026-06-14 00:33:04 -04:00
**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`.
2026-06-09 18:35:59 -04:00
---
## 🎨 Priority 5 — Gamer / Aesthetic / Customization
2026-06-14 00:33:04 -04:00
### P5-12 · Seasonal / Event Themes
**Mechanism:** Date-aware global overlays.
2026-06-10 22:55:32 -04:00
2026-06-14 00:33:04 -04:00
* **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`.
2026-06-09 18:35:59 -04:00
2026-06-14 00:33:04 -04:00
### P5-13 · Avatar Frame / Border Decorations
**Mechanism:** Static variant of P5-14.
2026-06-10 22:55:32 -04:00
2026-06-14 00:33:04 -04:00
* **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.
2026-06-09 18:35:59 -04:00
2026-06-14 00:33:04 -04:00
### P5-2 · Additional Color Theme Presets
**Mechanism:** Standard vanilla-extract theme multiplication.
2026-06-10 22:55:32 -04:00
2026-06-14 00:33:04 -04:00
* **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.
2026-06-09 18:35:59 -04:00
2026-06-14 00:33:04 -04:00
### P5-15 · In-Call Soundboard
**Mechanism:** Local-to-Global Audio Bridge.
2026-06-10 22:55:32 -04:00
2026-06-14 00:33:04 -04:00
* **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.
2026-06-09 18:35:59 -04:00
### P5-20 · Quick Reply from Browser Notification
2026-06-14 00:33:04 -04:00
**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
})
});
}
});
` ``
2026-06-09 18:35:59 -04:00
---
2026-06-14 00:33:04 -04:00
## 🔬 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.