From ea9a29d5319e1972898312d439853807902b8add Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 09:35:59 +0000 Subject: [PATCH 1/2] Initial plan From 9e0161e9ecbc5466f9ce3ead988f1a3dc9bc0534 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 3 Jan 2026 09:47:19 +0000 Subject: [PATCH 2/2] feat: Modernize UI with gradient themes, glassmorphism cards, and professional design Co-authored-by: EdgeTypE <34396598+EdgeTypE@users.noreply.github.com> --- app.py | 470 +++++++++++++-- patternanalyzer/static_ui/index.html | 846 +++++++++++++++++++++------ patternanalyzer/tui.py | 198 ++++++- 3 files changed, 1282 insertions(+), 232 deletions(-) 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']}

+
+

🔬 {lang['main_title']}

+

{lang['main_desc']}

+
""", unsafe_allow_html=True) - st.write(lang['main_desc']) + st.markdown(f""" -

{lang['results_title']}

+
+ 📊 {lang['results_title']} +
""", unsafe_allow_html=True) - # Sidebar + # Sidebar with modern styling with st.sidebar: + # Sidebar header with logo + st.markdown(""" +
+
🔬
+
Pattern Analyzer
+
+ """, 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""" +
+ 📈 {lang['scorecard']} +
+ """, 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""" +
+ 🔍 {lang['findings']} +
+ """, 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""" +
+ 🎯 {lang['select_result']} +
+ """, 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""" +
+ 📋 {lang['selected_details']} +
+ """, 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""" +
+ 🖼️ {lang['visuals']} +
+ """, 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'{vname}', unsafe_allow_html=True) + # Display SVG using markdown with modern styling + st.markdown(f''' +
+ {vname} +

{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ı.
-
-
+ +
+ +

Pattern Analyzer

+

Binary veri analizi için güçlü istatistiksel testler. Dosya yükleyin veya base64 verisi yapıştırın ve kapsamlı bir analiz raporu elde edin.

+
+ + +
+
+ 📁 + Veri Girişi +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ + +
+
+ 📊 + Analiz Sonuçları +
+ +
+
+
📋
+

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",