diff --git a/.claude/HISTORY.md b/.claude/HISTORY.md index 30a89475..95a99372 100644 --- a/.claude/HISTORY.md +++ b/.claude/HISTORY.md @@ -1,6 +1,6 @@ # SecuBox UI & Theme History -_Last updated: 2026-02-07_ +_Last updated: 2026-02-08_ 1. **Unified Dashboard Refresh (2025-12-20)** - Dashboard received the "sh-page-header" layout, hero stats, and SecuNav top tabs. @@ -990,3 +990,24 @@ _Last updated: 2026-02-07_ 2. Host: `./secubox-clone-station.sh clone` (detects, pulls, flashes target) 3. Target boots, resizes root, auto-joins mesh with pre-approved token - Part of v0.19 mesh deployment automation. + +29. **Evolution Dashboard Real-Time Commits (2026-02-08)** + - Enhanced `secubox-app-streamlit-evolution` with live GitHub commits display. + - New "🚀 Devel" tab (first tab) showing real-time development activity: + - Commits Today / This Week / Contributors / Stars metrics + - Commit type distribution (feat/fix/docs/refactor/chore) + - Recent commits list with: + - Short hash (7 chars) with link to GitHub + - Commit message (80 char truncated) + - Author name + - Relative time (e.g., "2h ago", "just now") + - Commit type color-coding (green=feat, red=fix, orange=docs, purple=refactor) + - Repository stats (forks, watchers, open issues) + - GitHub API integration: + - `fetch_commits(limit=30)` with 1-minute cache TTL for near real-time updates + - `fetch_repo_info()` for repository statistics + - `parse_commit_type()` for conventional commit parsing + - `format_time_ago()` for human-readable timestamps + - `get_commit_stats()` for daily/weekly aggregation + - Cyberpunk theme styling for commits (matching existing dashboard theme) + - Live indicator animation (pulsing green dot) diff --git a/.claude/WIP.md b/.claude/WIP.md index a214175f..4dd42799 100644 --- a/.claude/WIP.md +++ b/.claude/WIP.md @@ -1,6 +1,6 @@ # Work In Progress (Claude) -_Last updated: 2026-02-07_ +_Last updated: 2026-02-08_ > **Architecture Reference**: SecuBox Fanzine v3 — Les 4 Couches @@ -53,6 +53,14 @@ _Last updated: 2026-02-07_ ### Just Completed (2026-02-06/08) +- **Evolution Dashboard Real-Time Commits** — DONE (2026-02-08) + - New "🚀 Devel" tab with live GitHub commits (1-min cache) + - Commits Today / This Week / Contributors / Stars metrics + - Commit type distribution with color-coding (feat/fix/docs/refactor) + - Recent commits with hash, message, author, relative time + - Repository stats (forks, watchers, open issues) + - Cyberpunk-themed commit cards with pulsing live indicator + - **Station Cloner/Deployer** — DONE (2026-02-08) - Host-side `secubox-clone-station.sh` with MOKATOOL integration for dual USB serial control - On-device `secubox-cloner` CLI for build/serve/token/export diff --git a/package/secubox/secubox-app-streamlit-evolution/files/secubox_evolution.py b/package/secubox/secubox-app-streamlit-evolution/files/secubox_evolution.py index 2f28a0f6..e659b2dc 100644 --- a/package/secubox/secubox-app-streamlit-evolution/files/secubox_evolution.py +++ b/package/secubox/secubox-app-streamlit-evolution/files/secubox_evolution.py @@ -2,13 +2,15 @@ """ SecuBox Evolution Dashboard Interactive Streamlit landing page showing project evolution, history, WIP, TODO, and README +Real-time GitHub commits integration for development status tracking """ import streamlit as st import requests import re -from datetime import datetime +from datetime import datetime, timedelta from collections import Counter +import time # Page config st.set_page_config( @@ -161,11 +163,98 @@ st.markdown(""" background: #00d4aa; border-radius: 50%; } + + .commit-card { + background: #12121a; + border: 1px solid #2a2a3a; + border-radius: 8px; + padding: 1rem; + margin: 0.5rem 0; + transition: all 0.3s ease; + } + + .commit-card:hover { + border-color: #00a0ff; + transform: translateX(4px); + } + + .commit-hash { + font-family: 'JetBrains Mono', monospace; + color: #00a0ff; + font-size: 0.85rem; + background: #00a0ff22; + padding: 0.2rem 0.5rem; + border-radius: 4px; + display: inline-block; + } + + .commit-message { + color: #e0e0e0; + font-weight: 500; + margin: 0.5rem 0; + word-break: break-word; + } + + .commit-meta { + color: #808090; + font-size: 0.8rem; + } + + .commit-author { + color: #00d4aa; + } + + .commit-time { + color: #ffa500; + } + + .live-indicator { + display: inline-flex; + align-items: center; + gap: 0.5rem; + color: #00d4aa; + font-size: 0.85rem; + } + + .live-dot { + width: 8px; + height: 8px; + background: #00d4aa; + border-radius: 50%; + animation: pulse 2s infinite; + } + + @keyframes pulse { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.5; transform: scale(1.2); } + } + + .devel-status { + background: linear-gradient(135deg, #12121a 0%, #1a1a2e 100%); + border: 1px solid #2a2a3a; + border-radius: 12px; + padding: 1.5rem; + margin-bottom: 1rem; + } + + .devel-title { + color: #00a0ff; + font-size: 1.2rem; + font-weight: 600; + margin-bottom: 0.5rem; + } + + .commit-type-feat { border-left: 3px solid #00d4aa; } + .commit-type-fix { border-left: 3px solid #ff6b6b; } + .commit-type-docs { border-left: 3px solid #ffa500; } + .commit-type-refactor { border-left: 3px solid #a855f7; } + .commit-type-chore { border-left: 3px solid #808090; } """, unsafe_allow_html=True) # GitHub raw URLs GITHUB_BASE = "https://raw.githubusercontent.com/gkerma/secubox-openwrt/master" +GITHUB_API = "https://api.github.com/repos/gkerma/secubox-openwrt" FILES = { "HISTORY": f"{GITHUB_BASE}/.claude/HISTORY.md", "WIP": f"{GITHUB_BASE}/.claude/WIP.md", @@ -184,6 +273,110 @@ def fetch_file(url): except: return None +@st.cache_data(ttl=60) # 1-minute cache for near real-time updates +def fetch_commits(limit=30): + """Fetch recent commits from GitHub API""" + try: + response = requests.get( + f"{GITHUB_API}/commits", + params={"per_page": limit}, + headers={"Accept": "application/vnd.github.v3+json"}, + timeout=10 + ) + if response.status_code == 200: + return response.json() + return [] + except: + return [] + +@st.cache_data(ttl=60) +def fetch_repo_info(): + """Fetch repository information""" + try: + response = requests.get( + GITHUB_API, + headers={"Accept": "application/vnd.github.v3+json"}, + timeout=10 + ) + if response.status_code == 200: + return response.json() + return {} + except: + return {} + +def parse_commit_type(message): + """Extract commit type from conventional commit message""" + patterns = { + 'feat': r'^feat(\([^)]+\))?:', + 'fix': r'^fix(\([^)]+\))?:', + 'docs': r'^docs(\([^)]+\))?:', + 'refactor': r'^refactor(\([^)]+\))?:', + 'chore': r'^chore(\([^)]+\))?:', + 'test': r'^test(\([^)]+\))?:', + 'style': r'^style(\([^)]+\))?:', + 'perf': r'^perf(\([^)]+\))?:', + } + for ctype, pattern in patterns.items(): + if re.match(pattern, message, re.I): + return ctype + return 'other' + +def format_time_ago(iso_date): + """Convert ISO date to human-readable 'time ago' format""" + try: + dt = datetime.fromisoformat(iso_date.replace('Z', '+00:00')) + now = datetime.now(dt.tzinfo) + diff = now - dt + + if diff.days > 30: + return dt.strftime("%b %d, %Y") + elif diff.days > 0: + return f"{diff.days}d ago" + elif diff.seconds > 3600: + return f"{diff.seconds // 3600}h ago" + elif diff.seconds > 60: + return f"{diff.seconds // 60}m ago" + else: + return "just now" + except: + return iso_date[:10] + +def get_commit_stats(commits): + """Calculate commit statistics""" + if not commits: + return {} + + stats = { + 'total': len(commits), + 'types': Counter(), + 'authors': Counter(), + 'today': 0, + 'this_week': 0, + } + + now = datetime.now() + for c in commits: + commit = c.get('commit', {}) + message = commit.get('message', '').split('\n')[0] + stats['types'][parse_commit_type(message)] += 1 + + author = commit.get('author', {}).get('name', 'Unknown') + stats['authors'][author] += 1 + + try: + date_str = commit.get('author', {}).get('date', '') + if date_str: + dt = datetime.fromisoformat(date_str.replace('Z', '+00:00')) + days_ago = (now - dt.replace(tzinfo=None)).days + if days_ago == 0: + stats['today'] += 1 + if days_ago < 7: + stats['this_week'] += 1 + except: + pass + + return stats + def parse_history(content): """Parse HISTORY.md to extract milestones""" if not content: @@ -249,7 +442,7 @@ def count_features(content): def main(): # Header st.markdown('
Real-time project tracking • History • WIP • TODO • Documentation
', unsafe_allow_html=True) + st.markdown('Live GitHub commits • History • WIP • TODO • Documentation
', unsafe_allow_html=True) # Fetch all files with st.spinner("Fetching latest data from GitHub..."): @@ -328,14 +521,156 @@ def main(): st.markdown("[GitHub Repository](https://github.com/gkerma/secubox-openwrt)") st.markdown("[SecuBox Portal](https://secubox.in)") + st.markdown("---") + st.markdown("### 🚀 Devel Status") + st.markdown(' Live', unsafe_allow_html=True) + if st.button("🔄 Refresh Data"): st.cache_data.clear() st.rerun() + # Fetch commits for devel status + commits = fetch_commits(30) + commit_stats = get_commit_stats(commits) + repo_info = fetch_repo_info() + # Main tabs - tab1, tab2, tab3, tab4, tab5 = st.tabs(["📜 History", "🔧 WIP", "📋 TODO", "📖 README", "📈 Timeline"]) + tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs(["🚀 Devel", "📜 History", "🔧 WIP", "📋 TODO", "📖 README", "📈 Timeline"]) with tab1: + st.markdown("## 🚀 Development Status") + + # Live indicator + st.markdown(""" +