feat: P5-21 mention color, P5-22 font selector, P5-27 notification presets; update docs
- P5-21: Custom @mention highlight color picker in Settings → Appearance. CSS vars with luminance-computed text color; resets cleanly to theme default. - P5-22: Font selector (System, Inter, JetBrains Mono, Fira Code) in Settings → Appearance. Fira Code added to Google Fonts preload. - P5-27: Gaming/Work/Sleep preset buttons at top of Settings → Notifications. Each atomically applies a group of notification settings. - AppearanceEffects component in App.tsx applies CSS vars on settings change. - LOTUS_BUGS.md: mark presence + manifest icon bugs as resolved. - LOTUS_TODO.md: mark P3-6, P5-21, P5-22, P5-27 as [x]. - LOTUS_FEATURES.md: document all four completed features. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+3
-26
@@ -18,6 +18,8 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
* **Encrypted Search Misses Historic Events**: Fixed in `useLocalMessageSearch.ts`.
|
* **Encrypted Search Misses Historic Events**: Fixed in `useLocalMessageSearch.ts`.
|
||||||
* **Presence Updater Base URL Hack**: Fixed in `usePresenceUpdater.ts`.
|
* **Presence Updater Base URL Hack**: Fixed in `usePresenceUpdater.ts`.
|
||||||
* **Presence Badge Accessibility**: Fixed in `Presence.tsx` (`aria-label` on badge).
|
* **Presence Badge Accessibility**: Fixed in `Presence.tsx` (`aria-label` on badge).
|
||||||
|
* **Presence Updater Wipes Custom Status**: Fixed in `usePresenceUpdater.ts` (removed `status_msg: ''`).
|
||||||
|
* **Manifest Main Icon Paths 404**: Fixed in `public/manifest.json` (`./public/android/` → `./res/android/`). Shortcut icon was already correct.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -31,15 +33,7 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
* **Impact:** In encrypted rooms, the edit history shows ciphertext or "(no text)" for all previous versions.
|
* **Impact:** In encrypted rooms, the edit history shows ciphertext or "(no text)" for all previous versions.
|
||||||
* **Recommended Fix:** After fetching raw events, check if they are encrypted. Use `mx.decryptEventIfNeeded(event)` for each event in the chunk before rendering.
|
* **Recommended Fix:** After fetching raw events, check if they are encrypted. Use `mx.decryptEventIfNeeded(event)` for each event in the chunk before rendering.
|
||||||
|
|
||||||
### 2. Presence Updater Wipes Custom Status
|
### 2. Service Worker Ephemeral Sessions
|
||||||
**File:** `src/app/hooks/usePresenceUpdater.ts`
|
|
||||||
**Status:** **OPEN**
|
|
||||||
|
|
||||||
* **Issue:** `setOnline` and `setUnavailable` still send `status_msg: ''`.
|
|
||||||
* **Impact:** Custom status messages are wiped when the user goes idle/active.
|
|
||||||
* **Recommended Fix:** Remove `status_msg` from the `setPresence` payload in the updater hook.
|
|
||||||
|
|
||||||
### 3. Service Worker Ephemeral Sessions
|
|
||||||
**File:** `src/sw.ts`
|
**File:** `src/sw.ts`
|
||||||
**Status:** **OPEN**
|
**Status:** **OPEN**
|
||||||
|
|
||||||
@@ -75,21 +69,4 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
|||||||
* **Impact:** Two identical animations (e.g., Digital Rain) run simultaneously, doubling GPU usage on mobile.
|
* **Impact:** Two identical animations (e.g., Digital Rain) run simultaneously, doubling GPU usage on mobile.
|
||||||
* **Recommended Fix:** When Glassmorphism is active, make the `RoomView` background transparent and rely on the `document.body` background.
|
* **Recommended Fix:** When Glassmorphism is active, make the `RoomView` background transparent and rely on the `document.body` background.
|
||||||
|
|
||||||
### 4. Manifest Shortcut Icon 404
|
|
||||||
**File:** `public/manifest.json`
|
|
||||||
**Status:** **OPEN**
|
|
||||||
|
|
||||||
* **Issue:** The shortcut icon path is `res/android/...` but the file is copied to `public/android/...`.
|
|
||||||
* **Recommended Fix:** Change the path to `./public/android/android-chrome-96x96.png` in `manifest.json`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 UI/UX Consistency
|
|
||||||
|
|
||||||
### 1. Night Light Overlay Coverage
|
|
||||||
**File:** `src/app/pages/App.tsx`
|
|
||||||
**Status:** **OPEN**
|
|
||||||
|
|
||||||
* **Issue:** Overlay is inside `#root`, bypasses `#portalContainer` (modals/tooltips).
|
|
||||||
* **Recommended Fix:** Move to end of `document.body`.
|
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,30 @@ A warm orange overlay rendered over the entire UI to reduce blue light emission.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Font Selector (P5-22)
|
||||||
|
|
||||||
|
Users can choose the UI font in **Settings → Appearance**:
|
||||||
|
|
||||||
|
- **System Default** — `system-ui, -apple-system, sans-serif`
|
||||||
|
- **Inter** — `'InterVariable', sans-serif` (current default)
|
||||||
|
- **JetBrains Mono** — `'JetBrains Mono', monospace` (already loaded from Google Fonts)
|
||||||
|
- **Fira Code** — `'Fira Code', monospace` (added to Google Fonts preload in `index.html`)
|
||||||
|
|
||||||
|
Applied by overriding `--font-secondary` on `document.body` via `AppearanceEffects` in `App.tsx`. The TDS terminal mode font stack is unaffected.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom @Mention Highlight Color (P5-21)
|
||||||
|
|
||||||
|
Users can set a custom background color for `@mention` chips that highlight their own name, in **Settings → Appearance**.
|
||||||
|
|
||||||
|
- Color picker (native `<input type="color">`) with a **Reset** button to revert to the theme default
|
||||||
|
- Text color (black/white) auto-computed from the chosen background's luminance for readability
|
||||||
|
- Applied via CSS custom properties `--mention-highlight-bg`, `--mention-highlight-text`, `--mention-highlight-border` set on `document.body`
|
||||||
|
- `CustomHtml.css.ts` uses these as CSS `var()` fallbacks over the original folds `Success` token colors
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Voice / Video Call Improvements
|
## Voice / Video Call Improvements
|
||||||
|
|
||||||
### Element Call Upgrade
|
### Element Call Upgrade
|
||||||
@@ -589,6 +613,16 @@ A toggle in **Settings → Privacy** switches between sending `m.read` (public r
|
|||||||
|
|
||||||
A leading emoji in a room name is rendered at 1.15× size in the sidebar for visual hierarchy. An emoji picker button (😊) is added to all room name input fields, prepending the selected emoji to the room name.
|
A leading emoji in a room name is rendered at 1.15× size in the sidebar for visual hierarchy. An emoji picker button (😊) is added to all room name input fields, prepending the selected emoji to the room name.
|
||||||
|
|
||||||
|
### Configurable Composer Toolbar (P3-6)
|
||||||
|
|
||||||
|
Users can individually show or hide each composer toolbar button in **Settings → Editor → Composer Toolbar Buttons**:
|
||||||
|
|
||||||
|
- Format Toggle, Emoji, Sticker, GIF, Location, Poll, Voice Message, Schedule Message
|
||||||
|
- All default to **on** — no visible change for existing users
|
||||||
|
- New buttons added in future will also default to on (deep-merge in `getSettings`)
|
||||||
|
- Send and Attach File buttons are not hideable
|
||||||
|
- Sticker still respects the existing `width < 500px` auto-hide on top of the setting
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Room Customization
|
## Room Customization
|
||||||
@@ -691,6 +725,14 @@ A complete UI for managing Matrix push notification rules:
|
|||||||
- Delete button for removable rules
|
- Delete button for removable rules
|
||||||
- Add-rule form for creating new `room` and `sender` rules
|
- Add-rule form for creating new `room` and `sender` rules
|
||||||
|
|
||||||
|
### Notification Profile Presets (P5-27)
|
||||||
|
|
||||||
|
Three one-tap presets at the top of **Settings → Notifications** that apply a group of notification settings atomically:
|
||||||
|
|
||||||
|
- **Gaming 🎮** — notifications on, all sounds off (`messageSoundId: none`, `inviteSoundId: none`)
|
||||||
|
- **Work 💼** — all notifications and sounds on (restores defaults)
|
||||||
|
- **Sleep 🌙** — all notifications off (`showNotifications: false`, sounds off)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Server Integration
|
## Server Integration
|
||||||
|
|||||||
+4
-4
@@ -132,7 +132,7 @@ Status: `[ ]` pending · `[~]` in progress · `[x]` completed
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### [ ] P3-6 · Configurable Composer Toolbar
|
### [x] P3-6 · Configurable Composer Toolbar
|
||||||
|
|
||||||
**What:** Let users rearrange or hide individual composer toolbar buttons (GIF, Sticker, Emoji, File, Voice, Location). Changes stored in `settingsAtom`. Access via a small "⚙ Customize toolbar" option in toolbar overflow.
|
**What:** Let users rearrange or hide individual composer toolbar buttons (GIF, Sticker, Emoji, File, Voice, Location). Changes stored in `settingsAtom`. Access via a small "⚙ Customize toolbar" option in toolbar overflow.
|
||||||
**[AUDIT REQUIRED]** — Audit the current toolbar button rendering in `RoomInput.tsx`. Understand the layout system (is it a fixed array or already mapped from config?). Drag-to-reorder may require a DnD library; consider whether reorder is worth the complexity vs just toggle-visibility.
|
**[AUDIT REQUIRED]** — Audit the current toolbar button rendering in `RoomInput.tsx`. Understand the layout system (is it a fixed array or already mapped from config?). Drag-to-reorder may require a DnD library; consider whether reorder is worth the complexity vs just toggle-visibility.
|
||||||
@@ -326,14 +326,14 @@ Themes:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### [ ] P5-21 · Custom @Mention Highlight Color
|
### [x] P5-21 · Custom @Mention Highlight Color
|
||||||
|
|
||||||
**What:** Each user sets their own mention highlight color in Settings → Appearance. Applied as `--user-mention-color` CSS property override on mention-highlighted message rows.
|
**What:** Each user sets their own mention highlight color in Settings → Appearance. Applied as `--user-mention-color` CSS property override on mention-highlighted message rows.
|
||||||
**Complexity:** Low.
|
**Complexity:** Low.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### [ ] P5-22 · Font Selector for the UI
|
### [x] P5-22 · Font Selector for the UI
|
||||||
|
|
||||||
**What:** Font picker in Settings → Appearance. Options: JetBrains Mono, Inter, Geist, Fira Code, OpenDyslexic, System Default. Applied via CSS custom property overrides.
|
**What:** Font picker in Settings → Appearance. Options: JetBrains Mono, Inter, Geist, Fira Code, OpenDyslexic, System Default. Applied via CSS custom property overrides.
|
||||||
**[AUDIT REQUIRED]** Check if any fonts are already globally loaded to avoid double-loading.
|
**[AUDIT REQUIRED]** Check if any fonts are already globally loaded to avoid double-loading.
|
||||||
@@ -341,7 +341,7 @@ Themes:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### [ ] P5-27 · Notification Profile Presets (Gaming / Work / Sleep)
|
### [x] P5-27 · Notification Profile Presets (Gaming / Work / Sleep)
|
||||||
|
|
||||||
**What:** Saved presets that change all notification settings atomically. Gaming (mentions only), Work (DMs + mentions), Sleep (all off). Quick-switch from sidebar or settings.
|
**What:** Saved presets that change all notification settings atomically. Gaming (mentions only), Work (DMs + mentions), Sleep (all off). Quick-switch from sidebar or settings.
|
||||||
**Complexity:** Medium.
|
**Complexity:** Medium.
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@
|
|||||||
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link
|
<link
|
||||||
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,600;0,700;1,400&family=VT323&display=swap"
|
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600&family=JetBrains+Mono:ital,wght@0,400;0,600;0,700;1,400&family=VT323&display=swap"
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<link id="favicon" rel="shortcut icon" href="./public/favicon.ico" />
|
<link id="favicon" rel="shortcut icon" href="./public/favicon.ico" />
|
||||||
|
|||||||
@@ -333,6 +333,11 @@ function Appearance() {
|
|||||||
'glassmorphismSidebar',
|
'glassmorphismSidebar',
|
||||||
);
|
);
|
||||||
const [pauseAnimations, setPauseAnimations] = useSetting(settingsAtom, 'pauseAnimations');
|
const [pauseAnimations, setPauseAnimations] = useSetting(settingsAtom, 'pauseAnimations');
|
||||||
|
const [mentionHighlightColor, setMentionHighlightColor] = useSetting(
|
||||||
|
settingsAtom,
|
||||||
|
'mentionHighlightColor',
|
||||||
|
);
|
||||||
|
const [fontFamily, setFontFamily] = useSetting(settingsAtom, 'fontFamily');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box direction="Column" gap="100">
|
<Box direction="Column" gap="100">
|
||||||
@@ -494,6 +499,69 @@ function Appearance() {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</SequenceCard>
|
</SequenceCard>
|
||||||
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
|
<SettingTile
|
||||||
|
title="UI Font"
|
||||||
|
description="Font used throughout the interface."
|
||||||
|
after={
|
||||||
|
<select
|
||||||
|
value={fontFamily ?? 'inter'}
|
||||||
|
onChange={(e) =>
|
||||||
|
setFontFamily(
|
||||||
|
e.target.value as 'system' | 'inter' | 'jetbrains-mono' | 'fira-code',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
background: 'var(--bg-surface)',
|
||||||
|
color: 'inherit',
|
||||||
|
border: '1px solid var(--border-interactive-normal)',
|
||||||
|
borderRadius: '6px',
|
||||||
|
padding: '4px 8px',
|
||||||
|
fontSize: '14px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="system">System Default</option>
|
||||||
|
<option value="inter">Inter (default)</option>
|
||||||
|
<option value="jetbrains-mono">JetBrains Mono</option>
|
||||||
|
<option value="fira-code">Fira Code</option>
|
||||||
|
</select>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SequenceCard>
|
||||||
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
|
<SettingTile
|
||||||
|
title="@Mention Highlight Color"
|
||||||
|
description="Color used to highlight messages that mention you. Leave empty to use the theme default."
|
||||||
|
after={
|
||||||
|
<Box alignItems="Center" gap="200">
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
value={mentionHighlightColor || '#4caf50'}
|
||||||
|
onChange={(e) => setMentionHighlightColor(e.target.value)}
|
||||||
|
style={{ width: '36px', height: '28px', cursor: 'pointer', borderRadius: '4px', border: 'none', padding: '2px' }}
|
||||||
|
/>
|
||||||
|
{mentionHighlightColor && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setMentionHighlightColor('')}
|
||||||
|
style={{
|
||||||
|
background: 'none',
|
||||||
|
border: '1px solid var(--border-interactive-normal)',
|
||||||
|
borderRadius: '6px',
|
||||||
|
padding: '2px 8px',
|
||||||
|
cursor: 'pointer',
|
||||||
|
color: 'inherit',
|
||||||
|
fontSize: '12px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Reset
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SequenceCard>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text, IconButton, Icon, Icons, Scroll } from 'folds';
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||||||
|
import { Box, Text, IconButton, Icon, Icons, Scroll, config, toRem } from 'folds';
|
||||||
import { Page, PageContent, PageHeader } from '../../../components/page';
|
import { Page, PageContent, PageHeader } from '../../../components/page';
|
||||||
import { SystemNotification } from './SystemNotification';
|
import { SystemNotification } from './SystemNotification';
|
||||||
import { AllMessagesNotifications } from './AllMessages';
|
import { AllMessagesNotifications } from './AllMessages';
|
||||||
@@ -9,6 +10,95 @@ import { PushRuleEditor } from './PushRuleEditor';
|
|||||||
import { SequenceCard } from '../../../components/sequence-card';
|
import { SequenceCard } from '../../../components/sequence-card';
|
||||||
import { SequenceCardStyle } from '../styles.css';
|
import { SequenceCardStyle } from '../styles.css';
|
||||||
import { SettingTile } from '../../../components/setting-tile';
|
import { SettingTile } from '../../../components/setting-tile';
|
||||||
|
import { settingsAtom, Settings } from '../../../state/settings';
|
||||||
|
|
||||||
|
const PRESETS: Array<{
|
||||||
|
label: string;
|
||||||
|
emoji: string;
|
||||||
|
description: string;
|
||||||
|
patch: Partial<Settings>;
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
label: 'Gaming',
|
||||||
|
emoji: '🎮',
|
||||||
|
description: 'Notifications on, sounds off',
|
||||||
|
patch: {
|
||||||
|
showNotifications: true,
|
||||||
|
isNotificationSounds: false,
|
||||||
|
messageSoundId: 'none',
|
||||||
|
inviteSoundId: 'none',
|
||||||
|
quietHoursEnabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Work',
|
||||||
|
emoji: '💼',
|
||||||
|
description: 'All notifications and sounds on',
|
||||||
|
patch: {
|
||||||
|
showNotifications: true,
|
||||||
|
isNotificationSounds: true,
|
||||||
|
messageSoundId: 'notification',
|
||||||
|
inviteSoundId: 'invite',
|
||||||
|
quietHoursEnabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Sleep',
|
||||||
|
emoji: '🌙',
|
||||||
|
description: 'All notifications off',
|
||||||
|
patch: {
|
||||||
|
showNotifications: false,
|
||||||
|
isNotificationSounds: false,
|
||||||
|
quietHoursEnabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function NotificationPresets() {
|
||||||
|
const settings = useAtomValue(settingsAtom);
|
||||||
|
const setSettings = useSetAtom(settingsAtom);
|
||||||
|
|
||||||
|
const applyPreset = (patch: Partial<Settings>) => {
|
||||||
|
setSettings({ ...settings, ...patch });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box direction="Column" gap="100">
|
||||||
|
<Text size="L400">Quick Presets</Text>
|
||||||
|
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||||
|
<Box gap="300" style={{ padding: config.space.S300, flexWrap: 'wrap' }}>
|
||||||
|
{PRESETS.map((preset) => (
|
||||||
|
<button
|
||||||
|
key={preset.label}
|
||||||
|
type="button"
|
||||||
|
onClick={() => applyPreset(preset.patch)}
|
||||||
|
title={preset.description}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: toRem(4),
|
||||||
|
padding: `${toRem(8)} ${toRem(16)}`,
|
||||||
|
borderRadius: config.radii.R300,
|
||||||
|
border: '1px solid var(--border-interactive-normal)',
|
||||||
|
background: 'var(--bg-surface-low)',
|
||||||
|
color: 'inherit',
|
||||||
|
cursor: 'pointer',
|
||||||
|
minWidth: toRem(80),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span style={{ fontSize: toRem(24) }}>{preset.emoji}</span>
|
||||||
|
<Text size="T300" style={{ fontWeight: 600 }}>{preset.label}</Text>
|
||||||
|
<Text size="T200" priority="300" style={{ textAlign: 'center', maxWidth: toRem(120) }}>
|
||||||
|
{preset.description}
|
||||||
|
</Text>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</Box>
|
||||||
|
</SequenceCard>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
type NotificationsProps = {
|
type NotificationsProps = {
|
||||||
requestClose: () => void;
|
requestClose: () => void;
|
||||||
@@ -34,6 +124,7 @@ export function Notifications({ requestClose }: NotificationsProps) {
|
|||||||
<Scroll hideTrack visibility="Hover">
|
<Scroll hideTrack visibility="Hover">
|
||||||
<PageContent>
|
<PageContent>
|
||||||
<Box direction="Column" gap="700">
|
<Box direction="Column" gap="700">
|
||||||
|
<NotificationPresets />
|
||||||
<SystemNotification />
|
<SystemNotification />
|
||||||
<AllMessagesNotifications />
|
<AllMessagesNotifications />
|
||||||
<SpecialMessagesNotifications />
|
<SpecialMessagesNotifications />
|
||||||
|
|||||||
+38
-1
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
import { Provider as JotaiProvider, useAtomValue } from 'jotai';
|
import { Provider as JotaiProvider, useAtomValue } from 'jotai';
|
||||||
import { OverlayContainerProvider, PopOutContainerProvider, TooltipContainerProvider } from 'folds';
|
import { OverlayContainerProvider, PopOutContainerProvider, TooltipContainerProvider } from 'folds';
|
||||||
@@ -16,6 +16,42 @@ import { useCompositionEndTracking } from '../hooks/useComposingCheck';
|
|||||||
import { settingsAtom } from '../state/settings';
|
import { settingsAtom } from '../state/settings';
|
||||||
import { LotusToastContainer } from '../features/toast/LotusToastContainer';
|
import { LotusToastContainer } from '../features/toast/LotusToastContainer';
|
||||||
|
|
||||||
|
const FONT_MAP: Record<string, string> = {
|
||||||
|
system: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif",
|
||||||
|
inter: "'InterVariable', sans-serif",
|
||||||
|
'jetbrains-mono': "'JetBrains Mono', monospace",
|
||||||
|
'fira-code': "'Fira Code', monospace",
|
||||||
|
};
|
||||||
|
|
||||||
|
function AppearanceEffects() {
|
||||||
|
const settings = useAtomValue(settingsAtom);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const color = settings.mentionHighlightColor;
|
||||||
|
if (color) {
|
||||||
|
document.body.style.setProperty('--mention-highlight-bg', color);
|
||||||
|
// compute black or white text based on hex luminance
|
||||||
|
const r = parseInt(color.slice(1, 3), 16);
|
||||||
|
const g = parseInt(color.slice(3, 5), 16);
|
||||||
|
const b = parseInt(color.slice(5, 7), 16);
|
||||||
|
const lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
||||||
|
document.body.style.setProperty('--mention-highlight-text', lum > 0.5 ? '#000' : '#fff');
|
||||||
|
document.body.style.setProperty('--mention-highlight-border', color);
|
||||||
|
} else {
|
||||||
|
document.body.style.removeProperty('--mention-highlight-bg');
|
||||||
|
document.body.style.removeProperty('--mention-highlight-text');
|
||||||
|
document.body.style.removeProperty('--mention-highlight-border');
|
||||||
|
}
|
||||||
|
}, [settings.mentionHighlightColor]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const font = FONT_MAP[settings.fontFamily ?? 'inter'] ?? FONT_MAP.inter;
|
||||||
|
document.body.style.setProperty('--font-secondary', font);
|
||||||
|
}, [settings.fontFamily]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function NightLightOverlay() {
|
function NightLightOverlay() {
|
||||||
const settings = useAtomValue(settingsAtom);
|
const settings = useAtomValue(settingsAtom);
|
||||||
if (!settings.nightLightEnabled) return null;
|
if (!settings.nightLightEnabled) return null;
|
||||||
@@ -94,6 +130,7 @@ function App() {
|
|||||||
<ClientConfigProvider value={clientConfig}>
|
<ClientConfigProvider value={clientConfig}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<JotaiProvider>
|
<JotaiProvider>
|
||||||
|
<AppearanceEffects />
|
||||||
<RouterProvider router={createRouter(clientConfig, screenSize)} />
|
<RouterProvider router={createRouter(clientConfig, screenSize)} />
|
||||||
<NightLightOverlay />
|
<NightLightOverlay />
|
||||||
<LotusToastContainer />
|
<LotusToastContainer />
|
||||||
|
|||||||
@@ -125,6 +125,9 @@ export interface Settings {
|
|||||||
pauseAnimations: boolean;
|
pauseAnimations: boolean;
|
||||||
|
|
||||||
composerToolbarButtons: ComposerToolbarSettings;
|
composerToolbarButtons: ComposerToolbarSettings;
|
||||||
|
|
||||||
|
mentionHighlightColor: string;
|
||||||
|
fontFamily: 'system' | 'inter' | 'jetbrains-mono' | 'fira-code';
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultSettings: Settings = {
|
const defaultSettings: Settings = {
|
||||||
@@ -192,6 +195,9 @@ const defaultSettings: Settings = {
|
|||||||
pauseAnimations: false,
|
pauseAnimations: false,
|
||||||
|
|
||||||
composerToolbarButtons: DEFAULT_COMPOSER_TOOLBAR,
|
composerToolbarButtons: DEFAULT_COMPOSER_TOOLBAR,
|
||||||
|
|
||||||
|
mentionHighlightColor: '',
|
||||||
|
fontFamily: 'inter',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getSettings = (): Settings => {
|
export const getSettings = (): Settings => {
|
||||||
|
|||||||
@@ -169,9 +169,9 @@ export const Mention = recipe({
|
|||||||
variants: {
|
variants: {
|
||||||
highlight: {
|
highlight: {
|
||||||
true: {
|
true: {
|
||||||
backgroundColor: color.Success.Container,
|
backgroundColor: `var(--mention-highlight-bg, ${color.Success.Container as string})`,
|
||||||
color: color.Success.OnContainer,
|
color: `var(--mention-highlight-text, ${color.Success.OnContainer as string})`,
|
||||||
boxShadow: `0 0 0 ${config.borderWidth.B300} ${color.Success.ContainerLine}`,
|
boxShadow: `0 0 0 ${config.borderWidth.B300} var(--mention-highlight-border, ${color.Success.ContainerLine as string})`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
focus: {
|
focus: {
|
||||||
|
|||||||
Reference in New Issue
Block a user