- Convert roadmap to YAML: - Add data structure: id, priority, title, goal, tags, milestone - Add `details` field (supports list or block string); populated initial content - Quote scalars and use explicit nulls to avoid YAML parse edge cases - Update `updated` date to 2025-08-22 - Flask blueprint + loader: - New /roadmap view with section switching (roadmap | backlog | open_questions) - Filters: q (search), tag (multi, AND), min_priority, milestone - Dataclasses: RoadmapData/RoadmapItem; include `details` - `_normalize_details()` to accept string or list, normalize to list[str] - Configurable path via `ROADMAP_FILE` (env or defaults) - Remove cache layer for simplicity - UI (Tailwind): - `templates/roadmap.html` with responsive cards, tag chips, and filter form - Details modal (larger max width, scrollable body) showing ID/goal/priority/tags/milestone - Safe JSON payload to modal via `|tojson|forceescape` - JS: - DOM-ready, event-delegated handler for `data-item` buttons - Populate modal fields and render multi-paragraph details - Fixes & polish: - Resolved YAML `ScannerError` by quoting strings with `:` and `#` - Ensured `details` is passed through route to template and included in button payload - Minor styling tweaks for consistency with Tailwind setup Usage: - Set `ROADMAP_FILE` if not using default path - Visit /roadmap and filter via q/tag/min_priority/milestone
87 lines
2.8 KiB
HTML
87 lines
2.8 KiB
HTML
{# Base layout using Tailwind + Flowbite, non-destructive #}
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<title>{% block title %}{% endblock %} {{ app_name }} </title>
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
|
||
<!-- # Tailwind CSS # -->
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='tw.css') }}">
|
||
|
||
{# Your existing CSS stays; we’ll 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>
|
||
<li>
|
||
<a href="{{ url_for('roadmap.roadmap_view') }}">
|
||
Roadmap
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
|
||
{# 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>
|
||
<li>
|
||
<a href="{{ url_for('roadmap.roadmap_view') }}">
|
||
Roadmap
|
||
</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 %}
|
||
</div>
|
||
</main>
|
||
|
||
{# 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>
|