redesign index page with dark theme matching dictations viewer
This commit is contained in:
parent
401ce0eb76
commit
af5d3f148d
1 changed files with 145 additions and 7 deletions
150
server.py
150
server.py
|
|
@ -12,16 +12,152 @@ root = Flask(__name__)
|
|||
@root.route("/")
|
||||
def index():
|
||||
apps = [
|
||||
d.name for d in Path(__file__).parent.iterdir()
|
||||
d.name
|
||||
for d in Path(__file__).parent.iterdir()
|
||||
if d.is_dir() and (d / "app.py").exists()
|
||||
]
|
||||
links = "".join(f'<li><a href="/{a}">{a}</a></li>' for a in sorted(apps))
|
||||
items = "".join(
|
||||
f'<a href="/{a}" class="app-item" style="animation-delay:{i * 50}ms">'
|
||||
f'<span class="app-arrow">\u203a</span>'
|
||||
f'<div class="app-name">{a.replace("_", " ").replace("-", " ").title()}</div>'
|
||||
f'<div class="app-path">/{a}</div>'
|
||||
f"</a>"
|
||||
for i, a in enumerate(sorted(apps))
|
||||
)
|
||||
return render_template_string(f"""
|
||||
<!DOCTYPE html><html><head><title>Tools</title></head>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tools</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after {{ box-sizing: border-box; margin: 0; padding: 0; }}
|
||||
|
||||
:root {{
|
||||
--bg: #0f1117;
|
||||
--surface: #1a1d27;
|
||||
--surface-hover: #242836;
|
||||
--border: rgba(255,255,255,0.08);
|
||||
--text: #e4e6ed;
|
||||
--text-muted: #8b8fa3;
|
||||
--accent: #6c5ce7;
|
||||
--accent-glow: rgba(108,92,231,0.25);
|
||||
--accent-light: #a29bfe;
|
||||
--success: #00cec9;
|
||||
--radius: 12px;
|
||||
}}
|
||||
|
||||
body {{
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 100dvh;
|
||||
line-height: 1.5;
|
||||
}}
|
||||
|
||||
.container {{
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 16px 48px;
|
||||
}}
|
||||
|
||||
.header {{
|
||||
text-align: center;
|
||||
margin-bottom: 28px;
|
||||
}}
|
||||
|
||||
.header h1 {{
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: 4px;
|
||||
background: linear-gradient(135deg, var(--accent-light), var(--success));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}}
|
||||
|
||||
.header .subtitle {{
|
||||
font-size: 0.82rem;
|
||||
color: var(--text-muted);
|
||||
}}
|
||||
|
||||
.app-list {{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}}
|
||||
|
||||
.app-item {{
|
||||
display: block;
|
||||
padding: 14px 16px;
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
text-decoration: none;
|
||||
transition: background 0.2s, border-color 0.2s, transform 0.15s;
|
||||
opacity: 0;
|
||||
animation: fadeSlideIn 0.3s ease forwards;
|
||||
}}
|
||||
|
||||
.app-item:hover {{
|
||||
background: var(--surface-hover);
|
||||
border-color: rgba(108,92,231,0.3);
|
||||
transform: translateX(3px);
|
||||
}}
|
||||
|
||||
.app-item:active {{
|
||||
transform: translateX(1px);
|
||||
}}
|
||||
|
||||
.app-name {{
|
||||
font-size: 0.92rem;
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
}}
|
||||
|
||||
.app-path {{
|
||||
margin-top: 2px;
|
||||
font-size: 0.78rem;
|
||||
color: var(--text-muted);
|
||||
}}
|
||||
|
||||
.app-arrow {{
|
||||
float: right;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.8rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}}
|
||||
|
||||
.app-item:hover .app-arrow {{ opacity: 1; }}
|
||||
|
||||
@keyframes fadeSlideIn {{
|
||||
from {{ opacity: 0; transform: translateY(8px); }}
|
||||
to {{ opacity: 1; transform: translateY(0); }}
|
||||
}}
|
||||
|
||||
@media (max-width: 400px) {{
|
||||
.container {{ padding: 16px 12px 40px; }}
|
||||
.header h1 {{ font-size: 1.25rem; }}
|
||||
.app-item {{ padding: 12px 14px; }}
|
||||
}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>Tools</h1>
|
||||
<ul>{links}</ul>
|
||||
</body></html>
|
||||
<p class="subtitle">Flask applications</p>
|
||||
</header>
|
||||
<div class="app-list">{items}</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""")
|
||||
|
||||
|
||||
|
|
@ -34,7 +170,9 @@ def load_sub_apps():
|
|||
continue
|
||||
spec = importlib.util.spec_from_file_location(app_dir.name, app_file)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
sys.modules[app_dir.name] = mod # must be registered before exec so Flask(__name__) resolves root_path correctly
|
||||
sys.modules[app_dir.name] = (
|
||||
mod # must be registered before exec so Flask(__name__) resolves root_path correctly
|
||||
)
|
||||
spec.loader.exec_module(mod)
|
||||
mounts[f"/{app_dir.name}"] = mod.app
|
||||
print(f" mounted /{app_dir.name}")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue