Add Lotus Terminal Mode + fix all remaining Cinny branding

- New: Lotus Terminal Mode toggle in Appearance settings
  - Red phosphor color scheme (bg #0a0000, primary #ff3300, accent #00dd66)
  - Monospace font override (JetBrains Mono / Fira Code / Cascadia Code)
  - Retro CRT scanline overlay via CSS pseudo-element
  - Wired into ThemeManager with dedicated lotusTerminalBodyClass
- Branding: replace all user-visible Cinny references with Lotus Chat
  - WelcomePage, AuthLayout, SplashScreen, index.html meta tags
  - Device display names in login/register/token flows
  - System notification brand field
  - (Preserved internal Matrix protocol CinnySpaces event type)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
root
2026-05-13 22:22:06 -04:00
parent 5f0aa5c887
commit f3ec49fe88
14 changed files with 165 additions and 19 deletions
+2 -2
View File
@@ -29,8 +29,8 @@
<link rel="manifest" href="/manifest.json" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="application-name" content="Cinny" />
<meta name="apple-mobile-web-app-title" content="Cinny" />
<meta name="application-name" content="Lotus Chat" />
<meta name="apple-mobile-web-app-title" content="Lotus Chat" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
@@ -21,7 +21,7 @@ export function SplashScreen({ children }: SplashScreenProps) {
justifyContent="Center"
>
<Text size="H2" align="Center">
Cinny
Lotus Chat
</Text>
</Box>
</Box>
@@ -310,6 +310,7 @@ function Appearance() {
const [monochromeMode, setMonochromeMode] = useSetting(settingsAtom, 'monochromeMode');
const [twitterEmoji, setTwitterEmoji] = useSetting(settingsAtom, 'twitterEmoji');
const [perMessageProfiles, setPerMessageProfiles] = useSetting(settingsAtom, 'perMessageProfiles');
const [lotusTerminal, setLotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
return (
<Box direction="Column" gap="100">
@@ -371,6 +372,13 @@ function Appearance() {
after={<Switch variant="Primary" value={perMessageProfiles} onChange={setPerMessageProfiles} />}
/>
</SequenceCard>
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
<SettingTile
title="Lotus Terminal Mode"
description="Red phosphor color scheme, monospace font, and retro CRT scanlines. A full visual overhaul."
after={<Switch variant="Primary" value={lotusTerminal} onChange={setLotusTerminal} />}
/>
</SequenceCard>
</Box>
);
}
@@ -27,7 +27,7 @@ function EmailNotification() {
device_display_name: email,
lang: 'en',
data: {
brand: 'Cinny',
brand: 'Lotus Chat',
},
append: true,
});
+6 -1
View File
@@ -1,7 +1,7 @@
import { lightTheme } from 'folds';
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { onDarkFontWeight, onLightFontWeight } from '../../config.css';
import { butterTheme, darkTheme, silverTheme } from '../../colors.css';
import { butterTheme, darkTheme, lotusTerminalTheme, silverTheme } from '../../colors.css';
import { settingsAtom } from '../state/settings';
import { useSetting } from '../state/hooks/settings';
@@ -37,6 +37,11 @@ export const ButterTheme: Theme = {
kind: ThemeKind.Dark,
classNames: ['butter-theme', butterTheme, onDarkFontWeight, 'prism-dark'],
};
export const LotusTerminalTheme: Theme = {
id: 'lotus-terminal-theme',
kind: ThemeKind.Dark,
classNames: ['lotus-terminal-theme', lotusTerminalTheme, onDarkFontWeight, 'prism-dark'],
};
export const useThemes = (): Theme[] => {
const themes: Theme[] = useMemo(() => [LightTheme, SilverTheme, DarkTheme, ButterTheme], []);
+12 -6
View File
@@ -3,11 +3,13 @@ import { configClass, varsClass } from 'folds';
import {
DarkTheme,
LightTheme,
LotusTerminalTheme,
ThemeContextProvider,
ThemeKind,
useActiveTheme,
useSystemThemeKind,
} from '../hooks/useTheme';
import { lotusTerminalBodyClass } from '../../lotus-terminal.css';
import { useSetting } from '../state/hooks/settings';
import { settingsAtom } from '../state/settings';
@@ -31,19 +33,23 @@ export function UnAuthRouteThemeManager() {
export function AuthRouteThemeManager({ children }: { children: ReactNode }) {
const activeTheme = useActiveTheme();
const [monochromeMode] = useSetting(settingsAtom, 'monochromeMode');
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
const effectiveTheme = lotusTerminal ? LotusTerminalTheme : activeTheme;
useEffect(() => {
document.body.className = '';
document.body.classList.add(configClass, varsClass);
document.body.classList.add(...activeTheme.classNames);
if (monochromeMode) {
document.body.classList.add(...effectiveTheme.classNames);
if (lotusTerminal) {
document.body.classList.add(lotusTerminalBodyClass);
}
if (monochromeMode && !lotusTerminal) {
document.body.style.filter = 'grayscale(1)';
} else {
document.body.style.filter = '';
}
}, [activeTheme, monochromeMode]);
}, [effectiveTheme, monochromeMode, lotusTerminal]);
return <ThemeContextProvider value={activeTheme}>{children}</ThemeContextProvider>;
return <ThemeContextProvider value={effectiveTheme}>{children}</ThemeContextProvider>;
}
+1 -1
View File
@@ -135,7 +135,7 @@ export function AuthLayout() {
<Header className={css.AuthHeader} size="600" variant="Surface">
<Box grow="Yes" direction="Row" gap="300" alignItems="Center">
<img className={css.AuthLogo} src={LotusLogo} alt="Lotus Chat Logo" />
<Text size="H3">Cinny</Text>
<Text size="H3">Lotus Chat</Text>
</Box>
</Header>
<Box className={css.AuthCardContent} direction="Column">
@@ -133,7 +133,7 @@ export function PasswordLoginForm({ defaultUsername, defaultEmail }: PasswordLog
user: username,
},
password,
initial_device_display_name: 'Cinny Web',
initial_device_display_name: 'Lotus Chat Web',
});
};
@@ -151,7 +151,7 @@ export function PasswordLoginForm({ defaultUsername, defaultEmail }: PasswordLog
user: mxIdUsername,
},
password,
initial_device_display_name: 'Cinny Web',
initial_device_display_name: 'Lotus Chat Web',
});
};
const handleEmailLogin = (email: string, password: string) => {
@@ -163,7 +163,7 @@ export function PasswordLoginForm({ defaultUsername, defaultEmail }: PasswordLog
address: email,
},
password,
initial_device_display_name: 'Cinny Web',
initial_device_display_name: 'Lotus Chat Web',
});
};
+1 -1
View File
@@ -57,7 +57,7 @@ export function TokenLogin({ token }: TokenLoginProps) {
startLogin(baseUrl, {
type: 'm.login.token',
token,
initial_device_display_name: 'Cinny Web',
initial_device_display_name: 'Lotus Chat Web',
});
}, [baseUrl, token, startLogin]);
@@ -109,7 +109,7 @@ function RegisterUIAFlow({
auth: authDict,
password,
username,
initial_device_display_name: 'Cinny Web',
initial_device_display_name: 'Lotus Chat Web',
});
},
[onRegister, formData]
@@ -250,7 +250,7 @@ export function PasswordRegisterForm({
auth: {
session: authData.session,
},
initial_device_display_name: 'Cinny Web',
initial_device_display_name: 'Lotus Chat Web',
});
};
+1 -1
View File
@@ -15,7 +15,7 @@ export function WelcomePage() {
<PageHeroSection>
<PageHero
icon={<img width="70" height="70" src={CinnySVG} alt="Lotus Chat Logo" />}
title="Welcome to Cinny"
title="Welcome to Lotus Chat"
subTitle={
<span>
Yet another matrix client.{' '}
+2
View File
@@ -42,6 +42,7 @@ export interface Settings {
dateFormatString: string;
developerTools: boolean;
lotusTerminal: boolean;
chatBackground: ChatBackground;
perMessageProfiles: boolean;
@@ -79,6 +80,7 @@ const defaultSettings: Settings = {
dateFormatString: 'D MMM YYYY',
developerTools: false,
lotusTerminal: false,
chatBackground: 'none',
perMessageProfiles: false,
+97
View File
@@ -236,3 +236,100 @@ export const butterTheme = createTheme(color, {
OnContainer: '#F2EED3',
},
});
export const lotusTerminalTheme = createTheme(color, {
Background: {
Container: '#0a0000',
ContainerHover: '#140500',
ContainerActive: '#1e0800',
ContainerLine: '#2a0b00',
OnContainer: '#ffd0b8',
},
Surface: {
Container: '#140500',
ContainerHover: '#1e0800',
ContainerActive: '#2a0b00',
ContainerLine: '#360e00',
OnContainer: '#ffd0b8',
},
SurfaceVariant: {
Container: '#1e0800',
ContainerHover: '#2a0b00',
ContainerActive: '#360e00',
ContainerLine: '#421100',
OnContainer: '#ffd0b8',
},
Primary: {
Main: '#ff3300',
MainHover: '#ff4d1a',
MainActive: '#ff5c2e',
MainLine: '#ff6640',
OnMain: '#0a0000',
Container: '#3d0d00',
ContainerHover: '#4a1000',
ContainerActive: '#571300',
ContainerLine: '#641600',
OnContainer: '#ffb399',
},
Secondary: {
Main: '#00dd66',
MainHover: '#00c75c',
MainActive: '#00b352',
MainLine: '#009e49',
OnMain: '#0a0000',
Container: '#003318',
ContainerHover: '#00401e',
ContainerActive: '#004d24',
ContainerLine: '#005a2a',
OnContainer: '#99ffcc',
},
Success: {
Main: '#00dd66',
MainHover: '#00c75c',
MainActive: '#00b352',
MainLine: '#009e49',
OnMain: '#0a0000',
Container: '#003318',
ContainerHover: '#00401e',
ContainerActive: '#004d24',
ContainerLine: '#005a2a',
OnContainer: '#99ffcc',
},
Warning: {
Main: '#ffaa00',
MainHover: '#e69900',
MainActive: '#cc8800',
MainLine: '#b37700',
OnMain: '#0a0000',
Container: '#332200',
ContainerHover: '#402b00',
ContainerActive: '#4d3300',
ContainerLine: '#5a3c00',
OnContainer: '#ffd980',
},
Critical: {
Main: '#ff6666',
MainHover: '#ff5252',
MainActive: '#ff3d3d',
MainLine: '#ff2929',
OnMain: '#0a0000',
Container: '#3d0000',
ContainerHover: '#4a0000',
ContainerActive: '#570000',
ContainerLine: '#640000',
OnContainer: '#ffb3b3',
},
Other: {
FocusRing: 'rgba(255, 51, 0, 0.6)',
Shadow: 'rgba(0, 0, 0, 1)',
Overlay: 'rgba(0, 0, 0, 0.85)',
},
});
+28
View File
@@ -0,0 +1,28 @@
import { globalStyle, style } from '@vanilla-extract/css';
export const lotusTerminalBodyClass = style({});
globalStyle(`body.${lotusTerminalBodyClass}`, {
fontFamily: "'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, 'Courier New', monospace",
letterSpacing: '0.01em',
});
globalStyle(`body.${lotusTerminalBodyClass} *`, {
fontFamily: "'JetBrains Mono', 'Fira Code', 'Cascadia Code', Consolas, 'Courier New', monospace !important" as any,
});
globalStyle(`body.${lotusTerminalBodyClass}::after`, {
content: '""',
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
pointerEvents: 'none',
zIndex: 9999,
backgroundImage: 'repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0,0,0,0.12) 2px, rgba(0,0,0,0.12) 4px)',
});
globalStyle(`body.${lotusTerminalBodyClass} input, body.${lotusTerminalBodyClass} textarea`, {
caretColor: '#ff3300',
});