feat(ui): migrate to Tailwind (compiled) + Flowbite JS; new navbar/layout; Docker CSS build

- Add multi-stage CSS build that compiles Tailwind into app/static/tw.css
- Add Tailwind config with dark tokens (bg/nav/card) and purge globs
- Add assets/input.css (@tailwind base/components/utilities + small utilities)
- Replace Tailwind CDN + REMOVE Flowbite CSS (keep Flowbite JS only)
- New base_tailwind.html (top navbar, responsive container, {%- block scripts -%})
- Port pages to Tailwind look/feel with wider content column:
  - index: single-column form + recent results, fullscreen spinner overlay, copy-UUID
  - result: sticky jump list, Tailwind tables/badges, Suspicious Scripts/Forms sections
  - viewer: Monaco-based code viewer in Tailwind card, actions (copy/wrap/raw)
  - ssl_tls macro: rewritten with Tailwind (details/summary for raw JSON)
- Dockerfile: add css-builder stage and copy built tw.css into /app/app/static
- Remove Flowbite stylesheet to avoid overrides; Flowbite JS loaded with defer

BREAKING CHANGE:
Legacy CSS classes/components (.card, .badge, etc.) are replaced by Tailwind utilities.
All templates now expect tw.css to be served from /static.
This commit is contained in:
2025-08-22 10:36:10 -05:00
parent 965c953e00
commit 469334d137
12 changed files with 842 additions and 1109 deletions

View File

@@ -1,36 +1,76 @@
<!doctype html>
{# Base layout using Tailwind + Flowbite, non-destructive #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{ app_name }} {{ app_version }}</title>
<link rel="stylesheet" href="https://unpkg.com/sanitize.css" />
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
</head>
<body>
<header>
<h1>{{ app_name }} {{ app_version }}</h1>
</header>
<head>
<meta charset="utf-8" />
<title>{% block title %}{% endblock %} {{ app_name }} </title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul class="flash">
{% for category, message in messages %}
<li class="{{ category }}">{{ message }}</li>
{% endfor %}
<!-- # Tailwind CSS # -->
<link rel="stylesheet" href="{{ url_for('static', filename='tw.css') }}">
{# Your existing CSS stays; well keep only custom tweaks there. #}
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
{% block head %}{% endblock %}
</head>
<body class="bg-bg text-gray-200 min-h-screen flex flex-col">
{# Top Navbar (Flowbite collapse) #}
<nav class="bg-nav border-b border-gray-800">
<div class="max-w-7xl mx-auto px-4 py-3">
<div class="flex items-center justify-between">
<a href="{{ url_for('main.index') }}" class="text-xl font-bold text-white">
SneakyScope
</a>
{# Desktop nav #}
<ul class="hidden md:flex items-center space-x-6 text-sm">
<li>
<a href="{{ url_for('main.index') }}">
Home
</a>
</li>
</ul>
{% endif %}
{% endwith %}
<main>
{# Mobile toggle #}
<button data-collapse-toggle="main-menu" type="button"
class="md:hidden inline-flex items-center p-2 rounded hover:bg-gray-700"
aria-controls="main-menu" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 6h16M4 12h16M4 18h16"/>
</svg>
</button>
</div>
{# Mobile menu #}
<div class="hidden md:hidden" id="main-menu">
<ul class="mt-2 space-y-1 text-sm">
<li>
<a href="{{ url_for('main.index') }}">
Home
</a>
</li>
</ul>
</div>
</div>
</nav>
{# Page content wrapper #}
<main class="flex-1">
<div class="max-w-7xl mx-auto p-4 md:p-6">
{% block content %}{% endblock %}
</main>
</div>
</main>
<footer>
<small>{{ app_name }} - A self-hosted URL analysis sandbox - {{ app_version }}</small>
</footer>
</body>
{# Footer #}
<footer class="bg-nav border-t border-gray-800 text-center p-4">
<p class="text-sm text-gray-400">© {{ current_year }} SneakyScope {{ app_name }} {{ app_version }} - A selfhosted URL sandbox</p>
</footer>
{# Flowbite JS (enables collapse) #}
<script src="https://cdn.jsdelivr.net/npm/flowbite@2.5.2/dist/flowbite.min.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
{% block page_js %}
{% endblock %}