diff --git a/api/templates/index.html b/api/templates/index.html index 7ba9837..0619954 100644 --- a/api/templates/index.html +++ b/api/templates/index.html @@ -873,80 +873,275 @@ letter-spacing: 0.3px; } + /* Replay badges (FLASH = SPIFFS, RAM = in-memory dump) */ .replay-badge { - background: #6366f1; + display: inline-flex; + align-items: center; + gap: 0.25rem; + background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); color: white; - padding: 0.15rem 0.4rem; + padding: 0.15rem 0.45rem; border-radius: 3px; font-size: 0.7rem; - font-weight: 600; + font-weight: 700; text-transform: uppercase; - letter-spacing: 0.3px; + letter-spacing: 0.5px; + box-shadow: 0 0 8px rgba(99, 102, 241, 0.4); + } + .replay-badge.live { + background: linear-gradient(135deg, #0284c7 0%, #0ea5e9 100%); + box-shadow: 0 0 8px rgba(14, 165, 233, 0.4); + } + .replay-badge svg { + width: 10px; + height: 10px; + stroke: currentColor; + fill: none; + stroke-width: 2.5; + stroke-linecap: round; + stroke-linejoin: round; } - .replay-badge.live { background: #0ea5e9; } - .device-extra-controls { + .detection-item.replay { + border-left: 3px solid #6366f1; + background: linear-gradient(90deg, rgba(99, 102, 241, 0.08) 0%, transparent 30%); + } + .detection-item.replay.live-source { + border-left-color: #0ea5e9; + background: linear-gradient(90deg, rgba(14, 165, 233, 0.08) 0%, transparent 30%); + } + + /* ===================================================== + Device command toolbar — sticky strip below header + ===================================================== */ + .flock-command-bar { + position: sticky; + top: 0; + z-index: 50; display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.55rem 1rem; + background: linear-gradient(180deg, rgba(20, 12, 40, 0.96) 0%, rgba(15, 9, 30, 0.96) 100%); + border-bottom: 1px solid rgba(139, 92, 246, 0.35); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35); + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); + color: #e2e8f0; + font-size: 0.82rem; + transform: translateY(-100%); + opacity: 0; + transition: transform 0.25s ease, opacity 0.25s ease; + pointer-events: none; + } + .flock-command-bar.show { + transform: translateY(0); + opacity: 1; + pointer-events: auto; + } + + .fcb-label { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.2rem 0.5rem; + font-family: 'Orbitron', 'Courier New', monospace; + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 1.5px; + color: #c4b5fd; + text-transform: uppercase; + white-space: nowrap; + } + .fcb-label svg { + width: 16px; + height: 16px; + stroke: #c4b5fd; + fill: none; + stroke-width: 2; + stroke-linecap: round; + stroke-linejoin: round; + } + + .fcb-section { + display: inline-flex; + align-items: center; gap: 0.4rem; - margin-top: 0.5rem; - flex-wrap: wrap; } - .flock-cmd-btn { - background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); - border-color: #4338ca; - font-size: 0.85rem; - padding: 0.4rem 0.7rem; + + .fcb-divider { + width: 1px; + align-self: stretch; + background: linear-gradient(180deg, transparent, rgba(139, 92, 246, 0.35), transparent); + margin: 0 0.25rem; } - .flock-cmd-btn:hover { - background: linear-gradient(135deg, #6366f1 0%, #818cf8 100%); + + .fcb-spacer { flex: 1; } + + .fcb-status-dot { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.25rem 0.55rem; + border-radius: 999px; + background: rgba(34, 197, 94, 0.12); + border: 1px solid rgba(34, 197, 94, 0.4); + font-size: 0.7rem; + font-weight: 600; + color: #4ade80; + text-transform: uppercase; + letter-spacing: 0.5px; } - .flock-cmd-btn:disabled { - background: #475569; - border-color: #334155; + .fcb-status-dot::before { + content: ""; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background: #22c55e; + box-shadow: 0 0 6px #22c55e; + animation: fcb-pulse 1.8s ease-in-out infinite; + } + @keyframes fcb-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.4; } + } + + .fcb-btn { + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.4rem 0.75rem; + border-radius: 6px; + border: 1px solid transparent; + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-size: 0.78rem; + font-weight: 600; + cursor: pointer; + transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease, opacity 0.15s ease; + color: white; + white-space: nowrap; + } + .fcb-btn svg { + width: 14px; + height: 14px; + stroke: currentColor; + fill: none; + stroke-width: 2; + stroke-linecap: round; + stroke-linejoin: round; + flex-shrink: 0; + } + .fcb-btn:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35); + } + .fcb-btn:active { transform: translateY(0); } + .fcb-btn:disabled { cursor: not-allowed; - opacity: 0.65; + opacity: 0.5; transform: none; box-shadow: none; } - .flock-cmd-btn.danger { - background: linear-gradient(135deg, #b91c1c 0%, #dc2626 100%); - border-color: #991b1b; + .fcb-btn .fcb-btn-count { + display: inline-block; + min-width: 1.6rem; + padding: 0 0.35rem; + margin-left: 0.1rem; + border-radius: 10px; + background: rgba(255, 255, 255, 0.2); + font-size: 0.7rem; + font-weight: 700; + text-align: center; } - .flock-cmd-btn.danger:hover { - background: linear-gradient(135deg, #dc2626 0%, #ef4444 100%); + /* Pull Prev — purple/indigo, matches FLASH badge */ + .fcb-btn.fcb-pull-prev { + background: linear-gradient(135deg, #4f46e5 0%, #6366f1 100%); + border-color: #4338ca; + } + .fcb-btn.fcb-pull-prev:hover { + background: linear-gradient(135deg, #6366f1 0%, #818cf8 100%); + } + /* Pull Live — cyan, matches RAM badge */ + .fcb-btn.fcb-pull-live { + background: linear-gradient(135deg, #0284c7 0%, #0ea5e9 100%); + border-color: #0369a1; + } + .fcb-btn.fcb-pull-live:hover { + background: linear-gradient(135deg, #0ea5e9 0%, #38bdf8 100%); + } + /* Status — slate (neutral info) */ + .fcb-btn.fcb-status { + background: linear-gradient(135deg, #475569 0%, #64748b 100%); + border-color: #334155; + } + .fcb-btn.fcb-status:hover { + background: linear-gradient(135deg, #64748b 0%, #94a3b8 100%); + } + /* Clear — red (destructive). Subtler shade than the connect/disconnect danger + so the destructive ops don't dominate the bar. */ + .fcb-btn.fcb-danger { + background: linear-gradient(135deg, #991b1b 0%, #b91c1c 100%); + border-color: #7f1d1d; + } + .fcb-btn.fcb-danger:hover { + background: linear-gradient(135deg, #b91c1c 0%, #dc2626 100%); } + /* Toast — fixed top-right notification stack */ #flockToast { position: fixed; top: 1rem; right: 1rem; min-width: 280px; max-width: 420px; - padding: 0.7rem 1rem; - border-radius: 6px; - background: #1e293b; + padding: 0.85rem 1rem 0.85rem 1.1rem; + border-radius: 8px; + background: rgba(15, 9, 30, 0.97); color: #e2e8f0; + border: 1px solid rgba(139, 92, 246, 0.35); border-left: 4px solid #6366f1; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); - font-size: 0.9rem; + box-shadow: 0 8px 28px rgba(0,0,0,0.45); + font-size: 0.88rem; + line-height: 1.4; z-index: 9999; opacity: 0; - transform: translateY(-10px); - transition: opacity 0.2s, transform 0.2s; + transform: translate(20px, -10px); + transition: opacity 0.25s ease, transform 0.25s ease; pointer-events: none; + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); } #flockToast.show { opacity: 1; - transform: translateY(0); + transform: translate(0, 0); + } + #flockToast.success { + border-left-color: #22c55e; + box-shadow: 0 8px 28px rgba(0,0,0,0.45), 0 0 18px rgba(34, 197, 94, 0.2); + } + #flockToast.warning { + border-left-color: #f59e0b; + box-shadow: 0 8px 28px rgba(0,0,0,0.45), 0 0 18px rgba(245, 158, 11, 0.2); + } + #flockToast.error { + border-left-color: #ef4444; + box-shadow: 0 8px 28px rgba(0,0,0,0.45), 0 0 18px rgba(239, 68, 68, 0.25); + } + #flockToast.info { + border-left-color: #6366f1; + box-shadow: 0 8px 28px rgba(0,0,0,0.45), 0 0 18px rgba(99, 102, 241, 0.2); } - #flockToast.success { border-left-color: #22c55e; } - #flockToast.warning { border-left-color: #f59e0b; } - #flockToast.error { border-left-color: #ef4444; } - #flockToast.info { border-left-color: #6366f1; } - .detection-item.replay { - border-left: 3px solid #6366f1; - background: rgba(99, 102, 241, 0.04); + @media (max-width: 800px) { + .flock-command-bar { + flex-wrap: wrap; + gap: 0.5rem; + padding: 0.5rem 0.75rem; + } + .fcb-divider, .fcb-spacer, .fcb-status-dot { display: none; } + .fcb-label { font-size: 0.65rem; } + .fcb-btn { font-size: 0.72rem; padding: 0.35rem 0.6rem; } } .detection-count { @@ -1376,13 +1571,6 @@ -