File size: 3,681 Bytes
12cdb17
 
 
 
9ac8f1c
12cdb17
 
 
9ac8f1c
 
 
 
 
 
 
 
 
1b16c40
 
 
 
12cdb17
 
 
 
 
 
 
1b16c40
9ac8f1c
 
 
 
 
 
 
 
12cdb17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9ac8f1c
12cdb17
 
 
 
 
 
1b16c40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
"""Catppuccin Plotly template installer."""

import plotly.graph_objects as go
import plotly.io as pio
import streamlit as st
from catppuccin import PALETTE


def active_base() -> str:
    """Return the live theme flavor ("light"/"dark").

    Reflects the user's choice in Streamlit's built-in Settings-menu theme
    toggle, falling back to the configured ``theme.base`` default.
    """
    return st.context.theme.type or st.get_option("theme.base")


def _flavor(base: str | None):
    return PALETTE.latte if base == "light" else PALETTE.mocha


def install_catppuccin_theme(base: str | None = None) -> None:
    """Register a Catppuccin template and alias it as ``plotly_white``.

    Call once at startup. Persona-vectors pins ``template="plotly_white"`` on
    every figure, so replacing that entry themes all plots without any
    per-figure code.
    """
    c = _flavor(base).colors
    bg, text, subtext = c.base.hex, c.text.hex, c.subtext1.hex
    if base == "light":
        # Latte's surface0/1 are heavy grays; use lighter slots so the grid
        # stays subtle, the legend isn't a gray block, and diverging plots
        # fade through near-white instead of mud.
        surface, line, mid = c.mantle.hex, c.surface0.hex, c.base.hex
    else:
        surface, line, mid = c.surface0.hex, c.surface1.hex, c.surface0.hex

    axis = dict(
        gridcolor=line,
        zerolinecolor=line,
        linecolor=line,
        tickcolor=line,
        tickfont=dict(color=subtext),
        title=dict(font=dict(color=text)),
    )
    scene_axis = dict(
        backgroundcolor=bg,
        gridcolor=line,
        zerolinecolor=line,
        showbackground=True,
        color=text,
        tickfont=dict(color=subtext),
    )

    template = go.layout.Template(
        layout=dict(
            paper_bgcolor=bg,
            plot_bgcolor=bg,
            font=dict(color=text),
            colorway=[
                c.blue.hex,
                c.mauve.hex,
                c.green.hex,
                c.peach.hex,
                c.teal.hex,
                c.pink.hex,
                c.yellow.hex,
                c.sapphire.hex,
                c.lavender.hex,
                c.red.hex,
                c.sky.hex,
                c.maroon.hex,
            ],
            xaxis=axis,
            yaxis=axis,
            scene=dict(xaxis=scene_axis, yaxis=scene_axis, zaxis=scene_axis),
            legend=dict(bgcolor=surface, bordercolor=line, font=dict(color=text)),
            colorscale=dict(
                diverging=[[0.0, c.blue.hex], [0.5, mid], [1.0, c.red.hex]],
            ),
        )
    )
    pio.templates["catppuccin"] = template
    pio.templates["plotly_white"] = template
    pio.templates.default = "catppuccin"


def style_plotly_layer_controls(fig: go.Figure, base: str | None = None) -> go.Figure:
    """Theme persona-vectors layer sliders/buttons for this Streamlit app."""
    c = _flavor(base).colors
    surface = c.surface0.hex
    overlay = c.surface1.hex
    text = c.text.hex
    primary = c.blue.hex

    for slider in fig.layout.sliders:
        slider.bgcolor = surface
        slider.activebgcolor = primary
        slider.bordercolor = overlay
        slider.borderwidth = 1
        slider.font = dict(color=text, size=11)
        slider.tickcolor = primary
        slider.currentvalue = dict(
            slider.currentvalue.to_plotly_json(),
            font=dict(color=text, size=13),
        )

    for menu in fig.layout.updatemenus:
        if menu.type != "buttons":
            continue
        menu.bgcolor = surface
        menu.bordercolor = overlay
        menu.font = dict(color=text, size=13)

    return fig