diff --git a/crates/pinakes-ui/src/components/statistics.rs b/crates/pinakes-ui/src/components/statistics.rs index ba1d027..35576ad 100644 --- a/crates/pinakes-ui/src/components/statistics.rs +++ b/crates/pinakes-ui/src/components/statistics.rs @@ -1,4 +1,8 @@ use dioxus::prelude::*; +use dioxus_free_icons::Icon; +use dioxus_free_icons::icons::fa_solid_icons::{ + FaChartBar, FaCircle, FaClock, FaDatabase, FaFolder, FaLink, FaTags, +}; use super::utils::format_size; use crate::client::LibraryStatisticsResponse; @@ -10,184 +14,228 @@ pub fn Statistics( on_refresh: EventHandler<()>, ) -> Element { rsx! { - div { class: "card mb-16", - div { class: "card-header", - h3 { class: "card-title", "Library Statistics" } - button { - class: "btn btn-sm btn-secondary", - onclick: move |_| on_refresh.call(()), - "\u{21bb} Refresh" - } - } - - if let Some(ref err) = error { - div { class: "alert alert-error mb-8", - span { "{err}" } + div { class: "statistics-page", + div { class: "card", + div { class: "card-header", + h3 { class: "card-title", "Library Statistics" } button { - class: "btn btn-sm btn-secondary ml-8", + class: "btn btn-sm btn-secondary", onclick: move |_| on_refresh.call(()), - "Retry" + "\u{21bb} Refresh" } } - } - match stats.as_ref() { - Some(s) => { - let total_size = format_size(s.total_size_bytes); - let avg_size = format_size(s.avg_file_size_bytes); - rsx! { - // Overview - div { class: "stats-grid", - div { class: "stat-card", - div { class: "stat-value", "{s.total_media}" } - div { class: "stat-label", "Total Media" } - } - div { class: "stat-card", - div { class: "stat-value", "{total_size}" } - div { class: "stat-label", "Total Size" } - } - div { class: "stat-card", - div { class: "stat-value", "{avg_size}" } - div { class: "stat-label", "Avg File Size" } - } - div { class: "stat-card", - div { class: "stat-value", "{s.total_tags}" } - div { class: "stat-label", "Tags" } - } - div { class: "stat-card", - div { class: "stat-value", "{s.total_collections}" } - div { class: "stat-label", "Collections" } - } - div { class: "stat-card", - div { class: "stat-value", "{s.total_duplicates}" } - div { class: "stat-label", "Duplicate Hashes" } - } + if let Some(ref err) = error { + div { class: "alert alert-error mb-8", + span { "{err}" } + button { + class: "btn btn-sm btn-secondary ml-8", + onclick: move |_| on_refresh.call(()), + "Retry" } + } + } - // Media by Type + match stats.as_ref() { + Some(s) => { + let total_size = format_size(s.total_size_bytes); + let avg_size = format_size(s.avg_file_size_bytes); + rsx! { + div { class: "stats-overview", + div { class: "stat-card stat-primary", + div { class: "stat-icon", + Icon { icon: FaFolder, width: 20, height: 20 } + } + div { class: "stat-content", + div { class: "stat-value", "{s.total_media}" } + div { class: "stat-label", "Total Media" } + } + } + div { class: "stat-card stat-success", + div { class: "stat-icon", + Icon { icon: FaDatabase, width: 20, height: 20 } + } + div { class: "stat-content", + div { class: "stat-value", "{total_size}" } + div { class: "stat-label", "Total Size" } + } + } + div { class: "stat-card stat-info", + div { class: "stat-icon", + Icon { icon: FaChartBar, width: 20, height: 20 } + } + div { class: "stat-content", + div { class: "stat-value", "{avg_size}" } + div { class: "stat-label", "Average Size" } + } + } + div { class: "stat-card stat-warning", + div { class: "stat-icon", + Icon { icon: FaTags, width: 20, height: 20 } + } + div { class: "stat-content", + div { class: "stat-value", "{s.total_tags}" } + div { class: "stat-label", "Tags" } + } + } + div { class: "stat-card stat-purple", + div { class: "stat-icon", + Icon { icon: FaCircle, width: 20, height: 20 } + } + div { class: "stat-content", + div { class: "stat-value", "{s.total_collections}" } + div { class: "stat-label", "Collections" } + } + } + div { class: "stat-card stat-danger", + div { class: "stat-icon", + Icon { icon: FaLink, width: 20, height: 20 } + } + div { class: "stat-content", + div { class: "stat-value", "{s.total_duplicates}" } + div { class: "stat-label", "Duplicates" } + } + } + } - // Storage by Type + if !s.media_by_type.is_empty() { + { + let max_count = s.media_by_type.iter().map(|i| i.count).max().unwrap_or(1) as f64; - // Top Tags - - // Top Collections - - // Date Range - - - - - - - - if !s.media_by_type.is_empty() { - div { class: "card mt-16", - h4 { class: "card-title", "Media by Type" } - table { class: "table", - thead { - tr { - th { "Type" } - th { "Count" } + rsx! { + div { class: "stats-section", + h4 { class: "section-title", + Icon { icon: FaChartBar, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;" } + "Media by Type" + } + div { class: "chart-bars", + for item in s.media_by_type.iter() { + { + let percentage = (item.count as f64 / max_count) * 100.0; + let name = item.name.clone(); + let count = item.count; + rsx! { + div { key: "{name}", class: "bar-item", + div { class: "bar-label", "{name}" } + div { class: "bar-track", + div { + class: "bar-fill bar-primary", + style: "width: {percentage}%", + } + } + div { class: "bar-value", "{count}" } + } + } + } + } } } - tbody { - for item in s.media_by_type.iter() { - tr { - td { "{item.name}" } - td { "{item.count}" } + } + } + } + + if !s.storage_by_type.is_empty() { + { + let max_size = s.storage_by_type.iter().map(|i| i.count).max().unwrap_or(1) as f64; + + rsx! { + div { class: "stats-section", + h4 { class: "section-title", + Icon { icon: FaDatabase, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;" } + "Storage by Type" + } + div { class: "chart-bars", + for item in s.storage_by_type.iter() { + { + let percentage = (item.count as f64 / max_size) * 100.0; + let name = item.name.clone(); + let size_str = format_size(item.count); + rsx! { + div { key: "{name}", class: "bar-item", + div { class: "bar-label", "{name}" } + div { class: "bar-track", + div { + class: "bar-fill bar-success", + style: "width: {percentage}%", + } + } + div { class: "bar-value", "{size_str}" } + } + } + } + } } } } } } - } - if !s.storage_by_type.is_empty() { - div { class: "card mt-16", - h4 { class: "card-title", "Storage by Type" } - table { class: "table", - thead { - tr { - th { "Type" } - th { "Size" } - } + if !s.top_tags.is_empty() { + div { class: "stats-section", + h4 { class: "section-title", + Icon { icon: FaTags, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;" } + "Top Tags" } - tbody { - for item in s.storage_by_type.iter() { - tr { - td { "{item.name}" } - td { "{format_size(item.count)}" } - } - } - } - } - } - } - - if !s.top_tags.is_empty() { - div { class: "card mt-16", - h4 { class: "card-title", "Top Tags" } - table { class: "table", - thead { - tr { - th { "Tag" } - th { "Count" } - } - } - tbody { + div { class: "tag-list", for item in s.top_tags.iter() { - tr { - td { "{item.name}" } - td { "{item.count}" } + div { class: "tag-item", + span { class: "tag-badge", "{item.name}" } + span { class: "tag-count", "{item.count}" } } } } } } - } - if !s.top_collections.is_empty() { - div { class: "card mt-16", - h4 { class: "card-title", "Top Collections" } - table { class: "table", - thead { - tr { - th { "Collection" } - th { "Members" } - } + if !s.top_collections.is_empty() { + div { class: "stats-section", + h4 { class: "section-title", + Icon { icon: FaCircle, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;" } + "Top Collections" } - tbody { + div { class: "collection-list", for item in s.top_collections.iter() { - tr { - td { "{item.name}" } - td { "{item.count}" } + div { class: "collection-item", + Icon { icon: FaFolder, width: 16, height: 16, class: "collection-icon" } + span { class: "collection-name", "{item.name}" } + span { class: "collection-count", "{item.count}" } } } } } } - } - div { class: "card mt-16", - h4 { class: "card-title", "Date Range" } - div { class: "stats-grid", - div { class: "stat-card", - div { class: "stat-value", "{s.oldest_item.as_deref().unwrap_or(\"N/A\")}" } - div { class: "stat-label", "Oldest Item" } + div { class: "stats-section", + h4 { class: "section-title", + Icon { icon: FaClock, width: 16, height: 16, style: "margin-right: 8px; vertical-align: middle;" } + "Date Range" } - div { class: "stat-card", - div { class: "stat-value", "{s.newest_item.as_deref().unwrap_or(\"N/A\")}" } - div { class: "stat-label", "Newest Item" } + div { class: "date-range", + div { class: "date-item", + Icon { icon: FaClock, width: 16, height: 16 } + div { class: "date-content", + div { class: "date-label", "Oldest Item" } + div { class: "date-value", "{s.oldest_item.as_deref().unwrap_or(\"N/A\")}" } + } + } + div { class: "date-item", + Icon { icon: FaClock, width: 16, height: 16 } + div { class: "date-content", + div { class: "date-label", "Newest Item" } + div { class: "date-value", "{s.newest_item.as_deref().unwrap_or(\"N/A\")}" } + } + } } } } } + None => rsx! { + div { class: "empty-state", + div { class: "spinner" } + p { "Loading statistics..." } + } + }, } - None => rsx! { - div { class: "empty-state", - p { "Loading statistics..." } - } - }, } } } diff --git a/crates/pinakes-ui/src/components/tasks.rs b/crates/pinakes-ui/src/components/tasks.rs index 6f5c902..9afdf9f 100644 --- a/crates/pinakes-ui/src/components/tasks.rs +++ b/crates/pinakes-ui/src/components/tasks.rs @@ -1,4 +1,8 @@ use dioxus::prelude::*; +use dioxus_free_icons::Icon; +use dioxus_free_icons::icons::fa_solid_icons::{ + FaArrowsRotate, FaCalendar, FaCircleCheck, FaClock, FaPause, FaPlay, +}; use crate::client::ScheduledTaskResponse; @@ -11,80 +15,134 @@ pub fn Tasks( on_run_now: EventHandler, ) -> Element { rsx! { - div { class: "card mb-16", - div { class: "card-header", - h3 { class: "card-title", "Scheduled Tasks" } - button { - class: "btn btn-sm btn-secondary", - onclick: move |_| on_refresh.call(()), - "\u{21bb} Refresh" - } - } - - if let Some(ref err) = error { - div { class: "alert alert-error mb-8", - span { "{err}" } + div { class: "tasks-container", + div { class: "card mb-16", + div { class: "card-header", + h3 { class: "card-title", "Scheduled Tasks" } button { - class: "btn btn-sm btn-secondary ml-8", + class: "btn btn-sm btn-secondary", onclick: move |_| on_refresh.call(()), - "Retry" + Icon { icon: FaArrowsRotate, width: 14, height: 14 } + " Refresh" } } - } - if tasks.is_empty() { - div { class: "empty-state", - p { "No scheduled tasks configured." } - } - } else { - table { class: "table", - thead { - tr { - th { "Enabled" } - th { "Name" } - th { "Schedule" } - th { "Last Run" } - th { "Next Run" } - th { "Status" } - th { "Actions" } + if let Some(ref err) = error { + div { class: "alert alert-error mb-8", + span { "{err}" } + button { + class: "btn btn-sm btn-secondary ml-8", + onclick: move |_| on_refresh.call(()), + "Retry" } } - tbody { + } + + if tasks.is_empty() { + div { class: "empty-state", + div { class: "empty-icon", + Icon { icon: FaCalendar, width: 48, height: 48 } + } + p { "No scheduled tasks configured." } + p { class: "text-muted", "Tasks will appear here once configured on the server." } + } + } else { + div { class: "tasks-grid", for task in tasks.iter() { { let task_id_toggle = task.id.clone(); let task_id_run = task.id.clone(); - let last_run = task.last_run.clone().unwrap_or_else(|| "-".to_string()); - let next_run = task.next_run.clone().unwrap_or_else(|| "-".to_string()); - let last_status = task.last_status.clone().unwrap_or_else(|| "-".to_string()); + let last_run = task.last_run.clone().unwrap_or_else(|| "Never".to_string()); + let next_run = task.next_run.clone().unwrap_or_else(|| "Not scheduled".to_string()); + let last_status = task.last_status.clone().unwrap_or_else(|| "No runs yet".to_string()); + let is_enabled = task.enabled; + let task_name = task.name.clone(); + let schedule = task.schedule.clone(); + rsx! { - tr { - td { - if task.enabled { - span { class: "badge badge-success", "\u{2713}" } - } else { - span { class: "badge badge-muted", "\u{2715}" } + div { + class: if is_enabled { "task-card task-card-enabled" } else { "task-card task-card-disabled" }, + + // Header with status and actions + div { class: "task-card-header", + div { class: "task-header-left", + div { class: "task-name", "{task_name}" } + div { class: "task-schedule", + span { class: "schedule-icon", + Icon { icon: FaClock, width: 14, height: 14 } + } + "{schedule}" + } + } + div { class: "task-status-badge", + if is_enabled { + span { class: "status-badge status-enabled", + span { class: "status-dot" } + "Active" + } + } else { + span { class: "status-badge status-disabled", + span { class: "status-dot" } + "Disabled" + } + } } } - td { "{task.name}" } - td { "{task.schedule}" } - td { "{last_run}" } - td { "{next_run}" } - td { "{last_status}" } - td { + + // Task info grid + div { class: "task-info-grid", + div { class: "task-info-item", + div { class: "task-info-icon", + Icon { icon: FaClock, width: 16, height: 16 } + } + div { class: "task-info-content", + div { class: "task-info-label", "Last Run" } + div { class: "task-info-value", "{last_run}" } + } + } + div { class: "task-info-item", + div { class: "task-info-icon", + Icon { icon: FaClock, width: 16, height: 16 } + } + div { class: "task-info-content", + div { class: "task-info-label", "Next Run" } + div { class: "task-info-value", "{next_run}" } + } + } + div { class: "task-info-item", + div { class: "task-info-icon", + Icon { icon: FaCircleCheck, width: 16, height: 16 } + } + div { class: "task-info-content", + div { class: "task-info-label", "Last Status" } + div { class: "task-info-value", "{last_status}" } + } + } + } + + // Actions + div { class: "task-card-actions", button { - class: "btn btn-sm btn-secondary mr-8", + class: if is_enabled { "btn btn-sm btn-secondary" } else { "btn btn-sm btn-primary" }, onclick: move |_| on_toggle.call(task_id_toggle.clone()), - if task.enabled { - "Disable" + if is_enabled { + span { + Icon { icon: FaPause, width: 14, height: 14 } + " Disable" + } } else { - "Enable" + span { + Icon { icon: FaPlay, width: 14, height: 14 } + " Enable" + } } } button { class: "btn btn-sm btn-primary", onclick: move |_| on_run_now.call(task_id_run.clone()), - "Run Now" + disabled: !is_enabled, + Icon { icon: FaPlay, width: 14, height: 14 } + " Run Now" } } } diff --git a/crates/pinakes-ui/src/styles.rs b/crates/pinakes-ui/src/styles.rs index 1a8b240..462a92c 100644 --- a/crates/pinakes-ui/src/styles.rs +++ b/crates/pinakes-ui/src/styles.rs @@ -53,7 +53,7 @@ body { outline-offset: 2px; } -/* ── Layout ── */ +/* Layout */ .app { display: flex; height: 100vh; @@ -233,7 +233,7 @@ body { padding: 20px; } -/* ── Table ── */ +/* Table */ .data-table { width: 100%; border-collapse: collapse; @@ -282,7 +282,7 @@ body { border-bottom: none; } -/* ── Buttons ── */ +/* Buttons */ .btn { padding: 5px 12px; border-radius: var(--radius-sm); @@ -349,7 +349,7 @@ body { } .btn-icon:hover { color: var(--text-0); } -/* ── Cards ── */ +/* Cards */ .card { background: var(--bg-2); border: 1px solid var(--border); @@ -369,7 +369,7 @@ body { font-weight: 600; } -/* ── Forms ── */ +/* Forms */ input[type="text"], textarea, select { padding: 6px 10px; border-radius: var(--radius-sm); @@ -412,7 +412,7 @@ input[type="text"]:focus, textarea:focus, select:focus { flex: 1; } -/* ── Toast ── */ +/* Toast */ .toast { position: fixed; bottom: 16px; @@ -437,7 +437,7 @@ input[type="text"]:focus, textarea:focus, select:focus { to { opacity: 1; transform: translateY(0); } } -/* ── Detail ── */ +/* Detail */ .detail-actions { display: flex; gap: 6px; @@ -482,7 +482,7 @@ input[type="text"]:focus, textarea:focus, select:focus { color: var(--text-1); } -/* ── Stats ── */ +/* Stats */ .statistics-page { padding: 20px; } @@ -692,7 +692,7 @@ input[type="text"]:focus, textarea:focus, select:focus { font-family: 'Menlo', 'Monaco', 'Courier New', monospace; } -/* ── Tasks ── */ +/* Tasks */ .tasks-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); @@ -875,7 +875,7 @@ input[type="text"]:focus, textarea:focus, select:focus { .type-text { background: rgba(200, 160, 36, 0.1); color: #c4a840; } .type-other { background: rgba(128, 128, 160, 0.08); color: var(--text-2); } -/* ── Tags ── */ +/* Tags */ .tag-list { display: flex; flex-wrap: wrap; gap: 4px; } .tag-badge { @@ -909,7 +909,7 @@ input[type="text"]:focus, textarea:focus, select:focus { cursor: pointer; } -/* ── Empty state ── */ +/* Empty state */ .empty-state { text-align: center; padding: 48px 16px; @@ -936,7 +936,7 @@ input[type="text"]:focus, textarea:focus, select:focus { line-height: 1.5; } -/* ── Settings ── */ +/* Settings */ .settings-section { margin-bottom: 24px; } .section-title { @@ -1016,7 +1016,7 @@ input[type="text"]:focus, textarea:focus, select:focus { .info-label { color: var(--text-1); font-weight: 500; } .info-value { color: var(--text-0); } -/* ── Scrollbar ── */ +/* Scrollbar */ ::-webkit-scrollbar { width: 5px; height: 5px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 3px; } @@ -1032,7 +1032,7 @@ input[type="text"]:focus, textarea:focus, select:focus { scrollbar-color: rgba(255,255,255,0.08) transparent; } -/* ── Import Tabs ── */ +/* Import Tabs */ .import-tabs { display: flex; gap: 0; @@ -1061,7 +1061,7 @@ input[type="text"]:focus, textarea:focus, select:focus { border-bottom-color: var(--accent); } -/* ── Batch Actions ── */ +/* Batch Actions */ .batch-actions { display: flex; align-items: center; @@ -1076,13 +1076,13 @@ input[type="text"]:focus, textarea:focus, select:focus { color: var(--accent-text); } -/* ── Action badges (audit) ── */ +/* Action badges (audit) */ .action-danger { background: rgba(228, 88, 88, 0.1); color: #d47070; } -/* ── Tag hierarchy ── */ +/* Tag hierarchy */ .tag-group { margin-bottom: 6px; } @@ -1095,7 +1095,7 @@ input[type="text"]:focus, textarea:focus, select:focus { gap: 4px; } -/* ── Detail field inputs ── */ +/* Detail field inputs */ .detail-field input[type="text"], .detail-field textarea, .detail-field select { @@ -1108,7 +1108,7 @@ input[type="text"]:focus, textarea:focus, select:focus { resize: vertical; } -/* ── Checkbox ── */ +/* Checkbox */ input[type="checkbox"] { appearance: none; -webkit-appearance: none; @@ -1192,7 +1192,7 @@ input[type="number"]:focus { border-color: var(--accent); } -/* ── Select ── */ +/* Select */ select { appearance: none; -webkit-appearance: none; @@ -1203,7 +1203,7 @@ select { min-width: 100px; } -/* ── Code ── */ +/* Code */ code { padding: 1px 5px; border-radius: var(--radius-sm); @@ -1216,7 +1216,7 @@ code { ul { list-style: none; padding: 0; } ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } -/* ── Status indicator ── */ +/* Status indicator */ .status-indicator { display: flex; align-items: center; @@ -1262,7 +1262,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } overflow: visible; } -/* ── Modal ── */ +/* Modal */ .modal-overlay { position: fixed; inset: 0; @@ -1308,7 +1308,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } gap: 6px; } -/* ── Saved Searches ── */ +/* Saved Searches */ .saved-searches-list { display: flex; flex-direction: column; @@ -1363,7 +1363,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } margin: 0; } -/* ── Offline banner ── */ +/* Offline banner */ .offline-banner { background: rgba(228, 88, 88, 0.06); border: 1px solid rgba(228, 88, 88, 0.2); @@ -1382,7 +1382,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } flex-shrink: 0; } -/* ── Utility ── */ +/* Utility */ .flex-row { display: flex; align-items: center; gap: 8px; } .flex-between { display: flex; justify-content: space-between; align-items: center; } .mb-16 { margin-bottom: 16px; } @@ -1391,7 +1391,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } .text-sm { font-size: 11px; } .mono { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 12px; } -/* ── Filter bar ── */ +/* Filter bar */ .filter-bar { display: flex; flex-direction: column; @@ -1575,7 +1575,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } display: block; } -/* ── Form label row ── */ +/* Form label row */ .form-label-row { display: flex; align-items: center; @@ -1587,7 +1587,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } margin-bottom: 0; } -/* ── Read-only banner ── */ +/* Read-only banner */ .readonly-banner { display: flex; align-items: center; @@ -1601,7 +1601,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } color: var(--warning); } -/* ── Config path ── */ +/* Config path */ .config-path { font-size: 11px; color: var(--text-2); @@ -1613,7 +1613,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } border: 1px solid var(--border-subtle); } -/* ── Settings cards ── */ +/* Settings cards */ .settings-card { background: var(--bg-2); border: 1px solid var(--border); @@ -1656,7 +1656,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } color: var(--error); } -/* ── Disabled button ── */ +/* Disabled button */ .btn:disabled, .btn[disabled] { opacity: 0.4; cursor: not-allowed; @@ -1671,7 +1671,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } cursor: help; } -/* ── Library Toolbar ── */ +/* Library Toolbar */ .library-toolbar { display: flex; justify-content: space-between; @@ -1694,7 +1694,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } gap: 10px; } -/* ── View Toggle ── */ +/* View Toggle */ .view-toggle { display: flex; border: 1px solid var(--border); @@ -1727,7 +1727,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } color: var(--accent-text); } -/* ── Sort & Page Size Controls ── */ +/* Sort & Page Size Controls */ .sort-control select, .page-size-control select { padding: 4px 24px 4px 8px; @@ -1741,7 +1741,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } gap: 4px; } -/* ── Media Grid ── */ +/* Media Grid */ .media-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); @@ -1768,7 +1768,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } box-shadow: 0 0 0 1px var(--accent); } -/* ── Card Checkbox ── */ +/* Card Checkbox */ .card-checkbox { position: absolute; top: 6px; @@ -1790,7 +1790,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5)); } -/* ── Card Thumbnail ── */ +/* Card Thumbnail */ .card-thumbnail { width: 100%; aspect-ratio: 1; @@ -1827,7 +1827,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } z-index: 0; } -/* ── Card Info ── */ +/* Card Info */ .card-info { padding: 8px 10px; } @@ -1854,7 +1854,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } font-size: 10px; } -/* ── Table Thumbnail ── */ +/* Table Thumbnail */ .table-thumb-cell { width: 36px; padding: 4px 6px !important; @@ -1889,7 +1889,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } z-index: 0; } -/* ── Type Filter Row ── */ +/* Type Filter Row */ .type-filter-row { display: flex; align-items: center; @@ -1922,7 +1922,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } border-color: var(--accent); } -/* ── Library Stats Row ── */ +/* Library Stats Row */ .library-stats { display: flex; justify-content: space-between; @@ -1931,7 +1931,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } font-size: 11px; } -/* ── Sortable Table Headers ── */ +/* Sortable Table Headers */ .sortable-header { cursor: pointer; user-select: none; @@ -1942,7 +1942,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } color: var(--accent-text); } -/* ── Card extra info ── */ +/* Card extra info */ .card-title, .card-artist { font-size: 10px; @@ -1952,7 +1952,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } line-height: 1.3; } -/* ── Pagination ── */ +/* Pagination */ .pagination { display: flex; align-items: center; @@ -1975,7 +1975,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } user-select: none; } -/* ── Loading indicator ── */ +/* Loading indicator */ .loading-overlay { display: flex; align-items: center; @@ -2017,7 +2017,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } flex-shrink: 0; } -/* ── Toast container (stacked) ── */ +/* Toast container (stacked) */ .toast-container { position: fixed; bottom: 16px; @@ -2034,7 +2034,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } transform: none; } -/* ── Nav badge ── */ +/* Nav badge */ .nav-badge { margin-left: auto; font-size: 10px; @@ -2048,7 +2048,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } font-variant-numeric: tabular-nums; } -/* ── Detail preview ── */ +/* Detail preview */ .detail-preview { margin-bottom: 16px; background: var(--bg-0); @@ -2086,7 +2086,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } margin: 0 auto; } -/* ── Action badge styles (audit) ── */ +/* Action badge styles (audit) */ .action-updated { background: rgba(59, 120, 200, 0.1); color: #6ca0d4; @@ -2112,7 +2112,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } color: var(--text-2); } -/* ── Audit controls ── */ +/* Audit controls */ .audit-controls { display: flex; align-items: center; @@ -2126,7 +2126,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } background: var(--bg-2); } -/* ── Clickable elements ── */ +/* Clickable elements */ .clickable { cursor: pointer; color: var(--accent-text); @@ -2144,7 +2144,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } background: rgba(255,255,255,0.03); } -/* ── Progress bar ── */ +/* Progress bar */ .progress-bar { width: 100%; height: 8px; @@ -2171,7 +2171,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } 100% { transform: translateX(400%); } } -/* ── Import status panel ── */ +/* Import status panel */ .import-status-panel { background: var(--bg-2); border: 1px solid var(--accent); @@ -2238,7 +2238,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } color: var(--text-2); } -/* ── Sidebar import progress ── */ +/* Sidebar import progress */ .sidebar-import-progress { padding: 8px 12px; background: var(--bg-2); @@ -2276,7 +2276,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } display: none; } -/* ── Tag confirmation ── */ +/* Tag confirmation */ .tag-confirm-delete { display: inline-flex; align-items: center; @@ -2305,7 +2305,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } text-decoration: underline; } -/* ── Help overlay ── */ +/* Help overlay */ .help-overlay { position: fixed; inset: 0; @@ -2381,14 +2381,14 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } background: rgba(255,255,255,0.06); } -/* ── Add media modal (collections) ── */ +/* Add media modal (collections) */ .modal.wide { max-width: 600px; max-height: 70vh; overflow-y: auto; } -/* ── Database management ── */ +/* Database management */ .db-actions { display: flex; flex-direction: column; @@ -2428,7 +2428,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } border: 1px solid rgba(228, 88, 88, 0.25); } -/* ── Library select-all banner ── */ +/* Library select-all banner */ .select-all-banner { display: flex; align-items: center; @@ -2457,7 +2457,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } color: var(--text-0); } -/* ── Media Player ── */ +/* Media Player */ .media-player { position: relative; background: var(--bg-0); @@ -2610,7 +2610,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } border: none; } -/* ── Image Viewer ── */ +/* Image Viewer */ .image-viewer-overlay { position: fixed; inset: 0; @@ -2687,7 +2687,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } -webkit-user-drag: none; } -/* ── Markdown Viewer ── */ +/* Markdown Viewer */ .markdown-viewer { padding: 16px; text-align: left; @@ -2861,7 +2861,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } font-size: 0.8em; } -/* ── Frontmatter Card ── */ +/* Frontmatter Card */ .frontmatter-card { max-width: 800px; background: var(--bg-2); @@ -2891,7 +2891,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } margin: 0; } -/* ── Duplicates ── */ +/* Duplicates */ .duplicates-view { padding: 0; } @@ -3058,7 +3058,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } font-weight: 600; } -/* ── Login ── */ +/* Login */ .login-container { display: flex; align-items: center; @@ -3113,7 +3113,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } margin-top: 4px; } -/* ── User Info (sidebar) ── */ +/* User Info (sidebar) */ .user-info { display: flex; align-items: center; @@ -3156,7 +3156,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } .role-editor { background: rgba(34, 160, 80, 0.1); color: #5cb97a; } .role-viewer { background: rgba(59, 120, 200, 0.1); color: #6ca0d4; } -/* ── Settings fields ── */ +/* Settings fields */ .settings-field { display: flex; justify-content: space-between; @@ -3175,7 +3175,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } padding-top: 4px; } -/* ── Detail no preview ── */ +/* Detail no preview */ .detail-no-preview { padding: 32px 16px; text-align: center; @@ -3185,7 +3185,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } gap: 12px; } -/* ── Light Theme ── */ +/* Light Theme */ .theme-light { --bg-0: #f5f5f7; --bg-1: #eeeef0; @@ -3217,7 +3217,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } background: rgba(0, 0, 0, 0.05); } -/* ── Skeleton Loading States ── */ +/* Skeleton Loading States */ @keyframes skeleton-pulse { 0% { opacity: 0.6; } 50% { opacity: 0.3; } @@ -3307,7 +3307,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } font-size: 0.9rem; } -/* ── Breadcrumb ── */ +/* Breadcrumb */ .breadcrumb { display: flex; align-items: center; @@ -3337,7 +3337,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } font-weight: 500; } -/* ── Queue Panel ── */ +/* Queue Panel */ .queue-panel { display: flex; flex-direction: column; @@ -4135,7 +4135,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } margin-top: 8px; } -/* ── Wikilink Styles (in markdown) ── */ +/* Wikilink Styles (in markdown) */ .wikilink { color: var(--accent-text); text-decoration: none; @@ -4160,7 +4160,7 @@ ul li { padding: 3px 0; font-size: 12px; color: var(--text-1); } cursor: default; } -/* ── Light theme adjustments for links and graph ── */ +/* Light theme adjustments for links and graph */ .theme-light .graph-nodes .graph-node text { fill: var(--text-0); }