web: show description preview, improve task layout with aligned subtitle and expanded body
change purtvxlprnkvrmptwzmusotlkumpxuop
commit ddf184fdfabcbff451ee9f11b89a7a54daecfb49
author Alpha Chen <alpha@kejadlen.dev>
date
parent xnxnmkqn
diff --git a/src/bin/ranger/commands/serve.rs b/src/bin/ranger/commands/serve.rs
index 98b5e6b..0977844 100644
--- a/src/bin/ranger/commands/serve.rs
+++ b/src/bin/ranger/commands/serve.rs
@@ -364,44 +364,68 @@ fn render_column_panel(
     }
 }
 
+fn truncate_desc(s: &str, max: usize) -> String {
+    let first_line = s.lines().next().unwrap_or(s);
+    if first_line.len() <= max {
+        first_line.to_string()
+    } else {
+        format!("{}…", &first_line[..max])
+    }
+}
+
 fn render_task(task: &TaskView) -> Markup {
     let has_details = task.description.is_some();
     html! {
         @if has_details {
             details.task draggable="true" data-key=(task.key) {
-                summary.task-header tabindex="0" {
-                    span.key {
-                        span.key-prefix { (task.key_prefix) }
-                        span.key-rest { (task.key_rest) }
-                    }
-                    span.title { (task.title) }
-                    @if !task.tags.is_empty() {
-                        span.tags {
-                            @for tag in &task.tags {
-                                span.tag { (tag) }
+                summary tabindex="0" {
+                    div.task-row {
+                        span.key {
+                            span.key-prefix { (task.key_prefix) }
+                            span.key-rest { (task.key_rest) }
+                        }
+                        div.task-content {
+                            div.task-title-row {
+                                span.title { (task.title) }
+                                @if !task.tags.is_empty() {
+                                    span.tags {
+                                        @for tag in &task.tags {
+                                            span.tag { (tag) }
+                                        }
+                                    }
+                                }
+                            }
+                            @if let Some(desc) = &task.description {
+                                div.subtitle { (truncate_desc(desc, 80)) }
                             }
                         }
                     }
-                    span.expand-icon { "›" }
                 }
-                div.task-body {
-                    @if let Some(desc) = &task.description {
-                        div.desc { (desc) }
+                @if let Some(desc) = &task.description {
+                    div.task-row {
+                        span.key-spacer {}
+                        div.task-content {
+                            div.desc { (desc) }
+                        }
                     }
                 }
             }
         } @else {
             div.task data-key=(task.key) tabindex="0" {
-                div.task-header {
+                div.task-row {
                     span.key {
                         span.key-prefix { (task.key_prefix) }
                         span.key-rest { (task.key_rest) }
                     }
-                    span.title { (task.title) }
-                    @if !task.tags.is_empty() {
-                        span.tags {
-                            @for tag in &task.tags {
-                                span.tag { (tag) }
+                    div.task-content {
+                        div.task-title-row {
+                            span.title { (task.title) }
+                            @if !task.tags.is_empty() {
+                                span.tags {
+                                    @for tag in &task.tags {
+                                        span.tag { (tag) }
+                                    }
+                                }
                             }
                         }
                     }
diff --git a/static/style.css b/static/style.css
index f2998d1..924e987 100644
--- a/static/style.css
+++ b/static/style.css
@@ -290,13 +290,30 @@ details.task > summary::marker {
   content: "";
 }
 
-.task .task-header,
-.task > summary.task-header {
+.task-row {
   display: flex;
   align-items: baseline;
   gap: 8px;
 }
 
+.task-content {
+  flex: 1;
+  min-width: 0;
+}
+
+.task-title-row {
+  display: flex;
+  align-items: baseline;
+  gap: 8px;
+}
+
+.key-spacer {
+  width: 8ch;
+  flex-shrink: 0;
+  font-family: var(--font-mono);
+  font-size: var(--step-mono);
+}
+
 .task .key {
   font-family: var(--font-mono);
   font-size: var(--step-mono);
@@ -321,25 +338,26 @@ details.task > summary::marker {
   min-width: 0;
 }
 
-/* === Expand icon === */
-.task .expand-icon {
+
+
+/* === Description subtitle === */
+.task .subtitle {
   font-size: var(--step-mono);
-  color: #7a7868;
-  margin-left: auto;
-  flex-shrink: 0;
-  transition: transform 0.15s ease;
-  display: inline-block;
+  color: var(--color-text-muted);
+  line-height: 1.4;
+  padding-top: 2px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 
-details.task[open] > summary .expand-icon {
-  transform: rotate(90deg);
+details.task[open] > summary .subtitle {
+  display: none;
 }
 
 /* === Task body (expanded) === */
-.task-body {
-  padding: 6px 0 2px 0;
-  margin-top: 4px;
-  border-top: 1px solid rgba(0, 0, 0, 0.1);
+details.task > .task-row {
+  padding-top: 4px;
 }
 
 .task .desc {