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.
Files changed (106) hide show
  1. package/.agents/skills/agent-browser/SKILL.md +55 -0
  2. package/.agents/skills/developing-with-streamlit/SKILL.md +220 -0
  3. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-chat-ui/SKILL.md +195 -0
  4. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/SKILL.md +233 -0
  5. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/packaged-components.md +212 -0
  6. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/state-sync.md +149 -0
  7. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/theme-css-variables.md +243 -0
  8. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-custom-components-v2/references/troubleshooting.md +121 -0
  9. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-dashboards/SKILL.md +147 -0
  10. package/.agents/skills/developing-with-streamlit/skills/building-streamlit-multipage-apps/SKILL.md +218 -0
  11. package/.agents/skills/developing-with-streamlit/skills/choosing-streamlit-selection-widgets/SKILL.md +139 -0
  12. package/.agents/skills/developing-with-streamlit/skills/connecting-streamlit-to-snowflake/SKILL.md +188 -0
  13. package/.agents/skills/developing-with-streamlit/skills/creating-streamlit-themes/SKILL.md +486 -0
  14. package/.agents/skills/developing-with-streamlit/skills/displaying-streamlit-data/SKILL.md +199 -0
  15. package/.agents/skills/developing-with-streamlit/skills/improving-streamlit-design/SKILL.md +191 -0
  16. package/.agents/skills/developing-with-streamlit/skills/optimizing-streamlit-performance/SKILL.md +323 -0
  17. package/.agents/skills/developing-with-streamlit/skills/organizing-streamlit-code/SKILL.md +91 -0
  18. package/.agents/skills/developing-with-streamlit/skills/setting-up-streamlit-environment/SKILL.md +128 -0
  19. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-cli/SKILL.md +165 -0
  20. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-custom-components/SKILL.md +170 -0
  21. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-layouts/SKILL.md +229 -0
  22. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-markdown/SKILL.md +207 -0
  23. package/.agents/skills/developing-with-streamlit/skills/using-streamlit-session-state/SKILL.md +144 -0
  24. package/.agents/skills/developing-with-streamlit/templates/.gitattributes +3 -0
  25. package/.agents/skills/developing-with-streamlit/templates/apps/README.md +188 -0
  26. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/pyproject.toml +12 -0
  27. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-companies/streamlit_app.py +365 -0
  28. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/pyproject.toml +12 -0
  29. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute/streamlit_app.py +461 -0
  30. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/.streamlit/secrets.toml.example +20 -0
  31. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/pyproject.toml +11 -0
  32. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/snowflake.yml +18 -0
  33. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-compute-snowflake/streamlit_app.py +527 -0
  34. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/pyproject.toml +12 -0
  35. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-feature-usage/streamlit_app.py +307 -0
  36. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/pyproject.toml +12 -0
  37. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics/streamlit_app.py +426 -0
  38. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/.streamlit/secrets.toml.example +20 -0
  39. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/pyproject.toml +11 -0
  40. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/snowflake.yml +18 -0
  41. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-metrics-snowflake/streamlit_app.py +463 -0
  42. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/pyproject.toml +12 -0
  43. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-seattle-weather/streamlit_app.py +252 -0
  44. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/pyproject.toml +12 -0
  45. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers/streamlit_app.py +342 -0
  46. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/.streamlit/secrets.toml.example +20 -0
  47. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/pyproject.toml +11 -0
  48. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/snowflake.yml +18 -0
  49. package/.agents/skills/developing-with-streamlit/templates/apps/dashboard-stock-peers-snowflake/streamlit_app.py +384 -0
  50. package/.agents/skills/developing-with-streamlit/templates/themes/README.md +128 -0
  51. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/dracula.toml +39 -0
  52. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/github.toml +37 -0
  53. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/minimal.toml +39 -0
  54. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/nord.toml +39 -0
  55. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/snowflake.toml +42 -0
  56. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/solarized-light.toml +38 -0
  57. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/spotify.toml +34 -0
  58. package/.agents/skills/developing-with-streamlit/templates/themes/_configs/stripe.toml +35 -0
  59. package/.agents/skills/developing-with-streamlit/templates/themes/_shared/streamlit_app.py +336 -0
  60. package/.agents/skills/developing-with-streamlit/templates/themes/_templates/pyproject.toml.tmpl +12 -0
  61. package/.agents/skills/developing-with-streamlit/templates/themes/dracula/.streamlit/config.toml +39 -0
  62. package/.agents/skills/developing-with-streamlit/templates/themes/dracula/pyproject.toml +10 -0
  63. package/.agents/skills/developing-with-streamlit/templates/themes/dracula/streamlit_app.py +337 -0
  64. package/.agents/skills/developing-with-streamlit/templates/themes/github/.streamlit/config.toml +37 -0
  65. package/.agents/skills/developing-with-streamlit/templates/themes/github/pyproject.toml +10 -0
  66. package/.agents/skills/developing-with-streamlit/templates/themes/github/streamlit_app.py +337 -0
  67. package/.agents/skills/developing-with-streamlit/templates/themes/manage.py +332 -0
  68. package/.agents/skills/developing-with-streamlit/templates/themes/minimal/.streamlit/config.toml +39 -0
  69. package/.agents/skills/developing-with-streamlit/templates/themes/minimal/pyproject.toml +10 -0
  70. package/.agents/skills/developing-with-streamlit/templates/themes/minimal/streamlit_app.py +337 -0
  71. package/.agents/skills/developing-with-streamlit/templates/themes/nord/.streamlit/config.toml +39 -0
  72. package/.agents/skills/developing-with-streamlit/templates/themes/nord/pyproject.toml +10 -0
  73. package/.agents/skills/developing-with-streamlit/templates/themes/nord/streamlit_app.py +337 -0
  74. package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/.streamlit/config.toml +42 -0
  75. package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/pyproject.toml +10 -0
  76. package/.agents/skills/developing-with-streamlit/templates/themes/snowflake/streamlit_app.py +337 -0
  77. package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/.streamlit/config.toml +38 -0
  78. package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/pyproject.toml +10 -0
  79. package/.agents/skills/developing-with-streamlit/templates/themes/solarized-light/streamlit_app.py +337 -0
  80. package/.agents/skills/developing-with-streamlit/templates/themes/spotify/.streamlit/config.toml +34 -0
  81. package/.agents/skills/developing-with-streamlit/templates/themes/spotify/pyproject.toml +10 -0
  82. package/.agents/skills/developing-with-streamlit/templates/themes/spotify/streamlit_app.py +337 -0
  83. package/.agents/skills/developing-with-streamlit/templates/themes/stripe/.streamlit/config.toml +35 -0
  84. package/.agents/skills/developing-with-streamlit/templates/themes/stripe/pyproject.toml +10 -0
  85. package/.agents/skills/developing-with-streamlit/templates/themes/stripe/streamlit_app.py +337 -0
  86. package/.agents/skills/fastapi/SKILL.md +436 -0
  87. package/.agents/skills/fastapi/references/dependencies.md +142 -0
  88. package/.agents/skills/fastapi/references/other-tools.md +76 -0
  89. package/.agents/skills/fastapi/references/streaming.md +105 -0
  90. package/.agents/skills/frontend-design/LICENSE.txt +177 -0
  91. package/.agents/skills/frontend-design/SKILL.md +42 -0
  92. package/.agents/skills/gradio/SKILL.md +84 -0
  93. package/.agents/skills/gradio/references/api-signatures.md +115 -0
  94. package/.agents/skills/gradio/references/event-listeners.md +130 -0
  95. package/.agents/skills/gradio/references/examples.md +613 -0
  96. package/.agents/skills/ui-ux-pro-max/SKILL.md +659 -0
  97. package/.agents/skills/ui-ux-pro-max/data +1 -0
  98. package/.agents/skills/ui-ux-pro-max/scripts +1 -0
  99. package/.env.axiom +8 -0
  100. package/AGENTS.md +2 -1
  101. package/README.md +19 -3
  102. package/bin/cli.js +11 -1
  103. package/opencode.json +7 -4
  104. package/package.json +1 -1
  105. package/skills-lock.json +36 -0
  106. package/.agents/skills/n8n-patterns/SKILL.md +0 -272
@@ -0,0 +1,337 @@
1
+ """
2
+ Streamlit Element Explorer - Theme Demo
3
+
4
+ A comprehensive single-page app showcasing all major Streamlit components
5
+ with custom theming. Use this to preview how your theme looks across
6
+ different element types.
7
+ """
8
+ # DO NOT EDIT — managed by manage.py, edit _shared/streamlit_app.py instead
9
+
10
+ import numpy as np
11
+ import pandas as pd
12
+ import streamlit as st
13
+
14
+ st.set_page_config(page_title="Element Explorer", page_icon="🎨", layout="wide")
15
+
16
+ # Initialize sample data in session state
17
+ if "chart_data" not in st.session_state:
18
+ np.random.seed(42)
19
+ st.session_state.chart_data = pd.DataFrame(
20
+ np.random.randn(20, 3), columns=["a", "b", "c"]
21
+ )
22
+
23
+ chart_data = st.session_state.chart_data
24
+
25
+ st.title("Streamlit Element Explorer")
26
+ st.markdown(
27
+ "Explore how Streamlit's built-in elements look with this theme. "
28
+ "Select a category below to preview different components."
29
+ )
30
+
31
+ # Navigation using segmented_control for better performance
32
+ section = st.segmented_control(
33
+ "Section",
34
+ ["Widgets", "Data", "Charts", "Text", "Layouts", "Chat", "Status"],
35
+ default="Widgets",
36
+ label_visibility="collapsed",
37
+ )
38
+
39
+ st.divider()
40
+
41
+ # -----------------------------------------------------------------------------
42
+ # WIDGETS SECTION
43
+ # -----------------------------------------------------------------------------
44
+ if section == "Widgets":
45
+ st.header("Widgets")
46
+
47
+ # Buttons
48
+ st.subheader("Buttons")
49
+ cols = st.columns(4)
50
+ cols[0].button("Primary", type="primary")
51
+ cols[1].button("Secondary", type="secondary")
52
+ cols[2].button("Tertiary", type="tertiary")
53
+ cols[3].link_button("Link", url="https://streamlit.io", icon=":material/open_in_new:")
54
+
55
+ # Form
56
+ with st.form(key="demo_form"):
57
+ st.subheader("Form")
58
+ form_cols = st.columns(2)
59
+ form_cols[0].text_input("Name", placeholder="Enter your name")
60
+ form_cols[1].text_input("Email", placeholder="you@example.com")
61
+ st.form_submit_button("Submit", type="primary")
62
+
63
+ # Selection widgets
64
+ st.subheader("Selection Widgets")
65
+ sel_cols = st.columns(2)
66
+
67
+ with sel_cols[0]:
68
+ st.checkbox("Checkbox option")
69
+ st.toggle("Toggle switch")
70
+ st.selectbox("Selectbox", options=["Option A", "Option B", "Option C"])
71
+ st.multiselect("Multiselect", options=["Tag 1", "Tag 2", "Tag 3"], default=["Tag 1"])
72
+
73
+ with sel_cols[1]:
74
+ st.radio("Radio buttons", options=["Choice 1", "Choice 2", "Choice 3"], horizontal=True)
75
+ st.pills("Pills", options=["Small", "Medium", "Large"], default="Medium")
76
+ st.segmented_control("Segmented", options=["Day", "Week", "Month"], default="Week")
77
+ st.caption("Feedback widget")
78
+ st.feedback("stars")
79
+
80
+ # Numeric & Sliders
81
+ st.subheader("Numeric Inputs")
82
+ num_cols = st.columns(3)
83
+ num_cols[0].number_input("Number input", value=42)
84
+ num_cols[1].slider("Slider", 0, 100, 50)
85
+ num_cols[2].select_slider("Select slider", options=["XS", "S", "M", "L", "XL"], value="M")
86
+
87
+ # Date/Time
88
+ st.subheader("Date & Time")
89
+ dt_cols = st.columns(2)
90
+ dt_cols[0].date_input("Date input")
91
+ dt_cols[1].time_input("Time input")
92
+
93
+ # Text inputs
94
+ st.subheader("Text Inputs")
95
+ txt_cols = st.columns(2)
96
+ txt_cols[0].text_input("Text input", placeholder="Type something...")
97
+ txt_cols[1].text_area("Text area", placeholder="Longer text goes here...", height=100)
98
+
99
+ # File upload
100
+ st.subheader("File Upload")
101
+ st.file_uploader("Upload a file", type=["csv", "txt", "pdf"])
102
+
103
+ # -----------------------------------------------------------------------------
104
+ # DATA SECTION
105
+ # -----------------------------------------------------------------------------
106
+ elif section == "Data":
107
+ st.header("Data Display")
108
+
109
+ # Metrics
110
+ st.subheader("Metrics")
111
+ m_cols = st.columns(4)
112
+ m_cols[0].metric("Revenue", "$45,231", "+12.5%")
113
+ m_cols[1].metric("Users", "2,847", "+8.2%")
114
+ m_cols[2].metric("Conversion", "3.24%", "-0.4%", delta_color="inverse")
115
+ m_cols[3].metric("Avg. Session", "4m 32s", "+1.2%")
116
+
117
+ st.divider()
118
+
119
+ # Dataframe
120
+ st.subheader("Dataframe")
121
+ df = pd.DataFrame({
122
+ "Name": ["Alice", "Bob", "Charlie", "Diana", "Eve"],
123
+ "Department": ["Engineering", "Sales", "Marketing", "Engineering", "Sales"],
124
+ "Salary": [95000, 78000, 82000, 105000, 71000],
125
+ "Start Date": pd.date_range("2022-01-15", periods=5, freq="3M"),
126
+ "Active": [True, True, False, True, True],
127
+ })
128
+ st.dataframe(
129
+ df,
130
+ hide_index=True,
131
+ column_config={
132
+ "Salary": st.column_config.NumberColumn(format="$%d"),
133
+ "Start Date": st.column_config.DateColumn(format="MMM DD, YYYY"),
134
+ "Active": st.column_config.CheckboxColumn("Active?"),
135
+ },
136
+ )
137
+
138
+ # Table
139
+ st.subheader("Static Table")
140
+ st.table(chart_data.head(5))
141
+
142
+ # JSON
143
+ st.subheader("JSON Display")
144
+ st.json({"name": "Streamlit", "version": "1.41.0", "features": ["themes", "widgets", "charts"]})
145
+
146
+ # -----------------------------------------------------------------------------
147
+ # CHARTS SECTION
148
+ # -----------------------------------------------------------------------------
149
+ elif section == "Charts":
150
+ st.header("Charts")
151
+
152
+ chart_cols = st.columns(2)
153
+
154
+ with chart_cols[0]:
155
+ st.subheader("Line Chart")
156
+ st.line_chart(chart_data, height=250)
157
+
158
+ st.subheader("Bar Chart")
159
+ st.bar_chart(chart_data, height=250)
160
+
161
+ with chart_cols[1]:
162
+ st.subheader("Area Chart")
163
+ st.area_chart(chart_data, height=250)
164
+
165
+ st.subheader("Scatter Chart")
166
+ st.scatter_chart(chart_data, height=250)
167
+
168
+ # -----------------------------------------------------------------------------
169
+ # TEXT SECTION
170
+ # -----------------------------------------------------------------------------
171
+ elif section == "Text":
172
+ st.header("Text Elements")
173
+
174
+ # Headers
175
+ st.subheader("Headers")
176
+ st.title("Title Element")
177
+ st.header("Header Element")
178
+ st.subheader("Subheader Element")
179
+ st.caption("Caption text - smaller, muted")
180
+
181
+ st.divider()
182
+
183
+ # Markdown
184
+ st.subheader("Markdown Formatting")
185
+ st.markdown(
186
+ "**Bold text**, *italic text*, ~~strikethrough~~, "
187
+ "`inline code`, [link](https://streamlit.io)"
188
+ )
189
+ st.markdown("Math: $E = mc^2$ and $\\int_0^\\infty e^{-x^2} dx = \\frac{\\sqrt{\\pi}}{2}$")
190
+ st.markdown("Emojis: 🚀 🎨 📊 ✨ and icons: :material/home: :material/settings:")
191
+
192
+ # Colored text
193
+ st.subheader("Colored Text")
194
+ color_cols = st.columns(3)
195
+ color_cols[0].markdown(":red[Red text] and :orange[Orange text]")
196
+ color_cols[1].markdown(":green[Green text] and :blue[Blue text]")
197
+ color_cols[2].markdown(":violet[Violet text] and :rainbow[Rainbow text]")
198
+
199
+ # Code blocks
200
+ st.subheader("Code Block")
201
+ st.code(
202
+ '''import streamlit as st
203
+
204
+ # Create a themed dashboard
205
+ st.set_page_config(page_title="My App", layout="wide")
206
+ st.title("Hello, Streamlit!")
207
+
208
+ # Display metrics
209
+ col1, col2 = st.columns(2)
210
+ col1.metric("Users", "1,234", "+5%")
211
+ col2.metric("Revenue", "$56K", "+12%")''',
212
+ language="python",
213
+ )
214
+
215
+ # -----------------------------------------------------------------------------
216
+ # LAYOUTS SECTION
217
+ # -----------------------------------------------------------------------------
218
+ elif section == "Layouts":
219
+ st.header("Layout Elements")
220
+
221
+ # Columns
222
+ st.subheader("Columns with Borders")
223
+ layout_cols = st.columns(3, border=True)
224
+ layout_cols[0].write("**Column 1**\n\nFirst column content")
225
+ layout_cols[1].write("**Column 2**\n\nSecond column content")
226
+ layout_cols[2].write("**Column 3**\n\nThird column content")
227
+
228
+ # Tabs
229
+ st.subheader("Tabs")
230
+ tab1, tab2, tab3 = st.tabs(["📈 Chart", "📋 Data", "⚙️ Settings"])
231
+ with tab1:
232
+ st.write("Chart tab content")
233
+ st.line_chart(chart_data["a"], height=150)
234
+ with tab2:
235
+ st.write("Data tab content")
236
+ st.dataframe(chart_data.head(3))
237
+ with tab3:
238
+ st.write("Settings tab content")
239
+ st.checkbox("Enable feature X")
240
+ st.checkbox("Enable feature Y", value=True)
241
+
242
+ # Expander
243
+ st.subheader("Expander")
244
+ with st.expander("Click to expand"):
245
+ st.write("This content is hidden by default.")
246
+ st.image("https://placehold.co/400x200/29B5E8/white?text=Expanded+Content")
247
+
248
+ # Popover
249
+ st.subheader("Popover")
250
+ pop_cols = st.columns(3)
251
+ with pop_cols[0].popover("Open popover", icon=":material/info:"):
252
+ st.write("Popover content here!")
253
+ st.slider("Popover slider", 0, 100, 50)
254
+
255
+ # Container
256
+ st.subheader("Container with Border")
257
+ with st.container(border=True):
258
+ st.write("**Bordered Container**")
259
+ st.write("Content inside a container with a visible border.")
260
+ st.button("Button inside container")
261
+
262
+ # -----------------------------------------------------------------------------
263
+ # CHAT SECTION
264
+ # -----------------------------------------------------------------------------
265
+ elif section == "Chat":
266
+ st.header("Chat Elements")
267
+
268
+ # Chat messages
269
+ st.subheader("Chat Messages")
270
+ with st.chat_message("user"):
271
+ st.write("Hello! How can I analyze my sales data?")
272
+
273
+ with st.chat_message("assistant"):
274
+ st.write("I can help you with that! Here are a few options:")
275
+ st.markdown("""
276
+ 1. **Revenue trends** - View monthly/quarterly patterns
277
+ 2. **Top products** - Identify best sellers
278
+ 3. **Customer segments** - Analyze by region or category
279
+ """)
280
+
281
+ with st.chat_message("user"):
282
+ st.write("Show me the revenue trends please.")
283
+
284
+ with st.chat_message("assistant"):
285
+ st.write("Here's your revenue trend for the past 20 periods:")
286
+ st.line_chart(chart_data["a"], height=200)
287
+
288
+ # Chat input
289
+ st.chat_input("Type a message...")
290
+
291
+ # -----------------------------------------------------------------------------
292
+ # STATUS SECTION
293
+ # -----------------------------------------------------------------------------
294
+ elif section == "Status":
295
+ st.header("Status Elements")
296
+
297
+ # Alert messages
298
+ st.subheader("Alert Messages")
299
+ st.error("Error: Something went wrong with the data pipeline.")
300
+ st.warning("Warning: API rate limit approaching (80% used).")
301
+ st.info("Info: New features available in the latest release.")
302
+ st.success("Success: Data exported successfully to warehouse.")
303
+
304
+ # Exception
305
+ st.subheader("Exception Display")
306
+ try:
307
+ raise ValueError("This is an example exception for demonstration")
308
+ except ValueError as e:
309
+ st.exception(e)
310
+
311
+ # Interactive status
312
+ st.subheader("Interactive Status")
313
+ status_cols = st.columns(3)
314
+ if status_cols[0].button("Show Toast", icon=":material/notifications:"):
315
+ st.toast("This is a toast notification!", icon="🔔")
316
+ if status_cols[1].button("Balloons", icon=":material/celebration:"):
317
+ st.balloons()
318
+ if status_cols[2].button("Snow", icon=":material/ac_unit:"):
319
+ st.snow()
320
+
321
+ # Progress
322
+ st.subheader("Progress Indicators")
323
+ st.progress(0.7, text="70% complete")
324
+ with st.spinner("Loading..."):
325
+ st.write("Spinner is active (non-blocking in this demo)")
326
+
327
+ # -----------------------------------------------------------------------------
328
+ # SIDEBAR
329
+ # -----------------------------------------------------------------------------
330
+ with st.sidebar:
331
+ st.header("Settings")
332
+ st.selectbox("Time Period", ["Last 7 days", "Last 30 days", "Last 90 days", "All time"])
333
+ st.multiselect("Metrics", ["Revenue", "Users", "Sessions"], default=["Revenue", "Users"])
334
+ st.slider("Confidence threshold", 0.0, 1.0, 0.8)
335
+ st.divider()
336
+ st.caption("Element Explorer v1.0")
337
+ st.caption("Theme: **GitHub**")
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env python3
2
+ """Manage theme template directories (fully generated from _configs/).
3
+
4
+ Usage:
5
+ python manage.py sync # Regenerate all theme directories
6
+ python manage.py check # Verify generated files haven't drifted
7
+ python manage.py new NAME # Scaffold a new theme config
8
+ """
9
+
10
+ import ast
11
+ import re
12
+ import shutil
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ ROOT = Path(__file__).parent
17
+ SHARED = ROOT / "_shared"
18
+ TEMPLATES = ROOT / "_templates"
19
+ CONFIGS = ROOT / "_configs"
20
+ FONTS = SHARED / "fonts"
21
+
22
+ MANAGED_HEADER_PY = (
23
+ "# DO NOT EDIT — managed by manage.py, edit _shared/streamlit_app.py instead\n"
24
+ )
25
+ MANAGED_HEADER_TOML = (
26
+ "# DO NOT EDIT — managed by manage.py, edit _configs/{slug}.toml instead\n"
27
+ )
28
+
29
+ GITATTR_START = "# BEGIN managed by manage.py"
30
+ GITATTR_END = "# END managed by manage.py"
31
+
32
+
33
+ # ---------------------------------------------------------------------------
34
+ # Theme discovery
35
+ # ---------------------------------------------------------------------------
36
+
37
+ TITLE_OVERRIDES = {"github": "GitHub"}
38
+
39
+
40
+ def slug_to_title(slug):
41
+ """Derive a display title from a directory slug: 'solarized-light' -> 'Solarized Light'."""
42
+ if slug in TITLE_OVERRIDES:
43
+ return TITLE_OVERRIDES[slug]
44
+ return " ".join(w.capitalize() for w in slug.split("-"))
45
+
46
+
47
+ def discover_themes():
48
+ """Find themes by scanning _configs/*.toml."""
49
+ return [
50
+ {"slug": c.stem, "title": slug_to_title(c.stem)}
51
+ for c in sorted(CONFIGS.glob("*.toml"))
52
+ ]
53
+
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # Font discovery from config content
57
+ # ---------------------------------------------------------------------------
58
+
59
+ def discover_fonts(config_text):
60
+ """Extract font filenames referenced in config.toml content."""
61
+ return re.findall(r'url\s*=\s*["\']app/static/([^"\']+\.(?:ttf|otf|woff2?))["\']', config_text)
62
+
63
+
64
+ # ---------------------------------------------------------------------------
65
+ # Content builders
66
+ # ---------------------------------------------------------------------------
67
+
68
+ def expected_app(title):
69
+ """Build expected streamlit_app.py content for a theme."""
70
+ source = (SHARED / "streamlit_app.py").read_text()
71
+ body = source.replace("{{title}}", title)
72
+ # Insert managed header after the module docstring
73
+ tree = ast.parse(body)
74
+ if ast.get_docstring(tree) is not None:
75
+ # The docstring is the first statement; find end of its line
76
+ docstring_node = tree.body[0]
77
+ end_line = docstring_node.end_lineno # 1-indexed
78
+ lines = body.split("\n")
79
+ insert_pos = end_line
80
+ return "\n".join(lines[:insert_pos]) + "\n" + MANAGED_HEADER_PY + "\n".join(lines[insert_pos:])
81
+ return MANAGED_HEADER_PY + body
82
+
83
+
84
+ def expected_config(slug):
85
+ """Build expected .streamlit/config.toml content for a theme."""
86
+ source = (CONFIGS / f"{slug}.toml").read_text()
87
+ header = MANAGED_HEADER_TOML.replace("{slug}", slug)
88
+ return header + source
89
+
90
+
91
+ def expected_from_template(tmpl_path, replacements):
92
+ """Build expected file content from a .tmpl template."""
93
+ text = tmpl_path.read_text()
94
+ for key, value in replacements.items():
95
+ text = text.replace("{{" + key + "}}", value)
96
+ return text
97
+
98
+
99
+ # ---------------------------------------------------------------------------
100
+ # Sync
101
+ # ---------------------------------------------------------------------------
102
+
103
+ def sync_theme(theme):
104
+ """Regenerate all files for a single theme directory."""
105
+ slug = theme["slug"]
106
+ title = theme["title"]
107
+ identifier = slug.replace("-", "_")
108
+ theme_dir = ROOT / slug
109
+
110
+ # Create directories
111
+ theme_dir.mkdir(exist_ok=True)
112
+ (theme_dir / ".streamlit").mkdir(exist_ok=True)
113
+ (theme_dir / "static").mkdir(exist_ok=True)
114
+
115
+ # .streamlit/config.toml — from _configs/
116
+ (theme_dir / ".streamlit" / "config.toml").write_text(expected_config(slug))
117
+
118
+ # streamlit_app.py
119
+ (theme_dir / "streamlit_app.py").write_text(expected_app(title))
120
+
121
+ # pyproject.toml
122
+ (theme_dir / "pyproject.toml").write_text(
123
+ expected_from_template(
124
+ TEMPLATES / "pyproject.toml.tmpl",
125
+ {"slug": slug, "title": title},
126
+ )
127
+ )
128
+
129
+ # snowflake.yml
130
+ (theme_dir / "snowflake.yml").write_text(
131
+ expected_from_template(
132
+ TEMPLATES / "snowflake.yml.tmpl",
133
+ {"slug": slug, "title": title, "identifier": identifier},
134
+ )
135
+ )
136
+
137
+ # Fonts — copy from _shared/fonts/ based on config references
138
+ config_text = (CONFIGS / f"{slug}.toml").read_text()
139
+ font_names = discover_fonts(config_text)
140
+ static_dir = theme_dir / "static"
141
+ for fname in font_names:
142
+ src = FONTS / fname
143
+ if not src.exists():
144
+ print(f" Warning: font {fname} referenced in _configs/{slug}.toml not found in _shared/fonts/", file=sys.stderr)
145
+ continue
146
+ shutil.copy2(src, static_dir / fname)
147
+
148
+
149
+ def update_gitattributes():
150
+ """Update .gitattributes with entries for generated theme files."""
151
+ gitattr_path = ROOT / ".gitattributes"
152
+
153
+ new_section = "\n".join([
154
+ GITATTR_START,
155
+ "*/.streamlit/config.toml linguist-generated",
156
+ "*/streamlit_app.py linguist-generated",
157
+ "*/pyproject.toml linguist-generated",
158
+ "*/snowflake.yml linguist-generated",
159
+ "*/static/*.ttf linguist-generated",
160
+ GITATTR_END,
161
+ ])
162
+
163
+ if gitattr_path.exists():
164
+ content = gitattr_path.read_text()
165
+ if GITATTR_START in content:
166
+ start = content.index(GITATTR_START)
167
+ end = content.index(GITATTR_END) + len(GITATTR_END)
168
+ content = content[:start] + new_section + content[end:]
169
+ else:
170
+ content = content.rstrip() + "\n\n" + new_section + "\n"
171
+ else:
172
+ content = new_section + "\n"
173
+
174
+ gitattr_path.write_text(content)
175
+
176
+
177
+ def cmd_sync():
178
+ themes = discover_themes()
179
+ for t in themes:
180
+ sync_theme(t)
181
+ print(f" Synced {t['slug']}/")
182
+
183
+ # Remove orphaned theme directories (directories not matching any config)
184
+ config_slugs = {t["slug"] for t in themes}
185
+ orphans = [
186
+ d for d in sorted(ROOT.iterdir())
187
+ if d.is_dir() and not d.name.startswith("_") and d.name not in config_slugs
188
+ ]
189
+ if orphans:
190
+ print("\nOrphaned directories (no matching config):")
191
+ for d in orphans:
192
+ print(f" {d.name}/")
193
+ answer = input("Remove these directories? [y/N] ").strip().lower()
194
+ if answer == "y":
195
+ for d in orphans:
196
+ shutil.rmtree(d)
197
+ print(f" Removed {d.name}/")
198
+ else:
199
+ print(" Skipped orphan removal.")
200
+
201
+ update_gitattributes()
202
+ print(f"\nSynced {len(themes)} theme directories.")
203
+
204
+
205
+ # ---------------------------------------------------------------------------
206
+ # Check
207
+ # ---------------------------------------------------------------------------
208
+
209
+ def cmd_check():
210
+ themes = discover_themes()
211
+ drifted = []
212
+ missing = []
213
+
214
+ for theme in themes:
215
+ slug = theme["slug"]
216
+ title = theme["title"]
217
+ identifier = slug.replace("-", "_")
218
+ theme_dir = ROOT / slug
219
+
220
+ # .streamlit/config.toml
221
+ target = theme_dir / ".streamlit" / "config.toml"
222
+ expected = expected_config(slug)
223
+ if not target.exists():
224
+ missing.append(f"{slug}/.streamlit/config.toml")
225
+ elif target.read_text() != expected:
226
+ drifted.append(f"{slug}/.streamlit/config.toml")
227
+
228
+ # streamlit_app.py
229
+ target = theme_dir / "streamlit_app.py"
230
+ if not target.exists():
231
+ missing.append(f"{slug}/streamlit_app.py")
232
+ elif target.read_text() != expected_app(title):
233
+ drifted.append(f"{slug}/streamlit_app.py")
234
+
235
+ # pyproject.toml
236
+ target = theme_dir / "pyproject.toml"
237
+ expected = expected_from_template(
238
+ TEMPLATES / "pyproject.toml.tmpl",
239
+ {"slug": slug, "title": title},
240
+ )
241
+ if not target.exists():
242
+ missing.append(f"{slug}/pyproject.toml")
243
+ elif target.read_text() != expected:
244
+ drifted.append(f"{slug}/pyproject.toml")
245
+
246
+ # snowflake.yml
247
+ target = theme_dir / "snowflake.yml"
248
+ expected = expected_from_template(
249
+ TEMPLATES / "snowflake.yml.tmpl",
250
+ {"slug": slug, "title": title, "identifier": identifier},
251
+ )
252
+ if not target.exists():
253
+ missing.append(f"{slug}/snowflake.yml")
254
+ elif target.read_text() != expected:
255
+ drifted.append(f"{slug}/snowflake.yml")
256
+
257
+ # Fonts
258
+ config_text = (CONFIGS / f"{slug}.toml").read_text()
259
+ font_names = discover_fonts(config_text)
260
+ for fname in font_names:
261
+ src = FONTS / fname
262
+ dest = theme_dir / "static" / fname
263
+ if not dest.exists():
264
+ missing.append(f"{slug}/static/{fname}")
265
+ elif src.exists() and src.read_bytes() != dest.read_bytes():
266
+ drifted.append(f"{slug}/static/{fname}")
267
+
268
+ ok = True
269
+ if missing:
270
+ print("Missing files (run 'python manage.py sync' to fix):")
271
+ for f in missing:
272
+ print(f" {f}")
273
+ ok = False
274
+ if drifted:
275
+ print("Drifted files (run 'python manage.py sync' to fix):")
276
+ for f in drifted:
277
+ print(f" {f}")
278
+ ok = False
279
+ if ok:
280
+ print(f"All generated files in sync across {len(themes)} theme directories.")
281
+ else:
282
+ sys.exit(1)
283
+
284
+
285
+ # ---------------------------------------------------------------------------
286
+ # New
287
+ # ---------------------------------------------------------------------------
288
+
289
+ def cmd_new(name):
290
+ config_path = CONFIGS / f"{name}.toml"
291
+ if config_path.exists():
292
+ print(f"Error: _configs/{name}.toml already exists", file=sys.stderr)
293
+ sys.exit(1)
294
+
295
+ config_path.write_text(
296
+ f"[server]\nenableStaticServing = true\n\n"
297
+ f"# {name} theme\n[theme]\nbase = \"dark\"\n"
298
+ )
299
+
300
+ print(f"Created _configs/{name}.toml — edit it, then run 'python manage.py sync'")
301
+
302
+
303
+ # ---------------------------------------------------------------------------
304
+ # CLI
305
+ # ---------------------------------------------------------------------------
306
+
307
+ if __name__ == "__main__":
308
+ if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help"):
309
+ print(__doc__.strip())
310
+ sys.exit(0 if len(sys.argv) > 1 else 1)
311
+
312
+ if not SHARED.is_dir():
313
+ print(f"Error: {SHARED} not found", file=sys.stderr)
314
+ sys.exit(1)
315
+
316
+ if not CONFIGS.is_dir():
317
+ print(f"Error: {CONFIGS} not found", file=sys.stderr)
318
+ sys.exit(1)
319
+
320
+ cmd = sys.argv[1]
321
+ if cmd == "sync":
322
+ cmd_sync()
323
+ elif cmd == "new":
324
+ if len(sys.argv) < 3:
325
+ print("Usage: python manage.py new NAME", file=sys.stderr)
326
+ sys.exit(1)
327
+ cmd_new(sys.argv[2])
328
+ elif cmd == "check":
329
+ cmd_check()
330
+ else:
331
+ print(f"Unknown command: {cmd}", file=sys.stderr)
332
+ sys.exit(1)