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" /> <link rel="manifest" href="/manifest.json" />
<meta name="mobile-web-app-capable" content="yes" /> <meta name="mobile-web-app-capable" content="yes" />
<meta name="application-name" content="Cinny" /> <meta name="application-name" content="Lotus Chat" />
<meta name="apple-mobile-web-app-title" content="Cinny" /> <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-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
@@ -21,7 +21,7 @@ export function SplashScreen({ children }: SplashScreenProps) {
justifyContent="Center" justifyContent="Center"
> >
<Text size="H2" align="Center"> <Text size="H2" align="Center">
Cinny Lotus Chat
</Text> </Text>
</Box> </Box>
</Box> </Box>
@@ -310,6 +310,7 @@ function Appearance() {
const [monochromeMode, setMonochromeMode] = useSetting(settingsAtom, 'monochromeMode'); const [monochromeMode, setMonochromeMode] = useSetting(settingsAtom, 'monochromeMode');
const [twitterEmoji, setTwitterEmoji] = useSetting(settingsAtom, 'twitterEmoji'); const [twitterEmoji, setTwitterEmoji] = useSetting(settingsAtom, 'twitterEmoji');
const [perMessageProfiles, setPerMessageProfiles] = useSetting(settingsAtom, 'perMessageProfiles'); const [perMessageProfiles, setPerMessageProfiles] = useSetting(settingsAtom, 'perMessageProfiles');
const [lotusTerminal, setLotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
return ( return (
<Box direction="Column" gap="100"> <Box direction="Column" gap="100">
@@ -371,6 +372,13 @@ function Appearance() {
after={<Switch variant="Primary" value={perMessageProfiles} onChange={setPerMessageProfiles} />} after={<Switch variant="Primary" value={perMessageProfiles} onChange={setPerMessageProfiles} />}
/> />
</SequenceCard> </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> </Box>
); );
} }
@@ -27,7 +27,7 @@ function EmailNotification() {
device_display_name: email, device_display_name: email,
lang: 'en', lang: 'en',
data: { data: {
brand: 'Cinny', brand: 'Lotus Chat',
}, },
append: true, append: true,
}); });
+6 -1
View File
@@ -1,7 +1,7 @@
import { lightTheme } from 'folds'; import { lightTheme } from 'folds';
import { createContext, useContext, useEffect, useMemo, useState } from 'react'; import { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { onDarkFontWeight, onLightFontWeight } from '../../config.css'; 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 { settingsAtom } from '../state/settings';
import { useSetting } from '../state/hooks/settings'; import { useSetting } from '../state/hooks/settings';
@@ -37,6 +37,11 @@ export const ButterTheme: Theme = {
kind: ThemeKind.Dark, kind: ThemeKind.Dark,
classNames: ['butter-theme', butterTheme, onDarkFontWeight, 'prism-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[] => { export const useThemes = (): Theme[] => {
const themes: Theme[] = useMemo(() => [LightTheme, SilverTheme, DarkTheme, ButterTheme], []); const themes: Theme[] = useMemo(() => [LightTheme, SilverTheme, DarkTheme, ButterTheme], []);
+12 -6
View File
@@ -3,11 +3,13 @@ import { configClass, varsClass } from 'folds';
import { import {
DarkTheme, DarkTheme,
LightTheme, LightTheme,
LotusTerminalTheme,
ThemeContextProvider, ThemeContextProvider,
ThemeKind, ThemeKind,
useActiveTheme, useActiveTheme,
useSystemThemeKind, useSystemThemeKind,
} from '../hooks/useTheme'; } from '../hooks/useTheme';
import { lotusTerminalBodyClass } from '../../lotus-terminal.css';
import { useSetting } from '../state/hooks/settings'; import { useSetting } from '../state/hooks/settings';
import { settingsAtom } from '../state/settings'; import { settingsAtom } from '../state/settings';
@@ -31,19 +33,23 @@ export function UnAuthRouteThemeManager() {
export function AuthRouteThemeManager({ children }: { children: ReactNode }) { export function AuthRouteThemeManager({ children }: { children: ReactNode }) {
const activeTheme = useActiveTheme(); const activeTheme = useActiveTheme();
const [monochromeMode] = useSetting(settingsAtom, 'monochromeMode'); const [monochromeMode] = useSetting(settingsAtom, 'monochromeMode');
const [lotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
const effectiveTheme = lotusTerminal ? LotusTerminalTheme : activeTheme;
useEffect(() => { useEffect(() => {
document.body.className = ''; document.body.className = '';
document.body.classList.add(configClass, varsClass); document.body.classList.add(configClass, varsClass);
document.body.classList.add(...effectiveTheme.classNames);
document.body.classList.add(...activeTheme.classNames); if (lotusTerminal) {
document.body.classList.add(lotusTerminalBodyClass);
if (monochromeMode) { }
if (monochromeMode && !lotusTerminal) {
document.body.style.filter = 'grayscale(1)'; document.body.style.filter = 'grayscale(1)';
} else { } else {
document.body.style.filter = ''; 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"> <Header className={css.AuthHeader} size="600" variant="Surface">
<Box grow="Yes" direction="Row" gap="300" alignItems="Center"> <Box grow="Yes" direction="Row" gap="300" alignItems="Center">
<img className={css.AuthLogo} src={LotusLogo} alt="Lotus Chat Logo" /> <img className={css.AuthLogo} src={LotusLogo} alt="Lotus Chat Logo" />
<Text size="H3">Cinny</Text> <Text size="H3">Lotus Chat</Text>
</Box> </Box>
</Header> </Header>
<Box className={css.AuthCardContent} direction="Column"> <Box className={css.AuthCardContent} direction="Column">
@@ -133,7 +133,7 @@ export function PasswordLoginForm({ defaultUsername, defaultEmail }: PasswordLog
user: username, user: username,
}, },
password, 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, user: mxIdUsername,
}, },
password, password,
initial_device_display_name: 'Cinny Web', initial_device_display_name: 'Lotus Chat Web',
}); });
}; };
const handleEmailLogin = (email: string, password: string) => { const handleEmailLogin = (email: string, password: string) => {
@@ -163,7 +163,7 @@ export function PasswordLoginForm({ defaultUsername, defaultEmail }: PasswordLog
address: email, address: email,
}, },
password, 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, { startLogin(baseUrl, {
type: 'm.login.token', type: 'm.login.token',
token, token,
initial_device_display_name: 'Cinny Web', initial_device_display_name: 'Lotus Chat Web',
}); });
}, [baseUrl, token, startLogin]); }, [baseUrl, token, startLogin]);
@@ -109,7 +109,7 @@ function RegisterUIAFlow({
auth: authDict, auth: authDict,
password, password,
username, username,
initial_device_display_name: 'Cinny Web', initial_device_display_name: 'Lotus Chat Web',
}); });
}, },
[onRegister, formData] [onRegister, formData]
@@ -250,7 +250,7 @@ export function PasswordRegisterForm({
auth: { auth: {
session: authData.session, 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> <PageHeroSection>
<PageHero <PageHero
icon={<img width="70" height="70" src={CinnySVG} alt="Lotus Chat Logo" />} icon={<img width="70" height="70" src={CinnySVG} alt="Lotus Chat Logo" />}
title="Welcome to Cinny" title="Welcome to Lotus Chat"
subTitle={ subTitle={
<span> <span>
Yet another matrix client.{' '} Yet another matrix client.{' '}
+2
View File
@@ -42,6 +42,7 @@ export interface Settings {
dateFormatString: string; dateFormatString: string;
developerTools: boolean; developerTools: boolean;
lotusTerminal: boolean;
chatBackground: ChatBackground; chatBackground: ChatBackground;
perMessageProfiles: boolean; perMessageProfiles: boolean;
@@ -79,6 +80,7 @@ const defaultSettings: Settings = {
dateFormatString: 'D MMM YYYY', dateFormatString: 'D MMM YYYY',
developerTools: false, developerTools: false,
lotusTerminal: false,
chatBackground: 'none', chatBackground: 'none',
perMessageProfiles: false, perMessageProfiles: false,
+97
View File
@@ -236,3 +236,100 @@ export const butterTheme = createTheme(color, {
OnContainer: '#F2EED3', 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',
});