flashstudio 0.1.0__tar.gz
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.
- flashstudio-0.1.0/PKG-INFO +133 -0
- flashstudio-0.1.0/README.md +102 -0
- flashstudio-0.1.0/flashstudio/__init__.py +5 -0
- flashstudio-0.1.0/flashstudio/app.py +64 -0
- flashstudio-0.1.0/flashstudio/cli.py +18 -0
- flashstudio-0.1.0/flashstudio/components/__init__.py +0 -0
- flashstudio-0.1.0/flashstudio/components/sidebar.py +45 -0
- flashstudio-0.1.0/flashstudio/components/styles.py +273 -0
- flashstudio-0.1.0/flashstudio/components/wizard.py +46 -0
- flashstudio-0.1.0/flashstudio/launcher.py +73 -0
- flashstudio-0.1.0/flashstudio/pages/__init__.py +0 -0
- flashstudio-0.1.0/flashstudio/pages/dashboard.py +168 -0
- flashstudio-0.1.0/flashstudio/pages/data.py +272 -0
- flashstudio-0.1.0/flashstudio/pages/export.py +212 -0
- flashstudio-0.1.0/flashstudio/pages/inference.py +1112 -0
- flashstudio-0.1.0/flashstudio/pages/model.py +370 -0
- flashstudio-0.1.0/flashstudio/pages/training.py +672 -0
- flashstudio-0.1.0/flashstudio/utils/__init__.py +0 -0
- flashstudio-0.1.0/flashstudio/utils/device.py +58 -0
- flashstudio-0.1.0/flashstudio.egg-info/PKG-INFO +133 -0
- flashstudio-0.1.0/flashstudio.egg-info/SOURCES.txt +25 -0
- flashstudio-0.1.0/flashstudio.egg-info/dependency_links.txt +1 -0
- flashstudio-0.1.0/flashstudio.egg-info/entry_points.txt +2 -0
- flashstudio-0.1.0/flashstudio.egg-info/requires.txt +17 -0
- flashstudio-0.1.0/flashstudio.egg-info/top_level.txt +1 -0
- flashstudio-0.1.0/pyproject.toml +55 -0
- flashstudio-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flashstudio
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Interactive Training & Inference UI for FlashDet â runs on Google Colab
|
|
5
|
+
Author: Gaurav14cs17
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/FlashVision/FlashStudio
|
|
8
|
+
Project-URL: Repository, https://github.com/FlashVision/FlashStudio
|
|
9
|
+
Keywords: object-detection,flashdet,training,inference,ui,colab
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: streamlit>=1.28.0
|
|
18
|
+
Requires-Dist: plotly>=5.0
|
|
19
|
+
Requires-Dist: pandas>=1.5
|
|
20
|
+
Requires-Dist: pillow>=9.0
|
|
21
|
+
Requires-Dist: numpy>=1.21
|
|
22
|
+
Provides-Extra: full
|
|
23
|
+
Requires-Dist: pyngrok>=6.0; extra == "full"
|
|
24
|
+
Requires-Dist: torch>=2.0; extra == "full"
|
|
25
|
+
Requires-Dist: opencv-python>=4.5; extra == "full"
|
|
26
|
+
Provides-Extra: colab
|
|
27
|
+
Requires-Dist: pyngrok>=6.0; extra == "colab"
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: ruff; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest; extra == "dev"
|
|
31
|
+
|
|
32
|
+
# ⥠FlashStudio
|
|
33
|
+
|
|
34
|
+
**Interactive Training & Inference UI for FlashDet** â runs locally or on Google Colab with a Streamlit interface.
|
|
35
|
+
|
|
36
|
+
<p align="center">
|
|
37
|
+
<img src="docs/mockups/flashstudio_streamlit_mockup.png" width="800" alt="FlashStudio UI"/>
|
|
38
|
+
</p>
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- đī¸ **Training Dashboard** â Real-time monitoring with live loss curves, visualizations, GT verification
|
|
43
|
+
- đ§ **Model Config** â All 6 FlashDet sizes + YOLOv8/v9/v10/v11/YOLOX with accurate params
|
|
44
|
+
- đ **Inference Pipeline** â 4-step wizard: Model â Data â Zone â Run (17 solutions, 6 trackers)
|
|
45
|
+
- đ¤ **Export** â ONNX export with FP16 auto-generated weights
|
|
46
|
+
- đĻ **Data** â Native `flashdet download` datasets + custom upload
|
|
47
|
+
- đ **Colab Support** â ngrok tunneling for remote access
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
### Install from GitHub
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install git+https://github.com/FlashVision/FlashStudio.git
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Install locally (development)
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
git clone https://github.com/FlashVision/FlashStudio.git
|
|
61
|
+
cd FlashStudio
|
|
62
|
+
pip install -e .
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Install with all dependencies (FlashDet + PyTorch)
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install "flashstudio[full] @ git+https://github.com/FlashVision/FlashStudio.git"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Run
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# CLI
|
|
75
|
+
flashstudio --port 8501
|
|
76
|
+
|
|
77
|
+
# Or directly
|
|
78
|
+
streamlit run flashstudio/app.py
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Python API (for Colab)
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from flashstudio import launch
|
|
85
|
+
launch() # Opens ngrok tunnel in Colab, localhost otherwise
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Google Colab
|
|
89
|
+
|
|
90
|
+
| Notebook | Description | Link |
|
|
91
|
+
|----------|-------------|------|
|
|
92
|
+
| Training | Train FlashDet models | [](https://colab.research.google.com/github/FlashVision/FlashStudio/blob/main/notebooks/FlashStudio_Train.ipynb) |
|
|
93
|
+
| Inference | Run detection on images/video | [](https://colab.research.google.com/github/FlashVision/FlashStudio/blob/main/notebooks/FlashStudio_Inference.ipynb) |
|
|
94
|
+
|
|
95
|
+
## Architecture
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
FlashStudio/
|
|
99
|
+
âââ flashstudio/
|
|
100
|
+
â âââ __init__.py # Package init + launch() export
|
|
101
|
+
â âââ app.py # Main Streamlit app (wizard flow)
|
|
102
|
+
â âââ launcher.py # Colab/local launcher with ngrok
|
|
103
|
+
â âââ cli.py # CLI entrypoint
|
|
104
|
+
â âââ pages/
|
|
105
|
+
â â âââ dashboard.py # Overview + recent runs
|
|
106
|
+
â â âââ data.py # Dataset upload/download
|
|
107
|
+
â â âââ model.py # Architecture & hyperparameter config
|
|
108
|
+
â â âââ training.py # Training monitor (reads real workspace)
|
|
109
|
+
â â âââ export.py # ONNX export
|
|
110
|
+
â â âââ inference.py # 4-step inference pipeline
|
|
111
|
+
â âââ components/
|
|
112
|
+
â â âââ sidebar.py # Navigation sidebar
|
|
113
|
+
â â âââ styles.py # Custom CSS
|
|
114
|
+
â â âââ wizard.py # Step indicator & navigation
|
|
115
|
+
â âââ utils/
|
|
116
|
+
â âââ device.py # GPU/environment detection
|
|
117
|
+
âââ notebooks/
|
|
118
|
+
â âââ FlashStudio_Train.ipynb
|
|
119
|
+
â âââ FlashStudio_Inference.ipynb
|
|
120
|
+
âââ .streamlit/config.toml
|
|
121
|
+
âââ pyproject.toml
|
|
122
|
+
âââ README.md
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Requirements
|
|
126
|
+
|
|
127
|
+
- Python >= 3.9
|
|
128
|
+
- FlashDet (install separately: `pip install git+https://github.com/FlashVision/FlashDet.git`)
|
|
129
|
+
- GPU recommended for training (T4 or better)
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
Apache-2.0
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# ⥠FlashStudio
|
|
2
|
+
|
|
3
|
+
**Interactive Training & Inference UI for FlashDet** â runs locally or on Google Colab with a Streamlit interface.
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="docs/mockups/flashstudio_streamlit_mockup.png" width="800" alt="FlashStudio UI"/>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- đī¸ **Training Dashboard** â Real-time monitoring with live loss curves, visualizations, GT verification
|
|
12
|
+
- đ§ **Model Config** â All 6 FlashDet sizes + YOLOv8/v9/v10/v11/YOLOX with accurate params
|
|
13
|
+
- đ **Inference Pipeline** â 4-step wizard: Model â Data â Zone â Run (17 solutions, 6 trackers)
|
|
14
|
+
- đ¤ **Export** â ONNX export with FP16 auto-generated weights
|
|
15
|
+
- đĻ **Data** â Native `flashdet download` datasets + custom upload
|
|
16
|
+
- đ **Colab Support** â ngrok tunneling for remote access
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### Install from GitHub
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install git+https://github.com/FlashVision/FlashStudio.git
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Install locally (development)
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
git clone https://github.com/FlashVision/FlashStudio.git
|
|
30
|
+
cd FlashStudio
|
|
31
|
+
pip install -e .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Install with all dependencies (FlashDet + PyTorch)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install "flashstudio[full] @ git+https://github.com/FlashVision/FlashStudio.git"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Run
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# CLI
|
|
44
|
+
flashstudio --port 8501
|
|
45
|
+
|
|
46
|
+
# Or directly
|
|
47
|
+
streamlit run flashstudio/app.py
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Python API (for Colab)
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from flashstudio import launch
|
|
54
|
+
launch() # Opens ngrok tunnel in Colab, localhost otherwise
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Google Colab
|
|
58
|
+
|
|
59
|
+
| Notebook | Description | Link |
|
|
60
|
+
|----------|-------------|------|
|
|
61
|
+
| Training | Train FlashDet models | [](https://colab.research.google.com/github/FlashVision/FlashStudio/blob/main/notebooks/FlashStudio_Train.ipynb) |
|
|
62
|
+
| Inference | Run detection on images/video | [](https://colab.research.google.com/github/FlashVision/FlashStudio/blob/main/notebooks/FlashStudio_Inference.ipynb) |
|
|
63
|
+
|
|
64
|
+
## Architecture
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
FlashStudio/
|
|
68
|
+
âââ flashstudio/
|
|
69
|
+
â âââ __init__.py # Package init + launch() export
|
|
70
|
+
â âââ app.py # Main Streamlit app (wizard flow)
|
|
71
|
+
â âââ launcher.py # Colab/local launcher with ngrok
|
|
72
|
+
â âââ cli.py # CLI entrypoint
|
|
73
|
+
â âââ pages/
|
|
74
|
+
â â âââ dashboard.py # Overview + recent runs
|
|
75
|
+
â â âââ data.py # Dataset upload/download
|
|
76
|
+
â â âââ model.py # Architecture & hyperparameter config
|
|
77
|
+
â â âââ training.py # Training monitor (reads real workspace)
|
|
78
|
+
â â âââ export.py # ONNX export
|
|
79
|
+
â â âââ inference.py # 4-step inference pipeline
|
|
80
|
+
â âââ components/
|
|
81
|
+
â â âââ sidebar.py # Navigation sidebar
|
|
82
|
+
â â âââ styles.py # Custom CSS
|
|
83
|
+
â â âââ wizard.py # Step indicator & navigation
|
|
84
|
+
â âââ utils/
|
|
85
|
+
â âââ device.py # GPU/environment detection
|
|
86
|
+
âââ notebooks/
|
|
87
|
+
â âââ FlashStudio_Train.ipynb
|
|
88
|
+
â âââ FlashStudio_Inference.ipynb
|
|
89
|
+
âââ .streamlit/config.toml
|
|
90
|
+
âââ pyproject.toml
|
|
91
|
+
âââ README.md
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Requirements
|
|
95
|
+
|
|
96
|
+
- Python >= 3.9
|
|
97
|
+
- FlashDet (install separately: `pip install git+https://github.com/FlashVision/FlashDet.git`)
|
|
98
|
+
- GPU recommended for training (T4 or better)
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
Apache-2.0
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""FlashStudio â Main Streamlit Application."""
|
|
2
|
+
|
|
3
|
+
import streamlit as st
|
|
4
|
+
|
|
5
|
+
st.set_page_config(
|
|
6
|
+
page_title="FlashStudio",
|
|
7
|
+
page_icon="âĄ",
|
|
8
|
+
layout="wide",
|
|
9
|
+
initial_sidebar_state="expanded",
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from flashstudio.components.styles import inject_custom_css # noqa: E402
|
|
13
|
+
from flashstudio.components.sidebar import render_sidebar # noqa: E402
|
|
14
|
+
from flashstudio.components.wizard import render_step_indicator, render_navigation # noqa: E402
|
|
15
|
+
from flashstudio.pages.dashboard import render_dashboard # noqa: E402
|
|
16
|
+
from flashstudio.pages.data import render_data_page # noqa: E402
|
|
17
|
+
from flashstudio.pages.model import render_model_page # noqa: E402
|
|
18
|
+
from flashstudio.pages.training import render_training_page # noqa: E402
|
|
19
|
+
from flashstudio.pages.export import render_export_page # noqa: E402
|
|
20
|
+
from flashstudio.pages.inference import render_inference_page # noqa: E402
|
|
21
|
+
|
|
22
|
+
STEPS = [
|
|
23
|
+
{"id": "dashboard", "label": "Dashboard", "icon": "đ "},
|
|
24
|
+
{"id": "data", "label": "Data", "icon": "đĻ"},
|
|
25
|
+
{"id": "model", "label": "Model", "icon": "đ§ "},
|
|
26
|
+
{"id": "training", "label": "Training", "icon": "đī¸"},
|
|
27
|
+
{"id": "export", "label": "Export", "icon": "đ¤"},
|
|
28
|
+
{"id": "inference", "label": "Inference", "icon": "đ"},
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
PAGE_RENDERERS = {
|
|
32
|
+
"dashboard": render_dashboard,
|
|
33
|
+
"data": render_data_page,
|
|
34
|
+
"model": render_model_page,
|
|
35
|
+
"training": render_training_page,
|
|
36
|
+
"export": render_export_page,
|
|
37
|
+
"inference": render_inference_page,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main():
|
|
42
|
+
import traceback as _tb
|
|
43
|
+
|
|
44
|
+
inject_custom_css()
|
|
45
|
+
|
|
46
|
+
if "current_step" not in st.session_state:
|
|
47
|
+
st.session_state["current_step"] = 0
|
|
48
|
+
|
|
49
|
+
render_sidebar()
|
|
50
|
+
render_step_indicator(STEPS, st.session_state["current_step"])
|
|
51
|
+
|
|
52
|
+
current = STEPS[st.session_state["current_step"]]
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
PAGE_RENDERERS[current["id"]]()
|
|
56
|
+
except Exception as _e:
|
|
57
|
+
st.error(f"Error: {_e}")
|
|
58
|
+
st.code(_tb.format_exc())
|
|
59
|
+
|
|
60
|
+
render_navigation(STEPS, st.session_state["current_step"])
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
main()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""FlashStudio CLI entrypoint."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
parser = argparse.ArgumentParser(description="FlashStudio â Training & Inference UI for FlashDet")
|
|
8
|
+
parser.add_argument("--port", type=int, default=8501, help="Port to serve on")
|
|
9
|
+
parser.add_argument("--no-share", action="store_true", help="Disable ngrok sharing in Colab")
|
|
10
|
+
parser.add_argument("--ngrok-token", type=str, default=None, help="ngrok auth token")
|
|
11
|
+
args = parser.parse_args()
|
|
12
|
+
|
|
13
|
+
from flashstudio.launcher import launch
|
|
14
|
+
launch(port=args.port, share=not args.no_share, ngrok_token=args.ngrok_token)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
if __name__ == "__main__":
|
|
18
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""FlashStudio sidebar â compact, working navigation."""
|
|
2
|
+
|
|
3
|
+
import streamlit as st
|
|
4
|
+
from flashstudio.utils.device import get_gpu_info, get_colab_runtime_type
|
|
5
|
+
|
|
6
|
+
PAGES = ["đ Dashboard", "đĻ Data", "đ§ Model", "đī¸ Training", "đ¤ Export", "đ Inference"]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def render_sidebar():
|
|
10
|
+
"""Render sidebar with reliable button navigation."""
|
|
11
|
+
with st.sidebar:
|
|
12
|
+
# Logo
|
|
13
|
+
st.markdown(
|
|
14
|
+
"<div style='text-align:center; padding:0.5rem 0;'>"
|
|
15
|
+
"<span style='font-size:1.6rem;'>âĄ</span> "
|
|
16
|
+
"<b style='font-size:1.1rem;'>FlashStudio</b>"
|
|
17
|
+
"</div>",
|
|
18
|
+
unsafe_allow_html=True,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
st.divider()
|
|
22
|
+
|
|
23
|
+
# Navigation buttons
|
|
24
|
+
current = st.session_state.get("current_step", 0)
|
|
25
|
+
for i, page in enumerate(PAGES):
|
|
26
|
+
btn_type = "primary" if i == current else "secondary"
|
|
27
|
+
if st.button(page, key=f"sb_{i}", type=btn_type, use_container_width=True):
|
|
28
|
+
st.session_state["current_step"] = i
|
|
29
|
+
st.rerun()
|
|
30
|
+
|
|
31
|
+
st.divider()
|
|
32
|
+
|
|
33
|
+
# Compact status
|
|
34
|
+
st.caption(f"Model: {st.session_state.get('model_arch', 'FlashDet-Nano')}")
|
|
35
|
+
st.caption(f"Dataset: {st.session_state.get('dataset_name', 'â')}")
|
|
36
|
+
st.caption(f"Status: {st.session_state.get('training_status', 'Not started')}")
|
|
37
|
+
|
|
38
|
+
st.divider()
|
|
39
|
+
|
|
40
|
+
# Environment
|
|
41
|
+
gpu = get_gpu_info()
|
|
42
|
+
if gpu["available"]:
|
|
43
|
+
st.caption(f"đĨī¸ {gpu['name']}")
|
|
44
|
+
else:
|
|
45
|
+
st.caption("đģ CPU Mode")
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""FlashStudio â Custom CSS Styles matching the professional mockup (light theme)."""
|
|
2
|
+
|
|
3
|
+
import streamlit as st
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def inject_custom_css():
|
|
7
|
+
"""Inject custom CSS to match the professional mockup design â light theme, dark sidebar."""
|
|
8
|
+
st.markdown("""
|
|
9
|
+
<style>
|
|
10
|
+
/* âââââââ Global âââââââ */
|
|
11
|
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
|
|
12
|
+
|
|
13
|
+
.stApp {
|
|
14
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* âââââââ Sidebar âââââââ */
|
|
18
|
+
section[data-testid="stSidebar"] {
|
|
19
|
+
background: #FAFBFC !important;
|
|
20
|
+
border-right: 1px solid #E8E8EF;
|
|
21
|
+
width: 240px !important;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
section[data-testid="stSidebar"] > div {
|
|
25
|
+
width: 240px !important;
|
|
26
|
+
padding: 1rem 0.8rem !important;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
section[data-testid="stSidebar"] hr {
|
|
30
|
+
border-color: #E8E8EF !important;
|
|
31
|
+
margin: 0.4rem 0 !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
section[data-testid="stSidebar"] .stButton > button {
|
|
35
|
+
font-size: 0.82rem !important;
|
|
36
|
+
padding: 0.4rem 0.7rem !important;
|
|
37
|
+
border-radius: 6px !important;
|
|
38
|
+
text-align: left !important;
|
|
39
|
+
justify-content: flex-start !important;
|
|
40
|
+
min-height: 0 !important;
|
|
41
|
+
height: auto !important;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
section[data-testid="stSidebar"] .stButton > button[kind="secondary"] {
|
|
45
|
+
background: transparent !important;
|
|
46
|
+
border: none !important;
|
|
47
|
+
color: #374151 !important;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
section[data-testid="stSidebar"] .stButton > button[kind="secondary"]:hover {
|
|
51
|
+
background: #F5F3FF !important;
|
|
52
|
+
color: #7C3AED !important;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
section[data-testid="stSidebar"] .stButton > button[kind="primary"] {
|
|
56
|
+
background: #F5F3FF !important;
|
|
57
|
+
border: 1px solid #EDE9FE !important;
|
|
58
|
+
color: #7C3AED !important;
|
|
59
|
+
font-weight: 600 !important;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Hide Streamlit's auto multipage nav */
|
|
63
|
+
[data-testid="stSidebarNav"] {
|
|
64
|
+
display: none !important;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* âââââââ Main Content âââââââ */
|
|
68
|
+
.stApp > header {
|
|
69
|
+
background: transparent;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* âââââââ Metric Cards âââââââ */
|
|
73
|
+
div[data-testid="stMetric"] {
|
|
74
|
+
background: #FFFFFF;
|
|
75
|
+
border: 1px solid #E8E8EF;
|
|
76
|
+
border-radius: 12px;
|
|
77
|
+
padding: 1rem 1.2rem;
|
|
78
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
|
|
79
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
div[data-testid="stMetric"]:hover {
|
|
83
|
+
transform: translateY(-2px);
|
|
84
|
+
box-shadow: 0 4px 12px rgba(124, 58, 237, 0.08);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
div[data-testid="stMetric"] label {
|
|
88
|
+
font-size: 0.75rem !important;
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
letter-spacing: 0.05em;
|
|
91
|
+
color: #6B7280 !important;
|
|
92
|
+
font-weight: 500;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
div[data-testid="stMetric"] div[data-testid="stMetricValue"] {
|
|
96
|
+
font-size: 1.6rem !important;
|
|
97
|
+
font-weight: 700;
|
|
98
|
+
color: #1A1A2E !important;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/* âââââââ Primary Buttons âââââââ */
|
|
102
|
+
.main .stButton > button[kind="primary"] {
|
|
103
|
+
background: #7C3AED !important;
|
|
104
|
+
border: none;
|
|
105
|
+
border-radius: 8px;
|
|
106
|
+
font-weight: 600;
|
|
107
|
+
color: #FFFFFF !important;
|
|
108
|
+
letter-spacing: 0.02em;
|
|
109
|
+
transition: all 0.2s;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.main .stButton > button[kind="primary"]:hover {
|
|
113
|
+
background: #6D28D9 !important;
|
|
114
|
+
box-shadow: 0 4px 15px rgba(124, 58, 237, 0.3);
|
|
115
|
+
transform: translateY(-1px);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.main .stButton > button[kind="secondary"] {
|
|
119
|
+
border-radius: 8px;
|
|
120
|
+
border: 1px solid #E8E8EF !important;
|
|
121
|
+
font-weight: 500;
|
|
122
|
+
color: #1A1A2E !important;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.main .stButton > button[kind="secondary"]:hover {
|
|
126
|
+
border-color: #7C3AED !important;
|
|
127
|
+
color: #7C3AED !important;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* âââââââ Containers / Cards âââââââ */
|
|
131
|
+
div[data-testid="stVerticalBlockBorderWrapper"] {
|
|
132
|
+
border-radius: 12px !important;
|
|
133
|
+
border-color: #E8E8EF !important;
|
|
134
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* âââââââ Step Indicator âââââââ */
|
|
138
|
+
.step-indicator {
|
|
139
|
+
display: flex;
|
|
140
|
+
justify-content: center;
|
|
141
|
+
gap: 0.3rem;
|
|
142
|
+
padding: 0.5rem 0.5rem;
|
|
143
|
+
margin-bottom: 1rem;
|
|
144
|
+
background: #FAFAFA;
|
|
145
|
+
border-radius: 8px;
|
|
146
|
+
border: 1px solid #F0F0F5;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.step-item {
|
|
150
|
+
text-align: center;
|
|
151
|
+
flex: 1;
|
|
152
|
+
position: relative;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.step-icon { font-size: 1.3rem; display: block; }
|
|
156
|
+
.step-label { font-size: 0.7rem; margin-top: 0.2rem; font-weight: 500; color: #6B7280; }
|
|
157
|
+
.step-bar { height: 3px; border-radius: 2px; margin-top: 0.4rem; }
|
|
158
|
+
|
|
159
|
+
.step-active .step-label { color: #7C3AED; font-weight: 700; }
|
|
160
|
+
.step-active .step-bar { background: #7C3AED; }
|
|
161
|
+
.step-done .step-bar { background: #10B981; }
|
|
162
|
+
.step-pending .step-bar { background: #E5E7EB; }
|
|
163
|
+
.step-pending .step-label { opacity: 0.5; }
|
|
164
|
+
|
|
165
|
+
/* âââââââ Progress Bar âââââââ */
|
|
166
|
+
.stProgress > div > div {
|
|
167
|
+
background: linear-gradient(90deg, #7C3AED, #A78BFA) !important;
|
|
168
|
+
border-radius: 4px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* âââââââ Tabs âââââââ */
|
|
172
|
+
.stTabs [data-baseweb="tab-list"] {
|
|
173
|
+
gap: 2rem;
|
|
174
|
+
border-bottom: 2px solid #F0F0F5;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.stTabs [data-baseweb="tab"] {
|
|
178
|
+
font-weight: 500;
|
|
179
|
+
color: #6B7280;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.stTabs [aria-selected="true"] {
|
|
183
|
+
color: #7C3AED !important;
|
|
184
|
+
font-weight: 600;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* âââââââ Info Bar (bottom) âââââââ */
|
|
188
|
+
.info-bar {
|
|
189
|
+
background: #F5F3FF;
|
|
190
|
+
border: 1px solid #EDE9FE;
|
|
191
|
+
border-radius: 8px;
|
|
192
|
+
padding: 0.6rem 1.2rem;
|
|
193
|
+
text-align: center;
|
|
194
|
+
font-size: 0.8rem;
|
|
195
|
+
margin-top: 1.5rem;
|
|
196
|
+
color: #4C1D95;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.info-bar b {
|
|
200
|
+
color: #1A1A2E;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* âââââââ File Uploader âââââââ */
|
|
204
|
+
[data-testid="stFileUploader"] section {
|
|
205
|
+
border-radius: 12px;
|
|
206
|
+
border: 2px dashed #D4D4D8 !important;
|
|
207
|
+
transition: border-color 0.2s;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
[data-testid="stFileUploader"] section:hover {
|
|
211
|
+
border-color: #7C3AED !important;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* âââââââ Sliders âââââââ */
|
|
215
|
+
.stSlider > div > div > div > div {
|
|
216
|
+
background: #7C3AED !important;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* âââââââ Selectbox âââââââ */
|
|
220
|
+
.main .stSelectbox > div > div {
|
|
221
|
+
border-radius: 8px;
|
|
222
|
+
border-color: #E8E8EF;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* âââââââ Dataframe âââââââ */
|
|
226
|
+
.stDataFrame {
|
|
227
|
+
border-radius: 8px;
|
|
228
|
+
overflow: hidden;
|
|
229
|
+
border: 1px solid #E8E8EF;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* âââââââ Expander âââââââ */
|
|
233
|
+
.streamlit-expanderHeader {
|
|
234
|
+
font-weight: 600;
|
|
235
|
+
color: #1A1A2E;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* âââââââ Download Buttons âââââââ */
|
|
239
|
+
.stDownloadButton > button[kind="primary"] {
|
|
240
|
+
background: #7C3AED !important;
|
|
241
|
+
color: white !important;
|
|
242
|
+
border-radius: 8px;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.stDownloadButton > button {
|
|
246
|
+
border-radius: 8px;
|
|
247
|
+
border: 1px solid #E8E8EF;
|
|
248
|
+
}
|
|
249
|
+
</style>
|
|
250
|
+
""", unsafe_allow_html=True)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def render_info_bar(items: dict):
|
|
254
|
+
"""Render a professional info bar at the bottom (like mockup)."""
|
|
255
|
+
inner = " âĸ ".join(f"{k}: <b>{v}</b>" for k, v in items.items())
|
|
256
|
+
st.markdown(
|
|
257
|
+
f'<div class="info-bar">âšī¸ {inner}</div>',
|
|
258
|
+
unsafe_allow_html=True,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def render_page_header(icon: str, title: str, subtitle: str):
|
|
263
|
+
"""Render a professional page header with icon and subtitle."""
|
|
264
|
+
st.markdown(
|
|
265
|
+
f"""<div style="margin-bottom: 1.5rem;">
|
|
266
|
+
<h1 style="margin:0; display:flex; align-items:center; gap:0.6rem;
|
|
267
|
+
font-size:1.8rem; font-weight:700; color:#1A1A2E;">
|
|
268
|
+
<span style="font-size:2rem;">{icon}</span> {title}
|
|
269
|
+
</h1>
|
|
270
|
+
<p style="margin:0.4rem 0 0; color:#6B7280; font-size:0.95rem;">{subtitle}</p>
|
|
271
|
+
</div>""",
|
|
272
|
+
unsafe_allow_html=True,
|
|
273
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Step indicator and navigation wizard component."""
|
|
2
|
+
|
|
3
|
+
import streamlit as st
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def render_step_indicator(steps: list, current_step: int):
|
|
7
|
+
"""Render horizontal step progress indicator."""
|
|
8
|
+
items_html = ""
|
|
9
|
+
for i, step in enumerate(steps):
|
|
10
|
+
if i < current_step:
|
|
11
|
+
state_class = "step-done"
|
|
12
|
+
icon = "â
"
|
|
13
|
+
elif i == current_step:
|
|
14
|
+
state_class = "step-active"
|
|
15
|
+
icon = step["icon"]
|
|
16
|
+
else:
|
|
17
|
+
state_class = "step-pending"
|
|
18
|
+
icon = step["icon"]
|
|
19
|
+
|
|
20
|
+
items_html += f"""
|
|
21
|
+
<div class="step-item {state_class}">
|
|
22
|
+
<span class="step-icon">{icon}</span>
|
|
23
|
+
<div class="step-label">{step['label']}</div>
|
|
24
|
+
<div class="step-bar"></div>
|
|
25
|
+
</div>"""
|
|
26
|
+
|
|
27
|
+
st.markdown(f'<div class="step-indicator">{items_html}</div>', unsafe_allow_html=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def render_navigation(steps: list, current_step: int):
|
|
31
|
+
"""Render back/next navigation buttons."""
|
|
32
|
+
col_back, col_spacer, col_next = st.columns([1, 3, 1])
|
|
33
|
+
|
|
34
|
+
with col_back:
|
|
35
|
+
if current_step > 0:
|
|
36
|
+
prev_label = f"â {steps[current_step - 1]['label']}"
|
|
37
|
+
if st.button(prev_label, key="wizard_back", use_container_width=True):
|
|
38
|
+
st.session_state["current_step"] = current_step - 1
|
|
39
|
+
st.rerun()
|
|
40
|
+
|
|
41
|
+
with col_next:
|
|
42
|
+
if current_step < len(steps) - 1:
|
|
43
|
+
next_label = f"{steps[current_step + 1]['label']} â"
|
|
44
|
+
if st.button(next_label, key="wizard_next", use_container_width=True, type="primary"):
|
|
45
|
+
st.session_state["current_step"] = current_step + 1
|
|
46
|
+
st.rerun()
|