瀏覽代碼

cdk-ldk web ui updates (#1027)

Erik 2 月之前
父節點
當前提交
39f256a648

+ 1 - 4
crates/cdk-ldk-node/Cargo.toml

@@ -15,7 +15,7 @@ async-trait.workspace = true
 axum.workspace = true
 cdk-common = { workspace = true, features = ["mint"] }
 futures.workspace = true
-tokio.workspace = true 
+tokio.workspace = true
 tokio-util.workspace = true
 tracing.workspace = true
 thiserror.workspace = true
@@ -29,6 +29,3 @@ tower-http.workspace = true
 rust-embed = "8.5.0"
 serde_urlencoded = "0.7"
 urlencoding = "2.1"
-
-
-

+ 61 - 29
crates/cdk-ldk-node/src/web/handlers/channels.rs

@@ -213,7 +213,7 @@ pub async fn post_open_channel(
 }
 
 pub async fn close_channel_page(
-    State(_state): State<AppState>,
+    State(state): State<AppState>,
     query: Query<HashMap<String, String>>,
 ) -> Result<Html<String>, StatusCode> {
     let channel_id = query.get("channel_id").unwrap_or(&"".to_string()).clone();
@@ -229,24 +229,40 @@ pub async fn close_channel_page(
         return Ok(Html(layout("Close Channel Error", content).into_string()));
     }
 
+    // Get channel information for amount display
+    let channels = state.node.inner.list_channels();
+    let channel = channels
+        .iter()
+        .find(|c| c.user_channel_id.0.to_string() == channel_id);
+
     let content = form_card(
         "Close Channel",
         html! {
-            p { "Are you sure you want to close this channel?" }
-            div class="info-item" {
-                span class="info-label" { "User Channel ID:" }
-                span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (channel_id) }
-            }
-            div class="info-item" {
-                span class="info-label" { "Node ID:" }
-                span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (node_id) }
+            p style="margin-bottom: 1.5rem;" { "Are you sure you want to close this channel?" }
+
+            // Channel details in consistent format
+            div class="channel-details" {
+                div class="detail-row" {
+                    span class="detail-label" { "User Channel ID" }
+                    span class="detail-value-amount" { (channel_id) }
+                }
+                div class="detail-row" {
+                    span class="detail-label" { "Node ID" }
+                    span class="detail-value-amount" { (node_id) }
+                }
+                @if let Some(ch) = channel {
+                    div class="detail-row" {
+                        span class="detail-label" { "Channel Amount" }
+                        span class="detail-value-amount" { (format_sats_as_btc(ch.channel_value_sats)) }
+                    }
+                }
             }
-            form method="post" action="/channels/close" style="margin-top: 1rem;" {
+
+            form method="post" action="/channels/close" style="margin-top: 1rem; display: flex; justify-content: space-between; align-items: center;" {
                 input type="hidden" name="channel_id" value=(channel_id) {}
                 input type="hidden" name="node_id" value=(node_id) {}
-                button type="submit" style="background: #dc3545;" { "Close Channel" }
-                " "
-                a href="/balance" { button type="button" { "Cancel" } }
+                a href="/balance" { button type="button" class="button-secondary" { "Cancel" } }
+                button type="submit" class="button-destructive" { "Close Channel" }
             }
         },
     );
@@ -255,7 +271,7 @@ pub async fn close_channel_page(
 }
 
 pub async fn force_close_channel_page(
-    State(_state): State<AppState>,
+    State(state): State<AppState>,
     query: Query<HashMap<String, String>>,
 ) -> Result<Html<String>, StatusCode> {
     let channel_id = query.get("channel_id").unwrap_or(&"".to_string()).clone();
@@ -273,32 +289,48 @@ pub async fn force_close_channel_page(
         ));
     }
 
+    // Get channel information for amount display
+    let channels = state.node.inner.list_channels();
+    let channel = channels
+        .iter()
+        .find(|c| c.user_channel_id.0.to_string() == channel_id);
+
     let content = form_card(
         "Force Close Channel",
         html! {
-            div style="border: 2px solid #d63384; background-color: rgba(214, 51, 132, 0.1); padding: 1rem; margin-bottom: 1rem; border-radius: 0.5rem;" {
-                h4 style="color: #d63384; margin: 0 0 0.5rem 0;" { "⚠️ Warning: Force Close" }
-                p style="color: #d63384; margin: 0; font-size: 0.9rem;" {
+            div style="border: 2px solid #f97316; background-color: rgba(249, 115, 22, 0.1); padding: 1rem; margin-bottom: 1rem; border-radius: 0.5rem;" {
+                h4 style="color: #f97316; margin: 0 0 0.5rem 0;" { "⚠️ Warning: Force Close" }
+                p style="color: #f97316; margin: 0; font-size: 0.9rem;" {
                     "Force close should NOT be used if normal close is preferred. "
                     "Force close will immediately broadcast the latest commitment transaction and may result in delayed fund recovery. "
                     "Only use this if the channel counterparty is unresponsive or there are other issues preventing normal closure."
                 }
             }
-            p { "Are you sure you want to force close this channel?" }
-            div class="info-item" {
-                span class="info-label" { "User Channel ID:" }
-                span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (channel_id) }
-            }
-            div class="info-item" {
-                span class="info-label" { "Node ID:" }
-                span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (node_id) }
+            p style="margin-bottom: 1.5rem;" { "Are you sure you want to force close this channel?" }
+
+            // Channel details in consistent format
+            div class="channel-details" {
+                div class="detail-row" {
+                    span class="detail-label" { "User Channel ID" }
+                    span class="detail-value-amount" { (channel_id) }
+                }
+                div class="detail-row" {
+                    span class="detail-label" { "Node ID" }
+                    span class="detail-value-amount" { (node_id) }
+                }
+                @if let Some(ch) = channel {
+                    div class="detail-row" {
+                        span class="detail-label" { "Channel Amount" }
+                        span class="detail-value-amount" { (format_sats_as_btc(ch.channel_value_sats)) }
+                    }
+                }
             }
-            form method="post" action="/channels/force-close" style="margin-top: 1rem;" {
+
+            form method="post" action="/channels/force-close" style="margin-top: 1rem; display: flex; justify-content: space-between; align-items: center;" {
                 input type="hidden" name="channel_id" value=(channel_id) {}
                 input type="hidden" name="node_id" value=(node_id) {}
-                button type="submit" style="background: #d63384;" { "Force Close Channel" }
-                " "
-                a href="/balance" { button type="button" { "Cancel" } }
+                a href="/balance" { button type="button" class="button-secondary" { "Cancel" } }
+                button type="submit" class="button-destructive" { "Force Close Channel" }
             }
         },
     );

+ 103 - 54
crates/cdk-ldk-node/src/web/handlers/lightning.rs

@@ -3,7 +3,7 @@ use axum::http::StatusCode;
 use axum::response::Html;
 use maud::html;
 
-use crate::web::handlers::AppState;
+use crate::web::handlers::utils::AppState;
 use crate::web::templates::{format_sats_as_btc, layout};
 
 pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>, StatusCode> {
@@ -26,18 +26,35 @@ pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>,
         html! {
             h2 style="text-align: center; margin-bottom: 3rem;" { "Lightning" }
 
-            // Quick Actions section - matching dashboard style
+            // Quick Actions section - individual cards
             div class="card" style="margin-bottom: 2rem;" {
                 h2 { "Quick Actions" }
-                div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
-                    a href="/channels/open" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                        button class="button-primary" style="width: 100%;" { "Open Channel" }
+                div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
+                    // Open Channel Card
+                    div class="quick-action-card" {
+                        h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Open Channel" }
+                        p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Create a new Lightning Network channel to connect with another node." }
+                        a href="/channels/open" style="text-decoration: none;" {
+                            button class="button-outline" { "Open Channel" }
+                        }
                     }
-                    a href="/invoices" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                        button class="button-primary" style="width: 100%;" { "Create Invoice" }
+
+                    // Create Invoice Card
+                    div class="quick-action-card" {
+                        h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Create Invoice" }
+                        p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a Lightning invoice to receive payments from other users or services." }
+                        a href="/invoices" style="text-decoration: none;" {
+                            button class="button-outline" { "Create Invoice" }
+                        }
                     }
-                    a href="/payments/send" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                        button class="button-primary" style="width: 100%;" { "Make Lightning Payment" }
+
+                    // Make Payment Card
+                    div class="quick-action-card" {
+                        h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Make Lightning Payment" }
+                        p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Lightning payments to other users using invoices. BOLT 11 & 12 supported." }
+                        a href="/invoices" style="text-decoration: none;" {
+                            button class="button-outline" { "Make Payment" }
+                        }
                     }
                 }
             }
@@ -73,18 +90,35 @@ pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>,
         html! {
             h2 style="text-align: center; margin-bottom: 3rem;" { "Lightning" }
 
-            // Quick Actions section - matching dashboard style
+            // Quick Actions section - individual cards
             div class="card" style="margin-bottom: 2rem;" {
                 h2 { "Quick Actions" }
-                div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
-                    a href="/channels/open" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                        button class="button-primary" style="width: 100%;" { "Open Channel" }
+                div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
+                    // Open Channel Card
+                    div class="quick-action-card" {
+                        h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Open Channel" }
+                        p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Create a new Lightning channel by connecting with another node." }
+                        a href="/channels/open" style="text-decoration: none;" {
+                            button class="button-outline" { "Open Channel" }
+                        }
                     }
-                    a href="/invoices" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                        button class="button-primary" style="width: 100%;" { "Create Invoice" }
+
+                    // Create Invoice Card
+                    div class="quick-action-card" {
+                        h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Create Invoice" }
+                        p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a Lightning invoice to receive payments." }
+                        a href="/invoices" style="text-decoration: none;" {
+                            button class="button-outline" { "Create Invoice" }
+                        }
                     }
-                    a href="/payments/send" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                        button class="button-primary" style="width: 100%;" { "Make Lightning Payment" }
+
+                    // Make Payment Card
+                    div class="quick-action-card" {
+                        h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Make Lightning Payment" }
+                        p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Lightning payments to other users using invoices." }
+                        a href="/payments/send" style="text-decoration: none;" {
+                            button class="button-outline" { "Make Payment" }
+                        }
                     }
                 }
             }
@@ -112,57 +146,72 @@ pub async fn balance_page(State(state): State<AppState>) -> Result<Html<String>,
                 }
             }
 
-            div class="card" {
-                h2 { "Channel Details" }
+            // Channel Details header (outside card)
+            h2 class="section-header" { "Channel Details" }
+
+            // Channels list
+            @for (index, channel) in channels.iter().enumerate() {
+                @let node_id = channel.counterparty_node_id.to_string();
+                @let channel_number = index + 1;
 
-                // Channels list
-                @for channel in &channels {
-                    div class="channel-item" {
-                        div class="channel-header" {
-                            span class="channel-id" { "Channel ID: " (channel.channel_id.to_string()) }
+                div class="channel-box" {
+                    // Channel number as prominent header
+                    div class="channel-alias" { (format!("Channel {}", channel_number)) }
+
+                    // Channel details in left-aligned format
+                    div class="channel-details" {
+                        div class="detail-row" {
+                            span class="detail-label" { "Channel ID" }
+                            span class="detail-value-amount" { (channel.channel_id.to_string()) }
+                        }
+                        @if let Some(short_channel_id) = channel.short_channel_id {
+                            div class="detail-row" {
+                                span class="detail-label" { "Short Channel ID" }
+                                span class="detail-value-amount" { (short_channel_id.to_string()) }
+                            }
+                        }
+                        div class="detail-row" {
+                            span class="detail-label" { "Node ID" }
+                            span class="detail-value-amount" { (node_id) }
+                        }
+                        div class="detail-row" {
+                            span class="detail-label" { "Status" }
                             @if channel.is_usable {
                                 span class="status-badge status-active" { "Active" }
                             } @else {
                                 span class="status-badge status-inactive" { "Inactive" }
                             }
                         }
-                        div class="info-item" {
-                            span class="info-label" { "Counterparty" }
-                            span class="info-value" style="font-family: monospace; font-size: 0.85rem;" { (channel.counterparty_node_id.to_string()) }
+                    }
+
+                    // Balance information cards (keeping existing style)
+                    div class="balance-info" {
+                        div class="balance-item" {
+                            div class="balance-amount" { (format_sats_as_btc(channel.outbound_capacity_msat / 1000)) }
+                            div class="balance-label" { "Outbound" }
                         }
-                        @if let Some(short_channel_id) = channel.short_channel_id {
-                            div class="info-item" {
-                                span class="info-label" { "Short Channel ID" }
-                                span class="info-value" { (short_channel_id.to_string()) }
-                            }
+                        div class="balance-item" {
+                            div class="balance-amount" { (format_sats_as_btc(channel.inbound_capacity_msat / 1000)) }
+                            div class="balance-label" { "Inbound" }
                         }
-                        div class="balance-info" {
-                            div class="balance-item" {
-                                div class="balance-amount" { (format_sats_as_btc(channel.outbound_capacity_msat / 1000)) }
-                                div class="balance-label" { "Outbound" }
-                            }
-                            div class="balance-item" {
-                                div class="balance-amount" { (format_sats_as_btc(channel.inbound_capacity_msat / 1000)) }
-                                div class="balance-label" { "Inbound" }
-                            }
-                            div class="balance-item" {
-                                div class="balance-amount" { (format_sats_as_btc(channel.channel_value_sats)) }
-                                div class="balance-label" { "Total" }
-                            }
+                        div class="balance-item" {
+                            div class="balance-amount" { (format_sats_as_btc(channel.channel_value_sats)) }
+                            div class="balance-label" { "Total" }
                         }
-                        @if channel.is_usable {
-                            div style="margin-top: 1rem; display: flex; gap: 0.5rem;" {
-                                a href=(format!("/channels/close?channel_id={}&node_id={}", channel.user_channel_id.0, channel.counterparty_node_id)) {
-                                    button style="background: #dc3545;" { "Close Channel" }
-                                }
-                                a href=(format!("/channels/force-close?channel_id={}&node_id={}", channel.user_channel_id.0, channel.counterparty_node_id)) {
-                                    button style="background: #d63384;" title="Force close should not be used if normal close is preferred. Force close will broadcast the latest commitment transaction immediately." { "Force Close" }
-                                }
+                    }
+
+                    // Action buttons
+                    @if channel.is_usable {
+                        div class="channel-actions" {
+                            a href=(format!("/channels/close?channel_id={}&node_id={}", channel.user_channel_id.0, channel.counterparty_node_id)) {
+                                button class="button-secondary" { "Close Channel" }
+                            }
+                            a href=(format!("/channels/force-close?channel_id={}&node_id={}", channel.user_channel_id.0, channel.counterparty_node_id)) {
+                                button class="button-destructive" title="Force close should not be used if normal close is preferred. Force close will broadcast the latest commitment transaction immediately." { "Force Close" }
                             }
                         }
                     }
                 }
-
             }
         }
     };

+ 54 - 21
crates/cdk-ldk-node/src/web/handlers/onchain.rs

@@ -79,15 +79,26 @@ pub async fn onchain_page(
     let mut content = html! {
         h2 style="text-align: center; margin-bottom: 3rem;" { "On-chain" }
 
-        // Quick Actions section - matching dashboard style
+        // Quick Actions section - individual cards
         div class="card" style="margin-bottom: 2rem;" {
             h2 { "Quick Actions" }
-            div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
-                a href="/onchain?action=receive" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                    button class="button-primary" style="width: 100%;" { "Receive Bitcoin" }
+            div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
+                // Receive Bitcoin Card
+                div class="quick-action-card" {
+                    h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Receive Bitcoin" }
+                    p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a new Bitcoin address to receive on-chain payments from other users or services." }
+                    a href="/onchain?action=receive" style="text-decoration: none;" {
+                        button class="button-outline" { "Receive Bitcoin" }
+                    }
                 }
-                a href="/onchain?action=send" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                    button class="button-primary" style="width: 100%;" { "Send Bitcoin" }
+
+                // Send Bitcoin Card
+                div class="quick-action-card" {
+                    h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Send Bitcoin" }
+                    p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Bitcoin to another address on the blockchain. Standard on-chain transactions." }
+                    a href="/onchain?action=send" style="text-decoration: none;" {
+                        button class="button-outline" { "Send Bitcoin" }
+                    }
                 }
             }
         }
@@ -113,15 +124,26 @@ pub async fn onchain_page(
             content = html! {
                 h2 style="text-align: center; margin-bottom: 3rem;" { "On-chain" }
 
-                // Quick Actions section - matching dashboard style
+                // Quick Actions section - individual cards
                 div class="card" style="margin-bottom: 2rem;" {
                     h2 { "Quick Actions" }
-                    div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
-                        a href="/onchain?action=receive" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                            button class="button-primary" style="width: 100%;" { "Receive Bitcoin" }
+                    div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
+                        // Receive Bitcoin Card
+                        div class="quick-action-card" {
+                            h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Receive Bitcoin" }
+                            p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a new Bitcoin address to receive on-chain payments from other users or services." }
+                            a href="/onchain?action=receive" style="text-decoration: none;" {
+                                button class="button-outline" { "Receive Bitcoin" }
+                            }
                         }
-                        a href="/onchain?action=send" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                            button class="button-primary" style="width: 100%;" { "Send Bitcoin" }
+
+                        // Send Bitcoin Card
+                        div class="quick-action-card" {
+                            h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Send Bitcoin" }
+                            p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Bitcoin to another address on the blockchain. Standard on-chain transactions." }
+                            a href="/onchain?action=send" style="text-decoration: none;" {
+                                button class="button-outline" { "Send Bitcoin" }
+                            }
                         }
                     }
                 }
@@ -141,7 +163,7 @@ pub async fn onchain_page(
                             }
                             input type="hidden" id="send_action" name="send_action" value="send" {}
                             div style="display: flex; justify-content: space-between; gap: 1rem; margin-top: 2rem;" {
-                                a href="/onchain" { button type="button" { "Cancel" } }
+                                a href="/onchain" { button type="button" class="button-secondary" { "Cancel" } }
                                 div style="display: flex; gap: 0.5rem;" {
                                     button type="submit" onclick="document.getElementById('send_action').value='send'" { "Send Payment" }
                                     button type="submit" onclick="document.getElementById('send_action').value='send_all'; document.getElementById('amount_sat').value=''" { "Send All" }
@@ -171,15 +193,26 @@ pub async fn onchain_page(
             content = html! {
                 h2 style="text-align: center; margin-bottom: 3rem;" { "On-chain" }
 
-                // Quick Actions section - matching dashboard style
+                // Quick Actions section - individual cards
                 div class="card" style="margin-bottom: 2rem;" {
                     h2 { "Quick Actions" }
-                    div style="display: flex; gap: 1rem; margin-top: 1rem; flex-wrap: wrap;" {
-                        a href="/onchain?action=receive" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                            button class="button-primary" style="width: 100%;" { "Receive Bitcoin" }
+                    div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;" {
+                        // Receive Bitcoin Card
+                        div class="quick-action-card" {
+                            h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Receive Bitcoin" }
+                            p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Generate a new Bitcoin address to receive on-chain payments from other users or services." }
+                            a href="/onchain?action=receive" style="text-decoration: none;" {
+                                button class="button-outline" { "Receive Bitcoin" }
+                            }
                         }
-                        a href="/onchain?action=send" style="text-decoration: none; flex: 1; min-width: 200px;" {
-                            button class="button-primary" style="width: 100%;" { "Send Bitcoin" }
+
+                        // Send Bitcoin Card
+                        div class="quick-action-card" {
+                            h3 style="font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-primary);" { "Send Bitcoin" }
+                            p style="font-size: 0.875rem; color: var(--text-muted); margin-bottom: 1rem; line-height: 1.4;" { "Send Bitcoin to another address on the blockchain. Standard on-chain transactions." }
+                            a href="/onchain?action=send" style="text-decoration: none;" {
+                                button class="button-outline" { "Send Bitcoin" }
+                            }
                         }
                     }
                 }
@@ -191,7 +224,7 @@ pub async fn onchain_page(
                         form method="post" action="/onchain/new-address" {
                             p style="margin-bottom: 2rem;" { "Click the button below to generate a new Bitcoin address for receiving on-chain payments." }
                             div style="display: flex; justify-content: space-between; gap: 1rem;" {
-                                a href="/onchain" { button type="button" { "Cancel" } }
+                                a href="/onchain" { button type="button" class="button-secondary" { "Cancel" } }
                                 button class="button-primary" type="submit" { "Generate New Address" }
                             }
                         }
@@ -345,7 +378,7 @@ pub async fn onchain_confirm_page(
         div class="card" {
             div style="display: flex; justify-content: space-between; gap: 1rem; margin-top: 2rem;" {
                 a href="/onchain?action=send" {
-                    button type="button" class="button-secondary" { "Cancel" }
+                    button type="button" class="button-secondary" { "Cancel" }
                 }
                 div style="display: flex; gap: 0.5rem;" {
                     a href=(confirmation_url) {

File diff suppressed because it is too large
+ 447 - 144
crates/cdk-ldk-node/src/web/templates/layout.rs


+ 1 - 1
crates/cdk-ldk-node/src/web/templates/payments.rs

@@ -17,7 +17,7 @@ pub fn payment_list_item(
     let status_class = match status {
         "Succeeded" => "status-active",
         "Failed" => "status-inactive",
-        "Pending" => "status-badge",
+        "Pending" => "status-pending",
         _ => "status-badge",
     };
 

二進制
crates/cdk-ldk-node/static/images/bg-dark.jpg


Some files were not shown because too many files changed in this diff