@@ -435,9 +435,20 @@ async def compact_thread(request):
435435
436436 async def get_user_avatar (req ):
437437 user = ctx .get_username (req )
438- mode = req .query .get ("mode" , "light" )
438+ theme = req .query .get ("theme" )
439+
440+ # Fall back to default 'user' avatar
441+ mode = "dark" if theme == "dark" else "light"
442+ bg_color = "#1e3a8a" if mode == "dark" else "#dbeafe"
443+ text_color = "#f3f4f6" if mode == "dark" else "#111827"
444+
445+ if theme :
446+ config = get_theme_config (theme , req )
447+ vars = config .get ("vars" , {})
448+ mode = vars .get ("colorScheme" , mode )
449+ bg_color = vars .get ("--user-bg" , bg_color )
450+ text_color = vars .get ("--user-text" , text_color )
439451
440- # Cache for 1 hour # "Cache-Control": "public, max-age=3600",
441452 headers = {"Content-Type" : "image/svg+xml" }
442453
443454 candidate_paths = [
@@ -456,10 +467,6 @@ async def get_user_avatar(req):
456467 headers ["Content-Type" ] = "image/png" if path .endswith (".png" ) else "image/svg+xml"
457468 return web .FileResponse (path , headers = headers )
458469
459- # Fall back to default 'user' avatar
460- bg_color = "#1e3a8a" if mode == "dark" else "#bfdbfe"
461- text_color = "#f3f4f6" if mode == "dark" else "#111827"
462-
463470 default_avatar = f"""
464471 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" style="color:{ text_color } ">
465472 <circle cx="16" cy="16" r="16" fill="{ bg_color } "/>
@@ -473,9 +480,21 @@ async def get_user_avatar(req):
473480 ctx .add_get ("/avatar/user" , get_user_avatar )
474481
475482 async def get_agent_avatar (req ):
476- mode = req .query .get ("mode" , "light" )
483+ theme = req .query .get ("theme" )
484+
485+ # Fall back to default 'user' avatar
486+ mode = "dark" if theme == "dark" else "light"
487+ # Fall back to default 'agent' avatar
488+ bg_color = "#1e293b" if mode == "dark" else "#f3f4f6"
489+ text_color = "#f1f5f9" if mode == "dark" else "#111827"
490+
491+ if theme :
492+ config = get_theme_config (theme , req )
493+ vars = config .get ("vars" , {})
494+ mode = vars .get ("colorScheme" , mode )
495+ bg_color = vars .get ("--assistant-bg" , bg_color )
496+ text_color = vars .get ("--assistant-text" , text_color )
477497
478- # Cache for 1 hour # "Cache-Control": "public, max-age=3600",
479498 headers = {"Content-Type" : "image/svg+xml" }
480499
481500 candidate_paths = [
@@ -490,10 +509,6 @@ async def get_agent_avatar(req):
490509 headers ["Content-Type" ] = "image/png" if path .endswith (".png" ) else "image/svg+xml"
491510 return web .FileResponse (path , headers = headers )
492511
493- # Fall back to default 'agent' avatar
494- bg_color = "#1f2937" if mode == "dark" else "#eceef1"
495- text_color = "#f3f4f6" if mode == "dark" else "#111827"
496-
497512 default_avatar = f"""
498513 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" style="color:{ text_color } ">
499514 <circle cx="16" cy="16" r="16" fill="{ bg_color } "/>
@@ -622,6 +637,20 @@ def get_theme_roots(request):
622637 themes_dirs .append (os .path .join (ctx .get_user_path (user ), "themes" ))
623638 return themes_dirs
624639
640+ def get_theme_config (theme , request = None ):
641+ themes_dirs = get_theme_roots (request )
642+ for themes_dir in themes_dirs :
643+ if os .path .exists (themes_dir ):
644+ theme_path = os .path .join (themes_dir , theme )
645+ config_path = os .path .join (theme_path , "theme.json" )
646+ if os .path .isdir (theme_path ) and os .path .exists (config_path ):
647+ try :
648+ with open (config_path ) as f :
649+ return json .load (f )
650+ except Exception as e :
651+ ctx .dbg (f"Error loading theme { theme } : { e } " )
652+ return None
653+
625654 # THEMES
626655 async def get_themes (request ):
627656 themes = {}
@@ -631,10 +660,10 @@ async def get_themes(request):
631660 if os .path .exists (themes_dir ):
632661 for theme_name in os .listdir (themes_dir ):
633662 theme_path = os .path .join (themes_dir , theme_name )
634- styles_path = os .path .join (theme_path , "theme.json" )
635- if os .path .isdir (theme_path ) and os .path .exists (styles_path ):
663+ config_path = os .path .join (theme_path , "theme.json" )
664+ if os .path .isdir (theme_path ) and os .path .exists (config_path ):
636665 try :
637- with open (styles_path , encoding = "utf-8" ) as f :
666+ with open (config_path , encoding = "utf-8" ) as f :
638667 themes [theme_name ] = json .load (f )
639668 except Exception as e :
640669 if hasattr (ctx , "err" ):
0 commit comments