We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jango-blockchained/mcp-freecad'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Centralized Theme System for FreeCAD AI GUI Components"""
from enum import Enum
from PySide2 import QtCore, QtWidgets
class Theme(Enum):
"""Available themes."""
LIGHT = "light"
DARK = "dark"
AUTO = "auto" # Follow system theme
class ColorScheme:
"""Color scheme definitions."""
def __init__(self, theme: Theme = Theme.LIGHT):
self.theme = theme
self._load_colors()
def _load_colors(self):
"""Load colors based on theme."""
if self.theme == Theme.LIGHT:
self.colors = {
# Background colors
"background_primary": "#ffffff",
"background_secondary": "#f8f9fa",
"background_tertiary": "#e9ecef",
"background_accent": "#e3f2fd",
# Text colors
"text_primary": "#212529",
"text_secondary": "#6c757d",
"text_muted": "#adb5bd",
"text_inverse": "#ffffff",
# Brand colors
"primary": "#2196F3",
"secondary": "#6c757d",
"success": "#4CAF50",
"warning": "#FF9800",
"error": "#f44336",
"info": "#17a2b8",
# Chat-specific colors
"user_message_bg": "#e3f2fd",
"ai_message_bg": "#e8f5e9",
"system_message_bg": "#fff3e0",
"user_text": "#2196F3",
"ai_text": "#4CAF50",
"system_text": "#FF9800",
# UI element colors
"border": "#dee2e6",
"border_focus": "#2196F3",
"button_primary": "#2196F3",
"button_success": "#4CAF50",
"button_warning": "#FF9800",
"button_danger": "#f44336",
# Status colors
"status_connected": "#4CAF50",
"status_warning": "#FF9800",
"status_error": "#f44336",
"status_unknown": "#9E9E9E",
}
else: # DARK theme
self.colors = {
# Background colors
"background_primary": "#1a1a1a",
"background_secondary": "#2d2d2d",
"background_tertiary": "#3a3a3a",
"background_accent": "#1e3a5f",
# Text colors
"text_primary": "#ffffff",
"text_secondary": "#b3b3b3",
"text_muted": "#808080",
"text_inverse": "#000000",
# Brand colors (slightly adjusted for dark theme)
"primary": "#42A5F5",
"secondary": "#9e9e9e",
"success": "#66BB6A",
"warning": "#FFA726",
"error": "#EF5350",
"info": "#29B6F6",
# Chat-specific colors
"user_message_bg": "#1e3a5f",
"ai_message_bg": "#2e5d33",
"system_message_bg": "#4a3326",
"user_text": "#42A5F5",
"ai_text": "#66BB6A",
"system_text": "#FFA726",
# UI element colors
"border": "#4a4a4a",
"border_focus": "#42A5F5",
"button_primary": "#42A5F5",
"button_success": "#66BB6A",
"button_warning": "#FFA726",
"button_danger": "#EF5350",
# Status colors
"status_connected": "#66BB6A",
"status_warning": "#FFA726",
"status_error": "#EF5350",
"status_unknown": "#9E9E9E",
}
def get_color(self, color_name: str) -> str:
"""Get color by name."""
return self.colors.get(color_name, "#000000")
def get_rgba(self, color_name: str, alpha: float = 1.0) -> str:
"""Get color as rgba string."""
hex_color = self.get_color(color_name)
if hex_color.startswith("#"):
hex_color = hex_color[1:]
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
return f"rgba({r}, {g}, {b}, {alpha})"
class StyleSheet:
"""Generate consistent stylesheets."""
def __init__(self, color_scheme: ColorScheme):
self.colors = color_scheme
def get_button_style(self, button_type: str = "primary") -> str:
"""Get button stylesheet."""
color_map = {
"primary": self.colors.get_color("button_primary"),
"success": self.colors.get_color("button_success"),
"warning": self.colors.get_color("button_warning"),
"danger": self.colors.get_color("button_danger"),
}
base_color = color_map.get(button_type, self.colors.get_color("primary"))
hover_color = self._darken_color(base_color, 0.1)
return f"""
QPushButton {{
background-color: {base_color};
color: {self.colors.get_color("text_inverse")};
border: none;
padding: 8px 16px;
border-radius: 4px;
font-weight: bold;
}}
QPushButton:hover {{
background-color: {hover_color};
}}
QPushButton:pressed {{
background-color: {self._darken_color(base_color, 0.2)};
}}
QPushButton:disabled {{
background-color: {self.colors.get_color("text_muted")};
}}
"""
def get_input_style(self) -> str:
"""Get input field stylesheet."""
return f"""
QLineEdit, QTextEdit {{
background-color: {self.colors.get_color("background_primary")};
border: 2px solid {self.colors.get_color("border")};
border-radius: 4px;
padding: 8px;
color: {self.colors.get_color("text_primary")};
}}
QLineEdit:focus, QTextEdit:focus {{
border-color: {self.colors.get_color("border_focus")};
}}
"""
def get_groupbox_style(self) -> str:
"""Get group box stylesheet."""
return f"""
QGroupBox {{
background-color: {self.colors.get_color("background_secondary")};
border: 1px solid {self.colors.get_color("border")};
border-radius: 6px;
margin-top: 10px;
padding-top: 10px;
color: {self.colors.get_color("text_primary")};
font-weight: bold;
}}
QGroupBox::title {{
subcontrol-origin: margin;
left: 10px;
padding: 0 5px 0 5px;
}}
"""
def get_conversation_style(self) -> str:
"""Get conversation display stylesheet."""
return f"""
QTextBrowser {{
background-color: {self.colors.get_color("background_primary")};
border: 1px solid {self.colors.get_color("border")};
border-radius: 4px;
padding: 10px;
color: {self.colors.get_color("text_primary")};
font-family: Arial, sans-serif;
}}
"""
def get_status_indicator_style(self, status: str) -> str:
"""Get status indicator stylesheet."""
status_colors = {
"connected": self.colors.get_color("status_connected"),
"warning": self.colors.get_color("status_warning"),
"error": self.colors.get_color("status_error"),
"unknown": self.colors.get_color("status_unknown"),
}
color = status_colors.get(status, self.colors.get_color("status_unknown"))
return f"""
QLabel {{
color: {color};
font-weight: bold;
font-size: 16px;
}}
"""
def get_tab_style(self) -> str:
"""Get tab widget stylesheet."""
return f"""
QTabWidget::pane {{
border: 1px solid {self.colors.get_color("border")};
background-color: {self.colors.get_color("background_primary")};
}}
QTabBar::tab {{
background-color: {self.colors.get_color("background_secondary")};
color: {self.colors.get_color("text_primary")};
padding: 8px 16px;
margin-right: 2px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}}
QTabBar::tab:selected {{
background-color: {self.colors.get_color("primary")};
color: {self.colors.get_color("text_inverse")};
}}
QTabBar::tab:hover {{
background-color: {self.colors.get_rgba("primary", 0.2)};
}}
"""
def _darken_color(self, hex_color: str, factor: float) -> str:
"""Darken a hex color by factor (0.0 to 1.0)."""
if hex_color.startswith("#"):
hex_color = hex_color[1:]
r = int(hex_color[0:2], 16)
g = int(hex_color[2:4], 16)
b = int(hex_color[4:6], 16)
r = max(0, int(r * (1 - factor)))
g = max(0, int(g * (1 - factor)))
b = max(0, int(b * (1 - factor)))
return f"#{r:02x}{g:02x}{b:02x}"
class ThemeManager:
"""Manages themes across the application."""
def __init__(self):
self.current_theme = Theme.LIGHT
self.color_scheme = ColorScheme(self.current_theme)
self.stylesheet = StyleSheet(self.color_scheme)
self._theme_changed_callbacks = []
def set_theme(self, theme: Theme):
"""Set application theme."""
if theme == self.current_theme:
return
self.current_theme = theme
self.color_scheme = ColorScheme(theme)
self.stylesheet = StyleSheet(self.color_scheme)
# Notify callbacks
for callback in self._theme_changed_callbacks:
try:
callback(theme)
except Exception as e:
print(f"ThemeManager: Error in theme change callback: {e}")
def register_theme_change_callback(self, callback):
"""Register callback for theme changes."""
self._theme_changed_callbacks.append(callback)
def apply_theme_to_widget(self, widget: QtWidgets.QWidget):
"""Apply current theme to a widget."""
try:
# Apply base styling
widget.setStyleSheet(f"""
QWidget {{
background-color: {self.color_scheme.get_color("background_primary")};
color: {self.color_scheme.get_color("text_primary")};
}}
""")
# Apply specific styling based on widget type
self._apply_specific_styling(widget)
except Exception as e:
print(f"ThemeManager: Error applying theme to widget: {e}")
def _apply_specific_styling(self, widget: QtWidgets.QWidget):
"""Apply theme-specific styling to widget types."""
# Find and style specific widget types
buttons = widget.findChildren(QtWidgets.QPushButton)
for button in buttons:
# Determine button type from style or text
if "danger" in button.objectName().lower() or "delete" in button.text().lower():
button.setStyleSheet(self.stylesheet.get_button_style("danger"))
elif "success" in button.objectName().lower() or "save" in button.text().lower():
button.setStyleSheet(self.stylesheet.get_button_style("success"))
elif "warning" in button.objectName().lower():
button.setStyleSheet(self.stylesheet.get_button_style("warning"))
else:
button.setStyleSheet(self.stylesheet.get_button_style("primary"))
# Style input fields
inputs = widget.findChildren(QtWidgets.QLineEdit) + widget.findChildren(QtWidgets.QTextEdit)
for input_widget in inputs:
input_widget.setStyleSheet(self.stylesheet.get_input_style())
# Style group boxes
groupboxes = widget.findChildren(QtWidgets.QGroupBox)
for groupbox in groupboxes:
groupbox.setStyleSheet(self.stylesheet.get_groupbox_style())
# Style tab widgets
tabwidgets = widget.findChildren(QtWidgets.QTabWidget)
for tabwidget in tabwidgets:
tabwidget.setStyleSheet(self.stylesheet.get_tab_style())
# Global theme manager instance
_theme_manager = None
def get_theme_manager() -> ThemeManager:
"""Get global theme manager instance."""
global _theme_manager
if _theme_manager is None:
_theme_manager = ThemeManager()
return _theme_manager
def apply_theme_to_widget(widget: QtWidgets.QWidget, theme: Theme = None):
"""Convenience function to apply theme to widget."""
manager = get_theme_manager()
if theme and theme != manager.current_theme:
manager.set_theme(theme)
manager.apply_theme_to_widget(widget)
def get_current_color_scheme() -> ColorScheme:
"""Get current color scheme."""
return get_theme_manager().color_scheme
def get_current_stylesheet() -> StyleSheet:
"""Get current stylesheet generator."""
return get_theme_manager().stylesheet