diff --git a/app.py b/app.py
index ddecfbe..3fb71ca 100644
--- a/app.py
+++ b/app.py
@@ -4,7 +4,297 @@
import json
from patternanalyzer.engine import Engine
-st.set_page_config(page_title="Pattern Analyzer Analizi", layout="wide", page_icon="🔬")
+st.set_page_config(page_title="Pattern Analyzer", layout="wide", page_icon="🔬")
+
+# Modern Custom CSS Theme
+st.markdown("""
+
+""", unsafe_allow_html=True)
engine = Engine()
@@ -270,27 +560,44 @@ def main():
}
}[st.session_state.language]
- # Main content
+ # Main content with modern header
st.markdown(f"""
-
+
+
🔬 {lang['main_title']}
+
+
""", unsafe_allow_html=True)
- st.write(lang['main_desc'])
+
st.markdown(f"""
- {lang['results_title']}
+
""", unsafe_allow_html=True)
- # Sidebar
+ # Sidebar with modern styling
with st.sidebar:
+ # Sidebar header with logo
+ st.markdown("""
+
+ """, unsafe_allow_html=True)
+
# Language selector
- selected_lang = st.selectbox(lang['language'], options=["tr", "en"], format_func=lambda x: "Türkçe" if x == "tr" else "English", index=0 if st.session_state.language == "tr" else 1)
+ selected_lang = st.selectbox(f"🌐 {lang['language']}", options=["tr", "en"], format_func=lambda x: "🇹🇷 Türkçe" if x == "tr" else "🇬🇧 English", index=0 if st.session_state.language == "tr" else 1)
if selected_lang != st.session_state.language:
st.session_state.language = selected_lang
st.rerun()
- st.header(lang['control_panel'])
- # st.divider()
+ st.markdown(f"""
+
+ ⚙️ {lang['control_panel']}
+
+ """, unsafe_allow_html=True)
+
# Tabs for input
- tab1, tab2 = st.tabs([lang['file_tab'], lang['text_tab']])
+ tab1, tab2 = st.tabs([f"📁 {lang['file_tab']}", f"📝 {lang['text_tab']}"])
with tab1:
uploaded_file = st.file_uploader(
@@ -306,7 +613,11 @@ def main():
height=100
)
- st.subheader(lang['test_selection'])
+ st.markdown(f"""
+
+ 🧪 {lang['test_selection']}
+
+ """, unsafe_allow_html=True)
available_tests = engine.get_available_tests()
default_tests = ["monobit", "approximate_entropy", "autocorrelation"] # From HTML
@@ -321,22 +632,26 @@ def main():
)
# Test açıklamaları için expander
- with st.expander("Test Açıklamaları" if st.session_state.language == "tr" else "Test Explanations"):
+ with st.expander("📖 " + ("Test Açıklamaları" if st.session_state.language == "tr" else "Test Explanations")):
for test in available_tests:
desc = lang['test_explanations'].get(test, "Açıklama yok." if st.session_state.language == "tr" else "No description.")
- st.write(f"**{test}**: {desc}")
+ st.markdown(f"**`{test}`**: {desc}")
col1, col2 = st.columns(2)
with col1:
- if st.button(lang['all_tests']):
+ if st.button(f"✅ {lang['all_tests']}", use_container_width=True):
st.session_state.selected_tests = available_tests
st.rerun()
with col2:
- if st.button(lang['no_tests']):
+ if st.button(f"❌ {lang['no_tests']}", use_container_width=True):
st.session_state.selected_tests = []
st.rerun()
- st.subheader(lang['transform_selection'])
+ st.markdown(f"""
+
+ 🔄 {lang['transform_selection']}
+
+ """, unsafe_allow_html=True)
available_transforms = engine.get_available_transforms()
if 'selected_transforms' not in st.session_state:
@@ -351,15 +666,19 @@ def main():
col3, col4 = st.columns(2)
with col3:
- if st.button(lang['all_transforms']):
+ if st.button(f"✅ {lang['all_transforms']}", use_container_width=True):
st.session_state.selected_transforms = available_transforms
st.rerun()
with col4:
- if st.button(lang['no_transforms']):
+ if st.button(f"❌ {lang['no_transforms']}", use_container_width=True):
st.session_state.selected_transforms = []
st.rerun()
- st.subheader(lang['analysis_settings'])
+ st.markdown(f"""
+
+ ⚡ {lang['analysis_settings']}
+
+ """, unsafe_allow_html=True)
fdr_q = st.slider(
lang['fdr_label'],
min_value=0.01,
@@ -370,11 +689,12 @@ def main():
help=lang.get('fdr_help', '')
)
+ st.markdown("", unsafe_allow_html=True)
col5, col6 = st.columns(2)
with col5:
- start_button = st.button(lang['start_analysis'], type="primary")
+ start_button = st.button(f"🚀 {lang['start_analysis']}", type="primary", use_container_width=True)
with col6:
- clear_button = st.button(lang['clear'], type="secondary")
+ clear_button = st.button(f"🗑️ {lang['clear']}", type="secondary", use_container_width=True)
# Handle buttons
if clear_button:
@@ -410,7 +730,7 @@ def main():
if 'analysis_result' in st.session_state:
result = st.session_state['analysis_result']
if isinstance(result, dict) and 'error' in result:
- st.error(result['error'])
+ st.error(f"❌ {result['error']}")
else:
# Compute additional stats
results = result.get('results', []) if isinstance(result, dict) else []
@@ -418,21 +738,63 @@ def main():
run_tests = sum(1 for r in results if r.get('status') != 'skipped')
skipped_tests = total_tests - run_tests
failed_tests = sum(1 for r in results if not r.get('passed', True) and r.get('status') != 'skipped')
+ passed_tests = run_tests - failed_tests
- # scorecard'ı st.metric ile göster
+ # Modern scorecard with cards
scorecard = result.get('scorecard', {}) if isinstance(result, dict) else {}
- if scorecard:
- st.subheader(lang['scorecard'])
- # Custom metrics
+ if scorecard or results:
+ st.markdown(f"""
+
+ """, unsafe_allow_html=True)
+
+ # Modern metric cards
cols = st.columns(5)
- cols[0].metric(lang['failed_tests'], f"{failed_tests} / {total_tests}")
- cols[1].metric(lang['mean_effect_size'], format_val(scorecard.get('mean_effect_size', 'None')), help=lang.get('mean_effect_size_desc', ''))
- cols[2].metric(lang['p_value_distribution'], format_val(scorecard.get('p_value_distribution', {}), max_len=40), help=lang.get('p_value_distribution_desc', ''))
- cols[3].metric(lang['run_tests'], run_tests)
- cols[4].metric(lang['skipped_tests'], skipped_tests, help=lang.get('skipped_tests_desc', ''))
+ with cols[0]:
+ st.markdown(f"""
+
+
{passed_tests}/{total_tests}
+
✅ {"Başarılı" if st.session_state.language == "tr" else "Passed"}
+
+ """, unsafe_allow_html=True)
+ with cols[1]:
+ st.markdown(f"""
+
+
{failed_tests}
+
❌ {lang['failed_tests']}
+
+ """, unsafe_allow_html=True)
+ with cols[2]:
+ effect_size_val = format_val(scorecard.get('mean_effect_size', 'N/A'))
+ st.markdown(f"""
+
+
{effect_size_val}
+
📊 {lang['mean_effect_size']}
+
+ """, unsafe_allow_html=True)
+ with cols[3]:
+ st.markdown(f"""
+
+
{run_tests}
+
🔬 {lang['run_tests']}
+
+ """, unsafe_allow_html=True)
+ with cols[4]:
+ st.markdown(f"""
+
+
{skipped_tests}
+
⏭️ {lang['skipped_tests']}
+
+ """, unsafe_allow_html=True)
if results:
- st.subheader(lang['findings'])
+ st.markdown(f"""
+
+ """, unsafe_allow_html=True)
+
df = pd.DataFrame(results)
# Reindex to include all possible columns
expected_columns = [
@@ -447,42 +809,61 @@ def main():
if 'p_value' in df.columns:
def _p_style(v):
try:
- return 'background-color: red' if float(v) < fdr_q else ''
+ return 'background-color: rgba(238, 9, 121, 0.3); color: #ff6a00;' if float(v) < fdr_q else 'background-color: rgba(17, 153, 142, 0.2); color: #38ef7d;'
except Exception:
return ''
styled = df.style.map(_p_style, subset=['p_value'])
st.dataframe(styled, column_config={
col: st.column_config.TextColumn(help=lang['column_explanations'].get(col, '')) for col in expected_columns
- })
+ }, use_container_width=True)
else:
st.dataframe(df, column_config={
col: st.column_config.TextColumn(help=lang['column_explanations'].get(col, '')) for col in expected_columns
- })
+ }, use_container_width=True)
# Select a result for details
+ st.markdown(f"""
+
+ """, unsafe_allow_html=True)
option_labels = [f"{i} - {r.get('test_name', 'Unknown')}" for i, r in enumerate(results)]
- selected_label = st.selectbox(lang['select_result'], options=option_labels)
+ selected_label = st.selectbox("", options=option_labels, label_visibility="collapsed")
if selected_label:
selected_idx = int(selected_label.split(" - ")[0])
selected_result = results[selected_idx]
- st.subheader(lang['selected_details'])
+
+ # Modern card for selected result
+ st.markdown(f"""
+
+ """, unsafe_allow_html=True)
+
+ # Display result in a modern card
+ st.markdown('', unsafe_allow_html=True)
st.json(selected_result)
+ st.markdown('
', unsafe_allow_html=True)
# Test-specific explanation
test_name = selected_result.get('test_name')
desc = lang['test_explanations'].get(test_name, "Açıklama yok." if st.session_state.language == "tr" else "No description.")
- st.write(f"**Test Açıklaması**: {desc}")
+ st.info(f"💡 **{'Test Açıklaması' if st.session_state.language == 'tr' else 'Test Description'}**: {desc}")
# If skipped or error, show reason
status = selected_result.get('status')
if status == 'skipped' or status == 'error':
reason = selected_result.get('reason', 'Bilinmeyen neden' if st.session_state.language == "tr" else 'Unknown reason')
- st.warning(f"Bu test {status} oldu. Neden: {reason}")
+ st.warning(f"⚠️ {'Bu test' if st.session_state.language == 'tr' else 'This test'} {status} {'oldu. Neden' if st.session_state.language == 'tr' else '. Reason'}: {reason}")
# Visuals if any
visuals = selected_result.get('visuals', {})
if visuals:
- st.subheader(lang['visuals'])
+ st.markdown(f"""
+
+ """, unsafe_allow_html=True)
for vname, vdata in visuals.items():
if isinstance(vdata, dict):
if 'data_base64' in vdata:
@@ -490,8 +871,13 @@ def _p_style(v):
mime = vdata.get('mime', 'image/svg+xml')
base64_data = vdata['data_base64']
if mime == 'image/svg+xml':
- # Display SVG using markdown
- st.markdown(f'
', unsafe_allow_html=True)
+ # Display SVG using markdown with modern styling
+ st.markdown(f'''
+
+

+
{vname}
+
+ ''', unsafe_allow_html=True)
else:
img_data = base64.b64decode(base64_data)
st.image(img_data, caption=vname, use_container_width=True)
diff --git a/patternanalyzer/static_ui/index.html b/patternanalyzer/static_ui/index.html
index 5c5b1a8..ba73e9c 100644
--- a/patternanalyzer/static_ui/index.html
+++ b/patternanalyzer/static_ui/index.html
@@ -3,63 +3,551 @@
-Pattern Analyzer UI
+Pattern Analyzer
+
+
+
-
Pattern Analyzer - Basit Web UI
-
Kısa: dosya yükleyin veya base64 verisi yapıştırın, ardından "Analyze" tuşuna basın. Arka planda çalışan job durumunu göstereceğiz.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Sonuç
-
-
Henüz analiz yapılmadı.
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
📋
+
Henüz analiz yapılmadı. Yukarıdan dosya yükleyin veya veri girin.
+
+
+
+
+
+
+
+
+
diff --git a/patternanalyzer/tui.py b/patternanalyzer/tui.py
index b1334e0..37f6a06 100644
--- a/patternanalyzer/tui.py
+++ b/patternanalyzer/tui.py
@@ -37,15 +37,181 @@ class PatternAnalyzerTUI(App):
"""
CSS = """
- #body { height: 1fr; }
- #left { width: 50%; min-width: 30; }
- #right { width: 50%; padding: 1 1; }
- #tests_scroll { height: 1fr; border: round $accent; padding: 1; }
- #controls { padding-top: 1; }
- #loading { padding-left: 1; }
- #scorecard { border: round $accent; padding: 1; height: auto; max-height: 10; overflow: auto; }
- #results_table { height: 1fr; border: round $accent; padding: 1; }
- #results_scroll { height: 10; border: round $accent; padding: 1; }
+ /* Modern Dark Theme for Pattern Analyzer TUI */
+ Screen {
+ background: #0f0f23;
+ }
+
+ Header {
+ background: #1a1a2e;
+ color: #667eea;
+ text-style: bold;
+ }
+
+ Footer {
+ background: #1a1a2e;
+ color: #667eea;
+ }
+
+ #body {
+ height: 1fr;
+ background: #0f0f23;
+ }
+
+ #left {
+ width: 45%;
+ min-width: 30;
+ background: #16213e;
+ border: solid #667eea;
+ border-title-color: #667eea;
+ padding: 1;
+ margin: 1;
+ }
+
+ #right {
+ width: 55%;
+ padding: 1;
+ margin: 1;
+ background: #16213e;
+ border: solid #667eea;
+ }
+
+ #file_label, #tests_label, #score_label, #results_label, #results_list_label {
+ color: #667eea;
+ text-style: bold;
+ padding: 0 0 1 0;
+ }
+
+ #tests_scroll {
+ height: 1fr;
+ border: round #764ba2;
+ padding: 1;
+ background: #1a1a2e;
+ }
+
+ #controls {
+ padding-top: 1;
+ dock: bottom;
+ }
+
+ #loading {
+ padding-left: 1;
+ color: #38ef7d;
+ }
+
+ #scorecard {
+ border: round #38ef7d;
+ padding: 1;
+ height: auto;
+ max-height: 10;
+ overflow: auto;
+ background: #1a1a2e;
+ color: #e6eef6;
+ }
+
+ #results_table {
+ height: 1fr;
+ border: round #764ba2;
+ padding: 1;
+ background: #0b1220;
+ }
+
+ #results_scroll {
+ height: 10;
+ border: round #764ba2;
+ padding: 1;
+ background: #1a1a2e;
+ }
+
+ #status {
+ color: #38ef7d;
+ padding: 1 0;
+ }
+
+ Button {
+ background: #667eea;
+ color: white;
+ border: none;
+ margin: 0 1;
+ }
+
+ Button:hover {
+ background: #764ba2;
+ }
+
+ Button#start_btn {
+ background: #11998e;
+ }
+
+ Button#start_btn:hover {
+ background: #38ef7d;
+ color: #0f0f23;
+ }
+
+ Button#exit_btn {
+ background: #ee0979;
+ }
+
+ Button#exit_btn:hover {
+ background: #ff6a00;
+ }
+
+ Checkbox {
+ background: transparent;
+ padding: 0 1;
+ }
+
+ Checkbox:focus {
+ background: #667eea 20%;
+ }
+
+ Checkbox.-on {
+ color: #38ef7d;
+ }
+
+ DirectoryTree {
+ background: #0b1220;
+ padding: 1;
+ }
+
+ DirectoryTree:focus {
+ border: solid #667eea;
+ }
+
+ DataTable {
+ background: #0b1220;
+ }
+
+ DataTable > .datatable--header {
+ background: #667eea;
+ color: white;
+ text-style: bold;
+ }
+
+ DataTable > .datatable--cursor {
+ background: #764ba2;
+ }
+
+ /* Modal styling */
+ #modal_title {
+ background: #667eea;
+ color: white;
+ text-style: bold;
+ padding: 1;
+ text-align: center;
+ }
+
+ #modal_metrics {
+ background: #0b1220;
+ color: #e6eef6;
+ padding: 1;
+ border: solid #764ba2;
+ }
+
+ #modal_close {
+ background: #667eea;
+ margin: 1;
+ }
"""
def compose(self) -> ComposeResult:
@@ -55,24 +221,24 @@ def compose(self) -> ComposeResult:
with Horizontal():
# Left: file tree / selector
yield Vertical(
- Static("File Selection", id="file_label"),
+ Static("📁 File Selection", id="file_label"),
DirectoryTree(".", id="file_tree"),
id="left",
)
# Right: test list, results, and control buttons
yield Vertical(
- Static("Available Tests (Select with Checkboxes)", id="tests_label"),
+ Static("🧪 Available Tests", id="tests_label"),
ScrollView(id="tests_scroll"),
- Static("Scorecard", id="score_label"),
+ Static("📊 Scorecard", id="score_label"),
Static("", id="scorecard", expand=False),
- Static("Results (Table)", id="results_label"),
+ Static("📋 Results", id="results_label"),
DataTable(id="results_table"),
- Static("Clickable Results List", id="results_list_label"),
+ Static("🔍 Details", id="results_list_label"),
ScrollView(id="results_scroll"),
Static("", id="status", expand=False), # For status messages
Horizontal(
- Button("Start", id="start_btn", variant="success"),
- Button("Exit", id="exit_btn", variant="error"),
+ Button("🚀 Start Analysis", id="start_btn", variant="success"),
+ Button("🚪 Exit", id="exit_btn", variant="error"),
id="controls",
),
id="right",