pinky-streamlit 0.1.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pinky_streamlit/__init__.py +70 -0
- pinky_streamlit/core/__init__.py +52 -0
- pinky_streamlit/core/constants.py +467 -0
- pinky_streamlit/core/db.py +285 -0
- pinky_streamlit/core/page.py +284 -0
- pinky_streamlit/core/session.py +103 -0
- pinky_streamlit/core/static/fonts/DMSans.woff2 +0 -0
- pinky_streamlit/core/static/style.css +311 -0
- pinky_streamlit/ui_components/__init__.py +3 -0
- pinky_streamlit-0.1.0.dev1.dist-info/METADATA +106 -0
- pinky_streamlit-0.1.0.dev1.dist-info/RECORD +13 -0
- pinky_streamlit-0.1.0.dev1.dist-info/WHEEL +4 -0
- pinky_streamlit-0.1.0.dev1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pinky-streamlit
|
|
3
|
+
========================
|
|
4
|
+
Reusable Streamlit in Snowflake components for the pinky suite.
|
|
5
|
+
|
|
6
|
+
Modules are implemented incrementally. Import with sks_ aliases to avoid
|
|
7
|
+
shadowing Streamlit's own st.* names:
|
|
8
|
+
|
|
9
|
+
from pinky_streamlit import PALETTES, PALETTE, setup_page, Logo
|
|
10
|
+
from pinky_streamlit import text as sks_text, header as sks_header
|
|
11
|
+
from pinky_streamlit import metric as sks_metric, detail as sks_detail
|
|
12
|
+
from pinky_streamlit import bar_chart as sks_bar_chart
|
|
13
|
+
from pinky_streamlit import filters as sks_filters, action_grid as sks_action_grid
|
|
14
|
+
from pinky_streamlit import card_grid as sks_card_grid, divider as sks_divider
|
|
15
|
+
from pinky_streamlit import status_card as sks_status_card
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from pinky_streamlit.core import (
|
|
19
|
+
ICONS,
|
|
20
|
+
MATERIAL_ICONS,
|
|
21
|
+
PALETTE,
|
|
22
|
+
PALETTES,
|
|
23
|
+
AppFont,
|
|
24
|
+
DEFAULT_FONT,
|
|
25
|
+
Icon,
|
|
26
|
+
LocalSessionConfig,
|
|
27
|
+
SessionInfo,
|
|
28
|
+
Logo,
|
|
29
|
+
Palette,
|
|
30
|
+
PaletteColors,
|
|
31
|
+
add_status_icons,
|
|
32
|
+
get_session,
|
|
33
|
+
insert_data,
|
|
34
|
+
invalidate_crud,
|
|
35
|
+
load_analytic,
|
|
36
|
+
load_crud,
|
|
37
|
+
load_nocache,
|
|
38
|
+
load_static,
|
|
39
|
+
setup_page,
|
|
40
|
+
update_data,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
# design system
|
|
45
|
+
"Icon",
|
|
46
|
+
"ICONS",
|
|
47
|
+
"PaletteColors",
|
|
48
|
+
"Palette",
|
|
49
|
+
"PALETTES",
|
|
50
|
+
"PALETTE",
|
|
51
|
+
"MATERIAL_ICONS",
|
|
52
|
+
# session
|
|
53
|
+
"LocalSessionConfig",
|
|
54
|
+
"SessionInfo",
|
|
55
|
+
"get_session",
|
|
56
|
+
# db
|
|
57
|
+
"update_data",
|
|
58
|
+
"insert_data",
|
|
59
|
+
"add_status_icons",
|
|
60
|
+
"load_static",
|
|
61
|
+
"load_analytic",
|
|
62
|
+
"load_crud",
|
|
63
|
+
"invalidate_crud",
|
|
64
|
+
"load_nocache",
|
|
65
|
+
# page
|
|
66
|
+
"AppFont",
|
|
67
|
+
"DEFAULT_FONT",
|
|
68
|
+
"Logo",
|
|
69
|
+
"setup_page",
|
|
70
|
+
]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""pinky_streamlit.core — foundational layer (session, DB, design system)."""
|
|
2
|
+
|
|
3
|
+
from .constants import (
|
|
4
|
+
ICONS,
|
|
5
|
+
MATERIAL_ICONS,
|
|
6
|
+
PALETTE,
|
|
7
|
+
PALETTES,
|
|
8
|
+
Icon,
|
|
9
|
+
Palette,
|
|
10
|
+
PaletteColors,
|
|
11
|
+
)
|
|
12
|
+
from .db import (
|
|
13
|
+
add_status_icons,
|
|
14
|
+
insert_data,
|
|
15
|
+
invalidate_crud,
|
|
16
|
+
load_analytic,
|
|
17
|
+
load_crud,
|
|
18
|
+
load_nocache,
|
|
19
|
+
load_static,
|
|
20
|
+
update_data,
|
|
21
|
+
)
|
|
22
|
+
from .page import DEFAULT_FONT, AppFont, Logo, setup_page
|
|
23
|
+
from .session import LocalSessionConfig, SessionInfo, get_session
|
|
24
|
+
|
|
25
|
+
__all__ = [ # noqa: RUF022 — grouped by layer, not alpha
|
|
26
|
+
# design system
|
|
27
|
+
"Icon",
|
|
28
|
+
"ICONS",
|
|
29
|
+
"PaletteColors",
|
|
30
|
+
"Palette",
|
|
31
|
+
"PALETTES",
|
|
32
|
+
"PALETTE",
|
|
33
|
+
"MATERIAL_ICONS",
|
|
34
|
+
# session
|
|
35
|
+
"LocalSessionConfig",
|
|
36
|
+
"SessionInfo",
|
|
37
|
+
"get_session",
|
|
38
|
+
# db
|
|
39
|
+
"update_data",
|
|
40
|
+
"insert_data",
|
|
41
|
+
"add_status_icons",
|
|
42
|
+
"load_static",
|
|
43
|
+
"load_analytic",
|
|
44
|
+
"load_crud",
|
|
45
|
+
"invalidate_crud",
|
|
46
|
+
"load_nocache",
|
|
47
|
+
# page
|
|
48
|
+
"AppFont",
|
|
49
|
+
"DEFAULT_FONT",
|
|
50
|
+
"Logo",
|
|
51
|
+
"setup_page",
|
|
52
|
+
]
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pinky_streamlit.constants
|
|
3
|
+
==================================
|
|
4
|
+
Shared constants for the design system: color palette and icon registry.
|
|
5
|
+
|
|
6
|
+
Usage::
|
|
7
|
+
|
|
8
|
+
from pinky_streamlit import ICONS, PALETTE
|
|
9
|
+
|
|
10
|
+
st.badge("", icon=ICONS.SUCCESS.material) # :material/check_circle:
|
|
11
|
+
st.badge("✅", icon=ICONS.SUCCESS.emoji) # ✅
|
|
12
|
+
st.badge("", icon=str(ICONS.SUCCESS)) # same as .material (default)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import colorsys
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
|
|
20
|
+
# ── Icon ──────────────────────────────────────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class Icon:
|
|
25
|
+
"""A design-system icon with both a Material Symbol and an emoji variant.
|
|
26
|
+
|
|
27
|
+
``str(icon)`` returns the material string — safe to pass to any Streamlit
|
|
28
|
+
argument that accepts a Material Symbol (icon=, st.badge, st.button…).
|
|
29
|
+
Use ``.emoji`` for components that don't support ``:material/…:`` syntax.
|
|
30
|
+
|
|
31
|
+
To extend the registry with app-specific icons, subclass ICONS::
|
|
32
|
+
|
|
33
|
+
from pinky_streamlit import ICONS, Icon
|
|
34
|
+
|
|
35
|
+
class MyIcons(ICONS):
|
|
36
|
+
CAT = Icon(":material/pets:", "🐱")
|
|
37
|
+
RABBIT = Icon(":material/cruelty_free:", "🐰")
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
material: str
|
|
41
|
+
emoji: str
|
|
42
|
+
|
|
43
|
+
def __str__(self) -> str:
|
|
44
|
+
return self.material
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ICONS:
|
|
48
|
+
"""Kit icon registry — subclass to add app-specific icons.
|
|
49
|
+
|
|
50
|
+
Access via ``ICONS.<NAME>.material`` or ``ICONS.<NAME>.emoji``.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
# ── Navigation & layout ───────────────────────────────────────────────────
|
|
54
|
+
HOME = Icon(":material/home:", "🏠")
|
|
55
|
+
DASHBOARD = Icon(":material/dashboard:", "📋")
|
|
56
|
+
WIDGETS = Icon(":material/widgets:", "🧩")
|
|
57
|
+
SETTINGS = Icon(":material/settings:", "⚙️")
|
|
58
|
+
ADMIN = Icon(":material/admin_panel_settings:", "🛡️")
|
|
59
|
+
FILTER = Icon(":material/filter_list:", "🔽")
|
|
60
|
+
SORT = Icon(":material/sort:", "↕️")
|
|
61
|
+
SEARCH = Icon(":material/search:", "🔍")
|
|
62
|
+
EXPAND = Icon(":material/expand_more:", "▼")
|
|
63
|
+
COLLAPSE = Icon(":material/expand_less:", "▲")
|
|
64
|
+
BACK = Icon(":material/arrow_back:", "◀")
|
|
65
|
+
FORWARD = Icon(":material/arrow_forward:", "▶")
|
|
66
|
+
ARROW_RIGHT = Icon(":material/arrow_right:", "→")
|
|
67
|
+
LINK = Icon(":material/link:", "🔗")
|
|
68
|
+
PIN = Icon(":material/push_pin:", "📌")
|
|
69
|
+
TAG = Icon(":material/tag:", "🏷️")
|
|
70
|
+
COPY = Icon(":material/content_copy:", "📋")
|
|
71
|
+
|
|
72
|
+
# ── Status & feedback ─────────────────────────────────────────────────────
|
|
73
|
+
SUCCESS = Icon(":material/check_circle:", "✅")
|
|
74
|
+
WARNING = Icon(":material/warning:", "⚠️")
|
|
75
|
+
ERROR = Icon(":material/error:", "🚨")
|
|
76
|
+
INFO = Icon(":material/info:", "ℹ️") # noqa: RUF001
|
|
77
|
+
HELP = Icon(":material/help:", "❓")
|
|
78
|
+
LEGEND = Icon(":material/live_help:", "💡")
|
|
79
|
+
UNDEFINED = Icon(":material/remove:", "—")
|
|
80
|
+
CELEBRATION = Icon(":material/celebration:", "🎉")
|
|
81
|
+
BLOCKED = Icon(":material/block:", "🚫")
|
|
82
|
+
DO_NOT_DISTURB = Icon(":material/do_not_disturb_on:", "⛔")
|
|
83
|
+
CHECK = Icon(":material/check:", "✓")
|
|
84
|
+
CANCEL = Icon(":material/cancel:", "❌")
|
|
85
|
+
FLAG = Icon(":material/flag:", "🚩")
|
|
86
|
+
STAR = Icon(":material/star:", "⭐")
|
|
87
|
+
SKULL = Icon(":material/skull:", "💀")
|
|
88
|
+
NOTIFICATION = Icon(":material/notifications:", "🔔")
|
|
89
|
+
SATISFIED = Icon(":material/sentiment_satisfied:", "🙂")
|
|
90
|
+
NEUTRAL = Icon(":material/sentiment_neutral:", "😐")
|
|
91
|
+
DISSATISFIED = Icon(":material/sentiment_dissatisfied:", "☹️")
|
|
92
|
+
|
|
93
|
+
# ── Actions ───────────────────────────────────────────────────────────────
|
|
94
|
+
ADD = Icon(":material/add:", "+")
|
|
95
|
+
ADD_ROW = Icon(":material/docs_add_on:", "+")
|
|
96
|
+
EDIT = Icon(":material/edit:", "✏️")
|
|
97
|
+
EDIT_COLUMN = Icon(":material/edit_note:", "📝")
|
|
98
|
+
DELETE = Icon(":material/delete:", "🗑️")
|
|
99
|
+
SAVE = Icon(":material/save:", "💾")
|
|
100
|
+
CLOSE = Icon(":material/close:", "✖️")
|
|
101
|
+
REFRESH = Icon(":material/refresh:", "🔄")
|
|
102
|
+
SYNC = Icon(":material/sync:", "🔄")
|
|
103
|
+
PLAY = Icon(":material/play_arrow:", "▶️")
|
|
104
|
+
PAUSE = Icon(":material/pause:", "⏸️")
|
|
105
|
+
STOP = Icon(":material/stop:", "⏹️")
|
|
106
|
+
EXECUTE = Icon(":material/electric_bolt:", "⚡")
|
|
107
|
+
MANUAL = Icon(":material/touch_app:", "👆")
|
|
108
|
+
TRIGGERED = Icon(":material/calendar_clock:", "⏰")
|
|
109
|
+
SP_PARAMS = Icon(":material/settings_timelapse:", "⏱️")
|
|
110
|
+
LOCK = Icon(":material/lock:", "🔒")
|
|
111
|
+
UNLOCK = Icon(":material/lock_open:", "🔓")
|
|
112
|
+
VISIBLE = Icon(":material/visibility:", "👁️")
|
|
113
|
+
HIDDEN = Icon(":material/visibility_off:", "🙈")
|
|
114
|
+
|
|
115
|
+
# ── Files & data ──────────────────────────────────────────────────────────
|
|
116
|
+
DOWNLOAD = Icon(":material/cloud_download:", "📥")
|
|
117
|
+
UPLOAD = Icon(":material/upload_file:", "📤")
|
|
118
|
+
CLOUD_UPLOAD = Icon(":material/cloud_upload:", "☁️")
|
|
119
|
+
FILE = Icon(":material/description:", "📄")
|
|
120
|
+
FOLDER = Icon(":material/folder:", "📁")
|
|
121
|
+
TABLE = Icon(":material/table:", "📋")
|
|
122
|
+
TABLE_EDIT = Icon(":material/table_edit:", "📝")
|
|
123
|
+
TABLE_READONLY = Icon(":material/edit_off:", "🔒")
|
|
124
|
+
OUTPUT = Icon(":material/output:", "📤")
|
|
125
|
+
|
|
126
|
+
# ── Content ───────────────────────────────────────────────────────────────
|
|
127
|
+
ARTICLE = Icon(":material/article:", "📄")
|
|
128
|
+
IMAGE = Icon(":material/image:", "🖼️")
|
|
129
|
+
CHAT = Icon(":material/chat:", "💬")
|
|
130
|
+
CARDS = Icon(":material/cards:", "🃏")
|
|
131
|
+
MAIL = Icon(":material/mail:", "📧")
|
|
132
|
+
|
|
133
|
+
# ── Users & org ───────────────────────────────────────────────────────────
|
|
134
|
+
USER = Icon(":material/person:", "👤")
|
|
135
|
+
USERS = Icon(":material/group:", "👥")
|
|
136
|
+
BUILDING = Icon(":material/apartment:", "🏢")
|
|
137
|
+
COMPANY = Icon(":material/business:", "🏢")
|
|
138
|
+
|
|
139
|
+
# ── Analytics & time ──────────────────────────────────────────────────────
|
|
140
|
+
ANALYTICS = Icon(":material/analytics:", "📊")
|
|
141
|
+
CHART = Icon(":material/bar_chart:", "📊")
|
|
142
|
+
CHARTS = Icon(":material/insert_chart:", "📊")
|
|
143
|
+
TIMELINE = Icon(":material/timeline:", "📈")
|
|
144
|
+
TRENDING_UP = Icon(":material/trending_up:", "📈")
|
|
145
|
+
TRENDING_DOWN = Icon(":material/trending_down:", "📉")
|
|
146
|
+
CALENDAR = Icon(":material/calendar_month:", "📅")
|
|
147
|
+
CLOCK = Icon(":material/schedule:", "🕐")
|
|
148
|
+
HISTORY = Icon(":material/history:", "🕰️")
|
|
149
|
+
UPDATE = Icon(":material/update:", "🔄")
|
|
150
|
+
|
|
151
|
+
# ── Finance ───────────────────────────────────────────────────────────────
|
|
152
|
+
MONEY = Icon(":material/payments:", "💰")
|
|
153
|
+
RECEIPT = Icon(":material/receipt_long:", "🧾")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# ── Palette ───────────────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass(frozen=True)
|
|
160
|
+
class PaletteColors:
|
|
161
|
+
"""Color set for one mode (light or dark).
|
|
162
|
+
|
|
163
|
+
``primary``, ``secondary``, ``tertiary`` are the brand accent colors.
|
|
164
|
+
Semantic colors default to the pythia theme values and are usually kept
|
|
165
|
+
stable across themes.
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
primary: str
|
|
169
|
+
secondary: str
|
|
170
|
+
tertiary: str
|
|
171
|
+
success: str = "#09ab3b"
|
|
172
|
+
warning: str = "#ffa421"
|
|
173
|
+
error: str = "#ff4b4b"
|
|
174
|
+
grey: str = "#535353"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@dataclass(frozen=True)
|
|
178
|
+
class Palette:
|
|
179
|
+
"""A complete theme with light and dark color sets.
|
|
180
|
+
|
|
181
|
+
Pass to ``setup_page(palette=...)`` to inject brand colors as CSS variables.
|
|
182
|
+
All ``color-mix()`` shades in style.css are derived automatically from these
|
|
183
|
+
base colors — no CSS file needs editing.
|
|
184
|
+
|
|
185
|
+
Usage::
|
|
186
|
+
|
|
187
|
+
from pinky_streamlit import PALETTES, setup_page
|
|
188
|
+
|
|
189
|
+
messages, main, df = setup_page(session, loaders=[...], palette=PALETTES.APPLE)
|
|
190
|
+
|
|
191
|
+
Extend with a custom palette::
|
|
192
|
+
|
|
193
|
+
from pinky_streamlit import Palette, PaletteColors
|
|
194
|
+
|
|
195
|
+
# Dark auto-computed from light
|
|
196
|
+
MY_PALETTE = Palette(
|
|
197
|
+
light=PaletteColors(primary="#6200ea", secondary="#00bcd4", tertiary="#76ff03"),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Full control over dark variant
|
|
201
|
+
MY_PALETTE = Palette(
|
|
202
|
+
light=PaletteColors(primary="#6200ea", secondary="#00bcd4", tertiary="#76ff03"),
|
|
203
|
+
dark=PaletteColors( primary="#b388ff", secondary="#80deea", tertiary="#ccff90"),
|
|
204
|
+
)
|
|
205
|
+
"""
|
|
206
|
+
|
|
207
|
+
light: PaletteColors
|
|
208
|
+
dark: PaletteColors | None = None
|
|
209
|
+
|
|
210
|
+
def to_config_toml(
|
|
211
|
+
self,
|
|
212
|
+
font_files: list[str] | None = None,
|
|
213
|
+
) -> str:
|
|
214
|
+
"""Generate ``.streamlit/config.toml`` content from this palette.
|
|
215
|
+
|
|
216
|
+
For Streamlit Container Runtime apps: sets ``primaryColor`` natively
|
|
217
|
+
for both light and dark themes. This fixes the KNOWN LIMITATIONS in
|
|
218
|
+
``style.css`` (slider track fill, date picker selected day) that
|
|
219
|
+
cannot be overridden via CSS — Streamlit now applies the color
|
|
220
|
+
at the React level.
|
|
221
|
+
|
|
222
|
+
``setup_page()`` still injects ``style.css`` + CSS variables for the
|
|
223
|
+
full design system (``--primary-darker``, ``color-mix()`` shades,
|
|
224
|
+
component overrides). This method handles what ``config.toml``
|
|
225
|
+
can absorb; CSS handles the rest.
|
|
226
|
+
|
|
227
|
+
Font injection via base64 (``AppFont``) is no longer needed in
|
|
228
|
+
Container Runtime — pass font files instead and set ``font=None``
|
|
229
|
+
in ``setup_page()``.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
font_files: Project-relative paths to font files to bundle
|
|
233
|
+
(e.g. ``["static/DMSans.woff2"]``). ``None`` = no
|
|
234
|
+
custom font declared in config.toml.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
String content to write to ``.streamlit/config.toml``.
|
|
238
|
+
|
|
239
|
+
Example::
|
|
240
|
+
|
|
241
|
+
from pinky_streamlit import PALETTES
|
|
242
|
+
|
|
243
|
+
toml = PALETTES.PYTHIA.to_config_toml(
|
|
244
|
+
font_files=["static/DMSans.woff2"]
|
|
245
|
+
)
|
|
246
|
+
# Write to .streamlit/config.toml in your app project.
|
|
247
|
+
"""
|
|
248
|
+
dc = self.dark if self.dark is not None else _auto_dark_palette(self.light)
|
|
249
|
+
lines: list[str] = [
|
|
250
|
+
"[theme.light]",
|
|
251
|
+
f'primaryColor = "{self.light.primary}"',
|
|
252
|
+
"",
|
|
253
|
+
"[theme.dark]",
|
|
254
|
+
f'primaryColor = "{dc.primary}"',
|
|
255
|
+
]
|
|
256
|
+
if font_files:
|
|
257
|
+
formatted = "[" + ", ".join(f'"{f}"' for f in font_files) + "]"
|
|
258
|
+
lines += [
|
|
259
|
+
"",
|
|
260
|
+
"[theme]",
|
|
261
|
+
'font = "custom"',
|
|
262
|
+
f"fontFiles = {formatted}",
|
|
263
|
+
]
|
|
264
|
+
return "\n".join(lines) + "\n"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# ── Dark-mode auto-computation ────────────────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _auto_dark_color(hex_color: str) -> str:
|
|
271
|
+
"""Return a dark-mode variant of *hex_color*.
|
|
272
|
+
|
|
273
|
+
Pulls lightness toward 0.60 (legible on dark backgrounds without glare)
|
|
274
|
+
and slightly desaturates. Hue is preserved.
|
|
275
|
+
"""
|
|
276
|
+
raw = hex_color.lstrip("#")
|
|
277
|
+
r, g, b = (int(raw[i : i + 2], 16) / 255.0 for i in (0, 2, 4))
|
|
278
|
+
h, lum, s = colorsys.rgb_to_hls(r, g, b)
|
|
279
|
+
new_lum = max(0.35, min(0.75, 0.60 + (lum - 0.60) * 0.30))
|
|
280
|
+
new_s = min(1.0, s * 0.90)
|
|
281
|
+
r2, g2, b2 = colorsys.hls_to_rgb(h, new_lum, new_s)
|
|
282
|
+
return f"#{round(r2 * 255):02x}{round(g2 * 255):02x}{round(b2 * 255):02x}"
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _auto_dark_palette(light: PaletteColors) -> PaletteColors:
|
|
286
|
+
"""Derive a dark-mode ``PaletteColors`` from a light-mode one."""
|
|
287
|
+
return PaletteColors(
|
|
288
|
+
primary=_auto_dark_color(light.primary),
|
|
289
|
+
secondary=_auto_dark_color(light.secondary),
|
|
290
|
+
tertiary=_auto_dark_color(light.tertiary),
|
|
291
|
+
success=_auto_dark_color(light.success),
|
|
292
|
+
warning=_auto_dark_color(light.warning),
|
|
293
|
+
error=_auto_dark_color(light.error),
|
|
294
|
+
grey=_auto_dark_color(light.grey),
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
# ── Pre-built palettes ────────────────────────────────────────────────────────
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
class PALETTES:
|
|
302
|
+
"""Pre-built color palettes — subclass to add app-specific themes.
|
|
303
|
+
|
|
304
|
+
Access via ``PALETTES.PYTHIA``, ``PALETTES.STREAMLIT``, etc.
|
|
305
|
+
Pass to ``setup_page(palette=PALETTES.PYTHIA)`` to activate.
|
|
306
|
+
Dark mode is auto-computed unless overridden via ``dark=PaletteColors(...)``.
|
|
307
|
+
|
|
308
|
+
To extend with custom palettes, subclass ``PALETTES``::
|
|
309
|
+
|
|
310
|
+
from pinky_streamlit import PALETTES, Palette, PaletteColors
|
|
311
|
+
|
|
312
|
+
class AppPalettes(PALETTES):
|
|
313
|
+
MY_BRAND = Palette(
|
|
314
|
+
light=PaletteColors(primary="#6200ea", secondary="#00bcd4", tertiary="#76ff03"),
|
|
315
|
+
)
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
# Pinky brand — coral / slate blue / raspberry pink
|
|
319
|
+
PINKY = Palette(
|
|
320
|
+
light=PaletteColors(primary="#f1a1a0", secondary="#7b8fa8", tertiary="#ca748f"),
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Pythia brand — violet / rose / cyan
|
|
324
|
+
PYTHIA = Palette(
|
|
325
|
+
light=PaletteColors(primary="#7056d7", secondary="#e07baa", tertiary="#60defb"),
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Anthropic brand — coral / teal / warm sand
|
|
329
|
+
ANTHROPIC = Palette(
|
|
330
|
+
light=PaletteColors(primary="#d97756", secondary="#4a8c8c", tertiary="#c9a07a"),
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# Streamlit brand — red / blue / green (exact brand colors)
|
|
334
|
+
STREAMLIT = Palette(
|
|
335
|
+
light=PaletteColors(primary="#ff4b4b", secondary="#0068c9", tertiary="#09ab3b"),
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# Apple — blue / orange / purple (with brand semantic tokens)
|
|
339
|
+
APPLE = Palette(
|
|
340
|
+
light=PaletteColors(
|
|
341
|
+
primary="#0066cc",
|
|
342
|
+
secondary="#ff9900",
|
|
343
|
+
tertiary="#9900cc",
|
|
344
|
+
success="#00cc44",
|
|
345
|
+
warning="#ffcc00",
|
|
346
|
+
error="#d10000",
|
|
347
|
+
),
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# GitHub Primer — blue / green / purple
|
|
351
|
+
GITHUB = Palette(
|
|
352
|
+
light=PaletteColors(primary="#0969da", secondary="#2da44e", tertiary="#9a3ecb"),
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
# Python — blue / yellow / navy (logo colors)
|
|
356
|
+
PYTHON = Palette(
|
|
357
|
+
light=PaletteColors(primary="#3776ab", secondary="#ffd43b", tertiary="#1e415e"),
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Duck DB — black / yellow / white (logo colors)
|
|
361
|
+
DUCK_DB = Palette(
|
|
362
|
+
light=PaletteColors(primary="#000000", secondary="#fcf150", tertiary="#ffffff"),
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# ── Color palette (backward compat) ───────────────────────────────────────────
|
|
367
|
+
# Used by ui_charts.py (CHART_COLORS). Mirrors PALETTES.PYTHIA.light.
|
|
368
|
+
PALETTE: dict[str, str] = {
|
|
369
|
+
"primary": PALETTES.PYTHIA.light.primary,
|
|
370
|
+
"secondary": PALETTES.PYTHIA.light.secondary,
|
|
371
|
+
"tertiary": PALETTES.PYTHIA.light.tertiary,
|
|
372
|
+
"success": PALETTES.PYTHIA.light.success,
|
|
373
|
+
"warning": PALETTES.PYTHIA.light.warning,
|
|
374
|
+
"error": PALETTES.PYTHIA.light.error,
|
|
375
|
+
"grey": PALETTES.PYTHIA.light.grey,
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
# ── Backward compat ───────────────────────────────────────────────────────────
|
|
379
|
+
# MATERIAL_ICONS is deprecated — migrate to ICONS.<NAME>.material
|
|
380
|
+
MATERIAL_ICONS: dict[str, str] = {
|
|
381
|
+
k: v
|
|
382
|
+
for k, v in {
|
|
383
|
+
"home": str(ICONS.HOME),
|
|
384
|
+
"widgets": str(ICONS.WIDGETS),
|
|
385
|
+
"data": str(ICONS.ARTICLE),
|
|
386
|
+
"charts": str(ICONS.CHARTS),
|
|
387
|
+
"image": str(ICONS.IMAGE),
|
|
388
|
+
"chat": str(ICONS.CHAT),
|
|
389
|
+
"cards": str(ICONS.CARDS),
|
|
390
|
+
"article": str(ICONS.ARTICLE),
|
|
391
|
+
"dashboard": str(ICONS.DASHBOARD),
|
|
392
|
+
"settings": str(ICONS.SETTINGS),
|
|
393
|
+
"admin": str(ICONS.ADMIN),
|
|
394
|
+
"user": str(ICONS.USER),
|
|
395
|
+
"users": str(ICONS.USERS),
|
|
396
|
+
"search": str(ICONS.SEARCH),
|
|
397
|
+
"filter": str(ICONS.FILTER),
|
|
398
|
+
"sort": str(ICONS.SORT),
|
|
399
|
+
"refresh": str(ICONS.REFRESH),
|
|
400
|
+
"sync": str(ICONS.SYNC),
|
|
401
|
+
"save": str(ICONS.SAVE),
|
|
402
|
+
"edit": str(ICONS.EDIT),
|
|
403
|
+
"edit_column": str(ICONS.EDIT_COLUMN),
|
|
404
|
+
"delete": str(ICONS.DELETE),
|
|
405
|
+
"add": str(ICONS.ADD),
|
|
406
|
+
"add_row": str(ICONS.ADD_ROW),
|
|
407
|
+
"close": str(ICONS.CLOSE),
|
|
408
|
+
"check": str(ICONS.CHECK),
|
|
409
|
+
"cancel": str(ICONS.CANCEL),
|
|
410
|
+
"info": str(ICONS.INFO),
|
|
411
|
+
"warning": str(ICONS.WARNING),
|
|
412
|
+
"error": str(ICONS.ERROR),
|
|
413
|
+
"success": str(ICONS.SUCCESS),
|
|
414
|
+
"help": str(ICONS.HELP),
|
|
415
|
+
"legend": str(ICONS.LEGEND),
|
|
416
|
+
"undefined": str(ICONS.UNDEFINED),
|
|
417
|
+
"blocked": str(ICONS.BLOCKED),
|
|
418
|
+
"do_not_disturb": str(ICONS.DO_NOT_DISTURB),
|
|
419
|
+
"download": str(ICONS.DOWNLOAD),
|
|
420
|
+
"upload": str(ICONS.UPLOAD),
|
|
421
|
+
"cloud_upload": str(ICONS.CLOUD_UPLOAD),
|
|
422
|
+
"file": str(ICONS.FILE),
|
|
423
|
+
"folder": str(ICONS.FOLDER),
|
|
424
|
+
"table": str(ICONS.TABLE),
|
|
425
|
+
"table_edit": str(ICONS.TABLE_EDIT),
|
|
426
|
+
"table_readonly": str(ICONS.TABLE_READONLY),
|
|
427
|
+
"chart": str(ICONS.CHART),
|
|
428
|
+
"timeline": str(ICONS.TIMELINE),
|
|
429
|
+
"calendar": str(ICONS.CALENDAR),
|
|
430
|
+
"clock": str(ICONS.CLOCK),
|
|
431
|
+
"history": str(ICONS.HISTORY),
|
|
432
|
+
"update": str(ICONS.UPDATE),
|
|
433
|
+
"mail": str(ICONS.MAIL),
|
|
434
|
+
"notification": str(ICONS.NOTIFICATION),
|
|
435
|
+
"lock": str(ICONS.LOCK),
|
|
436
|
+
"unlock": str(ICONS.UNLOCK),
|
|
437
|
+
"visible": str(ICONS.VISIBLE),
|
|
438
|
+
"hidden": str(ICONS.HIDDEN),
|
|
439
|
+
"expand": str(ICONS.EXPAND),
|
|
440
|
+
"collapse": str(ICONS.COLLAPSE),
|
|
441
|
+
"back": str(ICONS.BACK),
|
|
442
|
+
"forward": str(ICONS.FORWARD),
|
|
443
|
+
"arrow_right": str(ICONS.ARROW_RIGHT),
|
|
444
|
+
"link": str(ICONS.LINK),
|
|
445
|
+
"copy": str(ICONS.COPY),
|
|
446
|
+
"tag": str(ICONS.TAG),
|
|
447
|
+
"play": str(ICONS.PLAY),
|
|
448
|
+
"pause": str(ICONS.PAUSE),
|
|
449
|
+
"stop": str(ICONS.STOP),
|
|
450
|
+
"execute": str(ICONS.EXECUTE),
|
|
451
|
+
"output": str(ICONS.OUTPUT),
|
|
452
|
+
"manual": str(ICONS.MANUAL),
|
|
453
|
+
"triggered": str(ICONS.TRIGGERED),
|
|
454
|
+
"sp_params": str(ICONS.SP_PARAMS),
|
|
455
|
+
"building": str(ICONS.BUILDING),
|
|
456
|
+
"company": str(ICONS.COMPANY),
|
|
457
|
+
"money": str(ICONS.MONEY),
|
|
458
|
+
"receipt": str(ICONS.RECEIPT),
|
|
459
|
+
"analytics": str(ICONS.ANALYTICS),
|
|
460
|
+
"trending_up": str(ICONS.TRENDING_UP),
|
|
461
|
+
"trending_down": str(ICONS.TRENDING_DOWN),
|
|
462
|
+
"flag": str(ICONS.FLAG),
|
|
463
|
+
"star": str(ICONS.STAR),
|
|
464
|
+
"pin": str(ICONS.PIN),
|
|
465
|
+
"skull": str(ICONS.SKULL),
|
|
466
|
+
}.items()
|
|
467
|
+
}
|