diff --git a/importarr/static/importarr.css b/importarr/static/importarr.css index 61209ba..f2626ea 100644 --- a/importarr/static/importarr.css +++ b/importarr/static/importarr.css @@ -1,4 +1,281 @@ -:root{color-scheme:light;--bg:#fff;--surface:#fff;--surface-2:#f2f7ff;--line:#d9e6fb;--text:#2f3b52;--muted:#8da5c3;--heading:#3f3db6;--primary:#4b42b8;--primary-2:#776df2;--accent:#7db3f1;--cyan:#50dce5;--purple:#a56af0;--danger:#a56af0;--warn:#50dce5;--shadow:#4b42b814}body{font-family:system-ui,sans-serif;margin:0;background:var(--bg);color:var(--text);font-size:14px}header,main{max-width:1280px;margin:auto;box-sizing:border-box}.topbar{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:.8rem;align-items:center;background:var(--surface);border-bottom:1px solid var(--line);padding:.45rem .9rem;position:sticky;top:0;z-index:2;box-shadow:0 .45rem 1.2rem var(--shadow)}.brand h1{font-size:1.35rem;margin:0;color:var(--primary)}.brand p,.top-status span{margin:0;color:var(--muted)}.top-status{min-width:0;text-align:center}.top-status strong{font-size:.92rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.top-controls{display:flex;align-items:center;gap:.3rem}.top-controls button,.menu summary{min-width:2.25rem;min-height:2.15rem;padding:.35rem;border-radius:.3rem;background:var(--primary);color:#fff;border:1px solid var(--primary-2);text-align:center}.menu{position:relative}.menu summary{list-style:none;cursor:pointer;font-weight:800;font-size:1.15rem}.menu summary::-webkit-details-marker{display:none}.menu-panel{position:absolute;right:0;top:calc(100% + .35rem);display:grid;gap:.35rem;min-width:13rem;background:var(--surface);border:1px solid var(--line);padding:.5rem;box-shadow:0 .7rem 2rem var(--shadow)}.menu-panel a,.menu-panel button{display:block;width:100%;box-sizing:border-box;text-align:left;color:var(--text);background:var(--surface-2);text-decoration:none;border:1px solid var(--line);padding:.5rem;border-radius:.25rem;font-weight:700}.menu-panel .danger{background:var(--purple);color:#fff}main{padding:0 .9rem .9rem}.summary-strip{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;background:var(--line);margin-top:.8rem;border:1px solid var(--line);box-shadow:0 .6rem 1.5rem var(--shadow)}.summary-strip article,.panel{background:var(--surface)}.summary-strip article{padding:.55rem;min-width:0}.summary-strip strong{display:block;font-size:.95rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.summary-strip span,small,dd{color:var(--muted)}.panel{padding:.8rem;margin-top:.8rem;border:1px solid var(--line);box-shadow:0 .5rem 1.3rem var(--shadow)}.packed{background:var(--surface-2)}.packed summary{cursor:pointer;font-weight:800;font-size:1rem;color:var(--primary)}.queue-panel{background:var(--surface);color:var(--text)}.section-title{display:flex;justify-content:space-between;gap:1rem;align-items:center}.section-title h2,.job-group h3{font-size:1.55rem;line-height:1;margin:.2rem 0 .65rem;color:var(--heading)}.section-title span,.job-group h3 span{font-size:.85rem;color:var(--muted)}table{width:100%;border-collapse:collapse;background:var(--surface);margin-top:.35rem}.table-scroll{max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch}.table-scroll table{min-width:42rem}.jobs-table table{table-layout:fixed;min-width:0}.jobs-table th:nth-child(1){width:66%}.jobs-table th:nth-child(2){width:12%}.jobs-table th:nth-child(3){width:10%}.jobs-table th:nth-child(4){width:12%}.jobs-table td{overflow:hidden;text-overflow:ellipsis}.jobs-table .file-name{font-size:.9rem;line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.jobs-table small{display:block;font-size:.76rem;line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}th,td{padding:.36rem .45rem;border-bottom:1px solid var(--line);text-align:left;vertical-align:middle}th{font-size:.75rem;color:var(--accent);font-weight:700}input,button{padding:.48rem;border-radius:.25rem;border:1px solid var(--line);box-sizing:border-box}button{background:var(--primary);color:#fff;font-weight:700;cursor:pointer;min-height:2.15rem}.danger{background:var(--danger);color:#fff}.warn{background:var(--warn);color:#1e1b4b}.controls,.row-actions{display:flex;gap:.25rem;flex-wrap:nowrap}.row-actions button{width:1.85rem;min-width:1.85rem;min-height:1.85rem;padding:.2rem;font-size:.82rem;line-height:1}.inline-form{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:.6rem}.inline-form input[name=path]{min-width:min(100%,28rem);flex:1}.info{display:grid;grid-template-columns:10rem 1fr;gap:.35rem .8rem}.info dt{font-weight:700}.info dd{margin:0;overflow-wrap:anywhere}.state{background:var(--primary);border:1px solid var(--primary-2);border-radius:999px;padding:.08rem .42rem;display:inline-block;color:#fff;font-size:.75rem;font-weight:800;line-height:1.4}.job-group{margin-top:.8rem}.job-group h3{display:flex;justify-content:space-between}.job-cards{display:none}.job-card{background:var(--surface-2);border:1px solid var(--line);margin:.4rem 0;padding:.58rem}.job-card .file-name{font-size:.95rem;word-break:break-word}.job-card dl{display:grid;grid-template-columns:5rem 1fr;gap:.25rem;margin:.45rem 0}.job-card dt{font-weight:700}.job-card dd{margin:0;word-break:break-word}dialog{max-width:min(48rem,95vw);background:var(--surface);color:var(--text);border:1px solid var(--line);border-radius:.6rem;box-shadow:0 1rem 3rem var(--shadow)}dialog::backdrop{background:#0f172a99}fieldset{border:1px solid var(--line);margin:.8rem 0}label{display:grid;gap:.3rem;margin:.6rem 0}.success{color:var(--cyan)}.error{color:var(--purple)}.hint{color:var(--muted)} -@media (prefers-color-scheme:dark){:root{color-scheme:dark;--bg:#2f3d55;--surface:#40536f;--surface-2:#374963;--line:#506684;--text:#f8fbff;--muted:#bed1e9;--heading:#fff;--primary:#776df2;--primary-2:#4b42b8;--accent:#7db3f1;--cyan:#50dce5;--purple:#a56af0;--danger:#a56af0;--warn:#50dce5;--shadow:#18223666}.topbar{background:#2f3d55}.packed{background:#374963}.queue-panel,.summary-strip article,.panel,table,dialog{background:#40536f}.menu-panel .danger{background:var(--purple);color:#fff}} -@media (max-width:640px){header,main{max-width:1280px}.topbar{grid-template-columns:auto 1fr auto;padding:.4rem .55rem}.brand h1{font-size:1.05rem}.brand p{font-size:.75rem}.top-status{text-align:left}.top-status span{display:none}.top-status strong{font-size:.85rem}.summary-strip{grid-template-columns:1fr 1fr}.summary-strip article{padding:.5rem}.panel{padding:.65rem}.section-title h2,.job-group h3{font-size:1.35rem}.jobs-table{display:none}.job-cards{display:block}.table-scroll{overflow-x:auto}.controls,.inline-form{flex-direction:column}.controls button,.inline-form button,.inline-form input{width:100%}.info{grid-template-columns:1fr}.menu-panel{right:0;min-width:12rem}.job-card .file-name{word-break:break-word}.row-actions{justify-content:flex-end}.row-actions button{flex:0 0 1.9rem}} -html[data-theme="light"]{color-scheme:light;--bg:#fff;--surface:#fff;--surface-2:#f2f7ff;--line:#d9e6fb;--text:#2f3b52;--muted:#8da5c3;--heading:#3f3db6;--primary:#4b42b8;--primary-2:#776df2;--accent:#7db3f1;--cyan:#50dce5;--purple:#a56af0;--danger:#a56af0;--warn:#50dce5;--shadow:#4b42b814}html[data-theme="dark"]{color-scheme:dark;--bg:#2f3d55;--surface:#40536f;--surface-2:#374963;--line:#506684;--text:#f8fbff;--muted:#bed1e9;--heading:#fff;--primary:#776df2;--primary-2:#4b42b8;--accent:#7db3f1;--cyan:#50dce5;--purple:#a56af0;--danger:#a56af0;--warn:#50dce5;--shadow:#18223666}html[data-theme="dark"] .topbar{background:#2f3d55}html[data-theme="dark"] .queue-panel,html[data-theme="dark"] .summary-strip article,html[data-theme="dark"] .panel,html[data-theme="dark"] table,html[data-theme="dark"] dialog{background:#40536f}.top-controls>button,.menu summary{width:2.25rem;height:2.15rem;display:inline-grid;place-items:center}.current-panel progress,.job-group progress{width:100%;height:.8rem}.dialog-body,#settings-form{min-width:min(42rem,86vw);max-width:58rem}.menu-panel button{text-align:left} +:root, +html[data-theme="light"] { + color-scheme: light; + --bg: #fff; + --surface: #fff; + --surface-2: #f2f7ff; + --line: #d9e6fb; + --text: #2f3b52; + --muted: #6f86a4; + --heading: #3f3db6; + --primary: #4b42b8; + --primary-2: #776df2; + --accent: #3074c2; + --cyan: #168992; + --purple: #7d4ed8; + --danger: #7d4ed8; + --warn: #ff9f0a; + --shadow: #4b42b814; +} + +html[data-theme="dark"] { + color-scheme: dark; + --bg: #2f3d55; + --surface: #40536f; + --surface-2: #374963; + --line: #506684; + --text: #f8fbff; + --muted: #bed1e9; + --heading: #fff; + --primary: #776df2; + --primary-2: #4b42b8; + --accent: #7db3f1; + --cyan: #50dce5; + --purple: #a56af0; + --danger: #a56af0; + --warn: #ff9f0a; + --shadow: #18223666; +} + +@media (prefers-color-scheme: dark) { + html[data-theme="auto"] { + color-scheme: dark; + --bg: #2f3d55; + --surface: #40536f; + --surface-2: #374963; + --line: #506684; + --text: #f8fbff; + --muted: #bed1e9; + --heading: #fff; + --primary: #776df2; + --primary-2: #4b42b8; + --accent: #7db3f1; + --cyan: #50dce5; + --purple: #a56af0; + --danger: #a56af0; + --warn: #ff9f0a; + --shadow: #18223666; + } +} + +* { box-sizing: border-box; } + +body { + font-family: system-ui, sans-serif; + margin: 0; + background: var(--bg); + color: var(--text); + font-size: 14px; + overflow-x: hidden; +} + +header, +main { + max-width: 1280px; + margin: auto; +} + +.topbar { + display: grid; + grid-template-columns: auto minmax(0, 1fr) minmax(0, auto) auto; + gap: .8rem; + align-items: center; + background: var(--surface); + border-bottom: 1px solid var(--line); + padding: .45rem .9rem; + position: sticky; + top: 0; + z-index: 2; + box-shadow: 0 .45rem 1.2rem var(--shadow); + overflow: hidden; +} + +.brand, +.top-status, +.top-controls { min-width: 0; } +.brand h1 { font-size: 1.35rem; margin: 0; color: var(--primary); } +.brand p, +.top-status span { margin: 0; color: var(--muted); } +.top-status { text-align: center; overflow: hidden; } +.top-status strong { + display: block; + max-width: 100%; + font-size: .92rem; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.update-banner { + min-width: 0; + display: flex; + align-items: center; + gap: .45rem; + color: var(--text); +} +.update-banner[hidden] { display: none; } +.update-banner strong, +.update-banner code { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + +.top-controls { display: flex; align-items: center; gap: .3rem; } +.top-controls>button,.menu summary { + width: 2.25rem; + height: 2.15rem; + min-width: 2.25rem; + min-height: 2.15rem; + padding: .35rem; + border-radius: .3rem; + background: var(--primary); + color: #fff; + border: 1px solid var(--primary-2); + display: inline-grid; + place-items: center; + text-align: center; +} + +.menu { position: relative; } +.menu summary { list-style: none; cursor: pointer; font-weight: 800; font-size: 1.15rem; } +.menu summary::-webkit-details-marker { display: none; } +.menu-panel { + position: absolute; + right: 0; + top: calc(100% + .35rem); + display: grid; + gap: .35rem; + min-width: 13rem; + background: var(--surface); + border: 1px solid var(--line); + padding: .5rem; + box-shadow: 0 .7rem 2rem var(--shadow); +} +.menu-panel a, +.menu-panel button { + display: block; + width: 100%; + text-align: left; + color: var(--text); + background: var(--surface-2); + text-decoration: none; + border: 1px solid var(--line); + padding: .5rem; + border-radius: .25rem; + font-weight: 700; +} +.menu-panel .danger { background: var(--purple); color: #fff; } + +.update-banner { display: flex; align-items: center; justify-content: center; gap: .45rem; min-width: 0; color: #f59f00; font-weight: 800; } +.update-banner[hidden] { display: none; } +.update-banner button { border: 0; border-radius: .35rem; background: #ff9f0a; color: #fff; font-weight: 900; padding: .45rem .65rem; } +.update-banner code { color: #d68b00; font-size: .72rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 14rem; } +html[data-theme="dark"] .update-banner, +html[data-theme="dark"] .update-banner code { color: #ffb020; } + +main { padding: 0 .9rem .9rem; } +.summary-strip { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 1px; + background: var(--line); + margin-top: .8rem; + border: 1px solid var(--line); + box-shadow: 0 .6rem 1.5rem var(--shadow); +} +.summary-strip article, +.panel, +.queue-panel, +table, +dialog { background: var(--surface); } +.summary-strip article { padding: .55rem; min-width: 0; } +.summary-strip strong { display: block; font-size: .95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.summary-strip span, +small, +dd { color: var(--muted); } + +.panel { padding: .8rem; margin-top: .8rem; border: 1px solid var(--line); box-shadow: 0 .5rem 1.3rem var(--shadow); } +.packed { background: var(--surface-2); } +.packed summary { cursor: pointer; font-weight: 800; font-size: 1rem; color: var(--primary); } +.queue-panel { color: var(--text); } +.section-title { display: flex; justify-content: space-between; gap: 1rem; align-items: center; } +.section-title h2, +.job-group h3 { font-size: 1.55rem; line-height: 1; margin: .2rem 0 .65rem; color: var(--heading); } +.section-title span, +.job-group h3 span { font-size: .85rem; color: var(--muted); } + +.table-scroll { max-width: 100%; overflow: hidden; } /* overflow-x:auto intentionally avoided */ +table { width: 100%; border-collapse: collapse; margin-top: .35rem; table-layout: fixed; } +.jobs-table th:nth-child(1) { width: 66%; } +.jobs-table th:nth-child(2) { width: 12%; } +.jobs-table th:nth-child(3) { width: 10%; } +.jobs-table th:nth-child(4) { width: 12%; } +.jobs-table td, +td small, +.file-name { overflow: hidden; text-overflow: ellipsis; } +.jobs-table .file-name { display: block; font-size: .9rem; line-height: 1.15; white-space: nowrap; } +.jobs-table small { display: block; font-size: .76rem; line-height: 1.15; white-space: nowrap; } +th, +td { padding: .36rem .45rem; border-bottom: 1px solid var(--line); text-align: left; vertical-align: middle; } +th { font-size: .75rem; color: var(--accent); font-weight: 700; } + +input, +button { padding: .48rem; border-radius: .25rem; border: 1px solid var(--line); } +input { background: var(--surface); color: var(--text); } +button { background: var(--primary); color: #fff; font-weight: 700; cursor: pointer; min-height: 2.15rem; } +.danger { background: var(--danger); color: #fff; } +.warn { background: var(--warn); color: #1e1b4b; } +.controls, +.row-actions { display: flex; gap: .25rem; flex-wrap: nowrap; } +.row-actions button { width: 1.85rem; min-width: 1.85rem; min-height: 1.85rem; padding: .2rem; font-size: .82rem; line-height: 1; } +.inline-form { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .6rem; } +.inline-form input[name=path] { min-width: min(100%, 28rem); flex: 1; } +.info { display: grid; grid-template-columns: 10rem 1fr; gap: .35rem .8rem; } +.info dt { font-weight: 700; } +.info dd { margin: 0; overflow-wrap: anywhere; } +.state { background: var(--primary); border: 1px solid var(--primary-2); border-radius: 999px; padding: .08rem .42rem; display: inline-block; color: #fff; font-size: .75rem; font-weight: 800; line-height: 1.4; } +.job-group { margin-top: .8rem; } +.job-group h3 { display: flex; justify-content: space-between; } +.job-cards { display: none; } +.job-card { background: var(--surface-2); border: 1px solid var(--line); margin: .4rem 0; padding: .58rem; } +.job-card .file-name { font-size: .95rem; word-break: break-word; } +.job-card dl { display: grid; grid-template-columns: 5rem 1fr; gap: .25rem; margin: .45rem 0; } +.job-card dt { font-weight: 700; } +.job-card dd { margin: 0; word-break: break-word; } +dialog { max-width: min(48rem, 95vw); color: var(--text); border: 1px solid var(--line); border-radius: .6rem; box-shadow: 0 1rem 3rem var(--shadow); } +dialog::backdrop { background: #0f172a99; } +fieldset { border: 1px solid var(--line); margin: .8rem 0; } +label { display: grid; gap: .3rem; margin: .6rem 0; } +.success { color: var(--cyan); } +.error { color: var(--purple); } +.hint { color: var(--muted); } +.current-panel progress, +.job-group progress { width: 100%; height: .8rem; } +.dialog-body, +#settings-form { min-width: min(42rem, 86vw); max-width: 58rem; } + +@media (max-width:640px) { + header, + main { max-width: 1280px; } + .topbar { grid-template-columns: auto minmax(0, 1fr) auto; padding: .4rem .55rem; gap: .45rem; } + .update-banner { grid-column: 1 / -1; justify-content: flex-start; } + .brand h1 { font-size: 1.05rem; } + .brand p { font-size: .75rem; } + .top-status { text-align: left; } + .top-status span { display: none; } + .top-status strong { font-size: .85rem; } + .summary-strip { grid-template-columns: 1fr 1fr; } + .summary-strip article { padding: .5rem; } + .panel { padding: .65rem; } + .section-title h2, + .job-group h3 { font-size: 1.35rem; } + .jobs-table { display: none; } + .job-cards { display: block; } + .controls, + .inline-form { flex-direction: column; } + .controls button, + .inline-form button, + .inline-form input { width: 100%; } + .info { grid-template-columns: 1fr; } + .menu-panel { right: 0; min-width: 12rem; } + .job-card .file-name { word-break: break-word; } + .row-actions { justify-content: flex-end; } + .row-actions button { flex: 0 0 1.9rem; } +} diff --git a/importarr/templates/index.html b/importarr/templates/index.html index 0269f65..f7a1751 100644 --- a/importarr/templates/index.html +++ b/importarr/templates/index.html @@ -10,6 +10,11 @@

Importarr

{{ 'Running' if status.control.queue_mode == 'start' else status.control.queue_mode|capitalize }}

Current{{ status.current or 'idle' }}
+
@@ -123,6 +128,8 @@ diff --git a/tests/test_status.py b/tests/test_status.py index 0c7eb96..c9fb0eb 100644 --- a/tests/test_status.py +++ b/tests/test_status.py @@ -69,6 +69,10 @@ def test_index_packs_secondary_controls_into_menu(tmp_path, monkeypatch): assert "e.target===e.currentTarget" in response.text assert "function readiness" in response.text assert 'title="${esc(j.reason||j.state)}"' in response.text + assert "update-banner" in response.text + assert "checkForUpdates()" in response.text + assert "/api/control/update-check" in response.text + assert "update-now" in response.text def test_stylesheet_includes_theme_and_progress_rules(): @@ -79,7 +83,9 @@ def test_stylesheet_includes_theme_and_progress_rules(): assert 'html[data-theme="light"]' in css assert 'html[data-theme="dark"]' in css assert ".current-panel progress" in css - assert ".top-controls>button,.menu summary" in css + assert ".top-controls>button" in css + assert ".update-banner" in css + assert "#ff9f0a" in css def test_index_renders_settings_dialog(tmp_path, monkeypatch): @@ -122,23 +128,23 @@ def test_stylesheet_includes_mobile_responsive_rules(): assert "@media (max-width:640px)" in css assert ".table-scroll" in css - assert "overflow-x:auto" in css - assert "flex-direction:column" in css - assert ".jobs-table{display:none}" in css - assert ".job-cards{display:block}" in css - assert "word-break:break-word" in css - assert "max-width:1280px" in css - assert ".jobs-table table{table-layout:fixed;min-width:0}" in css - assert ".jobs-table th:nth-child(1){width:66%}" in css - assert ".row-actions button{width:1.85rem" in css + assert "overflow: hidden" in css + assert "flex-direction: column" in css + assert ".jobs-table { display: none" in css + assert ".job-cards { display: block" in css + assert "word-break: break-word" in css + assert "max-width: 1280px" in css + assert "table-layout: fixed" in css + assert ".jobs-table th:nth-child(1) { width: 66%" in css + assert ".row-actions button { width: 1.85rem" in css assert ".summary-strip" in css assert ".menu-panel" in css - assert "@media (prefers-color-scheme:dark)" in css - assert "--primary:#4b42b8" in css - assert "--cyan:#50dce5" in css - assert "--bg:#fff" in css - assert "--bg:#2f3d55" in css - assert "--danger:#a56af0" in css + assert "@media (prefers-color-scheme: dark)" in css + assert "--primary: #4b42b8" in css + assert "--cyan: #50dce5" in css + assert "--bg: #fff" in css + assert "--bg: #2f3d55" in css + assert "--danger: #a56af0" in css def test_settings_endpoint_persists_arr_connection_values(tmp_path, monkeypatch):