Merge pull request 'feat(settings): quick wins from the settings audit (#453 phase 1)' (#464) from feat/settings-quick-wins into lotus
CI / Build & Quality Checks (push) Successful in 4m16s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 22s
CI / Trigger Desktop Build (push) Successful in 14s
CI / Playwright smoke (e2e) (push) Successful in 21m43s
CI / Build & Quality Checks (push) Successful in 4m16s
CI / Docker image build & smoke test (push) Skipped
CI / Secret scan (gitleaks) (push) Successful in 22s
CI / Trigger Desktop Build (push) Successful in 14s
CI / Playwright smoke (e2e) (push) Successful in 21m43s
This commit was merged in pull request #464.
This commit is contained in:
@@ -156,7 +156,7 @@ function DesktopChromeSetting() {
|
||||
return (
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Custom Window Chrome (Beta)"
|
||||
title="Custom window chrome (beta)"
|
||||
description="Replace the system title bar with a Lotus-styled one. Desktop only — reloads to apply."
|
||||
after={<Switch variant="Primary" value={customChrome} onChange={handleToggle} />}
|
||||
/>
|
||||
@@ -264,7 +264,7 @@ function KeychainMirrorSetting() {
|
||||
}
|
||||
return (
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile title="Login in the system keychain" description={description} />
|
||||
<SettingTile title="Keep your sign-in in the system keychain" description={description} />
|
||||
</SequenceCard>
|
||||
);
|
||||
}
|
||||
@@ -295,12 +295,18 @@ const ThemeSelector = as<'div', ThemeSelectorProps>(
|
||||
),
|
||||
);
|
||||
|
||||
function SelectTheme({ disabled }: { disabled?: boolean }) {
|
||||
const themes = useThemes();
|
||||
function SelectTheme({ disabled, kind }: { disabled?: boolean; kind?: ThemeKind }) {
|
||||
const allThemes = useThemes();
|
||||
const themes = kind ? allThemes.filter((theme) => theme.kind === kind) : allThemes;
|
||||
const themeNames = useThemeNames();
|
||||
const [themeId, setThemeId] = useSetting(settingsAtom, 'themeId');
|
||||
const [, setLightThemeId] = useSetting(settingsAtom, 'lightThemeId');
|
||||
const [, setDarkThemeId] = useSetting(settingsAtom, 'darkThemeId');
|
||||
const [menuCords, setMenuCords] = useState<RectCords>();
|
||||
const selectedTheme = themes.find((theme) => theme.id === themeId) ?? LightTheme;
|
||||
const selectedTheme =
|
||||
themes.find((theme) => theme.id === themeId) ??
|
||||
themes[0] ??
|
||||
(kind === ThemeKind.Dark ? DarkTheme : LightTheme);
|
||||
|
||||
const handleThemeMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
|
||||
setMenuCords(evt.currentTarget.getBoundingClientRect());
|
||||
@@ -308,6 +314,10 @@ function SelectTheme({ disabled }: { disabled?: boolean }) {
|
||||
|
||||
const handleThemeSelect = (theme: Theme) => {
|
||||
setThemeId(theme.id);
|
||||
// [Gitea #455] Also remember it as this kind's variant, so switching to
|
||||
// System later keeps the user's pick.
|
||||
if (theme.kind === ThemeKind.Light) setLightThemeId(theme.id);
|
||||
else setDarkThemeId(theme.id);
|
||||
setMenuCords(undefined);
|
||||
};
|
||||
|
||||
@@ -392,7 +402,7 @@ function SystemThemePreferences() {
|
||||
return (
|
||||
<Box wrap="Wrap" gap="400">
|
||||
<SettingTile
|
||||
title="Light Theme:"
|
||||
title="Light theme:"
|
||||
after={
|
||||
<Chip
|
||||
variant={themeKind === ThemeKind.Light ? 'Primary' : 'Secondary'}
|
||||
@@ -433,7 +443,7 @@ function SystemThemePreferences() {
|
||||
}
|
||||
/>
|
||||
<SettingTile
|
||||
title="Dark Theme:"
|
||||
title="Dark theme:"
|
||||
after={
|
||||
<Chip
|
||||
variant={themeKind === ThemeKind.Dark ? 'Primary' : 'Secondary'}
|
||||
@@ -535,6 +545,26 @@ function PageZoomInput() {
|
||||
|
||||
function Appearance() {
|
||||
const [systemTheme, setSystemTheme] = useSetting(settingsAtom, 'useSystemTheme');
|
||||
const allThemes = useThemes();
|
||||
const [themeId, setThemeId] = useSetting(settingsAtom, 'themeId');
|
||||
const [lightThemeId] = useSetting(settingsAtom, 'lightThemeId');
|
||||
const [darkThemeId] = useSetting(settingsAtom, 'darkThemeId');
|
||||
const currentKind = allThemes.find((t) => t.id === themeId)?.kind ?? ThemeKind.Light;
|
||||
let themeMode: 'system' | 'light' | 'dark' = 'system';
|
||||
if (!systemTheme) themeMode = currentKind === ThemeKind.Dark ? 'dark' : 'light';
|
||||
const chooseThemeMode = (mode: 'system' | 'light' | 'dark') => {
|
||||
if (mode === 'system') {
|
||||
setSystemTheme(true);
|
||||
return;
|
||||
}
|
||||
setSystemTheme(false);
|
||||
// Use the variant already chosen for that kind (or the default one).
|
||||
const kind = mode === 'dark' ? ThemeKind.Dark : ThemeKind.Light;
|
||||
if (allThemes.find((t) => t.id === themeId)?.kind === kind) return;
|
||||
const preferred = mode === 'dark' ? darkThemeId : lightThemeId;
|
||||
const fallback = mode === 'dark' ? DarkTheme.id : LightTheme.id;
|
||||
setThemeId(allThemes.find((t) => t.id === preferred && t.kind === kind)?.id ?? fallback);
|
||||
};
|
||||
const [monochromeMode, setMonochromeMode] = useSetting(settingsAtom, 'monochromeMode');
|
||||
const [twitterEmoji, setTwitterEmoji] = useSetting(settingsAtom, 'twitterEmoji');
|
||||
const [perMessageProfiles, setPerMessageProfiles] = useSetting(
|
||||
@@ -576,38 +606,64 @@ function Appearance() {
|
||||
direction="Column"
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
title="System Theme"
|
||||
description="Choose between light and dark theme based on system preference."
|
||||
after={<Switch variant="Primary" value={systemTheme} onChange={setSystemTheme} />}
|
||||
/>
|
||||
{systemTheme && <SystemThemePreferences />}
|
||||
</SequenceCard>
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
{/* [Gitea #455] One control instead of a System switch plus a Theme
|
||||
dropdown that was greyed out whenever the switch was on. */}
|
||||
<SettingTile
|
||||
title="Theme"
|
||||
description="Theme to use when system theme is not enabled."
|
||||
after={<SelectTheme disabled={systemTheme} />}
|
||||
description={
|
||||
themeMode === 'system'
|
||||
? "Follows your device's light or dark setting."
|
||||
: 'Always use this theme, whatever your device is set to.'
|
||||
}
|
||||
after={
|
||||
<Box gap="100" role="radiogroup" aria-label="Theme" shrink="No" wrap="Wrap">
|
||||
{(
|
||||
[
|
||||
['system', 'System'],
|
||||
['light', 'Light'],
|
||||
['dark', 'Dark'],
|
||||
] as const
|
||||
).map(([mode, label]) => (
|
||||
<Chip
|
||||
key={mode}
|
||||
role="radio"
|
||||
aria-checked={themeMode === mode}
|
||||
variant={themeMode === mode ? 'Primary' : 'Secondary'}
|
||||
outlined={themeMode === mode}
|
||||
radii="Pill"
|
||||
onClick={() => chooseThemeMode(mode)}
|
||||
>
|
||||
<Text size="B300">{label}</Text>
|
||||
</Chip>
|
||||
))}
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
{themeMode === 'system' && <SystemThemePreferences />}
|
||||
{themeMode !== 'system' && (
|
||||
<SettingTile
|
||||
title={themeMode === 'light' ? 'Light theme:' : 'Dark theme:'}
|
||||
after={<SelectTheme kind={themeMode === 'light' ? ThemeKind.Light : ThemeKind.Dark} />}
|
||||
/>
|
||||
)}
|
||||
</SequenceCard>
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Monochrome Mode"
|
||||
title="Monochrome mode"
|
||||
after={<Switch variant="Primary" value={monochromeMode} onChange={setMonochromeMode} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Twitter Emoji"
|
||||
title="Emoji style: Twemoji"
|
||||
after={<Switch variant="Primary" value={twitterEmoji} onChange={setTwitterEmoji} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile title="Page Zoom" after={<PageZoomInput />} />
|
||||
<SettingTile title="Page zoom" after={<PageZoomInput />} />
|
||||
</SequenceCard>
|
||||
|
||||
<SequenceCard
|
||||
@@ -617,7 +673,7 @@ function Appearance() {
|
||||
gap="200"
|
||||
>
|
||||
<SettingTile
|
||||
title="Chat Background"
|
||||
title="Chat background"
|
||||
description="Pattern applied behind the message timeline."
|
||||
/>
|
||||
<Box style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}>
|
||||
@@ -627,7 +683,7 @@ function Appearance() {
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Pause Background Animations"
|
||||
title="Pause background animations"
|
||||
description="Stop background animation to reduce motion or improve performance."
|
||||
after={<Switch variant="Primary" value={pauseAnimations} onChange={setPauseAnimations} />}
|
||||
/>
|
||||
@@ -640,7 +696,7 @@ function Appearance() {
|
||||
gap="200"
|
||||
>
|
||||
<SettingTile
|
||||
title="Seasonal Theme"
|
||||
title="Seasonal theme"
|
||||
description="Decorative overlays for holidays and events. “Auto” follows the calendar — each theme below shows the dates it turns on. Click to select."
|
||||
/>
|
||||
<Box style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}>
|
||||
@@ -660,7 +716,7 @@ function Appearance() {
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Show Profile on Every Message"
|
||||
title="Show name and avatar on every message"
|
||||
description="Display avatar and name on each message instead of grouping consecutive messages."
|
||||
after={
|
||||
<Switch variant="Primary" value={perMessageProfiles} onChange={setPerMessageProfiles} />
|
||||
@@ -674,7 +730,7 @@ function Appearance() {
|
||||
gap="200"
|
||||
>
|
||||
<SettingTile
|
||||
title="Night Light"
|
||||
title="Night light"
|
||||
description="Reduce blue light with a warm orange tint overlay."
|
||||
after={
|
||||
<Switch variant="Primary" value={nightLightEnabled} onChange={setNightLightEnabled} />
|
||||
@@ -754,7 +810,7 @@ function Appearance() {
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Glassmorphism Sidebar"
|
||||
title="Glass sidebar"
|
||||
description="Semi-transparent sidebar with frosted glass effect. Works best with custom chat backgrounds."
|
||||
after={
|
||||
<Switch
|
||||
@@ -768,8 +824,8 @@ function Appearance() {
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Lotus Terminal Mode"
|
||||
description="LotusGuild Terminal Design System: Anduril Orange + Ice Cyan + Matrix Green, dot-grid background, CRT scanlines, and boot sequence animation (press Escape to skip)."
|
||||
title="Lotus Terminal mode"
|
||||
description="A retro terminal look: orange and cyan on a dot grid, CRT scanlines and a boot animation (press Esc to skip)."
|
||||
after={
|
||||
<Box direction="Row" gap="200" alignItems="Center">
|
||||
{lotusTerminal && (
|
||||
@@ -803,7 +859,7 @@ function Appearance() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="UI Font"
|
||||
title="Interface font"
|
||||
description="Font used throughout the interface."
|
||||
after={
|
||||
<SettingsSelect
|
||||
@@ -823,7 +879,7 @@ function Appearance() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="@Mention Highlight Color"
|
||||
title="@mention highlight colour"
|
||||
description="Color used to highlight messages that mention you. Leave empty to use the theme default."
|
||||
after={
|
||||
<HexColorPickerPopOut
|
||||
@@ -867,7 +923,7 @@ function Appearance() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Custom Accent Color"
|
||||
title="Custom accent colour"
|
||||
description={
|
||||
lotusTerminal
|
||||
? 'Only applies to non-TDS themes. Disable Lotus Terminal Mode to use a custom accent.'
|
||||
@@ -1241,7 +1297,7 @@ function SelectDateFormat() {
|
||||
return (
|
||||
<>
|
||||
<SettingTile
|
||||
title="Date Format"
|
||||
title="Date format"
|
||||
description={customDateFormat ? dayjs().format(dateFormatString) : ''}
|
||||
after={<PresetDateFormat value={selectedDateFormat} onChange={handlePresetChange} />}
|
||||
/>
|
||||
@@ -1257,10 +1313,10 @@ function DateAndTime() {
|
||||
|
||||
return (
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">Date & Time</Text>
|
||||
<Text size="L400">Date and time</Text>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="24-Hour Time Format"
|
||||
title="24-hour time"
|
||||
after={<Switch variant="Primary" value={hour24Clock} onChange={setHour24Clock} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
@@ -1472,7 +1528,7 @@ function Editor() {
|
||||
<Text size="L400">Editor</Text>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="ENTER for Newline"
|
||||
title="Enter for a new line"
|
||||
description={`Use ${
|
||||
isMacOS() ? KeySymbol.Command : 'Ctrl'
|
||||
} + ENTER to send message and ENTER for newline.`}
|
||||
@@ -1481,20 +1537,20 @@ function Editor() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Markdown Formatting"
|
||||
title="Markdown formatting"
|
||||
after={<Switch variant="Primary" value={isMarkdown} onChange={setIsMarkdown} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Offer to Format Pasted Code"
|
||||
title="Offer to format pasted code"
|
||||
description="When something you paste looks like code, offer to turn it into a code block. Nothing changes unless you accept."
|
||||
after={<Switch variant="Primary" value={offerCodePaste} onChange={setOfferCodePaste} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Formatting Toolbar"
|
||||
title="Formatting toolbar"
|
||||
description="Show bold, italic, code and other formatting buttons above the message input."
|
||||
after={<Switch variant="Primary" value={editorToolbar} onChange={setEditorToolbar} />}
|
||||
/>
|
||||
@@ -1506,7 +1562,7 @@ function Editor() {
|
||||
gap="300"
|
||||
>
|
||||
<SettingTile
|
||||
title="Composer Toolbar"
|
||||
title="Composer toolbar"
|
||||
description="Drag to reorder buttons, and tap a button to show or hide it in the message composer."
|
||||
/>
|
||||
<Box direction="Column" style={{ paddingBottom: config.space.S200 }}>
|
||||
@@ -1541,28 +1597,28 @@ function Privacy() {
|
||||
<Text size="L400">Privacy</Text>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Strip Tracking Parameters from Links"
|
||||
title="Strip tracking parameters from links"
|
||||
description="Remove utm_, fbclid, YouTube si= and other ad/analytics identifiers from links you paste or send, and from links shown in chat. Runs entirely on this device."
|
||||
after={<Switch variant="Primary" value={stripTracking} onChange={setStripTracking} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Remove Photo Metadata Before Sending"
|
||||
title="Remove photo metadata before sending"
|
||||
description="Strip location, camera model and timestamp (EXIF/XMP) from JPEG, PNG and WebP images you send or set as an avatar. Pixels are untouched; the rotation is kept. Videos and GIFs are not covered."
|
||||
after={<Switch variant="Primary" value={stripImageMeta} onChange={setStripImageMeta} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Hide Typing & Read Receipts"
|
||||
title="Hide typing and read receipts"
|
||||
description="Turn off both typing status and read receipts to keep your activity private."
|
||||
after={<Switch variant="Primary" value={hideActivity} onChange={setHideActivity} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Private Read Receipts"
|
||||
title="Private read receipts"
|
||||
description="Send read receipts only to you and the server — other users won't see when you've read messages."
|
||||
after={
|
||||
<Switch
|
||||
@@ -1575,7 +1631,7 @@ function Privacy() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Hide Online Status"
|
||||
title="Hide online status"
|
||||
description="Appear offline to everyone while still using the app. Disables online, away, and idle indicators."
|
||||
after={<Switch variant="Primary" value={hidePresence} onChange={setHidePresence} />}
|
||||
/>
|
||||
@@ -1657,7 +1713,7 @@ function SettingsSyncSection() {
|
||||
<Text size="L400">Sync</Text>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Sync Settings Across Devices"
|
||||
title="Sync settings across devices"
|
||||
description="Keep your preferences (theme, composer toolbar, notifications, quiet hours, call keys…) the same on every device, stored as account data on your own homeserver. Device-specific settings such as zoom, media auto-load, noise suppression and volumes stay local."
|
||||
after={<Switch variant="Primary" value={settingsSync} onChange={setSettingsSync} />}
|
||||
/>
|
||||
@@ -1665,7 +1721,7 @@ function SettingsSyncSection() {
|
||||
{settingsSync && (
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Use This Device's Settings Everywhere"
|
||||
title="Use this device's settings everywhere"
|
||||
description="Push this device's current preferences so every other device adopts them, even ones that changed something more recently."
|
||||
// [Gitea #273] On phones the two buttons squeezed the text into a
|
||||
// narrow column; put them under the description there.
|
||||
@@ -1800,14 +1856,14 @@ function Calls() {
|
||||
<Text size="L400">Calls</Text>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Join with Camera On"
|
||||
title="Join with camera on"
|
||||
description="Enable camera automatically when joining a voice or video call. Camera is off by default."
|
||||
after={<Switch variant="Primary" value={cameraOnJoin} onChange={setCameraOnJoin} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Noise Suppression"
|
||||
title="Noise suppression"
|
||||
description={
|
||||
<Box direction="Column" gap="200">
|
||||
{/* [Gitea #271] T200 like every other setting description. */}
|
||||
@@ -1864,7 +1920,7 @@ function Calls() {
|
||||
<Box direction="Column" gap="200">
|
||||
<Text size="L400">Model</Text>
|
||||
<SettingTile
|
||||
title="ML Model"
|
||||
title="ML model"
|
||||
description="Choose the machine learning model used for noise removal. Heavier models clean more aggressively at a higher CPU cost."
|
||||
after={
|
||||
<SettingsSelect<DenoiseModelId>
|
||||
@@ -1980,7 +2036,7 @@ function Calls() {
|
||||
>
|
||||
<Text size="L400">Enhancements</Text>
|
||||
<SettingTile
|
||||
title="Series Suppression"
|
||||
title="Also use the browser's noise suppression"
|
||||
description="Run the browser's native stationary noise filter before the ML model. Recommended for eliminating fan hum."
|
||||
after={
|
||||
<Switch
|
||||
@@ -1992,7 +2048,7 @@ function Calls() {
|
||||
/>
|
||||
|
||||
<SettingTile
|
||||
title="Noise Gate"
|
||||
title="Noise gate"
|
||||
description="Hard-cut audio when you aren't speaking to ensure absolute silence between sentences."
|
||||
after={
|
||||
<Switch variant="Primary" value={callDenoiseGate} onChange={setCallDenoiseGate} />
|
||||
@@ -2028,7 +2084,7 @@ function Calls() {
|
||||
borderTop: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
|
||||
}}
|
||||
>
|
||||
<Text size="L400">Test & calibrate</Text>
|
||||
<Text size="L400">Test and calibrate</Text>
|
||||
<Text size="T200" priority="300">
|
||||
Audition the selected model and tune the gate without joining a call. Changes to the
|
||||
settings above apply the next time you start a monitor or play a clip.
|
||||
@@ -2050,7 +2106,7 @@ function Calls() {
|
||||
gap="400"
|
||||
>
|
||||
<SettingTile
|
||||
title="After a Restart"
|
||||
title="Rejoin calls after a restart"
|
||||
description="If Lotus crashes, reloads or restarts for an update while you are in a voice room, put you back in."
|
||||
after={
|
||||
<SettingsSelect<'ask' | 'auto' | 'off'>
|
||||
@@ -2065,7 +2121,7 @@ function Calls() {
|
||||
}
|
||||
/>
|
||||
<SettingTile
|
||||
title="Summary After Calls"
|
||||
title="Summary after calls"
|
||||
description="When a call ends, show how long it was and how the connection held up, with a Details button for who was there, network numbers and your own talk/mute time."
|
||||
after={
|
||||
<Switch variant="Primary" value={callSummaryToast} onChange={setCallSummaryToast} />
|
||||
@@ -2073,19 +2129,19 @@ function Calls() {
|
||||
/>
|
||||
{hapticsSupported() && (
|
||||
<SettingTile
|
||||
title="Haptic Feedback"
|
||||
title="Haptic feedback"
|
||||
description="A short vibration when push-to-talk engages or releases and when you send a reaction. Off automatically when your system prefers reduced motion."
|
||||
after={<Switch variant="Primary" value={haptics} onChange={setHaptics} />}
|
||||
/>
|
||||
)}
|
||||
<SettingTile
|
||||
title="Push to Talk"
|
||||
title="Push to talk"
|
||||
description="Mute your microphone by default. Hold the PTT key to speak."
|
||||
after={<Switch variant="Primary" value={pttMode} onChange={setPttMode} />}
|
||||
/>
|
||||
{pttMode && (
|
||||
<SettingTile
|
||||
title="PTT Key"
|
||||
title="Push-to-talk key"
|
||||
description={pttBind.error ?? 'Press a key to bind it as your push-to-talk key.'}
|
||||
after={
|
||||
<Button
|
||||
@@ -2103,7 +2159,7 @@ function Calls() {
|
||||
/>
|
||||
)}
|
||||
<SettingTile
|
||||
title="Push to Deafen"
|
||||
title="Push to deafen"
|
||||
description={
|
||||
deafenBind.error ??
|
||||
(deafenHotkey
|
||||
@@ -2144,7 +2200,7 @@ function Calls() {
|
||||
/>
|
||||
{isTauriEnv() && (
|
||||
<SettingTile
|
||||
title="Hotkeys Work Outside the Window"
|
||||
title="Hotkeys work outside the window"
|
||||
description="Keep Push to Talk and Push to Deafen working while a game or another app has focus. The keys are only watched during a call and are never blocked from other apps. Windows only; some anti-cheat systems may prevent it."
|
||||
after={
|
||||
<Switch variant="Primary" value={globalCallHotkeys} onChange={setGlobalCallHotkeys} />
|
||||
@@ -2166,13 +2222,13 @@ function Calls() {
|
||||
}
|
||||
/>
|
||||
<SettingTile
|
||||
title="AFK Auto-Mute"
|
||||
title="AFK auto-mute"
|
||||
description="Automatically mute your microphone when you have been silent in a call."
|
||||
after={<Switch variant="Primary" value={afkAutoMute} onChange={setAfkAutoMute} />}
|
||||
/>
|
||||
{afkAutoMute && (
|
||||
<SettingTile
|
||||
title="Idle Timeout"
|
||||
title="Idle timeout"
|
||||
description="How long to wait before auto-muting."
|
||||
after={
|
||||
<SettingsSelect
|
||||
@@ -2204,7 +2260,7 @@ function Calls() {
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile title="Ringtone Volume" description="Volume of the incoming call ringtone." />
|
||||
<SettingTile title="Ringtone volume" description="Volume of the incoming call ringtone." />
|
||||
<Box
|
||||
direction="Row"
|
||||
alignItems="Center"
|
||||
@@ -2228,7 +2284,7 @@ function Calls() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Join & Leave Sounds"
|
||||
title="Join and leave sounds"
|
||||
description="Play a sound when someone joins or leaves a call you are in. Selecting an option plays a preview."
|
||||
after={
|
||||
<SettingsSelect
|
||||
@@ -2247,7 +2303,7 @@ function Calls() {
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Microphone Bitrate"
|
||||
title="Microphone bitrate"
|
||||
description="Cap the audio bitrate your mic sends in calls. Lower saves bandwidth; higher is clearer. Auto lets Element Call decide."
|
||||
after={
|
||||
<SettingsSelect<CallAudioBitrate>
|
||||
@@ -2261,7 +2317,7 @@ function Calls() {
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Screenshare Bitrate"
|
||||
title="Screen share bitrate"
|
||||
description="Cap the bitrate used when you share your screen. Lower is smoother on poor connections; higher is sharper."
|
||||
after={
|
||||
<SettingsSelect<ScreenshareBitrate>
|
||||
@@ -2275,7 +2331,7 @@ function Calls() {
|
||||
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Screenshare Framerate"
|
||||
title="Screen share frame rate"
|
||||
description="Cap the frames-per-second of your screenshare. 60 fps suits motion/gaming; 15 fps suits slides and saves bandwidth."
|
||||
after={
|
||||
<SettingsSelect<ScreenshareFramerate>
|
||||
@@ -2301,7 +2357,7 @@ function Calls() {
|
||||
/>
|
||||
{soundboardEnabled && (
|
||||
<>
|
||||
<SettingTile title="Soundboard Volume" />
|
||||
<SettingTile title="Soundboard volume" />
|
||||
<Box
|
||||
alignItems="Center"
|
||||
gap="200"
|
||||
@@ -2646,14 +2702,14 @@ function Messages() {
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">Messages</Text>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile title="Message Layout" after={<SelectMessageLayout />} />
|
||||
<SettingTile title="Message layout" after={<SelectMessageLayout />} />
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile title="Message Spacing" after={<SelectMessageSpacing />} />
|
||||
<SettingTile title="Message spacing" after={<SelectMessageSpacing />} />
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Legacy Username Color"
|
||||
title="Legacy username colour"
|
||||
after={
|
||||
<Switch
|
||||
variant="Primary"
|
||||
@@ -2665,7 +2721,7 @@ function Messages() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Hide Membership Change"
|
||||
title="Hide joins and leaves"
|
||||
after={
|
||||
<Switch
|
||||
variant="Primary"
|
||||
@@ -2677,7 +2733,7 @@ function Messages() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Hide Profile Change"
|
||||
title="Hide name and avatar changes"
|
||||
after={
|
||||
<Switch
|
||||
variant="Primary"
|
||||
@@ -2689,7 +2745,7 @@ function Messages() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Disable Media Auto Load"
|
||||
title="Don't load media automatically"
|
||||
after={
|
||||
<Switch
|
||||
variant="Primary"
|
||||
@@ -2701,14 +2757,14 @@ function Messages() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="URL Preview"
|
||||
title="Link previews"
|
||||
description="Your homeserver fetches and caches link previews. It will see the URLs of all links you preview."
|
||||
after={<Switch variant="Primary" value={urlPreview} onChange={setUrlPreview} />}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="URL Preview in Encrypted Rooms"
|
||||
title="Link previews in encrypted rooms"
|
||||
description={
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="T200" priority="300">
|
||||
@@ -2732,34 +2788,29 @@ function Messages() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Inline Media Players"
|
||||
title="Inline media players"
|
||||
description="Play YouTube, Vimeo, TikTok, Twitch, Spotify and more right in the chat. Nothing loads from the third-party site until you press play."
|
||||
after={
|
||||
<Switch variant="Primary" value={inlineMediaEmbeds} onChange={setInlineMediaEmbeds} />
|
||||
}
|
||||
/>
|
||||
</SequenceCard>
|
||||
{/* [Gitea #457] Only where this build has a GIF key: a disabled switch
|
||||
with a paragraph about missing configuration helps nobody. */}
|
||||
{gifAvailable && (
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="GIF picker"
|
||||
description="Every search term (and your IP address) is sent directly to Giphy, not through your homeserver. Off by default."
|
||||
after={
|
||||
<Switch variant="Primary" value={gifPickerEnabled} onChange={setGifPickerEnabled} />
|
||||
}
|
||||
/>
|
||||
</SequenceCard>
|
||||
)}
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="GIF Picker"
|
||||
description={
|
||||
gifAvailable
|
||||
? 'Every search term (and your IP address) is sent directly to Giphy, not through your homeserver. Off by default.'
|
||||
: "GIF search isn't set up for this version of the app (no Giphy key in its configuration), so there is no GIF button even when this is on."
|
||||
}
|
||||
after={
|
||||
<Switch
|
||||
variant="Primary"
|
||||
value={gifPickerEnabled && gifAvailable}
|
||||
onChange={setGifPickerEnabled}
|
||||
disabled={!gifAvailable}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Show Hidden Events"
|
||||
title="Show hidden events"
|
||||
after={
|
||||
<Switch variant="Primary" value={showHiddenEvents} onChange={setShowHiddenEvents} />
|
||||
}
|
||||
@@ -2767,7 +2818,7 @@ function Messages() {
|
||||
</SequenceCard>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile
|
||||
title="Enforce Message Retention"
|
||||
title="Enforce message retention"
|
||||
description="Permanently delete your own messages once a room's retention window (Room Settings → Message Retention) has passed. Off by default; only affects your own messages."
|
||||
after={
|
||||
<Switch
|
||||
@@ -2784,7 +2835,7 @@ function Messages() {
|
||||
direction="Column"
|
||||
>
|
||||
<SettingTile
|
||||
title="Translate Messages Into"
|
||||
title="Translate messages into"
|
||||
description={
|
||||
translationSupported
|
||||
? 'Translate messages written in other languages into this one. Runs entirely on your device — message text never leaves your computer or touches a cloud service.'
|
||||
@@ -2802,7 +2853,7 @@ function Messages() {
|
||||
/>
|
||||
{translationSupported && (
|
||||
<SettingTile
|
||||
title="Auto-translate Incoming Messages"
|
||||
title="Auto-translate incoming messages"
|
||||
description="Automatically translate messages that aren’t already in your language. When off, translate individual messages from their menu. The first time a language is used, its model downloads once (a few MB)."
|
||||
after={<Switch variant="Primary" value={autoTranslate} onChange={setAutoTranslate} />}
|
||||
/>
|
||||
@@ -2963,9 +3014,9 @@ function AppUpdates() {
|
||||
|
||||
return (
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">App Updates</Text>
|
||||
<Text size="L400">App updates</Text>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile title="Check for Updates" description={description} after={after} />
|
||||
<SettingTile title="Check for updates" description={description} after={after} />
|
||||
{packageUpdate && packageHelp && <PackageUpdate help={packageHelp} />}
|
||||
{status.state === 'error' && !packageUpdate && (
|
||||
<Text size="T200" priority="300" style={{ wordBreak: 'break-word' }}>
|
||||
|
||||
Reference in New Issue
Block a user