diff --git a/internal/server/server.go b/internal/server/server.go index 3c71dd0..00b1298 100755 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -69,6 +69,7 @@ func staticHandler() http.Handler { http.NotFound(writer, request) return } + writer.Header().Set("Cache-Control", "no-cache") fileServer.ServeHTTP(writer, request) }) } diff --git a/web/static/index.html b/web/static/index.html index 4f06b6b..13875fb 100755 --- a/web/static/index.html +++ b/web/static/index.html @@ -127,7 +127,7 @@ - +

Settings

@@ -135,73 +135,106 @@
-
- Downloads +
+
+

Downloads

+

How many files run at once and how fast each one downloads.

+
+
+
+ +
+
+

Safety

+

Limits that protect the server.

+
+
+
+
+ +
+
+

After download

+

Default choices for new downloads and moves.

+
+
+ + +
+
+ +
+
+

Download paths

+

Folders listed under "Saved paths" when you pick where a download goes.

+
-
+ -
- Defaults - - -
+
+
+

This browser

+

Saved only on this device.

+
+
+ + +
+
-
- Download paths - -
- -
- Appearance - - -
- -
- Server +
+
+

Server

+

Set in the server environment, read only.

+
Download folder
Destination roots
-
+
diff --git a/web/static/style.css b/web/static/style.css index 91ba3f4..46a925b 100755 --- a/web/static/style.css +++ b/web/static/style.css @@ -1,69 +1,76 @@ :root { color-scheme: light; - --background: #f2f3f6; + --background: #eef0f3; --surface: #ffffff; - --surface-alt: #f7f8fa; - --surface-hover: #eef0f4; - --border: #dde0e6; - --text: #191c22; - --muted: #687080; - --accent: #3b6ef5; - --accent-hover: #2f5ddb; - --accent-soft: rgba(59, 110, 245, 0.12); + --surface-alt: #f5f6f8; + --surface-hover: #e8ebf0; + --border: #d8dce3; + --text: #15181e; + --muted: #626a77; + --accent: #2f6bed; + --accent-hover: #2459cc; + --accent-soft: #dde7fc; --accent-text: #ffffff; - --danger: #d03c32; - --danger-soft: rgba(208, 60, 50, 0.1); - --success: #1f9d62; - --success-soft: rgba(31, 157, 98, 0.12); - --info: #3b6ef5; - --info-soft: rgba(59, 110, 245, 0.12); - --shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 2px 8px rgba(16, 24, 40, 0.04); - --radius: 12px; - --radius-small: 8px; + --danger: #c7372c; + --danger-soft: #fae2df; + --success: #17855a; + --success-soft: #d8f1e5; + --info: #2f6bed; + --info-soft: #dde7fc; + --warning: #a86400; + --warning-soft: #fcebc9; + --track: #e1e4ea; + --overlay: rgba(10, 12, 16, 0.6); + --radius: 10px; + --radius-small: 6px; } @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { color-scheme: dark; - --background: #0f1115; - --surface: #171a20; - --surface-alt: #1d2128; - --surface-hover: #242933; - --border: #2a2f39; - --text: #e7e9ee; - --muted: #949cab; - --accent: #5b87ff; - --accent-hover: #7298ff; - --accent-soft: rgba(91, 135, 255, 0.16); - --danger: #f06a5f; - --danger-soft: rgba(240, 106, 95, 0.14); - --success: #3fc486; - --success-soft: rgba(63, 196, 134, 0.14); - --info: #5b87ff; - --info-soft: rgba(91, 135, 255, 0.16); - --shadow: 0 1px 2px rgba(0, 0, 0, 0.3); + --background: #101216; + --surface: #171a1f; + --surface-alt: #1e2228; + --surface-hover: #262b33; + --border: #2c313a; + --text: #e6e8ec; + --muted: #949ba7; + --accent: #4f7cff; + --accent-hover: #6a90ff; + --accent-soft: #1f2a45; + --danger: #ef6a5e; + --danger-soft: #3a201e; + --success: #3bbf80; + --success-soft: #17332a; + --info: #4f7cff; + --info-soft: #1f2a45; + --warning: #e2a63c; + --warning-soft: #3a2e17; + --track: #262b33; } } :root[data-theme="dark"] { color-scheme: dark; - --background: #0f1115; - --surface: #171a20; - --surface-alt: #1d2128; - --surface-hover: #242933; - --border: #2a2f39; - --text: #e7e9ee; - --muted: #949cab; - --accent: #5b87ff; - --accent-hover: #7298ff; - --accent-soft: rgba(91, 135, 255, 0.16); - --danger: #f06a5f; - --danger-soft: rgba(240, 106, 95, 0.14); - --success: #3fc486; - --success-soft: rgba(63, 196, 134, 0.14); - --info: #5b87ff; - --info-soft: rgba(91, 135, 255, 0.16); - --shadow: 0 1px 2px rgba(0, 0, 0, 0.3); + --background: #101216; + --surface: #171a1f; + --surface-alt: #1e2228; + --surface-hover: #262b33; + --border: #2c313a; + --text: #e6e8ec; + --muted: #949ba7; + --accent: #4f7cff; + --accent-hover: #6a90ff; + --accent-soft: #1f2a45; + --danger: #ef6a5e; + --danger-soft: #3a201e; + --success: #3bbf80; + --success-soft: #17332a; + --info: #4f7cff; + --info-soft: #1f2a45; + --warning: #e2a63c; + --warning-soft: #3a2e17; + --track: #262b33; } * { @@ -89,7 +96,7 @@ body { } h2 { - font-size: 1.05rem; + font-size: 1rem; font-weight: 650; margin: 0 0 14px; overflow-wrap: anywhere; @@ -107,16 +114,29 @@ input:not([type="checkbox"]), select, textarea { width: 100%; - min-height: 42px; - padding: 9px 12px; + min-height: 40px; + padding: 8px 12px; border: 1px solid var(--border); border-radius: var(--radius-small); background: var(--surface-alt); - transition: border-color 0.15s ease, box-shadow 0.15s ease; + color: var(--text); + transition: border-color 0.15s ease; } textarea { resize: vertical; + line-height: 1.5; +} + +input::placeholder, +textarea::placeholder { + color: var(--muted); + opacity: 1; +} + +input:not([type="checkbox"]):disabled { + opacity: 0.5; + cursor: not-allowed; } input:not([type="checkbox"]):focus, @@ -124,7 +144,6 @@ select:focus, textarea:focus { outline: none; border-color: var(--accent); - box-shadow: 0 0 0 3px var(--accent-soft); } input[type="checkbox"] { @@ -144,12 +163,12 @@ button { font-weight: 600; cursor: pointer; white-space: nowrap; - transition: background 0.15s ease, border-color 0.15s ease, opacity 0.15s ease; + transition: background 0.15s ease, border-color 0.15s ease; } button:focus-visible { - outline: none; - box-shadow: 0 0 0 3px var(--accent-soft); + outline: 2px solid var(--accent); + outline-offset: 2px; } button:disabled { @@ -168,22 +187,22 @@ button.primary:hover:not(:disabled) { button.ghost, button.secondary { - background: transparent; + background: var(--surface); border-color: var(--border); } -button.ghost:hover, -button.secondary:hover { +button.ghost:hover:not(:disabled), +button.secondary:hover:not(:disabled) { background: var(--surface-hover); } button.danger { - background: transparent; + background: var(--surface); color: var(--danger); border-color: var(--danger); } -button.danger:hover { +button.danger:hover:not(:disabled) { background: var(--danger-soft); } @@ -225,7 +244,6 @@ button.block { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); - box-shadow: var(--shadow); padding: 20px; margin-bottom: 16px; } @@ -234,29 +252,31 @@ button.block { display: flex; flex-direction: column; gap: 6px; - font-size: 0.85rem; - font-weight: 550; + min-width: 0; + font-size: 0.8rem; + font-weight: 600; color: var(--muted); } .field input, -.field select { +.field select, +.field textarea { + font-size: 0.95rem; font-weight: 400; - color: var(--text); } .checkbox { display: flex; align-items: center; gap: 10px; - margin: 14px 0; + margin: 14px 0 0; min-height: 32px; cursor: pointer; } .brand-mark { font-weight: 800; - font-size: 1.15rem; + font-size: 1.1rem; letter-spacing: 0.06em; color: var(--accent); } @@ -309,9 +329,7 @@ button.block { position: sticky; top: 0; z-index: 10; - background: color-mix(in srgb, var(--surface) 88%, transparent); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); + background: var(--surface); border-bottom: 1px solid var(--border); padding-top: env(safe-area-inset-top); } @@ -339,13 +357,56 @@ button.block { .download-form { display: flex; + align-items: stretch; gap: 10px; } +.download-form textarea { + flex: 1 1 auto; + min-height: 76px; +} + +.download-form button { + min-width: 120px; +} + +.download-options { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; + margin-top: 16px; +} + +.list-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 10px; + margin-bottom: 14px; +} + +.list-toolbar h2 { + margin: 0; +} + +.list-toolbar-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.list-toolbar-actions select { + width: auto; + min-height: 32px; + padding: 4px 10px; + font-size: 0.85rem; +} + .empty-state { text-align: center; color: var(--muted); - padding: 24px 8px; + padding: 28px 8px; margin: 0; border: 1px dashed var(--border); border-radius: var(--radius-small); @@ -370,7 +431,6 @@ button.block { .job.selected { border-color: var(--accent); - box-shadow: 0 0 0 3px var(--accent-soft); } .job-header { @@ -413,7 +473,7 @@ button.block { text-transform: uppercase; letter-spacing: 0.04em; padding: 3px 9px; - border-radius: 999px; + border-radius: 4px; white-space: nowrap; flex-shrink: 0; } @@ -438,10 +498,15 @@ button.block { background: var(--info-soft); } +.badge-paused { + color: var(--warning); + background: var(--warning-soft); +} + .progress { - height: 8px; - background: var(--border); - border-radius: 999px; + height: 6px; + background: var(--track); + border-radius: 3px; overflow: hidden; } @@ -449,7 +514,6 @@ button.block { height: 100%; width: 0; background: var(--accent); - border-radius: 999px; transition: width 0.4s ease; } @@ -513,7 +577,7 @@ button.block { gap: 10px; padding: 4px 8px; min-height: 40px; - border-radius: 6px; + border-radius: 4px; } .tree-row:hover { @@ -546,11 +610,97 @@ button.block { .move-controls { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 14px; } +#job-detail .checkbox { + margin-bottom: 14px; +} + +.history-card { + padding: 0; +} + +.history-card summary { + display: flex; + align-items: center; + gap: 10px; + padding: 16px 20px; + cursor: pointer; + font-weight: 650; + list-style: none; +} + +.history-card summary::-webkit-details-marker { + display: none; +} + +.history-card summary::before { + content: "+"; + width: 18px; + text-align: center; + color: var(--muted); + font-weight: 700; +} + +.history-card[open] summary::before { + content: "-"; +} + +.history-card[open] summary { + border-bottom: 1px solid var(--border); +} + +.history-card > :not(summary) { + margin-left: 20px; + margin-right: 20px; +} + +.history-card > :last-child { + margin-bottom: 20px; +} + +.history-list { + list-style: none; + padding: 0; + margin-top: 16px; + margin-bottom: 12px; + display: flex; + flex-direction: column; + gap: 8px; + max-height: 420px; + overflow-y: auto; +} + +.history-list:empty { + display: none; +} + +.history-item { + border: 1px solid var(--border); + border-radius: var(--radius-small); + padding: 10px 12px; + background: var(--surface-alt); +} + +.history-name { + font-weight: 600; + overflow-wrap: anywhere; +} + +.history-meta { + font-size: 0.8rem; + margin: 4px 0 0; + overflow-wrap: anywhere; +} + +.history-card .empty-state { + margin-top: 16px; + margin-bottom: 20px; +} + .sheet { width: min(560px, calc(100% - 32px)); max-height: calc(100dvh - 48px); @@ -559,11 +709,10 @@ button.block { border-radius: var(--radius); background: var(--surface); color: var(--text); - box-shadow: 0 20px 50px rgba(0, 0, 0, 0.25); } .sheet::backdrop { - background: rgba(10, 12, 16, 0.55); + background: var(--overlay); } .sheet-form { @@ -595,38 +744,62 @@ button.block { } .sheet-body { - padding: 4px 20px 16px; + padding: 0 24px 8px; overflow-y: auto; -webkit-overflow-scrolling: touch; } -.settings-group { - border: none; - padding: 0; - margin: 16px 0 0; +.sheet-wide { + width: min(860px, calc(100% - 32px)); +} + +.settings-section { + display: grid; + grid-template-columns: 220px minmax(0, 1fr); + gap: 24px; + padding: 20px 0; + border-bottom: 1px solid var(--border); +} + +.settings-section:last-of-type { + border-bottom: none; +} + +.settings-intro h3 { + margin: 0; + font-size: 0.95rem; + font-weight: 650; +} + +.settings-intro p { + margin: 4px 0 0; + font-size: 0.85rem; +} + +.settings-stack { display: flex; flex-direction: column; - gap: 12px; + gap: 10px; } -.settings-group legend { - font-size: 0.75rem; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--muted); - padding: 0; - margin-bottom: 10px; -} - -.settings-group .checkbox { +.settings-stack .checkbox { margin: 0; } +.settings-narrow { + max-width: 240px; +} + .settings-grid { display: grid; - grid-template-columns: 1fr 1fr; - gap: 12px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; +} + +.field-hint { + font-size: 0.78rem; + font-weight: 400; + color: var(--muted); } .info-list { @@ -636,15 +809,29 @@ button.block { } .info-list dt { - font-size: 0.85rem; + font-size: 0.8rem; + font-weight: 600; color: var(--muted); - margin-top: 6px; +} + +.info-list dd + dt { + margin-top: 8px; } .info-list dd { margin: 0; } +@media (max-width: 760px) { + .download-options { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .download-options .field:first-child { + grid-column: 1 / -1; + } +} + @media (max-width: 640px) { body { font-size: 16px; @@ -652,7 +839,19 @@ button.block { .card { padding: 16px; - border-radius: 10px; + } + + .history-card { + padding: 0; + } + + .history-card summary { + padding: 14px 16px; + } + + .history-card > :not(summary) { + margin-left: 16px; + margin-right: 16px; } .content { @@ -674,17 +873,32 @@ button.block { flex-direction: column; } + .download-form textarea { + width: 100%; + } + .download-form button, button.block-mobile { width: 100%; min-height: 46px; } + .download-options, .move-controls, - .settings-grid { + .settings-grid, + .settings-section { grid-template-columns: 1fr; } + .settings-section { + gap: 12px; + padding: 16px 0; + } + + .settings-narrow { + max-width: none; + } + .job-actions button { flex: 1; min-height: 40px; @@ -708,7 +922,7 @@ button.block { max-width: none; max-height: 92dvh; margin: auto 0 0; - border-radius: 16px 16px 0 0; + border-radius: 12px 12px 0 0; border-bottom: none; } @@ -731,102 +945,3 @@ button.block { display: none; } } - -.badge-paused { - color: var(--muted); - background: var(--border); -} - -.download-form { - align-items: flex-start; -} - -.download-form textarea { - flex: 1 1 auto; - min-height: 64px; -} - -.download-options { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 12px; - margin-top: 14px; -} - -.list-toolbar { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - gap: 10px; - margin-bottom: 14px; -} - -.list-toolbar h2 { - margin: 0; -} - -.list-toolbar-actions { - display: flex; - align-items: center; - gap: 8px; -} - -.list-toolbar-actions select { - width: auto; - min-height: 36px; - padding: 6px 10px; -} - -.history-card summary { - cursor: pointer; - font-size: 1.05rem; - font-weight: 650; -} - -.history-list { - list-style: none; - padding: 0; - margin: 14px 0 12px; - display: flex; - flex-direction: column; - gap: 8px; - max-height: 420px; - overflow-y: auto; -} - -.history-list:empty { - display: none; -} - -.history-item { - border: 1px solid var(--border); - border-radius: var(--radius-small); - padding: 10px 12px; - background: var(--surface-alt); -} - -.history-name { - font-weight: 600; - overflow-wrap: anywhere; -} - -.history-meta { - font-size: 0.8rem; - margin: 4px 0 0; - overflow-wrap: anywhere; -} - -.history-card .empty-state { - margin: 14px 0 12px; -} - -@media (max-width: 640px) { - .download-form textarea { - width: 100%; - } - - .download-options { - grid-template-columns: 1fr; - } -}