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("/")
|
@root.route("/")
|
||||||
def index():
|
def index():
|
||||||
apps = [
|
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()
|
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"""
|
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>
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<header class="header">
|
||||||
<h1>Tools</h1>
|
<h1>Tools</h1>
|
||||||
<ul>{links}</ul>
|
<p class="subtitle">Flask applications</p>
|
||||||
</body></html>
|
</header>
|
||||||
|
<div class="app-list">{items}</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -34,7 +170,9 @@ def load_sub_apps():
|
||||||
continue
|
continue
|
||||||
spec = importlib.util.spec_from_file_location(app_dir.name, app_file)
|
spec = importlib.util.spec_from_file_location(app_dir.name, app_file)
|
||||||
mod = importlib.util.module_from_spec(spec)
|
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)
|
spec.loader.exec_module(mod)
|
||||||
mounts[f"/{app_dir.name}"] = mod.app
|
mounts[f"/{app_dir.name}"] = mod.app
|
||||||
print(f" mounted /{app_dir.name}")
|
print(f" mounted /{app_dir.name}")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue