commit 4bcfcc2fa99ea0ef92e30b9a2b4a54259522a41d
parent 62ea0bb343510a8c5cd635bb27c7fb1f98fe3b76
Author: Amit Dutta <mail@amit.is-a.dev>
Date: Wed, 30 Sep 2026 20:08:28 +0530
Merge pull request #26 from notamitgamer/ui/corporate-simple-frontend
WIP: Corporate-simple frontend redesign
Diffstat:
8 files changed, 240 insertions(+), 429 deletions(-)
diff --git a/app/main.py b/app/main.py
@@ -27,7 +27,6 @@ from .storage import (
upload_temp_file,
upload_folder_scoped,
repo_stats,
- get_recent_activity,
search_files,
write_batch_manifest,
get_batch_manifest,
@@ -984,9 +983,6 @@ async def serve(request: Request, path: str):
"raw_base_url": RAW_BASE_URL,
}
- if not clean_path:
- extra_ctx["recent_activity"] = await get_recent_activity()
-
ctx = await render_context(extra_ctx)
return templates.TemplateResponse(
diff --git a/app/storage.py b/app/storage.py
@@ -224,59 +224,6 @@ async def repo_stats():
_set_cache(cache_key, result)
return result
-_UPLOAD_FILE_RE = re.compile(r"^Upload (.+?)(?: with huggingface_hub)?$")
-_GH_SYNC_RE = re.compile(r"^gh-sync: update (.+)$")
-
-def _do_list_repo_commits():
- return list(api.list_repo_commits(repo_id=HF_REPO_ID, repo_type="dataset"))
-
-async def get_recent_activity(limit: int = 3):
- """Recently changed top-level paths, newest first, deduped by path.
-
- Derived from commit titles (list_repo_commits doesn't return changed
- file paths directly), so this is best-effort: commits whose title
- doesn't match one of this app's own upload patterns are skipped rather
- than guessed at. Cached briefly since it's one extra HF API call.
- """
- cache_key = "recent_activity"
- cached = _get_cache(cache_key)
- if cached is not None:
- return cached
-
- try:
- commits = await asyncio.to_thread(_do_list_repo_commits)
- except Exception:
- return []
-
- seen_paths = set()
- result = []
- for commit in commits:
- title = (commit.title or "").strip()
- gh_match = _GH_SYNC_RE.match(title)
- upload_match = _UPLOAD_FILE_RE.match(title) if not gh_match else None
- if gh_match:
- # already "owner/repo" - that whole pair is the unit, don't truncate it
- top_level = gh_match.group(1).strip().strip("/")
- elif upload_match:
- raw_path = upload_match.group(1).strip().strip("/")
- top_level = raw_path.split("/")[0] if raw_path else ""
- else:
- continue
- if not top_level or _is_hidden_path(top_level):
- continue
- if top_level in seen_paths:
- continue
- seen_paths.add(top_level)
- result.append({
- "path": top_level,
- "iso_time": commit.created_at.isoformat() if commit.created_at else None,
- })
- if len(result) >= limit:
- break
-
- _set_cache(cache_key, result)
- return result
-
async def search_files(query: str):
q = query.strip().lower()
if not q:
diff --git a/app/templates/_navbar.html b/app/templates/_navbar.html
@@ -1,184 +1,80 @@
+{% include "_styles.html" %}
<style>
-/* Shared navbar - single source of truth, included via this partial on every
- page. Self-contained with its own hardcoded values rather than each page's local theme variables, so it looks and behaves
- identically everywhere regardless of what a given page's own :root defines. */
+/* Navbar + breadcrumb. Uses the shared --ui- tokens from _styles.html. */
.navbar-wrapper {
- position: sticky;
- top: 0;
- z-index: 100;
- background: #09090b;
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
- margin-bottom: 2.75rem;
- box-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.2);
+ position: sticky; top: 0; z-index: 100;
+ background: var(--ui-bg);
+ border-bottom: 1px solid var(--ui-border);
+ margin-bottom: 1.5rem;
+ font-family: var(--ui-font);
}
.navbar {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 1.25rem 5%;
- max-width: 1000px;
- margin: 0 auto;
-}
-.wordmark {
- font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
- font-weight: 500;
- font-size: 1.1rem;
- color: #f4f4f5 !important;
- text-decoration: none !important;
- letter-spacing: -0.01em;
- transition: opacity 0.2s;
-}
-.wordmark:hover { opacity: 0.8; }
-.wordmark-slash { color: #818cf8; }
-.navlinks {
- display: flex;
- align-items: center;
- gap: 0.6rem;
+ display: flex; align-items: center; justify-content: space-between; gap: 1rem;
+ max-width: var(--ui-width); margin: 0 auto; padding: 0.75rem 1rem;
}
+.wordmark { font-weight: 600; font-size: 1rem; color: var(--ui-text) !important; text-decoration: none !important; white-space: nowrap; }
+.navlinks { display: flex; align-items: center; gap: 0.25rem; }
.navlinks a {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 2.1rem;
- height: 2.1rem;
- color: #b8b8c0;
- border-radius: 8px;
- transition: all 0.2s ease;
- padding: 0;
-}
-.navlinks a:hover {
- color: #f4f4f5;
- background: rgba(255, 255, 255, 0.05);
-}
-.navlinks a.active {
- color: #818cf8;
- background: rgba(129, 140, 248, 0.12);
-}
-.breadcrumb-container {
- max-width: 1000px;
- margin: 0 auto;
- padding: 0 5% 1rem 5%;
- background: #09090b;
-}
+ display: inline-flex; align-items: center; gap: 0.4rem;
+ padding: 0.4rem 0.65rem; border-radius: var(--ui-radius);
+ color: var(--ui-muted); font-size: 0.9rem; text-decoration: none; white-space: nowrap;
+}
+.navlinks a svg { flex-shrink: 0; }
+.navlinks a:hover { color: var(--ui-text); background: var(--ui-surface); }
+.navlinks a.active { color: var(--ui-accent); background: var(--ui-accent-bg); font-weight: 500; }
+.nav-toggle {
+ display: none; align-items: center; justify-content: center;
+ width: 2.25rem; height: 2.25rem; padding: 0;
+ background: transparent; border: 1px solid var(--ui-border); border-radius: var(--ui-radius);
+ color: var(--ui-text); cursor: pointer;
+}
+.nav-toggle:hover { background: var(--ui-surface); }
+.nav-toggle .icon-close { display: none; }
+.nav-toggle[aria-expanded="true"] .icon-open { display: none; }
+.nav-toggle[aria-expanded="true"] .icon-close { display: block; }
+@media (max-width: 720px) {
+ .navbar { flex-wrap: wrap; }
+ .nav-toggle { display: inline-flex; }
+ .navlinks { display: none; flex-basis: 100%; flex-direction: column; align-items: stretch; gap: 0.15rem; padding-top: 0.5rem; }
+ .navlinks.open { display: flex; }
+ .navlinks a { padding: 0.65rem 0.75rem; font-size: 1rem; }
+}
+.breadcrumb-container { max-width: var(--ui-width); margin: 0 auto; padding: 0 1rem 0.75rem; }
.breadcrumb {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- flex-wrap: wrap;
- color: #b8b8c0;
- font-weight: 500;
- font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
- font-size: 0.85rem;
-}
-.breadcrumb a {
- color: #f4f4f5;
- text-decoration: none;
- padding: 0.2rem 0.4rem;
- margin: -0.2rem -0.4rem;
- border-radius: 4px;
- transition: background 0.15s, color 0.15s;
-}
-.breadcrumb a:hover {
- color: #818cf8;
- background: #1e1e22;
- text-decoration: none;
-}
-.breadcrumb-collapsed {
- position: relative;
- display: inline-flex;
-}
-.breadcrumb-ellipsis {
- background: #121214;
- border: 1px solid rgba(255, 255, 255, 0.08);
- color: #b8b8c0;
- border-radius: 6px;
- padding: 0.25rem 0.75rem;
- cursor: pointer;
- font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
- font-size: 0.9rem;
- line-height: 1.4;
- transition: all 0.2s;
-}
-.breadcrumb-ellipsis:hover {
- color: #f4f4f5;
- border-color: rgba(255, 255, 255, 0.15);
- background: #1e1e22;
-}
+ display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap;
+ color: var(--ui-muted); font-family: var(--ui-mono); font-size: 0.85rem;
+}
+.breadcrumb a { color: var(--ui-accent); text-decoration: none; }
+.breadcrumb a:hover { text-decoration: underline; }
+.breadcrumb-collapsed { position: relative; display: inline-flex; }
+.breadcrumb-ellipsis, .copy-path-btn {
+ background: var(--ui-surface); border: 1px solid var(--ui-border); border-radius: var(--ui-radius);
+ color: var(--ui-muted); cursor: pointer; font: inherit; line-height: 1.4;
+}
+.breadcrumb-ellipsis { padding: 0.1rem 0.6rem; }
+.copy-path-btn { display: inline-flex; align-items: center; justify-content: center; padding: 0.3rem; margin-left: 0.4rem; }
+.breadcrumb-ellipsis:hover, .copy-path-btn:hover { color: var(--ui-text); border-color: var(--ui-muted); }
.breadcrumb-menu {
- display: none;
- position: absolute;
- top: calc(100% + 8px);
- left: 0;
- min-width: 180px;
- max-width: 70vw;
- max-height: 260px;
- overflow-y: auto;
- background: #121214;
- border: 1px solid rgba(255, 255, 255, 0.08);
- border-radius: 8px;
- padding: 0.5rem;
- flex-direction: column;
- gap: 0.25rem;
- z-index: 20;
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
+ display: none; position: absolute; top: calc(100% + 6px); left: 0;
+ min-width: 180px; max-width: 70vw; max-height: 260px; overflow-y: auto;
+ flex-direction: column; gap: 0.15rem; padding: 0.35rem; z-index: 20;
+ background: var(--ui-bg); border: 1px solid var(--ui-border); border-radius: var(--ui-radius);
}
.breadcrumb-menu.open { display: flex; }
-.breadcrumb-menu a {
- padding: 0.75rem 1rem;
- border-radius: 6px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- margin: 0;
-}
-.copy-path-btn {
- background: #121214;
- border: 1px solid rgba(255, 255, 255, 0.08);
- border-radius: 6px;
- color: #b8b8c0;
- cursor: pointer;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 0.4rem;
- margin-left: 0.5rem;
- transition: all 0.2s;
-}
-.copy-path-btn:hover {
- color: #f4f4f5;
- border-color: rgba(255, 255, 255, 0.15);
- background: #1e1e22;
-}
-.copy-path-btn:active {
- transform: scale(0.95);
-}
-@media (max-width: 480px) {
- .navbar { padding: 1rem 1.25rem; }
- .navlinks { gap: 0.25rem; }
-}
+.breadcrumb-menu a { padding: 0.5rem 0.75rem; border-radius: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+.breadcrumb-menu a:hover { background: var(--ui-surface); text-decoration: none; }
</style>
<div class="navbar-wrapper">
<header class="navbar">
- <a href="/" class="wordmark">amit<span class="wordmark-slash">/</span>cdn</a>
- <nav class="navlinks">
- <a href="/find" class="{{ 'active' if page == 'search' }}" title="Find" aria-label="Find"{{ ' aria-current="page"'|safe if page == 'search' }}>
- <svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
- </a>
- <a href="/new" class="{{ 'active' if page == 'upload' }}" title="New / Upload" aria-label="New / Upload"{{ ' aria-current="page"'|safe if page == 'upload' }}>
- <svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
- </a>
- <a href="/gh-sync" class="{{ 'active' if page == 'gh-sync' }}" title="GitHub Actions Sync" aria-label="GitHub Actions Sync"{{ ' aria-current="page"'|safe if page == 'gh-sync' }}>
- <svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg>
- </a>
- <a href="/documentation" class="{{ 'active' if page == 'documentation' }}" title="Documentation" aria-label="Documentation"{{ ' aria-current="page"'|safe if page == 'documentation' }}>
- <svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
- </a>
- <a href="/history" class="{{ 'active' if page == 'history' }}" title="History" aria-label="History"{{ ' aria-current="page"'|safe if page == 'history' }}>
- <svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
- </a>
- <a href="/shorten" class="{{ 'active' if page == 'shorten' }}" title="URL Shortener" aria-label="URL Shortener"{{ ' aria-current="page"'|safe if page == 'shorten' }}>
- <svg xmlns="http://www.w3.org/2000/svg" width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
- </a>
+ <a href="/" class="wordmark">amit/cdn</a>
+ <button type="button" class="nav-toggle" aria-label="Toggle menu" aria-expanded="false" aria-controls="primary-nav" onclick="var o=document.getElementById('primary-nav').classList.toggle('open');this.setAttribute('aria-expanded',o)"><svg class="icon-open" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg><svg class="icon-close" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg></button>
+ <nav class="navlinks" id="primary-nav" aria-label="Primary">
+ <a href="/find" class="{{ 'active' if page == 'search' }}"{{ ' aria-current="page"'|safe if page == 'search' }}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg><span>Find</span></a>
+ <a href="/new" class="{{ 'active' if page == 'upload' }}"{{ ' aria-current="page"'|safe if page == 'upload' }}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg><span>Upload</span></a>
+ <a href="/shorten" class="{{ 'active' if page == 'shorten' }}"{{ ' aria-current="page"'|safe if page == 'shorten' }}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg><span>Shorten</span></a>
+ <a href="/history" class="{{ 'active' if page == 'history' }}"{{ ' aria-current="page"'|safe if page == 'history' }}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg><span>History</span></a>
+ <a href="/gh-sync" class="{{ 'active' if page == 'gh-sync' }}"{{ ' aria-current="page"'|safe if page == 'gh-sync' }}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><polyline points="1 20 1 14 7 14"/><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/></svg><span>Sync</span></a>
+ <a href="/documentation" class="{{ 'active' if page == 'documentation' }}"{{ ' aria-current="page"'|safe if page == 'documentation' }}><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg><span>Docs</span></a>
</nav>
</header>
{% if not (page == 'listing' and not path) %}
diff --git a/app/templates/_styles.html b/app/templates/_styles.html
@@ -0,0 +1,59 @@
+<style>
+/* Shared design tokens (light by default, dark via prefers-color-scheme).
+ Prefixed --ui- so they never collide with a page's own legacy variables
+ while pages are being migrated. Included once, via _navbar.html. */
+:root {
+ color-scheme: light dark;
+ --ui-bg: #ffffff;
+ --ui-surface: #f6f8fa;
+ --ui-border: #d0d7de;
+ --ui-text: #1f2328;
+ --ui-muted: #59636e;
+ --ui-accent: #0b5fff;
+ --ui-accent-bg: #eaf1ff;
+ --ui-accent-solid: #0b5fff; /* filled buttons/badges with white text */
+ --ui-danger: #cf222e;
+ --ui-success: #116329;
+ --ui-font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ --ui-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ --ui-radius: 6px;
+ --ui-width: 960px;
+}
+@media (prefers-color-scheme: dark) {
+ :root {
+ --ui-bg: #0d1117;
+ --ui-surface: #161b22;
+ --ui-border: #30363d;
+ --ui-text: #e6edf3;
+ --ui-muted: #9198a1;
+ --ui-accent: #4c8dff;
+ --ui-accent-bg: #12233f;
+ --ui-accent-solid: #2f6feb;
+ --ui-danger: #f85149;
+ --ui-success: #3fb950;
+ }
+}
+:focus-visible { outline: 2px solid var(--ui-accent); outline-offset: 2px; }
+
+/* ---- Shared polish (loads after page CSS, so it wins at equal specificity) ---- */
+body { font-family: var(--ui-font); line-height: 1.55; -webkit-font-smoothing: antialiased; }
+h1 { font-size: clamp(1.6rem, 3.2vw, 2.1rem) !important; font-weight: 600 !important; letter-spacing: -0.01em !important; line-height: 1.25; }
+/* UI text in sans; monospace stays for paths, file names and code */
+input, button, textarea, select, .btn, .folder-option, .folder-picker-label, .file-pill, .file-ext-tag, footer, .history-tab { font-family: var(--ui-font) !important; }
+/* Notices: neutral info banner instead of an accent bar */
+blockquote { border-left: 1px solid var(--ui-border) !important; background: var(--ui-surface); border-radius: var(--ui-radius); max-width: none !important; }
+/* Inputs and buttons */
+.filter-input, .search-input, .url-input { font-size: 0.95rem; background: var(--ui-bg); }
+.filter-input:focus, .search-input:focus, .url-input:focus { border-color: var(--ui-accent) !important; outline: 2px solid var(--ui-accent-bg); }
+.btn { font-weight: 500 !important; }
+.btn-primary { font-weight: 500 !important; }
+/* Destination chips become a compact segmented control */
+.folder-option { border-radius: var(--ui-radius) !important; font-size: 0.85rem !important; font-weight: 500 !important; background: var(--ui-bg); }
+.folder-option.active { background: var(--ui-accent-solid) !important; border-color: var(--ui-accent-solid) !important; color: #fff !important; }
+.file-pill { border-radius: var(--ui-radius) !important; font-weight: 500 !important; }
+/* Lists: quiet hover, tabular numbers for sizes */
+.list-item:hover { background: var(--ui-surface); }
+.item-size, .file-pill { font-variant-numeric: tabular-nums; }
+footer { color: var(--ui-muted); font-size: 0.85rem; }
+@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; animation-duration: 0.01ms !important; } }
+</style>
diff --git a/app/templates/documentation.html b/app/templates/documentation.html
@@ -10,36 +10,34 @@
<link rel="icon" type="image/png" sizes="16x16" href="/static/icons/favicon-16x16.png">
<link rel="icon" type="image/svg+xml" href="/static/icons/logo.svg">
<link rel="apple-touch-icon" sizes="180x180" href="/static/icons/apple-touch-icon.png">
-<meta name="theme-color" content="#09090b">
-<meta name="color-scheme" content="dark">
-<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=Zilla+Slab:wght@500;600;700&family=Work+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
+<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
+<meta name="theme-color" content="#0d1117" media="(prefers-color-scheme: dark)">
+<meta name="color-scheme" content="light dark">
<style>
- :root{--bg:#09090b;--surface:#121214;--surface-2:#1e1e22;--border:#27272a;--text:#f4f4f5;--muted:#a1a1aa;--accent:#818cf8;--get:#4ade80;--post:#818cf8;--err:#f87171;}
+ :root{--bg:var(--ui-bg);--surface:var(--ui-surface);--surface-2:var(--ui-surface);--border:var(--ui-border);--text:var(--ui-text);--muted:var(--ui-muted);--accent:var(--ui-accent);--get:var(--ui-success);--post:var(--ui-accent);--err:var(--ui-danger);}
*{box-sizing:border-box;}
- body{background:var(--bg);color:var(--text);font-family:'Work Sans',system-ui,sans-serif;margin:0;line-height:1.6;}
+ body{background:var(--bg);color:var(--text);font-family:var(--ui-font);margin:0;line-height:1.6;}
.wrap{max-width:1000px;margin:0 auto;padding:48px 20px 96px;}
- h1{font-family:'Zilla Slab',Georgia,serif;font-weight:600;font-size:1.9rem;margin-bottom:.2em;}
- h2{font-size:1.15rem;margin:0 0 1rem;font-family:'JetBrains Mono',monospace;}
+ h1{font-family:var(--ui-font);font-weight:600;font-size:1.9rem;margin-bottom:.2em;}
+ h2{font-size:1.15rem;margin:0 0 1rem;font-family:var(--ui-mono);}
h3{font-size:.95rem;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;margin:1.1em 0 .4em;}
p{color:var(--muted);margin:.4em 0;}
- code{font-family:'JetBrains Mono',monospace;background:var(--surface-2);padding:.15em .4em;border-radius:4px;font-size:.85em;color:var(--text);word-break:break-word;overflow-wrap:anywhere;}
- pre{font-family:'JetBrains Mono',monospace;background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:14px;overflow-x:auto;font-size:.82rem;margin:.5em 0;}
+ code{font-family:var(--ui-mono);background:var(--surface-2);padding:.15em .4em;border-radius:4px;font-size:.85em;color:var(--text);word-break:break-word;overflow-wrap:anywhere;}
+ pre{font-family:var(--ui-mono);background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:14px;overflow-x:auto;font-size:.82rem;margin:.5em 0;}
a{color:var(--accent);}
.lede{max-width:70ch;}
- .toc{background:var(--surface);border:1px solid var(--border);border-radius:10px;padding:1.1rem 1.3rem;margin:1.6rem 0 2.4rem;}
+ .toc{background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:1.1rem 1.3rem;margin:1.6rem 0 2.4rem;}
.toc h3{margin-top:0;}
.toc ol{margin:0;padding-left:1.2em;color:var(--muted);columns:2;font-size:.92rem;}
.toc a{color:var(--text);text-decoration:none;}
.toc a:hover{color:var(--accent);}
- .endpoint{border:1px solid var(--border);border-radius:10px;padding:1.4rem 1.5rem;margin-bottom:1.6rem;background:var(--surface);scroll-margin-top:20px;}
+ .endpoint{border:1px solid var(--border);border-radius:6px;padding:1.4rem 1.5rem;margin-bottom:1.6rem;background:var(--surface);scroll-margin-top:20px;}
.route-row{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;margin-bottom:.6rem;}
- .method{font-family:'JetBrains Mono',monospace;font-weight:700;font-size:.78rem;padding:.2rem .5rem;border-radius:5px;letter-spacing:.03em;}
- .method.get{background:rgba(74,222,128,.12);color:var(--get);}
- .method.post{background:rgba(129,140,248,.15);color:var(--post);}
- .route-path{font-family:'JetBrains Mono',monospace;font-size:1rem;color:var(--text);}
- .domain-tag{font-family:'JetBrains Mono',monospace;font-size:.72rem;color:var(--muted);background:var(--surface-2);border:1px solid var(--border);border-radius:5px;padding:.15rem .4rem;}
+ .method{font-family:var(--ui-mono);font-weight:700;font-size:.78rem;padding:.2rem .5rem;border-radius:5px;letter-spacing:.03em;}
+ .method.get{background:color-mix(in srgb,var(--ui-success) 14%,transparent);color:var(--get);}
+ .method.post{background:var(--ui-accent-bg);color:var(--post);}
+ .route-path{font-family:var(--ui-mono);font-size:1rem;color:var(--text);}
+ .domain-tag{font-family:var(--ui-mono);font-size:.72rem;color:var(--muted);background:var(--surface-2);border:1px solid var(--border);border-radius:5px;padding:.15rem .4rem;}
.endpoint > p:first-of-type{margin-top:0;}
table{width:100%;border-collapse:collapse;margin:.5em 0;font-size:.88rem;}
th,td{text-align:left;padding:.4em .6em;border-bottom:1px solid var(--border);vertical-align:top;}
@@ -48,9 +46,9 @@
td.desc{color:var(--muted);}
.err-list{list-style:none;padding:0;margin:.4em 0;}
.err-list li{padding:.3em 0;font-size:.88rem;overflow:hidden;}
- .err-code{font-family:'JetBrains Mono',monospace;color:var(--err);font-weight:600;float:left;width:3.2em;margin-right:.6em;}
+ .err-code{font-family:var(--ui-mono);color:var(--err);font-weight:600;float:left;width:3.2em;margin-right:.6em;}
section{margin-top:2.6rem;}
- section > h2 { font-family:'Zilla Slab',Georgia,serif; font-size:1.4rem; font-weight:600; color:var(--text); }
+ section > h2 { font-family:var(--ui-font); font-size:1.4rem; font-weight:600; color:var(--text); }
.footnote{font-size:.82rem;color:var(--muted);margin-top:3rem;border-top:1px solid var(--border);padding-top:1.2rem;}
footer{margin-top:4rem;padding-top:1.5rem;border-top:1px solid var(--border);font-size:.85rem;color:var(--muted);}
</style>
diff --git a/app/templates/gh_sync_docs.html b/app/templates/gh_sync_docs.html
@@ -10,21 +10,19 @@
<link rel="icon" type="image/png" sizes="16x16" href="/static/icons/favicon-16x16.png">
<link rel="icon" type="image/svg+xml" href="/static/icons/logo.svg">
<link rel="apple-touch-icon" sizes="180x180" href="/static/icons/apple-touch-icon.png">
-<meta name="theme-color" content="#09090b">
-<meta name="color-scheme" content="dark">
+<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
+<meta name="theme-color" content="#0d1117" media="(prefers-color-scheme: dark)">
+<meta name="color-scheme" content="light dark">
-<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=Zilla+Slab:wght@500;600;700&family=Work+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
- --bg: #09090b;
- --surface: #121214;
- --surface-2: #1e1e22;
- --border: #27272a;
- --text: #f4f4f5;
- --muted: #a1a1aa;
- --accent: #818cf8;
+ --bg: var(--ui-bg);
+ --surface: var(--ui-surface);
+ --surface-2: var(--ui-surface);
+ --border: var(--ui-border);
+ --text: var(--ui-text);
+ --muted: var(--ui-muted);
+ --accent: var(--ui-accent);
}
*,
@@ -43,7 +41,7 @@
body {
background: var(--bg);
color: var(--text);
- font-family: 'Work Sans', system-ui, sans-serif;
+ font-family: var(--ui-font);
margin: 0;
line-height: 1.6;
}
@@ -55,7 +53,7 @@
}
h1 {
- font-family: 'Zilla Slab', Georgia, 'Times New Roman', serif;
+ font-family: var(--ui-font);
font-weight: 600;
font-size: 1.9rem;
margin-bottom: .2em;
@@ -73,7 +71,7 @@
}
code, pre {
- font-family: 'JetBrains Mono', monospace;
+ font-family: var(--ui-mono);
}
code {
diff --git a/app/templates/index.html b/app/templates/index.html
@@ -13,8 +13,9 @@
<link rel="apple-touch-icon" sizes="180x180" href="/static/icons/apple-touch-icon.png">
<link rel="manifest" href="/static/manifest.json">
- <meta name="theme-color" content="#09090b">
- <meta name="color-scheme" content="dark">
+ <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
+ <meta name="theme-color" content="#0d1117" media="(prefers-color-scheme: dark)">
+ <meta name="color-scheme" content="light dark">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="amit/cdn">
@@ -27,34 +28,28 @@
<script type="module" src="https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.4/dist/turbo.es2017-esm.js"></script>
- <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=Zilla+Slab:wght@500;600;700&family=Work+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
-
<style>
:root {
- --bg: #09090b;
- --surface: #121214;
- --surface-2: #1e1e22;
- --surface-3: #27272a;
- --text-main: #f4f4f5;
- --text-dim: #b8b8c0;
-
- --accent-primary: #818cf8;
- --accent-primary-hover: #a5b4fc;
- --accent-secondary: #f97316;
- --success: #10b981;
- --danger: #ef4444;
- --border: rgba(255, 255, 255, 0.08);
- --border-hover: rgba(255, 255, 255, 0.15);
-
- --font-heading: 'Zilla Slab', Georgia, 'Times New Roman', serif;
- --font-body: 'Work Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
- --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
-
- --radius-md: 8px;
- --radius-lg: 12px;
- --radius-xl: 16px;
+ /* Legacy names mapped onto the shared --ui- tokens (_styles.html) */
+ --bg: var(--ui-bg);
+ --surface: var(--ui-surface);
+ --surface-2: var(--ui-surface);
+ --surface-3: var(--ui-border);
+ --text-main: var(--ui-text);
+ --text-dim: var(--ui-muted);
+ --accent-primary: var(--ui-accent);
+ --accent-primary-hover: var(--ui-accent);
+ --accent-secondary: var(--ui-accent-solid);
+ --success: var(--ui-success);
+ --danger: var(--ui-danger);
+ --border: var(--ui-border);
+ --border-hover: var(--ui-muted);
+ --font-heading: var(--ui-font);
+ --font-body: var(--ui-font);
+ --font-mono: var(--ui-mono);
+ --radius-md: 6px;
+ --radius-lg: 6px;
+ --radius-xl: 6px;
}
* { box-sizing: border-box; }
@@ -100,7 +95,7 @@
font-family: var(--font-heading);
font-size: clamp(2.2rem, 5vw, 3rem);
letter-spacing: -0.02em;
- color: #ffffff;
+ color: var(--text-main);
font-weight: 600;
margin: 0 0 1.25rem 0;
line-height: 1.1;
@@ -121,46 +116,6 @@
.site-notice { margin-top: 0.5rem; }
- .recent-activity {
- margin-bottom: 2.25rem;
- border: 1px solid var(--border);
- border-radius: var(--radius-md);
- background: var(--surface);
- overflow: hidden;
- }
- .recent-activity-heading {
- display: flex;
- align-items: center;
- gap: 0.5rem;
- color: var(--text-dim);
- font-size: 0.9rem;
- font-weight: 500;
- padding: 0.7rem 1rem;
- }
- .recent-activity-list {
- list-style: none;
- margin: 0;
- padding: 0 1rem 0.6rem;
- }
- .recent-activity-list li {
- display: flex;
- justify-content: space-between;
- align-items: baseline;
- gap: 1rem;
- padding: 0.35rem 0;
- border-top: 1px solid var(--border);
- font-size: 0.9rem;
- }
- .recent-activity-list li:first-child { border-top: none; }
- .recent-activity-list a { text-decoration: none; }
- .recent-activity-list a:hover { text-decoration: underline; }
- .recent-activity-time {
- color: var(--text-dim);
- font-size: 0.8rem;
- white-space: nowrap;
- flex-shrink: 0;
- }
-
a { color: var(--accent-primary); text-decoration: none; transition: color 0.15s;}
a:hover { color: var(--accent-primary-hover); text-decoration: underline; }
@@ -232,7 +187,7 @@
flex-direction: column;
gap: 0.25rem;
z-index: 20;
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
+ box-shadow: none;
}
.breadcrumb-menu.open { display: flex; }
@@ -283,7 +238,7 @@
font-family: var(--font-mono);
font-size: 0.9rem;
transition: all 0.2s ease;
- box-shadow: 0 2px 10px rgba(0,0,0,0.1) inset;
+ box-shadow: none;
}
.search-input { font-size: 1rem; padding: 1rem 2.5rem 1rem 1.25rem; }
@@ -291,7 +246,7 @@
outline: none;
background: var(--surface-2);
border-color: var(--accent-primary);
- box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.15);
+ box-shadow: none;
}
.filter-input::placeholder, .search-input::placeholder, .url-input::placeholder {
color: var(--text-dim);
@@ -340,7 +295,7 @@
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
- box-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.2);
+ box-shadow: none;
margin-bottom: 2rem;
display: flex;
flex-direction: column;
@@ -359,8 +314,8 @@
}
.list-group .filter-input:focus, .list-group .search-input:focus {
- background: rgba(255, 255, 255, 0.02);
- box-shadow: inset 3px 0 0 var(--accent-primary);
+ background: var(--ui-surface);
+ box-shadow: none;
}
.list-item {
@@ -378,10 +333,10 @@
border-bottom: none;
}
.list-item:hover {
- background: rgba(255, 255, 255, 0.03);
+ background: var(--ui-surface);
}
.list-item:active {
- background: rgba(255, 255, 255, 0.05);
+ background: var(--ui-surface);
}
.item-name {
@@ -449,7 +404,7 @@
}
.download-icon:hover {
color: var(--accent-primary);
- background: rgba(129, 140, 248, 0.1);
+ background: var(--ui-accent-bg);
}
.download-icon:active {
transform: scale(0.9);
@@ -483,15 +438,15 @@
.btn:disabled { opacity: 0.6; cursor: not-allowed; }
.btn-primary {
- background: var(--accent-primary) !important;
+ background: var(--ui-accent-solid) !important;
color: #ffffff !important;
- border-color: var(--accent-primary) !important;
+ border-color: var(--ui-accent-solid) !important;
font-weight: 600;
transition: none !important;
}
.btn-primary:hover:not(:disabled), .btn-primary:focus:not(:disabled), .btn-primary:active:not(:disabled) {
- background: var(--accent-primary) !important;
- border-color: var(--accent-primary) !important;
+ background: var(--ui-accent-solid) !important;
+ border-color: var(--ui-accent-solid) !important;
color: #ffffff !important;
}
.btn-primary svg { stroke: #ffffff !important; }
@@ -512,7 +467,6 @@
border-color: var(--border-hover);
}
.icon-btn:hover:not(:disabled) svg { stroke: var(--accent-primary); }
- .icon-btn:active:not(:disabled) { transform: scale(0.95); }
.file-card {
border: 1px solid var(--border);
@@ -527,10 +481,6 @@
align-items: center;
justify-content: center;
background-color: var(--surface-2);
- background-image:
- linear-gradient(rgba(255, 255, 255, 0.03) 1px, transparent 1px),
- linear-gradient(90deg, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
- background-size: 24px 24px;
border-bottom: 1px solid var(--border);
min-height: 200px;
padding: 3rem 1rem;
@@ -574,7 +524,7 @@
margin: 0 0 0.75rem 0;
font-weight: 600;
font-size: 1.5rem;
- color: #ffffff;
+ color: var(--text-main);
word-break: break-all;
}
.file-meta-row {
@@ -588,8 +538,8 @@
font-size: 0.75rem;
font-weight: 600;
color: var(--accent-primary);
- border: 1px solid rgba(129, 140, 248, 0.2);
- background: rgba(129, 140, 248, 0.05);
+ border: 1px solid var(--ui-accent);
+ background: var(--ui-accent-bg);
border-radius: 6px;
padding: 0.35rem 0.85rem;
letter-spacing: 0.5px;
@@ -605,12 +555,12 @@
margin: 0 auto 2.5rem auto;
font-size: 0.85rem;
word-break: break-all;
- background: rgba(0, 0, 0, 0.3);
+ background: var(--ui-surface);
display: inline-block;
padding: 0.75rem 1.25rem;
border-radius: var(--radius-md);
border: 1px solid var(--border);
- box-shadow: inset 0 2px 4px rgba(0,0,0,0.2);
+ box-shadow: none;
max-width: 90%;
}
.file-actions {
@@ -663,7 +613,7 @@
cursor: pointer;
color: var(--text-dim);
margin-bottom: 1.5rem;
- background: rgba(255, 255, 255, 0.02);
+ background: var(--ui-surface);
transition: all 0.2s ease;
display: flex;
flex-direction: column;
@@ -680,9 +630,9 @@
}
#dropzone:hover, #dropzone.dragover {
border-color: var(--accent-primary);
- background: rgba(129, 140, 248, 0.08);
+ background: var(--ui-accent-bg);
color: var(--accent-primary);
- box-shadow: 0 0 0 4px rgba(129, 140, 248, 0.1);
+ box-shadow: none;
}
#dropzone:hover svg, #dropzone.dragover svg {
color: var(--accent-primary);
@@ -693,7 +643,7 @@
border: 1px solid var(--border);
border-radius: var(--radius-lg);
overflow: hidden;
- box-shadow: 0 4px 20px -2px rgba(0, 0, 0, 0.2);
+ box-shadow: none;
}
.file-row {
@@ -707,8 +657,8 @@
.file-row:last-child {
border-bottom: none;
}
- .file-row.done { background: rgba(16, 185, 129, 0.05); }
- .file-row.error { background: rgba(239, 68, 68, 0.05); }
+ .file-row.done { background: var(--ui-surface); }
+ .file-row.error { background: var(--ui-surface); }
.turbo-progress-bar {
height: 3px;
@@ -752,8 +702,8 @@
.modal-overlay {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
- background: rgba(0, 0, 0, 0.8);
- backdrop-filter: blur(4px);
+ background: rgba(0, 0, 0, 0.5);
+
display: flex; align-items: center; justify-content: center;
z-index: 1000;
}
@@ -764,12 +714,12 @@
padding: 2rem;
max-width: 440px;
width: 90%;
- box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
+ box-shadow: none;
}
.modal h3 {
font-family: var(--font-heading);
margin-top: 0; margin-bottom: 1rem;
- color: #fff; font-size: 1.4rem;
+ color: var(--text-main); font-size: 1.4rem;
font-weight: 600;
}
.modal p { margin-bottom: 2rem; color: var(--text-dim); }
@@ -825,7 +775,7 @@
}
.history-tab:hover {
color: var(--text-main);
- background: rgba(255, 255, 255, 0.03);
+ background: var(--ui-surface);
}
.history-tab.active {
color: var(--text-main);
@@ -859,7 +809,7 @@
font-size: 1.4rem;
font-weight: 600;
margin: 0;
- color: #ffffff;
+ color: var(--text-main);
}
.clear-history-btn {
background: transparent;
@@ -897,22 +847,6 @@
<a href="/gh-sync">Sync</a> at the top to see how to set it up. Project assets live here;
files and paths may change over time, so use direct links with that in mind.
</blockquote>
- {% if recent_activity %}
- <div class="recent-activity" id="recent-activity">
- <div class="recent-activity-heading">
- <svg xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
- <span>Recently modified</span>
- </div>
- <ul class="recent-activity-list">
- {% for entry in recent_activity %}
- <li>
- <a href="/{{ entry.path }}/">{{ entry.path }}/</a>
- {% if entry.iso_time %}<time class="recent-activity-time" datetime="{{ entry.iso_time }}">{{ entry.iso_time }}</time>{% endif %}
- </li>
- {% endfor %}
- </ul>
- </div>
- {% endif %}
{% endif %}
{% if page == 'listing' %}
@@ -1037,6 +971,7 @@
</a>
<button onclick="copyToClipboard('{{ cdn_base_url }}/{{ path }}', this)" class="btn">Copy Link</button>
<button onclick="copyToClipboard('{{ raw_base_url }}/{{ path }}', this)" class="btn">Copy Raw Link</button>
+ <button onclick="copyToClipboard('{{ cdn_base_url }}/api/get/{{ path | urlencode }}', this)" class="btn" title="Anyone opening this link downloads the file directly">Copy Download Link</button>
</div>
</div>
</div>
@@ -1769,7 +1704,7 @@
const label = batchId ? 'this batch' : (path ? path.split('/').pop() : 'root');
document.getElementById('zip-modal-text').innerHTML =
`Are you sure you want to download <strong>${label}</strong>?<br><br>` +
- `This will package <strong style="color:#fff;">${data.file_count} file(s)</strong> totaling <strong style="color:#fff;">${data.size_str}</strong> into a .zip.`;
+ `This will package <strong style="color:var(--text-main);">${data.file_count} file(s)</strong> totaling <strong style="color:var(--text-main);">${data.size_str}</strong> into a .zip.`;
document.getElementById('zip-modal-confirm').href = downloadUrl;
zipModalOverlay.style.display = 'flex';
@@ -1957,24 +1892,6 @@
}
</script>
<script>
- // --- Recently modified: convert timestamps to the visitor's own timezone. ---
- (function () {
- document.querySelectorAll('.recent-activity-time[datetime]').forEach(function (el) {
- var d = new Date(el.getAttribute('datetime'));
- if (isNaN(d.getTime())) return;
- var now = new Date();
- var diffMs = now - d;
- var diffMin = Math.round(diffMs / 60000);
- var label;
- if (diffMin < 1) label = 'just now';
- else if (diffMin < 60) label = diffMin + 'm ago';
- else if (diffMin < 60 * 24) label = Math.round(diffMin / 60) + 'h ago';
- else label = d.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
- el.textContent = label;
- el.title = d.toLocaleString();
- });
- })();
-
// --- Visited history: silently logs file/folder pages viewed on this browser,
// shown on /history. localStorage only - there's no account system, so this is
// per-browser, same limitation as ordinary browser history.
diff --git a/app/templates/url_shortener.html b/app/templates/url_shortener.html
@@ -11,32 +11,32 @@
<link rel="icon" type="image/svg+xml" href="/static/icons/logo.svg">
<link rel="apple-touch-icon" sizes="180x180" href="/static/icons/apple-touch-icon.png">
<link rel="manifest" href="/static/manifest.json">
- <meta name="theme-color" content="#09090b">
- <meta name="color-scheme" content="dark">
+ <meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
+<meta name="theme-color" content="#0d1117" media="(prefers-color-scheme: dark)">
+ <meta name="color-scheme" content="light dark">
<script type="module" src="https://cdn.jsdelivr.net/npm/@hotwired/turbo@8.0.4/dist/turbo.es2017-esm.js"></script>
- <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=Zilla+Slab:wght@500;600;700&family=Work+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
- --bg: #09090b;
- --surface: #121214;
- --surface-2: #1e1e22;
- --surface-3: #27272a;
- --text-main: #f4f4f5;
- --text-dim: #b8b8c0;
- --accent-primary: #818cf8;
- --accent-primary-hover: #a5b4fc;
- --success: #10b981;
- --danger: #ef4444;
- --border: rgba(255, 255, 255, 0.08);
- --border-hover: rgba(255, 255, 255, 0.15);
- --font-heading: 'Zilla Slab', Georgia, 'Times New Roman', serif;
- --font-body: 'Work Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
- --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
- --radius-md: 8px;
- --radius-lg: 12px;
- --radius-xl: 16px;
+ /* Legacy names mapped onto the shared --ui- tokens (_styles.html) */
+ --bg: var(--ui-bg);
+ --surface: var(--ui-surface);
+ --surface-2: var(--ui-surface);
+ --surface-3: var(--ui-border);
+ --text-main: var(--ui-text);
+ --text-dim: var(--ui-muted);
+ --accent-primary: var(--ui-accent);
+ --accent-primary-hover: var(--ui-accent);
+ --accent-secondary: var(--ui-accent-solid);
+ --success: var(--ui-success);
+ --danger: var(--ui-danger);
+ --border: var(--ui-border);
+ --border-hover: var(--ui-muted);
+ --font-heading: var(--ui-font);
+ --font-body: var(--ui-font);
+ --font-mono: var(--ui-mono);
+ --radius-md: 6px;
+ --radius-lg: 6px;
+ --radius-xl: 6px;
}
* { -webkit-tap-highlight-color: transparent; }
@@ -63,7 +63,7 @@
font-family: var(--font-heading);
font-size: clamp(2.2rem, 5vw, 3rem);
letter-spacing: -0.02em;
- color: #ffffff;
+ color: var(--text-main);
font-weight: 600;
margin: 0 0 1.25rem 0;
line-height: 1.1;
@@ -104,7 +104,7 @@
outline: none;
background: var(--surface-2);
border-color: var(--accent-primary);
- box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.15);
+ box-shadow:none;
}
.url-input::placeholder { color: var(--text-dim); }
.input-clear-btn {
@@ -145,8 +145,8 @@
.btn:hover:not(:disabled) { background: var(--surface-3); border-color: var(--border-hover); text-decoration: none; color: var(--text-main); }
.btn:active:not(:disabled) { transform: scale(0.97); }
.btn:disabled { opacity: 0.6; cursor: not-allowed; }
- .btn-primary { background: var(--accent-primary) !important; color: #ffffff !important; border-color: var(--accent-primary) !important; font-weight: 600; transition: none !important; }
- .btn-primary:hover:not(:disabled), .btn-primary:focus:not(:disabled), .btn-primary:active:not(:disabled) { background: var(--accent-primary) !important; border-color: var(--accent-primary) !important; color: #ffffff !important; }
+ .btn-primary { background: var(--ui-accent-solid) !important; color: #ffffff !important; border-color: var(--ui-accent-solid) !important; font-weight: 600; transition: none !important; }
+ .btn-primary:hover:not(:disabled), .btn-primary:focus:not(:disabled), .btn-primary:active:not(:disabled) { background: var(--ui-accent-solid) !important; border-color: var(--ui-accent-solid) !important; color: #ffffff !important; }
.btn-primary svg { stroke: #ffffff !important; }
.icon-btn {
display: inline-flex;
@@ -173,7 +173,7 @@
font-family: var(--font-heading);
font-size: 1.2rem;
margin: 0 0 1rem 0;
- color: #fff;
+ color: var(--text-main);
}
.result-url {
font-family: var(--font-mono);
@@ -197,8 +197,8 @@
color: var(--danger);
margin-top: 1rem;
padding: 1rem 1.25rem;
- border: 1px solid rgba(239, 68, 68, 0.5);
- background: rgba(239, 68, 68, 0.1);
+ border: 1px solid var(--ui-danger);
+ background: color-mix(in srgb, var(--ui-danger) 10%, transparent);
border-radius: var(--radius-md);
}
.info-text {