| | """ |
| | Brand-specific theming module for the AI Messaging System Visualization Tool. |
| | |
| | Provides dynamic color schemes and styling based on selected brand. |
| | """ |
| |
|
| | import streamlit as st |
| | from typing import Dict, Tuple |
| |
|
| | |
| | BRAND_COLORS = { |
| | "base": { |
| | "primary": "#FFD700", |
| | "secondary": "#2C2C2C", |
| | "accent": "#1A1A1A", |
| | "text": "#FFFFFF", |
| | "background": "#0D0D0D", |
| | "sidebar_bg": "#8B7500" |
| | }, |
| | "drumeo": { |
| | "primary": "#5DADE2", |
| | "secondary": "#FFD700", |
| | "accent": "#2874A6", |
| | "text": "#FFFFFF", |
| | "background": "#1C2833", |
| | "sidebar_bg": "#1A4D6B" |
| | }, |
| | "pianote": { |
| | "primary": "#EC7063", |
| | "secondary": "#FFD700", |
| | "accent": "#C0392B", |
| | "text": "#FFFFFF", |
| | "background": "#1C1C1C", |
| | "sidebar_bg": "#8B2500" |
| | }, |
| | "guitareo": { |
| | "primary": "#58D68D", |
| | "secondary": "#FFD700", |
| | "accent": "#229954", |
| | "text": "#FFFFFF", |
| | "background": "#1C2E1F", |
| | "sidebar_bg": "#1B5E20" |
| | }, |
| | "singeo": { |
| | "primary": "#BB8FCE", |
| | "secondary": "#FFD700", |
| | "accent": "#7D3C98", |
| | "text": "#FFFFFF", |
| | "background": "#1F1926", |
| | "sidebar_bg": "#4A148C" |
| | } |
| | } |
| |
|
| | |
| | BRAND_EMOJIS = { |
| | "drumeo": "🥁", |
| | "pianote": "🎹", |
| | "guitareo": "🎸", |
| | "singeo": "🎤" |
| | } |
| |
|
| |
|
| | def get_brand_theme(brand: str) -> Dict[str, str]: |
| | """ |
| | Get color theme for specified brand. |
| | |
| | Args: |
| | brand: Brand name (drumeo, pianote, guitareo, singeo) |
| | |
| | Returns: |
| | dict: Color theme dictionary |
| | """ |
| | brand_lower = brand.lower() if brand else "base" |
| | return BRAND_COLORS.get(brand_lower, BRAND_COLORS["base"]) |
| |
|
| |
|
| | def get_brand_emoji(brand: str) -> str: |
| | """ |
| | Get emoji for specified brand. |
| | |
| | Args: |
| | brand: Brand name |
| | |
| | Returns: |
| | str: Brand emoji |
| | """ |
| | brand_lower = brand.lower() if brand else "" |
| | return BRAND_EMOJIS.get(brand_lower, "🎵") |
| |
|
| |
|
| | def apply_theme(brand: str = None): |
| | """ |
| | Apply brand-specific theme to Streamlit app. |
| | |
| | Args: |
| | brand: Brand name (optional, uses session state if not provided) |
| | """ |
| | if brand is None: |
| | brand = st.session_state.get("selected_brand", "base") |
| |
|
| | theme = get_brand_theme(brand) |
| |
|
| | |
| | css = f""" |
| | <style> |
| | /* Global styles */ |
| | .stApp {{ |
| | background-color: {theme['background']}; |
| | }} |
| | |
| | /* Text colors */ |
| | h1, h2, h3, h4, h5, h6 {{ |
| | color: {theme['primary']} !important; |
| | }} |
| | |
| | p, span, div {{ |
| | color: {theme['text']} !important; |
| | }} |
| | |
| | /* Button styles */ |
| | .stButton > button {{ |
| | background-color: {theme['primary']}; |
| | color: {theme['background']}; |
| | border: none; |
| | border-radius: 8px; |
| | padding: 0.5rem 1rem; |
| | font-weight: 600; |
| | transition: all 0.3s ease; |
| | }} |
| | |
| | .stButton > button:hover {{ |
| | background-color: {theme['secondary']}; |
| | transform: translateY(-2px); |
| | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); |
| | }} |
| | |
| | /* Download button */ |
| | .stDownloadButton > button {{ |
| | background-color: {theme['accent']}; |
| | color: {theme['text']}; |
| | border-radius: 8px; |
| | font-weight: 600; |
| | }} |
| | |
| | /* Input fields */ |
| | .stTextInput > div > div > input, |
| | .stTextArea > div > div > textarea, |
| | .stNumberInput > div > div > input {{ |
| | background-color: {theme['secondary']}; |
| | color: {theme['text']}; |
| | border: 1px solid {theme['primary']}; |
| | border-radius: 5px; |
| | }} |
| | |
| | /* Select boxes */ |
| | .stSelectbox > div > div {{ |
| | background-color: {theme['secondary']}; |
| | color: {theme['text']}; |
| | border-radius: 5px; |
| | }} |
| | |
| | /* Multiselect */ |
| | .stMultiSelect > div > div {{ |
| | background-color: {theme['secondary']}; |
| | border-radius: 5px; |
| | }} |
| | |
| | /* Tabs */ |
| | .stTabs [data-baseweb="tab-list"] {{ |
| | gap: 8px; |
| | }} |
| | |
| | .stTabs [data-baseweb="tab"] {{ |
| | background-color: {theme['secondary']}; |
| | color: {theme['text']}; |
| | border-radius: 5px 5px 0 0; |
| | padding: 10px 20px; |
| | font-weight: 600; |
| | }} |
| | |
| | .stTabs [aria-selected="true"] {{ |
| | background-color: {theme['primary']}; |
| | color: {theme['background']}; |
| | }} |
| | |
| | /* Progress bar */ |
| | .stProgress > div > div > div {{ |
| | background-color: {theme['primary']}; |
| | }} |
| | |
| | /* Expanders */ |
| | .streamlit-expanderHeader {{ |
| | background-color: {theme['secondary']}; |
| | color: {theme['primary']}; |
| | border-radius: 5px; |
| | font-weight: 600; |
| | }} |
| | |
| | /* Cards/Containers */ |
| | .element-container {{ |
| | background-color: transparent; |
| | }} |
| | |
| | /* Sidebar */ |
| | [data-testid="stSidebar"] {{ |
| | background-color: {theme['sidebar_bg']}; |
| | }} |
| | |
| | [data-testid="stSidebar"] h1, |
| | [data-testid="stSidebar"] h2, |
| | [data-testid="stSidebar"] h3 {{ |
| | color: {theme['text']} !important; |
| | }} |
| | |
| | [data-testid="stSidebar"] p, |
| | [data-testid="stSidebar"] span, |
| | [data-testid="stSidebar"] div, |
| | [data-testid="stSidebar"] label {{ |
| | color: {theme['text']} !important; |
| | }} |
| | |
| | /* Metrics */ |
| | [data-testid="stMetricValue"] {{ |
| | color: {theme['primary']} !important; |
| | font-size: 2rem; |
| | font-weight: bold; |
| | }} |
| | |
| | /* Divider */ |
| | hr {{ |
| | border-color: {theme['primary']}; |
| | opacity: 0.3; |
| | }} |
| | |
| | /* Checkbox */ |
| | .stCheckbox {{ |
| | color: {theme['text']}; |
| | }} |
| | |
| | /* Radio */ |
| | .stRadio > div {{ |
| | color: {theme['text']}; |
| | }} |
| | |
| | /* Success/Info/Warning/Error boxes */ |
| | .stSuccess {{ |
| | background-color: rgba(88, 214, 141, 0.1); |
| | color: {theme['text']}; |
| | }} |
| | |
| | .stInfo {{ |
| | background-color: rgba(93, 173, 226, 0.1); |
| | color: {theme['text']}; |
| | }} |
| | |
| | .stWarning {{ |
| | background-color: rgba(255, 215, 0, 0.1); |
| | color: {theme['text']}; |
| | }} |
| | |
| | .stError {{ |
| | background-color: rgba(236, 112, 99, 0.1); |
| | color: {theme['text']}; |
| | }} |
| | |
| | /* DataFrames */ |
| | .dataframe {{ |
| | border: 1px solid {theme['primary']}; |
| | }} |
| | |
| | /* Custom card styling */ |
| | .message-card {{ |
| | background-color: {theme['secondary']}; |
| | border-left: 4px solid {theme['primary']}; |
| | border-radius: 8px; |
| | padding: 1rem; |
| | margin: 0.5rem 0; |
| | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); |
| | }} |
| | |
| | .message-card:hover {{ |
| | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); |
| | transform: translateX(4px); |
| | transition: all 0.3s ease; |
| | }} |
| | |
| | /* Small text */ |
| | .small {{ |
| | font-size: 0.85rem; |
| | opacity: 0.7; |
| | }} |
| | |
| | /* Badge styles */ |
| | .badge {{ |
| | display: inline-block; |
| | padding: 0.25rem 0.5rem; |
| | border-radius: 12px; |
| | font-size: 0.75rem; |
| | font-weight: 600; |
| | background-color: {theme['accent']}; |
| | color: {theme['text']}; |
| | }} |
| | </style> |
| | """ |
| |
|
| | st.markdown(css, unsafe_allow_html=True) |
| |
|
| |
|
| | def create_card(content: str, key: str = None) -> None: |
| | """ |
| | Create a styled card container. |
| | |
| | Args: |
| | content: HTML content to display in card |
| | key: Optional unique key for the card |
| | """ |
| | st.markdown( |
| | f'<div class="message-card">{content}</div>', |
| | unsafe_allow_html=True |
| | ) |
| |
|
| |
|
| | def create_badge(text: str, color: str = None) -> str: |
| | """ |
| | Create a styled badge element. |
| | |
| | Args: |
| | text: Badge text |
| | color: Optional custom color |
| | |
| | Returns: |
| | str: HTML for badge |
| | """ |
| | style = f'background-color: {color};' if color else '' |
| | return f'<span class="badge" style="{style}">{text}</span>' |
| |
|