axiom-coding-agent-setup 1.0.10 → 1.0.11
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.
- package/.agents/skills/agent-browser/SKILL.md +55 -0
- package/.agents/skills/developing-with-streamlit/SKILL.md +220 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-chat-ui/SKILL.md +195 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/SKILL.md +233 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/packaged-components.md +212 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/state-sync.md +149 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/theme-css-variables.md +243 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/troubleshooting.md +121 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-dashboards/SKILL.md +147 -0
- package/.agents/skills/developing-with-streamlit/skills/building-streamlit-multipage-apps/SKILL.md +218 -0
- package/.agents/skills/developing-with-streamlit/skills/choosing-streamlit-selection-widgets/SKILL.md +139 -0
- package/.agents/skills/developing-with-streamlit/skills/connecting-streamlit-to-snowflake/SKILL.md +188 -0
- package/.agents/skills/developing-with-streamlit/skills/creating-streamlit-themes/SKILL.md +486 -0
- package/.agents/skills/developing-with-streamlit/skills/displaying-streamlit-data/SKILL.md +199 -0
- package/.agents/skills/developing-with-streamlit/skills/improving-streamlit-design/SKILL.md +191 -0
- package/.agents/skills/developing-with-streamlit/skills/optimizing-streamlit-performance/SKILL.md +323 -0
- package/.agents/skills/developing-with-streamlit/skills/organizing-streamlit-code/SKILL.md +91 -0
- package/.agents/skills/developing-with-streamlit/skills/setting-up-streamlit-environment/SKILL.md +128 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-cli/SKILL.md +165 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-custom-components/SKILL.md +170 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-layouts/SKILL.md +229 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-markdown/SKILL.md +207 -0
- package/.agents/skills/developing-with-streamlit/skills/using-streamlit-session-state/SKILL.md +144 -0
- package/.agents/skills/developing-with-streamlit/templates/.gitattributes +3 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/README.md +188 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/streamlit_app.py +365 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/streamlit_app.py +461 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/streamlit_app.py +527 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/streamlit_app.py +307 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/streamlit_app.py +426 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/streamlit_app.py +463 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/streamlit_app.py +252 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/pyproject.toml +12 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/streamlit_app.py +342 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/.streamlit/secrets.toml.example +20 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/pyproject.toml +11 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/snowflake.yml +18 -0
- package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/streamlit_app.py +384 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/README.md +128 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/dracula.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/github.toml +37 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/minimal.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/nord.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/snowflake.toml +42 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/solarized-light.toml +38 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/spotify.toml +34 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_configs/stripe.toml +35 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_shared/streamlit_app.py +336 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/_templates/pyproject.toml.tmpl +12 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/dracula/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/.streamlit/config.toml +37 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/github/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/manage.py +332 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/minimal/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/.streamlit/config.toml +39 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/nord/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/.streamlit/config.toml +42 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/.streamlit/config.toml +38 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/.streamlit/config.toml +34 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/spotify/streamlit_app.py +337 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/.streamlit/config.toml +35 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/pyproject.toml +10 -0
- package/.agents/skills/developing-with-streamlit/templates/themes/stripe/streamlit_app.py +337 -0
- package/.agents/skills/fastapi/SKILL.md +436 -0
- package/.agents/skills/fastapi/references/dependencies.md +142 -0
- package/.agents/skills/fastapi/references/other-tools.md +76 -0
- package/.agents/skills/fastapi/references/streaming.md +105 -0
- package/.agents/skills/frontend-design/LICENSE.txt +177 -0
- package/.agents/skills/frontend-design/SKILL.md +42 -0
- package/.agents/skills/gradio/SKILL.md +84 -0
- package/.agents/skills/gradio/references/api-signatures.md +115 -0
- package/.agents/skills/gradio/references/event-listeners.md +130 -0
- package/.agents/skills/gradio/references/examples.md +613 -0
- package/.agents/skills/ui-ux-pro-max/SKILL.md +659 -0
- package/.agents/skills/ui-ux-pro-max/data +1 -0
- package/.agents/skills/ui-ux-pro-max/scripts +1 -0
- package/.env.axiom +8 -0
- package/AGENTS.md +2 -1
- package/README.md +19 -3
- package/bin/cli.js +11 -1
- package/opencode.json +7 -4
- package/package.json +1 -1
- package/skills-lock.json +36 -0
- package/.agents/skills/n8n-patterns/SKILL.md +0 -272
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Stock Peer Analysis Dashboard (Snowflake Edition)
|
|
3
|
+
|
|
4
|
+
A stock comparison dashboard demonstrating:
|
|
5
|
+
- Snowflake connection via st.connection("snowflake")
|
|
6
|
+
- Generating synthetic stock data in Snowflake
|
|
7
|
+
- Normalized price comparison charts
|
|
8
|
+
- Individual stock vs peer average analysis
|
|
9
|
+
|
|
10
|
+
This template uses synthetic stock data generated in Snowflake.
|
|
11
|
+
Replace the synthetic query with your actual stock data table.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import streamlit as st
|
|
15
|
+
import pandas as pd
|
|
16
|
+
import altair as alt
|
|
17
|
+
|
|
18
|
+
st.set_page_config(
|
|
19
|
+
page_title="Stock peer analysis dashboard",
|
|
20
|
+
page_icon=":chart_with_upwards_trend:",
|
|
21
|
+
layout="wide",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
# :material/query_stats: Stock peer analysis
|
|
26
|
+
|
|
27
|
+
Easily compare stocks against others in their peer group.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
"" # Add some space.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# =============================================================================
|
|
34
|
+
# Snowflake Connection
|
|
35
|
+
# =============================================================================
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_snowflake_connection():
|
|
39
|
+
"""Get Snowflake connection via st.connection.
|
|
40
|
+
|
|
41
|
+
Displays an error and stops the app if the connection fails.
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
return st.connection("snowflake")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
st.error(f"Failed to connect to Snowflake: {e}")
|
|
47
|
+
st.info(
|
|
48
|
+
"Make sure you have configured your Snowflake connection in "
|
|
49
|
+
"`.streamlit/secrets.toml` or via environment variables."
|
|
50
|
+
)
|
|
51
|
+
st.stop()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# =============================================================================
|
|
55
|
+
# Constants and Configuration
|
|
56
|
+
# =============================================================================
|
|
57
|
+
|
|
58
|
+
STOCKS = [
|
|
59
|
+
"AAPL", "ABBV", "ACN", "ADBE", "ADP", "AMD", "AMGN", "AMT", "AMZN", "APD",
|
|
60
|
+
"AVGO", "AXP", "BA", "BK", "BKNG", "BMY", "BSX", "C", "CAT", "CI",
|
|
61
|
+
"CL", "CMCSA", "COST", "CRM", "CSCO", "CVX", "DE", "DHR", "DIS", "DUK",
|
|
62
|
+
"ELV", "EOG", "EQR", "FDX", "GD", "GE", "GILD", "GOOG", "GOOGL", "HD",
|
|
63
|
+
"HON", "HUM", "IBM", "ICE", "INTC", "ISRG", "JNJ", "JPM", "KO", "LIN",
|
|
64
|
+
"LLY", "LMT", "LOW", "MA", "MCD", "MDLZ", "META", "MMC", "MO", "MRK",
|
|
65
|
+
"MSFT", "NEE", "NFLX", "NKE", "NOW", "NVDA", "ORCL", "PEP", "PFE", "PG",
|
|
66
|
+
"PLD", "PM", "PSA", "REGN", "RTX", "SBUX", "SCHW", "SLB", "SO", "SPGI",
|
|
67
|
+
"T", "TJX", "TMO", "TSLA", "TXN", "UNH", "UNP", "UPS", "V", "VZ",
|
|
68
|
+
"WFC", "WM", "WMT", "XOM",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
# Base prices for synthetic data (approximate real values for realism)
|
|
72
|
+
STOCK_BASE_PRICES = {
|
|
73
|
+
"AAPL": 175, "MSFT": 380, "GOOGL": 140, "AMZN": 180, "NVDA": 500,
|
|
74
|
+
"META": 350, "TSLA": 250, "JPM": 170, "V": 280, "UNH": 520,
|
|
75
|
+
"HD": 350, "PG": 160, "MA": 450, "COST": 580, "ABBV": 170,
|
|
76
|
+
"MRK": 120, "AVGO": 900, "PEP": 180, "KO": 60, "TMO": 550,
|
|
77
|
+
"ADBE": 550, "CRM": 280, "CSCO": 50, "ACN": 340, "NKE": 100,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
DEFAULT_STOCKS = ["AAPL", "MSFT", "GOOGL", "NVDA", "AMZN", "TSLA", "META"]
|
|
81
|
+
|
|
82
|
+
# Time horizon mapping
|
|
83
|
+
HORIZON_MAP = {
|
|
84
|
+
"1 Month": 30,
|
|
85
|
+
"3 Months": 90,
|
|
86
|
+
"6 Months": 180,
|
|
87
|
+
"1 Year": 365,
|
|
88
|
+
"2 Years": 730,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def stocks_to_str(stocks):
|
|
93
|
+
return ",".join(stocks)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# =============================================================================
|
|
97
|
+
# Data Loading
|
|
98
|
+
# =============================================================================
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# -----------------------------------------------------------------------------
|
|
102
|
+
# PRODUCTION PATTERN: Use parameterized queries for real stock data
|
|
103
|
+
# -----------------------------------------------------------------------------
|
|
104
|
+
# For production use with actual stock tables, use parameterized queries:
|
|
105
|
+
#
|
|
106
|
+
# STOCK_QUERY = """
|
|
107
|
+
# SELECT trade_date AS date, ticker, close_price
|
|
108
|
+
# FROM stock_prices
|
|
109
|
+
# WHERE ticker = ANY(:tickers)
|
|
110
|
+
# AND trade_date >= DATEADD(day, -:days, CURRENT_DATE())
|
|
111
|
+
# ORDER BY trade_date, ticker
|
|
112
|
+
# """
|
|
113
|
+
#
|
|
114
|
+
# def load_stock_data(tickers: list[str], days: int) -> pd.DataFrame:
|
|
115
|
+
# conn = get_snowflake_connection()
|
|
116
|
+
# df = conn.query(
|
|
117
|
+
# STOCK_QUERY,
|
|
118
|
+
# params={"tickers": tickers, "days": days}
|
|
119
|
+
# )
|
|
120
|
+
# return df
|
|
121
|
+
#
|
|
122
|
+
# The synthetic data generation below uses f-strings for the VALUES clause
|
|
123
|
+
# which cannot be parameterized. This is acceptable for demo/synthetic data
|
|
124
|
+
# but should NOT be used with user input in production.
|
|
125
|
+
# -----------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def generate_stock_data_query(tickers: list[str], days: int) -> str:
|
|
129
|
+
"""Generate SQL query that creates synthetic stock price data.
|
|
130
|
+
|
|
131
|
+
NOTE: This uses f-strings for VALUES clause construction which is acceptable
|
|
132
|
+
for synthetic data generation with controlled inputs. For production apps
|
|
133
|
+
with real tables, always use parameterized queries as shown above.
|
|
134
|
+
"""
|
|
135
|
+
# Build ticker values and base prices (controlled data, not user input)
|
|
136
|
+
ticker_values = []
|
|
137
|
+
for ticker in tickers:
|
|
138
|
+
base_price = STOCK_BASE_PRICES.get(ticker, 100 + hash(ticker) % 400)
|
|
139
|
+
growth_rate = 0.0003 + (hash(ticker) % 10) * 0.00005
|
|
140
|
+
volatility = 0.02 + (hash(ticker) % 5) * 0.005
|
|
141
|
+
ticker_values.append(f"('{ticker}', {base_price}, {growth_rate}, {volatility})")
|
|
142
|
+
|
|
143
|
+
tickers_cte = ", ".join(ticker_values)
|
|
144
|
+
|
|
145
|
+
return f"""
|
|
146
|
+
WITH tickers AS (
|
|
147
|
+
SELECT column1 AS ticker, column2 AS base_price, column3 AS growth_rate, column4 AS volatility
|
|
148
|
+
FROM VALUES {tickers_cte}
|
|
149
|
+
),
|
|
150
|
+
date_series AS (
|
|
151
|
+
SELECT DATEADD(day, -seq4(), CURRENT_DATE() - 1) AS trade_date
|
|
152
|
+
FROM TABLE(GENERATOR(ROWCOUNT => {days}))
|
|
153
|
+
),
|
|
154
|
+
raw_prices AS (
|
|
155
|
+
SELECT
|
|
156
|
+
d.trade_date,
|
|
157
|
+
t.ticker,
|
|
158
|
+
t.base_price * POWER(1 + t.growth_rate, DATEDIFF(day, DATEADD(day, -{days}, CURRENT_DATE()), d.trade_date))
|
|
159
|
+
* (1 + (RANDOM() / 10000000000000000000.0 - 0.5) * t.volatility * 2) AS close_price
|
|
160
|
+
FROM date_series d
|
|
161
|
+
CROSS JOIN tickers t
|
|
162
|
+
WHERE DAYOFWEEK(d.trade_date) NOT IN (0, 6) -- Exclude weekends
|
|
163
|
+
)
|
|
164
|
+
SELECT
|
|
165
|
+
trade_date AS date,
|
|
166
|
+
ticker,
|
|
167
|
+
ROUND(close_price, 2) AS close_price
|
|
168
|
+
FROM raw_prices
|
|
169
|
+
ORDER BY trade_date, ticker
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@st.cache_data(ttl=3600, show_spinner="Loading stock data from Snowflake...")
|
|
174
|
+
def load_stock_data(tickers: list[str], days: int) -> pd.DataFrame:
|
|
175
|
+
"""Load stock price data from Snowflake."""
|
|
176
|
+
conn = get_snowflake_connection()
|
|
177
|
+
query = generate_stock_data_query(tickers, days)
|
|
178
|
+
df = conn.query(query)
|
|
179
|
+
df.columns = df.columns.str.lower()
|
|
180
|
+
|
|
181
|
+
# Pivot to get tickers as columns
|
|
182
|
+
pivoted = df.pivot(index="date", columns="ticker", values="close_price")
|
|
183
|
+
pivoted.index = pd.to_datetime(pivoted.index)
|
|
184
|
+
return pivoted
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# =============================================================================
|
|
188
|
+
# Session State and Query Params
|
|
189
|
+
# =============================================================================
|
|
190
|
+
|
|
191
|
+
if "tickers_input" not in st.session_state:
|
|
192
|
+
st.session_state.tickers_input = st.query_params.get(
|
|
193
|
+
"stocks", stocks_to_str(DEFAULT_STOCKS)
|
|
194
|
+
).split(",")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# =============================================================================
|
|
198
|
+
# Page Layout
|
|
199
|
+
# =============================================================================
|
|
200
|
+
|
|
201
|
+
# Check Snowflake connection
|
|
202
|
+
get_snowflake_connection()
|
|
203
|
+
|
|
204
|
+
cols = st.columns([1, 3])
|
|
205
|
+
|
|
206
|
+
top_left_cell = cols[0].container(
|
|
207
|
+
border=True, height="stretch", vertical_alignment="center"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
with top_left_cell:
|
|
211
|
+
# Selectbox for stock tickers
|
|
212
|
+
tickers = st.multiselect(
|
|
213
|
+
"Stock tickers",
|
|
214
|
+
options=sorted(set(STOCKS) | set(st.session_state.tickers_input)),
|
|
215
|
+
default=st.session_state.tickers_input,
|
|
216
|
+
placeholder="Choose stocks to compare. Example: NVDA",
|
|
217
|
+
accept_new_options=True,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
# Time horizon selector
|
|
221
|
+
horizon = st.pills(
|
|
222
|
+
"Time horizon",
|
|
223
|
+
options=list(HORIZON_MAP.keys()),
|
|
224
|
+
default="6 Months",
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
tickers = [t.upper() for t in tickers]
|
|
228
|
+
|
|
229
|
+
# Update query param when text input changes
|
|
230
|
+
if tickers:
|
|
231
|
+
st.query_params["stocks"] = stocks_to_str(tickers)
|
|
232
|
+
else:
|
|
233
|
+
st.query_params.pop("stocks", None)
|
|
234
|
+
|
|
235
|
+
if not tickers:
|
|
236
|
+
top_left_cell.info("Pick some stocks to compare", icon=":material/info:")
|
|
237
|
+
st.stop()
|
|
238
|
+
|
|
239
|
+
right_cell = cols[1].container(
|
|
240
|
+
border=True, height="stretch", vertical_alignment="center"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
# Load the data from Snowflake
|
|
244
|
+
try:
|
|
245
|
+
data = load_stock_data(tickers, HORIZON_MAP[horizon])
|
|
246
|
+
except Exception as e:
|
|
247
|
+
st.error(f"Error loading stock data: {e}")
|
|
248
|
+
st.stop()
|
|
249
|
+
|
|
250
|
+
# Check for missing data
|
|
251
|
+
missing_tickers = [t for t in tickers if t not in data.columns]
|
|
252
|
+
if missing_tickers:
|
|
253
|
+
st.warning(f"No data available for: {', '.join(missing_tickers)}")
|
|
254
|
+
# Filter to available tickers
|
|
255
|
+
tickers = [t for t in tickers if t in data.columns]
|
|
256
|
+
if not tickers:
|
|
257
|
+
st.stop()
|
|
258
|
+
|
|
259
|
+
# Normalize prices (start at 1)
|
|
260
|
+
normalized = data[tickers].div(data[tickers].iloc[0])
|
|
261
|
+
|
|
262
|
+
latest_norm_values = {normalized[ticker].iat[-1]: ticker for ticker in tickers}
|
|
263
|
+
max_norm_value = max(latest_norm_values.items())
|
|
264
|
+
min_norm_value = min(latest_norm_values.items())
|
|
265
|
+
|
|
266
|
+
bottom_left_cell = cols[0].container(
|
|
267
|
+
border=True, height="stretch", vertical_alignment="center"
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
with bottom_left_cell:
|
|
271
|
+
metric_cols = st.columns(2)
|
|
272
|
+
metric_cols[0].metric(
|
|
273
|
+
"Best stock",
|
|
274
|
+
max_norm_value[1],
|
|
275
|
+
delta=f"{round((max_norm_value[0] - 1) * 100)}%",
|
|
276
|
+
width="content",
|
|
277
|
+
)
|
|
278
|
+
metric_cols[1].metric(
|
|
279
|
+
"Worst stock",
|
|
280
|
+
min_norm_value[1],
|
|
281
|
+
delta=f"{round((min_norm_value[0] - 1) * 100)}%",
|
|
282
|
+
width="content",
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# Plot normalized prices
|
|
286
|
+
with right_cell:
|
|
287
|
+
st.altair_chart(
|
|
288
|
+
alt.Chart(
|
|
289
|
+
normalized.reset_index().melt(
|
|
290
|
+
id_vars=["date"], var_name="Stock", value_name="Normalized price"
|
|
291
|
+
)
|
|
292
|
+
)
|
|
293
|
+
.mark_line()
|
|
294
|
+
.encode(
|
|
295
|
+
alt.X("date:T", title="Date"),
|
|
296
|
+
alt.Y("Normalized price:Q").scale(zero=False),
|
|
297
|
+
alt.Color("Stock:N"),
|
|
298
|
+
)
|
|
299
|
+
.properties(height=400)
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
""
|
|
303
|
+
""
|
|
304
|
+
|
|
305
|
+
# Plot individual stock vs peer average
|
|
306
|
+
"""
|
|
307
|
+
## Individual stocks vs peer average
|
|
308
|
+
|
|
309
|
+
For the analysis below, the "peer average" when analyzing stock X always
|
|
310
|
+
excludes X itself.
|
|
311
|
+
"""
|
|
312
|
+
|
|
313
|
+
if len(tickers) <= 1:
|
|
314
|
+
st.warning("Pick 2 or more tickers to compare them")
|
|
315
|
+
st.stop()
|
|
316
|
+
|
|
317
|
+
NUM_COLS = 4
|
|
318
|
+
chart_cols = st.columns(NUM_COLS)
|
|
319
|
+
|
|
320
|
+
for i, ticker in enumerate(tickers):
|
|
321
|
+
# Calculate peer average (excluding current stock)
|
|
322
|
+
peers = normalized.drop(columns=[ticker])
|
|
323
|
+
peer_avg = peers.mean(axis=1)
|
|
324
|
+
|
|
325
|
+
# Create DataFrame with peer average
|
|
326
|
+
plot_data = pd.DataFrame(
|
|
327
|
+
{
|
|
328
|
+
"Date": normalized.index,
|
|
329
|
+
ticker: normalized[ticker],
|
|
330
|
+
"Peer average": peer_avg,
|
|
331
|
+
}
|
|
332
|
+
).melt(id_vars=["Date"], var_name="Series", value_name="Price")
|
|
333
|
+
|
|
334
|
+
chart = (
|
|
335
|
+
alt.Chart(plot_data)
|
|
336
|
+
.mark_line()
|
|
337
|
+
.encode(
|
|
338
|
+
alt.X("Date:T"),
|
|
339
|
+
alt.Y("Price:Q").scale(zero=False),
|
|
340
|
+
alt.Color(
|
|
341
|
+
"Series:N",
|
|
342
|
+
scale=alt.Scale(domain=[ticker, "Peer average"], range=["red", "gray"]),
|
|
343
|
+
legend=alt.Legend(orient="bottom"),
|
|
344
|
+
),
|
|
345
|
+
alt.Tooltip(["Date", "Series", "Price"]),
|
|
346
|
+
)
|
|
347
|
+
.properties(title=f"{ticker} vs peer average", height=300)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
cell = chart_cols[(i * 2) % NUM_COLS].container(border=True)
|
|
351
|
+
cell.write("")
|
|
352
|
+
cell.altair_chart(chart)
|
|
353
|
+
|
|
354
|
+
# Create Delta chart
|
|
355
|
+
plot_data = pd.DataFrame(
|
|
356
|
+
{
|
|
357
|
+
"Date": normalized.index,
|
|
358
|
+
"Delta": normalized[ticker] - peer_avg,
|
|
359
|
+
}
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
chart = (
|
|
363
|
+
alt.Chart(plot_data)
|
|
364
|
+
.mark_area()
|
|
365
|
+
.encode(
|
|
366
|
+
alt.X("Date:T"),
|
|
367
|
+
alt.Y("Delta:Q").scale(zero=False),
|
|
368
|
+
)
|
|
369
|
+
.properties(title=f"{ticker} minus peer average", height=300)
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
cell = chart_cols[(i * 2 + 1) % NUM_COLS].container(border=True)
|
|
373
|
+
cell.write("")
|
|
374
|
+
cell.altair_chart(chart)
|
|
375
|
+
|
|
376
|
+
""
|
|
377
|
+
""
|
|
378
|
+
|
|
379
|
+
"""
|
|
380
|
+
## Raw data
|
|
381
|
+
"""
|
|
382
|
+
|
|
383
|
+
st.caption(":material/cloud: Data loaded from Snowflake (synthetic)")
|
|
384
|
+
data[tickers]
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Streamlit theme templates
|
|
2
|
+
|
|
3
|
+
Ready-to-use theme templates for Streamlit apps.
|
|
4
|
+
|
|
5
|
+
## Available themes
|
|
6
|
+
|
|
7
|
+
| Theme | Base | Primary color | Fonts |
|
|
8
|
+
|-------|------|---------------|-------|
|
|
9
|
+
| **snowflake** | Light | `#29B5E8` (cyan) | Inter, JetBrains Mono |
|
|
10
|
+
| **dracula** | Dark | `#BD93F9` (purple) | Fira Sans, JetBrains Mono |
|
|
11
|
+
| **nord** | Dark | `#88C0D0` (frost blue) | Inter, JetBrains Mono |
|
|
12
|
+
| **stripe** | Light | `#635BFF` (indigo) | Inter, Source Code Pro |
|
|
13
|
+
| **solarized-light** | Light | `#268BD2` (blue) | Source Sans 3, Source Code Pro |
|
|
14
|
+
| **spotify** | Dark | `#1DB954` (green) | Inter, Fira Code |
|
|
15
|
+
| **github** | Light | `#0969DA` (blue) | Inter, JetBrains Mono |
|
|
16
|
+
| **minimal** | Dark | `#6366f1` (indigo) | Inter, JetBrains Mono |
|
|
17
|
+
|
|
18
|
+
## Quick start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Run a theme locally
|
|
22
|
+
cd templates/themes/spotify
|
|
23
|
+
uv sync
|
|
24
|
+
uv run streamlit run streamlit_app.py
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Deploying to Snowflake
|
|
28
|
+
|
|
29
|
+
Before deploying, update `snowflake.yml` with your account-specific resources:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
# Find available compute pools
|
|
33
|
+
SHOW COMPUTE POOLS;
|
|
34
|
+
|
|
35
|
+
# Find available external access integrations
|
|
36
|
+
SHOW EXTERNAL ACCESS INTEGRATIONS;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Then edit `snowflake.yml` to replace the placeholders:
|
|
40
|
+
- `<YOUR_COMPUTE_POOL>` → e.g., `STREAMLIT_DEDICATED_POOL`
|
|
41
|
+
- `<YOUR_PYPI_INTEGRATION>` → e.g., `PYPI_ACCESS_INTEGRATION`
|
|
42
|
+
- `<FROM_CONNECTION>` values are filled from your active connection
|
|
43
|
+
|
|
44
|
+
Deploy with:
|
|
45
|
+
```bash
|
|
46
|
+
snow streamlit deploy --replace
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## How Streamlit theming works
|
|
50
|
+
|
|
51
|
+
A custom theme requires two things:
|
|
52
|
+
|
|
53
|
+
### 1. Theme configuration in `.streamlit/config.toml`
|
|
54
|
+
|
|
55
|
+
```toml
|
|
56
|
+
[theme]
|
|
57
|
+
base = "dark" # "dark" or "light"
|
|
58
|
+
primaryColor = "#1DB954" # Buttons, links, highlights
|
|
59
|
+
backgroundColor = "#121212" # Main background
|
|
60
|
+
secondaryBackgroundColor = "#181818" # Sidebar, cards
|
|
61
|
+
textColor = "#FFFFFF" # Main text color
|
|
62
|
+
font = "Inter" # Body font
|
|
63
|
+
codeFont = "FiraCode" # Code blocks
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. For Snowflake deployment: local font files
|
|
67
|
+
|
|
68
|
+
Snowflake doesn't allow remote URL fetches, so fonts must be bundled locally:
|
|
69
|
+
|
|
70
|
+
```toml
|
|
71
|
+
[server]
|
|
72
|
+
enableStaticServing = true # Required for static files
|
|
73
|
+
|
|
74
|
+
[[theme.fontFaces]]
|
|
75
|
+
family = "Inter"
|
|
76
|
+
url = "app/static/Inter-Regular.ttf" # Note: app/ prefix required
|
|
77
|
+
weight = 400
|
|
78
|
+
|
|
79
|
+
[[theme.fontFaces]]
|
|
80
|
+
family = "Inter"
|
|
81
|
+
url = "app/static/Inter-Bold.ttf"
|
|
82
|
+
weight = 700
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Font files go in `static/` directory and are referenced with `app/static/` prefix.
|
|
86
|
+
|
|
87
|
+
### Sidebar theming (optional)
|
|
88
|
+
|
|
89
|
+
```toml
|
|
90
|
+
[theme.sidebar]
|
|
91
|
+
backgroundColor = "#181818"
|
|
92
|
+
secondaryBackgroundColor = "#121212"
|
|
93
|
+
borderColor = "#282828"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Theme file structure
|
|
97
|
+
|
|
98
|
+
Each theme directory contains:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
{theme}/
|
|
102
|
+
├── .streamlit/config.toml # Theme colors and fonts
|
|
103
|
+
├── streamlit_app.py # Demo app showing the theme
|
|
104
|
+
├── pyproject.toml # Dependencies
|
|
105
|
+
├── snowflake.yml # Snowflake deployment config
|
|
106
|
+
└── static/ # Bundled font files (*.ttf)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Dependencies
|
|
110
|
+
|
|
111
|
+
All themes require Python >=3.11 and use:
|
|
112
|
+
- `snowflake-connector-python>=3.3.0` (required — `streamlit[snowflake]` silently skips this on Python 3.12+)
|
|
113
|
+
- `streamlit[snowflake]>=1.54.0`
|
|
114
|
+
- `altair>=5.5.0`
|
|
115
|
+
- `pandas>=2.2.3`
|
|
116
|
+
|
|
117
|
+
## Font licensing
|
|
118
|
+
|
|
119
|
+
All bundled fonts are licensed under the [SIL Open Font License 1.1](https://openfontlicense.org/), which permits free use, redistribution, and modification:
|
|
120
|
+
|
|
121
|
+
| Font | Used by | Source |
|
|
122
|
+
|------|---------|--------|
|
|
123
|
+
| Inter | snowflake, nord, spotify, github, minimal, stripe | [github.com/rsms/inter](https://github.com/rsms/inter) |
|
|
124
|
+
| JetBrains Mono | snowflake, dracula, nord, github, minimal | [github.com/JetBrains/JetBrainsMono](https://github.com/JetBrains/JetBrainsMono) |
|
|
125
|
+
| Fira Sans | dracula | [github.com/mozilla/Fira](https://github.com/mozilla/Fira) |
|
|
126
|
+
| Fira Code | spotify | [github.com/tonsky/FiraCode](https://github.com/tonsky/FiraCode) |
|
|
127
|
+
| Source Sans 3 | solarized-light | [github.com/adobe-fonts/source-sans](https://github.com/adobe-fonts/source-sans) |
|
|
128
|
+
| Source Code Pro | solarized-light, stripe | [github.com/adobe-fonts/source-code-pro](https://github.com/adobe-fonts/source-code-pro) |
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Dracula Theme for Streamlit
|
|
2
|
+
# Popular dark theme with vibrant colors on dark background
|
|
3
|
+
[theme]
|
|
4
|
+
base = "dark"
|
|
5
|
+
primaryColor = "#bd93f9"
|
|
6
|
+
backgroundColor = "#282a36"
|
|
7
|
+
secondaryBackgroundColor = "#21222c"
|
|
8
|
+
codeBackgroundColor = "#21222c"
|
|
9
|
+
textColor = "#f8f8f2"
|
|
10
|
+
linkColor = "#8be9fd"
|
|
11
|
+
borderColor = "#44475a"
|
|
12
|
+
showWidgetBorder = true
|
|
13
|
+
showSidebarBorder = true
|
|
14
|
+
baseRadius = "8px"
|
|
15
|
+
buttonRadius = "8px"
|
|
16
|
+
font = "'Fira Sans':https://fonts.googleapis.com/css2?family=Fira+Sans:wght@400;500;600;700&display=swap"
|
|
17
|
+
codeFont = "'JetBrains Mono':https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap"
|
|
18
|
+
codeFontSize = "0.875rem"
|
|
19
|
+
codeTextColor = "#f8f8f2"
|
|
20
|
+
baseFontSize = 14
|
|
21
|
+
baseFontWeight = 400
|
|
22
|
+
headingFontSizes = ["32px", "24px", "20px", "16px", "14px", "12px"]
|
|
23
|
+
headingFontWeights = [700, 600, 600, 600, 600, 600]
|
|
24
|
+
linkUnderline = false
|
|
25
|
+
chartCategoricalColors = ["#bd93f9", "#50fa7b", "#ff79c6", "#8be9fd", "#ffb86c", "#ff5555", "#f1fa8c"]
|
|
26
|
+
|
|
27
|
+
# Dracula color palette
|
|
28
|
+
violetColor = "#bd93f9"
|
|
29
|
+
greenColor = "#50fa7b"
|
|
30
|
+
redColor = "#ff5555"
|
|
31
|
+
blueColor = "#8be9fd"
|
|
32
|
+
yellowColor = "#f1fa8c"
|
|
33
|
+
orangeColor = "#ffb86c"
|
|
34
|
+
|
|
35
|
+
[theme.sidebar]
|
|
36
|
+
backgroundColor = "#21222c"
|
|
37
|
+
secondaryBackgroundColor = "#191a21"
|
|
38
|
+
codeBackgroundColor = "#191a21"
|
|
39
|
+
borderColor = "#44475a"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# GitHub Theme for Streamlit
|
|
2
|
+
# Clean, developer-friendly, functional with signature blue accents
|
|
3
|
+
[theme]
|
|
4
|
+
primaryColor = "#0969da"
|
|
5
|
+
backgroundColor = "#ffffff"
|
|
6
|
+
secondaryBackgroundColor = "#f6f8fa"
|
|
7
|
+
codeBackgroundColor = "#f6f8fa"
|
|
8
|
+
textColor = "#1F2328"
|
|
9
|
+
linkColor = "#0969da"
|
|
10
|
+
borderColor = "#d0d7de"
|
|
11
|
+
showWidgetBorder = true
|
|
12
|
+
showSidebarBorder = true
|
|
13
|
+
baseRadius = "6px"
|
|
14
|
+
buttonRadius = "6px"
|
|
15
|
+
font = "Inter:https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
|
16
|
+
codeFont = "'JetBrains Mono':https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap"
|
|
17
|
+
codeFontSize = "0.85rem"
|
|
18
|
+
codeTextColor = "#1F2328"
|
|
19
|
+
baseFontSize = 14
|
|
20
|
+
baseFontWeight = 400
|
|
21
|
+
headingFontSizes = ["32px", "24px", "20px", "16px", "14px", "12px"]
|
|
22
|
+
headingFontWeights = [600, 600, 600, 600, 600, 600]
|
|
23
|
+
linkUnderline = false
|
|
24
|
+
chartCategoricalColors = ["#0969da", "#1a7f37", "#bf3989", "#8250df", "#cf222e", "#bf8700", "#57606a"]
|
|
25
|
+
|
|
26
|
+
# GitHub color palette
|
|
27
|
+
blueColor = "#0969da"
|
|
28
|
+
greenColor = "#1a7f37"
|
|
29
|
+
redColor = "#cf222e"
|
|
30
|
+
violetColor = "#8250df"
|
|
31
|
+
orangeColor = "#bf8700"
|
|
32
|
+
|
|
33
|
+
[theme.sidebar]
|
|
34
|
+
backgroundColor = "#f6f8fa"
|
|
35
|
+
secondaryBackgroundColor = "#eaeef2"
|
|
36
|
+
codeBackgroundColor = "#eaeef2"
|
|
37
|
+
borderColor = "#d0d7de"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Minimal Dark Theme for Streamlit
|
|
2
|
+
# Clean, distraction-free dark theme with subtle accents
|
|
3
|
+
[theme]
|
|
4
|
+
base = "dark"
|
|
5
|
+
primaryColor = "#6366f1"
|
|
6
|
+
backgroundColor = "#18181b"
|
|
7
|
+
secondaryBackgroundColor = "#27272a"
|
|
8
|
+
codeBackgroundColor = "#27272a"
|
|
9
|
+
textColor = "#fafafa"
|
|
10
|
+
linkColor = "#818cf8"
|
|
11
|
+
borderColor = "#3f3f46"
|
|
12
|
+
showWidgetBorder = false
|
|
13
|
+
showSidebarBorder = false
|
|
14
|
+
baseRadius = "6px"
|
|
15
|
+
buttonRadius = "6px"
|
|
16
|
+
font = "Inter:https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
|
17
|
+
codeFont = "'JetBrains Mono':https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap"
|
|
18
|
+
codeFontSize = "0.85rem"
|
|
19
|
+
codeTextColor = "#e4e4e7"
|
|
20
|
+
baseFontSize = 14
|
|
21
|
+
baseFontWeight = 400
|
|
22
|
+
headingFontSizes = ["32px", "24px", "20px", "16px", "14px", "12px"]
|
|
23
|
+
headingFontWeights = [600, 600, 500, 500, 500, 500]
|
|
24
|
+
linkUnderline = false
|
|
25
|
+
chartCategoricalColors = ["#6366f1", "#8b5cf6", "#ec4899", "#14b8a6", "#f59e0b", "#ef4444", "#22c55e"]
|
|
26
|
+
|
|
27
|
+
# Color palette
|
|
28
|
+
violetColor = "#8b5cf6"
|
|
29
|
+
blueColor = "#6366f1"
|
|
30
|
+
greenColor = "#22c55e"
|
|
31
|
+
yellowColor = "#f59e0b"
|
|
32
|
+
orangeColor = "#f97316"
|
|
33
|
+
redColor = "#ef4444"
|
|
34
|
+
|
|
35
|
+
[theme.sidebar]
|
|
36
|
+
backgroundColor = "#09090b"
|
|
37
|
+
secondaryBackgroundColor = "#18181b"
|
|
38
|
+
codeBackgroundColor = "#18181b"
|
|
39
|
+
borderColor = "#27272a"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Nord Theme for Streamlit
|
|
2
|
+
# Arctic, north-bluish color palette with frost-inspired accents
|
|
3
|
+
[theme]
|
|
4
|
+
base = "dark"
|
|
5
|
+
primaryColor = "#88c0d0"
|
|
6
|
+
backgroundColor = "#2e3440"
|
|
7
|
+
secondaryBackgroundColor = "#3b4252"
|
|
8
|
+
codeBackgroundColor = "#3b4252"
|
|
9
|
+
textColor = "#eceff4"
|
|
10
|
+
linkColor = "#81a1c1"
|
|
11
|
+
borderColor = "#4c566a"
|
|
12
|
+
showWidgetBorder = true
|
|
13
|
+
showSidebarBorder = true
|
|
14
|
+
baseRadius = "4px"
|
|
15
|
+
buttonRadius = "4px"
|
|
16
|
+
font = "Inter:https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
|
17
|
+
codeFont = "'JetBrains Mono':https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap"
|
|
18
|
+
codeFontSize = "0.875rem"
|
|
19
|
+
codeTextColor = "#d8dee9"
|
|
20
|
+
baseFontSize = 14
|
|
21
|
+
baseFontWeight = 400
|
|
22
|
+
headingFontSizes = ["32px", "24px", "20px", "16px", "14px", "12px"]
|
|
23
|
+
headingFontWeights = [600, 600, 600, 600, 600, 600]
|
|
24
|
+
linkUnderline = false
|
|
25
|
+
chartCategoricalColors = ["#88c0d0", "#81a1c1", "#5e81ac", "#a3be8c", "#ebcb8b", "#d08770", "#bf616a"]
|
|
26
|
+
|
|
27
|
+
# Nord color palette (Frost + Aurora)
|
|
28
|
+
blueColor = "#81a1c1"
|
|
29
|
+
greenColor = "#a3be8c"
|
|
30
|
+
yellowColor = "#ebcb8b"
|
|
31
|
+
orangeColor = "#d08770"
|
|
32
|
+
redColor = "#bf616a"
|
|
33
|
+
violetColor = "#b48ead"
|
|
34
|
+
|
|
35
|
+
[theme.sidebar]
|
|
36
|
+
backgroundColor = "#3b4252"
|
|
37
|
+
secondaryBackgroundColor = "#434c5e"
|
|
38
|
+
codeBackgroundColor = "#434c5e"
|
|
39
|
+
borderColor = "#4c566a"
|