From f2bcd65a9bf03555a9a02164d2de6d8e6774af36 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 13 May 2026 21:51:19 -0400 Subject: [PATCH] Backgrounds: theme-aware patterns and visual preview grid --- .cinny-config.json | 17 +++ src/app/features/lotus/chatBackground.ts | 144 ++++++++++++++++++ src/app/features/room/RoomView.tsx | 77 +--------- src/app/features/settings/general/General.tsx | 112 +++++--------- 4 files changed, 206 insertions(+), 144 deletions(-) create mode 100644 .cinny-config.json create mode 100644 src/app/features/lotus/chatBackground.ts diff --git a/.cinny-config.json b/.cinny-config.json new file mode 100644 index 000000000..98cdf7505 --- /dev/null +++ b/.cinny-config.json @@ -0,0 +1,17 @@ +{ + "defaultHomeserver": 0, + "homeserverList": [ + "matrix.lotusguild.org" + ], + "allowCustomHomeservers": false, + "featuredCommunities": { + "openAsDefault": false, + "spaces": [], + "rooms": [], + "servers": [] + }, + "hashRouter": { + "enabled": false, + "basename": "/" + } +} \ No newline at end of file diff --git a/src/app/features/lotus/chatBackground.ts b/src/app/features/lotus/chatBackground.ts new file mode 100644 index 000000000..39345b635 --- /dev/null +++ b/src/app/features/lotus/chatBackground.ts @@ -0,0 +1,144 @@ +import { CSSProperties } from 'react'; +import { ChatBackground } from '../../state/settings'; + +export const BG_OPTIONS: { value: ChatBackground; label: string }[] = [ + { value: 'none', label: 'None' }, + { value: 'blueprint', label: 'Blueprint' }, + { value: 'carbon', label: 'Carbon' }, + { value: 'stars', label: 'Stars' }, + { value: 'topographic', label: 'Topographic' }, + { value: 'herringbone', label: 'Herringbone' }, + { value: 'crosshatch', label: 'Crosshatch' }, +]; + +const DARK: Record = { + none: {}, + + blueprint: { + backgroundColor: '#0a1628', + backgroundImage: [ + 'linear-gradient(rgba(100,149,237,0.14) 1px, transparent 1px)', + 'linear-gradient(90deg, rgba(100,149,237,0.14) 1px, transparent 1px)', + 'linear-gradient(rgba(100,149,237,0.05) 1px, transparent 1px)', + 'linear-gradient(90deg, rgba(100,149,237,0.05) 1px, transparent 1px)', + ].join(','), + backgroundSize: '80px 80px, 80px 80px, 16px 16px, 16px 16px', + }, + + carbon: { + backgroundColor: '#0e0e0e', + backgroundImage: [ + 'repeating-linear-gradient(45deg, rgba(255,255,255,0.035) 0, rgba(255,255,255,0.035) 2px, transparent 0, transparent 50%)', + 'repeating-linear-gradient(135deg, rgba(255,255,255,0.035) 0, rgba(255,255,255,0.035) 2px, transparent 0, transparent 50%)', + ].join(','), + backgroundSize: '8px 8px', + }, + + stars: { + backgroundColor: '#050510', + backgroundImage: [ + 'radial-gradient(circle, rgba(255,255,255,0.85) 1px, transparent 1px)', + 'radial-gradient(circle, rgba(255,255,255,0.55) 1px, transparent 1px)', + 'radial-gradient(circle, rgba(200,200,255,0.3) 1px, transparent 1px)', + ].join(','), + backgroundSize: '130px 130px, 190px 190px, 260px 260px', + backgroundPosition: '0 0, 65px 32px, 32px 97px', + }, + + topographic: { + backgroundColor: '#0f0f17', + backgroundImage: [ + 'repeating-radial-gradient(circle at 20% 20%, transparent 0, transparent 30px, rgba(152,0,0,0.07) 31px, transparent 32px)', + 'repeating-radial-gradient(circle at 80% 80%, transparent 0, transparent 25px, rgba(100,100,200,0.06) 26px, transparent 27px)', + 'repeating-radial-gradient(circle at 50% 10%, transparent 0, transparent 45px, rgba(152,0,0,0.04) 46px, transparent 47px)', + ].join(','), + }, + + herringbone: { + backgroundColor: '#111118', + backgroundImage: [ + 'repeating-linear-gradient(60deg, rgba(180,160,210,0.08) 0, rgba(180,160,210,0.08) 1px, transparent 0, transparent 50%)', + 'repeating-linear-gradient(120deg, rgba(180,160,210,0.08) 0, rgba(180,160,210,0.08) 1px, transparent 0, transparent 50%)', + ].join(','), + backgroundSize: '20px 36px', + }, + + crosshatch: { + backgroundColor: '#0f0f0f', + backgroundImage: [ + 'linear-gradient(rgba(255,255,255,0.06) 1px, transparent 1px)', + 'linear-gradient(90deg, rgba(255,255,255,0.06) 1px, transparent 1px)', + 'linear-gradient(rgba(255,255,255,0.022) 1px, transparent 1px)', + 'linear-gradient(90deg, rgba(255,255,255,0.022) 1px, transparent 1px)', + ].join(','), + backgroundSize: '60px 60px, 60px 60px, 12px 12px, 12px 12px', + }, +}; + +const LIGHT: Record = { + none: {}, + + blueprint: { + backgroundColor: '#eef3ff', + backgroundImage: [ + 'linear-gradient(rgba(50,100,220,0.16) 1px, transparent 1px)', + 'linear-gradient(90deg, rgba(50,100,220,0.16) 1px, transparent 1px)', + 'linear-gradient(rgba(50,100,220,0.06) 1px, transparent 1px)', + 'linear-gradient(90deg, rgba(50,100,220,0.06) 1px, transparent 1px)', + ].join(','), + backgroundSize: '80px 80px, 80px 80px, 16px 16px, 16px 16px', + }, + + carbon: { + backgroundColor: '#efefef', + backgroundImage: [ + 'repeating-linear-gradient(45deg, rgba(0,0,0,0.04) 0, rgba(0,0,0,0.04) 2px, transparent 0, transparent 50%)', + 'repeating-linear-gradient(135deg, rgba(0,0,0,0.04) 0, rgba(0,0,0,0.04) 2px, transparent 0, transparent 50%)', + ].join(','), + backgroundSize: '8px 8px', + }, + + // Stars is intentionally always dark — it's a night-sky theme + stars: { + backgroundColor: '#050510', + backgroundImage: [ + 'radial-gradient(circle, rgba(255,255,255,0.85) 1px, transparent 1px)', + 'radial-gradient(circle, rgba(255,255,255,0.55) 1px, transparent 1px)', + 'radial-gradient(circle, rgba(200,200,255,0.3) 1px, transparent 1px)', + ].join(','), + backgroundSize: '130px 130px, 190px 190px, 260px 260px', + backgroundPosition: '0 0, 65px 32px, 32px 97px', + }, + + topographic: { + backgroundColor: '#faf8f5', + backgroundImage: [ + 'repeating-radial-gradient(circle at 20% 20%, transparent 0, transparent 30px, rgba(100,60,60,0.09) 31px, transparent 32px)', + 'repeating-radial-gradient(circle at 80% 80%, transparent 0, transparent 25px, rgba(60,60,130,0.07) 26px, transparent 27px)', + 'repeating-radial-gradient(circle at 50% 10%, transparent 0, transparent 45px, rgba(100,60,60,0.05) 46px, transparent 47px)', + ].join(','), + }, + + herringbone: { + backgroundColor: '#f9f9f9', + backgroundImage: [ + 'repeating-linear-gradient(60deg, rgba(80,70,110,0.09) 0, rgba(80,70,110,0.09) 1px, transparent 0, transparent 50%)', + 'repeating-linear-gradient(120deg, rgba(80,70,110,0.09) 0, rgba(80,70,110,0.09) 1px, transparent 0, transparent 50%)', + ].join(','), + backgroundSize: '20px 36px', + }, + + crosshatch: { + backgroundColor: '#ffffff', + backgroundImage: [ + 'linear-gradient(rgba(0,0,0,0.07) 1px, transparent 1px)', + 'linear-gradient(90deg, rgba(0,0,0,0.07) 1px, transparent 1px)', + 'linear-gradient(rgba(0,0,0,0.025) 1px, transparent 1px)', + 'linear-gradient(90deg, rgba(0,0,0,0.025) 1px, transparent 1px)', + ].join(','), + backgroundSize: '60px 60px, 60px 60px, 12px 12px, 12px 12px', + }, +}; + +export const getChatBg = (bg: ChatBackground, isDark: boolean): CSSProperties => + isDark ? DARK[bg] : LIGHT[bg]; diff --git a/src/app/features/room/RoomView.tsx b/src/app/features/room/RoomView.tsx index dc95ecb28..f81661ddc 100644 --- a/src/app/features/room/RoomView.tsx +++ b/src/app/features/room/RoomView.tsx @@ -16,7 +16,9 @@ import { RoomInput } from './RoomInput'; import { RoomViewFollowing, RoomViewFollowingPlaceholder } from './RoomViewFollowing'; import { Page } from '../../components/page'; import { useSetting } from '../../state/hooks/settings'; -import { settingsAtom, ChatBackground } from '../../state/settings'; +import { settingsAtom } from '../../state/settings'; +import { useTheme, ThemeKind } from '../../hooks/useTheme'; +import { getChatBg } from '../lotus/chatBackground'; import { useKeyDown } from '../../hooks/useKeyDown'; import { editableActiveElement } from '../../utils/dom'; import { settingsAtom } from '../../state/settings'; @@ -57,80 +59,13 @@ const shouldFocusMessageField = (evt: KeyboardEvent): boolean => { }; -const CHAT_BG: Record = { - none: {}, - - // Dark navy with light-blue grid lines — technical/clean - blueprint: { - backgroundColor: '#0a1628', - backgroundImage: [ - 'linear-gradient(rgba(100,149,237,0.14) 1px, transparent 1px)', - 'linear-gradient(90deg, rgba(100,149,237,0.14) 1px, transparent 1px)', - 'linear-gradient(rgba(100,149,237,0.05) 1px, transparent 1px)', - 'linear-gradient(90deg, rgba(100,149,237,0.05) 1px, transparent 1px)', - ].join(','), - backgroundSize: '80px 80px, 80px 80px, 16px 16px, 16px 16px', - }, - - // Near-black diagonal weave — carbon fiber look - carbon: { - backgroundColor: '#0e0e0e', - backgroundImage: [ - 'repeating-linear-gradient(45deg, rgba(255,255,255,0.03) 0, rgba(255,255,255,0.03) 2px, transparent 0, transparent 50%)', - 'repeating-linear-gradient(135deg, rgba(255,255,255,0.03) 0, rgba(255,255,255,0.03) 2px, transparent 0, transparent 50%)', - ].join(','), - backgroundSize: '8px 8px', - }, - - // Deep space with three offset star layers - stars: { - backgroundColor: '#050510', - backgroundImage: [ - 'radial-gradient(circle, rgba(255,255,255,0.85) 1px, transparent 1px)', - 'radial-gradient(circle, rgba(255,255,255,0.55) 1px, transparent 1px)', - 'radial-gradient(circle, rgba(200,200,255,0.3) 1px, transparent 1px)', - ].join(','), - backgroundSize: '130px 130px, 190px 190px, 260px 260px', - backgroundPosition: '0 0, 65px 32px, 32px 97px', - }, - - // Concentric rings from three focal points — map contour effect - topographic: { - backgroundColor: '#0f0f17', - backgroundImage: [ - 'repeating-radial-gradient(circle at 20% 20%, transparent 0, transparent 30px, rgba(152,0,0,0.07) 31px, transparent 32px)', - 'repeating-radial-gradient(circle at 80% 80%, transparent 0, transparent 25px, rgba(100,100,200,0.06) 26px, transparent 27px)', - 'repeating-radial-gradient(circle at 50% 10%, transparent 0, transparent 45px, rgba(152,0,0,0.04) 46px, transparent 47px)', - ].join(','), - }, - - // 60°/120° diagonal stripe pair — classic herringbone weave - herringbone: { - backgroundColor: '#111118', - backgroundImage: [ - 'repeating-linear-gradient(60deg, rgba(180,160,210,0.07) 0, rgba(180,160,210,0.07) 1px, transparent 0, transparent 50%)', - 'repeating-linear-gradient(120deg, rgba(180,160,210,0.07) 0, rgba(180,160,210,0.07) 1px, transparent 0, transparent 50%)', - ].join(','), - backgroundSize: '20px 36px', - }, - - // Fine white grid with larger subdivision — graph paper - crosshatch: { - backgroundColor: '#0f0f0f', - backgroundImage: [ - 'linear-gradient(rgba(255,255,255,0.06) 1px, transparent 1px)', - 'linear-gradient(90deg, rgba(255,255,255,0.06) 1px, transparent 1px)', - 'linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px)', - 'linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px)', - ].join(','), - backgroundSize: '60px 60px, 60px 60px, 12px 12px, 12px 12px', - }, -}; export function RoomView({ eventId }: { eventId?: string }) { const roomInputRef = useRef(null); const roomViewRef = useRef(null); const [chatBackground] = useSetting(settingsAtom, 'chatBackground'); + const theme = useTheme(); + const isDark = theme.kind === ThemeKind.Dark; const [hideActivity] = useSetting(settingsAtom, 'hideActivity'); @@ -165,7 +100,7 @@ export function RoomView({ eventId }: { eventId?: string }) { ); return ( - + } /> - + } /> + + + @@ -759,81 +763,43 @@ function Editor() { } -const BG_OPTIONS: { value: ChatBackground; label: string }[] = [ - { value: 'none', label: 'None' }, - { value: 'blueprint', label: 'Blueprint' }, - { value: 'carbon', label: 'Carbon' }, - { value: 'stars', label: 'Stars' }, - { value: 'topographic', label: 'Topographic' }, - { value: 'herringbone', label: 'Herringbone' }, - { value: 'crosshatch', label: 'Crosshatch' }, -]; - -function SelectChatBackground() { - const [menuCords, setMenuCords] = useState(); +function ChatBgGrid() { const [chatBackground, setChatBackground] = useSetting(settingsAtom, 'chatBackground'); - - const handleMenu: MouseEventHandler = (evt) => { - setMenuCords(evt.currentTarget.getBoundingClientRect()); - }; - - const handleSelect = (value: ChatBackground) => { - setChatBackground(value); - setMenuCords(undefined); - }; + const theme = useTheme(); + const isDark = theme.kind === ThemeKind.Dark; return ( - <> - - setMenuCords(undefined), - clickOutsideDeactivates: true, - isKeyForward: (evt: KeyboardEvent) => - evt.key === 'ArrowDown' || evt.key === 'ArrowRight', - isKeyBackward: (evt: KeyboardEvent) => - evt.key === 'ArrowUp' || evt.key === 'ArrowLeft', - escapeDeactivates: stopPropagation, + + {BG_OPTIONS.map((opt) => ( + +