diff --git a/ui/index.html b/ui/index.html index 5f82d62..343ecc2 100644 --- a/ui/index.html +++ b/ui/index.html @@ -3,25 +3,26 @@ -Webshare Queue + +webshare-api -
- - -
+ - `; @@ -83,14 +98,14 @@ function renderSearchResults(files) { const name = btn.dataset.name; const size = parseInt(btn.dataset.size); btn.disabled = true; - btn.textContent = 'Adding...'; + btn.textContent = '[…]'; try { await addDownload(ident, name, size); btn.classList.add('added'); - btn.textContent = 'Added'; + btn.textContent = '[+]'; } catch (err) { - btn.textContent = 'Failed'; - setTimeout(() => { btn.disabled = false; btn.textContent = 'Add to Queue'; }, 2000); + btn.textContent = '[err]'; + setTimeout(() => { btn.disabled = false; btn.textContent = '[add]'; }, 2000); } }); }); @@ -139,7 +154,7 @@ function renderQueue() {
${e.state} ${escapeHtml(e.name)} - +
${downloaded} / ${total} @@ -158,7 +173,7 @@ function renderQueue() { btn.addEventListener('click', async () => { const hash = btn.dataset.hash; btn.disabled = true; - btn.textContent = '...'; + btn.textContent = '[…]'; try { await fetch(`${API_BASE}/api/queue/cancel`, { method: 'POST', diff --git a/ui/styles.css b/ui/styles.css index 8a2f059..00aaefc 100644 --- a/ui/styles.css +++ b/ui/styles.css @@ -5,114 +5,180 @@ } :root { - --bg: #0d1117; - --surface: #161b22; - --surface-hover: #1c2333; - --border: #30363d; - --text: #e6edf3; - --text-dim: #8b949e; - --accent: #58a6ff; - --accent-hover: #79b8ff; - --green: #3fb950; - --orange: #d29922; - --red: #f85149; - --radius: 0px; + --bg: #f0f0f0; + --fg: #000000; + --muted: #666666; + --border: #000000; + --fill: #000000; + --empty: #d8d8d8; + --surface: #e8e8e8; + --hover: #e0e0e0; + --font: "IBM Plex Mono", "SF Mono", "Menlo", "Consolas", "Liberation Mono", monospace; +} + +html { + height: 100%; + background: var(--bg); + color: var(--fg); } body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - background: var(--bg); - color: var(--text); - max-width: 860px; + height: 100%; + font-family: var(--font); + font-size: 13px; + line-height: 1.45; + max-width: 72rem; margin: 0 auto; - padding: 24px 16px 80px; + padding: 0.75rem 1rem; + display: flex; + flex-direction: column; + min-height: 100%; + overflow: hidden; + background: var(--bg); } -/* ── Tabs ─────────────────────────────────────────────── */ +/* ── Tabs (always pinned) ─────────────────────────────── */ .tabs { display: flex; gap: 0; - border-bottom: 1px solid var(--border); - margin-bottom: 24px; + flex-shrink: 0; + margin-bottom: 0.75rem; + border: 1px solid var(--border); + background: var(--bg); + position: sticky; + top: 0; + z-index: 20; } .tab { - background: none; + background: var(--bg); border: none; - color: var(--text-dim); - font-size: 14px; - font-weight: 500; - padding: 10px 20px; + border-right: 1px solid var(--border); + color: var(--fg); + font-family: inherit; + font-size: 13px; + font-weight: 400; + padding: 0.45rem 1rem; cursor: pointer; - border-bottom: 2px solid transparent; - margin-bottom: -1px; - transition: color 0.15s, border-color 0.15s; + letter-spacing: 0.02em; + text-transform: uppercase; } -.tab:hover { color: var(--text); } +.tab:last-child { + border-right: none; +} + +.tab:hover { + background: var(--hover); +} .tab.active { - color: var(--accent); - border-bottom-color: var(--accent); + background: var(--fg); + color: var(--bg); + font-weight: 700; } -.tab-content { display: none; } -.tab-content.active { display: block; } +/* no outer border / frame around panels */ +.tab-content { + display: none; + flex: 1; + min-height: 0; + border: none; + padding: 0; + background: var(--bg); + flex-direction: column; + overflow: hidden; +} -/* ── Search bar ───────────────────────────────────────── */ +.tab-content.active { + display: flex; +} + +/* ── Search bar (stays above scrolling list) ──────────── */ .search-bar { display: flex; - gap: 8px; - margin-bottom: 20px; + gap: 0; + flex-shrink: 0; + margin-bottom: 0.75rem; + border: 1px solid var(--border); + background: #fafafa; } .search-bar input { flex: 1; - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius); - color: var(--text); - font-size: 16px; - padding: 12px 16px; + background: #fafafa; + border: none; + border-right: 1px solid var(--border); + color: var(--fg); + font-family: inherit; + font-size: 13px; + padding: 0.55rem 0.75rem; outline: none; - transition: border-color 0.15s; + min-width: 0; } -.search-bar input:focus { border-color: var(--accent); } -.search-bar input::placeholder { color: var(--text-dim); } +.search-bar input::placeholder { + color: var(--muted); +} + +.search-bar input:focus { + background: #ffffff; +} .search-bar button { - background: var(--accent); - color: #fff; + background: var(--bg); + color: var(--fg); border: none; - border-radius: var(--radius); - font-size: 14px; - font-weight: 600; - padding: 0 20px; + font-family: inherit; + font-size: 12px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + padding: 0 1rem; cursor: pointer; - transition: background 0.15s; + white-space: nowrap; } -.search-bar button:hover { background: var(--accent-hover); } +.search-bar button:hover { + background: var(--fg); + color: var(--bg); +} + +/* ── Scrollable lists ─────────────────────────────────── */ + +.results { + display: flex; + flex-direction: column; + gap: 0; + flex: 1; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; +} /* ── Result items ─────────────────────────────────────── */ -.results { display: flex; flex-direction: column; gap: 8px; } - .result-item { display: flex; align-items: center; - gap: 12px; - background: var(--surface); + gap: 0.75rem; border: 1px solid var(--border); - border-radius: var(--radius); - padding: 12px 16px; - transition: border-color 0.15s; + border-bottom: none; + padding: 0.55rem 0.75rem; + background: #fafafa; + flex-shrink: 0; } -.result-item:hover { border-color: var(--text-dim); } +.result-item:last-child { + border-bottom: 1px solid var(--border); +} + +.result-item:hover { + background: var(--hover); +} .result-info { flex: 1; @@ -120,61 +186,76 @@ body { } .result-name { - font-size: 14px; - font-weight: 500; + font-size: 13px; + font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .result-meta { - font-size: 12px; - color: var(--text-dim); - margin-top: 2px; + font-size: 11px; + color: var(--muted); + margin-top: 0.15rem; + font-variant-numeric: tabular-nums; } .result-item button { - background: var(--green); - color: #fff; - border: none; - border-radius: var(--radius); - font-size: 13px; - font-weight: 600; - padding: 6px 14px; + background: var(--bg); + color: var(--fg); + border: 1px solid var(--border); + font-family: inherit; + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.03em; + padding: 0.35rem 0.65rem; cursor: pointer; white-space: nowrap; - transition: background 0.15s; } -.result-item button:hover { filter: brightness(1.15); } +.result-item button:hover:not(:disabled):not(.added) { + background: var(--fg); + color: var(--bg); +} -.result-item button.added { - background: var(--text-dim); +.result-item button.added, +.result-item button:disabled { + color: var(--muted); + border-color: var(--muted); cursor: default; + background: var(--bg); } /* ── Queue items ──────────────────────────────────────── */ .queue-item { - background: var(--surface); border: 1px solid var(--border); - border-radius: var(--radius); - padding: 14px 16px; + border-bottom: none; + padding: 0.65rem 0.75rem; + background: #fafafa; + flex-shrink: 0; +} + +.queue-item:last-child { + border-bottom: 1px solid var(--border); } .queue-name { - font-size: 14px; - font-weight: 500; - margin-bottom: 8px; + font-size: 13px; + margin-bottom: 0.45rem; display: flex; align-items: center; - gap: 8px; + gap: 0.5rem; } -.queue-name .badge { flex-shrink: 0; } +.queue-name .badge { + flex-shrink: 0; +} -.queue-name > span:last-child { +.queue-name > span:last-of-type { flex: 1; + min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -183,80 +264,145 @@ body { .cancel-btn { flex-shrink: 0; margin-left: auto; - background: var(--red); - color: #fff; - border: none; + background: var(--bg); + color: var(--fg); + border: 1px solid var(--border); + font-family: inherit; font-size: 11px; - font-weight: 600; - padding: 4px 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.03em; + padding: 0.25rem 0.55rem; cursor: pointer; - transition: filter 0.15s; } -.cancel-btn:hover { filter: brightness(1.2); } -.cancel-btn:disabled { opacity: 0.5; cursor: default; } +.cancel-btn:hover:not(:disabled) { + background: var(--fg); + color: var(--bg); +} + +.cancel-btn:disabled { + opacity: 0.45; + cursor: default; +} .queue-stats { display: flex; justify-content: space-between; - font-size: 12px; - color: var(--text-dim); - margin-bottom: 6px; + font-size: 11px; + color: var(--muted); + margin-bottom: 0.25rem; + font-variant-numeric: tabular-nums; } .queue-speed { - font-size: 12px; - color: var(--text-dim); - margin-bottom: 8px; + font-size: 11px; + color: var(--muted); + margin-bottom: 0.4rem; + font-variant-numeric: tabular-nums; + min-height: 1em; } /* ── Progress bar ─────────────────────────────────────── */ .progress-bar { - height: 6px; - background: var(--border); + height: 0.65rem; + background: var(--empty); + border: 1px solid var(--border); overflow: hidden; } .progress-fill { height: 100%; - background: var(--accent); - transition: width 0.5s ease; + background: var(--fill); + transition: width 0.4s steps(24, end); } -.progress-fill.done { background: var(--green); } -.progress-fill.error { background: var(--red); } -.progress-fill.queued { background: var(--orange); } +.progress-fill.done { + background: var(--fg); +} + +.progress-fill.error { + background: repeating-linear-gradient( + -45deg, + var(--fg), + var(--fg) 3px, + var(--bg) 3px, + var(--bg) 6px + ); +} + +.progress-fill.queued { + background: repeating-linear-gradient( + 90deg, + var(--fg), + var(--fg) 2px, + var(--bg) 2px, + var(--bg) 5px + ); +} /* ── Status badges ────────────────────────────────────── */ .badge { display: inline-block; - font-size: 11px; - font-weight: 600; - padding: 2px 8px; - border-radius: 0; + font-size: 10px; + font-weight: 700; + padding: 0.1rem 0.35rem; + border: 1px solid var(--border); text-transform: uppercase; - letter-spacing: 0.3px; + letter-spacing: 0.04em; + background: var(--bg); + color: var(--fg); } -.badge.downloading { background: #1f3a5f; color: var(--accent); } -.badge.uploading { background: #1a3a1a; color: var(--green); } -.badge.queuedDL { background: #3a2e1a; color: var(--orange); } -.badge.error { background: #3a1a1a; color: var(--red); } - -/* ── Empty state ──────────────────────────────────────── */ - -.empty { - text-align: center; - color: var(--text-dim); - padding: 40px 0; - font-size: 14px; +.badge.downloading, +.badge.uploading { + background: var(--fg); + color: var(--bg); + border-color: var(--fg); } +.badge.queuedDL { + border-style: dashed; + color: var(--muted); + border-color: var(--muted); +} + +.badge.error { + border-style: double; + border-width: 3px; +} + +/* ── Empty / spinner ──────────────────────────────────── */ + +.empty, .spinner { - text-align: center; - color: var(--text-dim); - padding: 20px 0; + text-align: left; + color: var(--muted); + padding: 1.25rem 0.25rem; font-size: 13px; + flex-shrink: 0; +} + +.spinner::before { + content: "> "; + color: var(--fg); +} + +.empty::before { + content: "· "; + color: var(--muted); +} + +/* ── Focus rings ──────────────────────────────────────── */ + +:focus-visible { + outline: 1px solid var(--fg); + outline-offset: 2px; +} + +button:focus:not(:focus-visible), +input:focus:not(:focus-visible) { + outline: none; }