Skip to content

Commit 376e157

Browse files
CopilotGZTimeWalker
andcommitted
Implement detailed views for Connections, Network Logs, and Settings pages
Co-authored-by: GZTimeWalker <[email protected]>
1 parent 8f63aab commit 376e157

File tree

4 files changed

+375
-27
lines changed

4 files changed

+375
-27
lines changed
Lines changed: 115 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,95 @@
11
// Connections view - Manage tunnels and connections
2-
use gpui::{Context, Render, Window, div, prelude::*};
2+
use gpui::{Context, Render, Window, div, prelude::*, SharedString, AnyElement};
33
use crate::styles::colors;
4+
use crate::models::{Tunnel, ConnectionStatus};
45

56
pub struct ConnectionsView {
7+
tunnels: Vec<Tunnel>,
68
}
79

810
impl ConnectionsView {
911
pub fn new(_window: &mut Window, _cx: &mut Context<Self>) -> Self {
10-
Self {}
12+
Self {
13+
tunnels: Vec::new(),
14+
}
15+
}
16+
17+
fn render_tunnel_item(&self, tunnel: &Tunnel, index: usize) -> impl IntoElement {
18+
let id = SharedString::from(format!("tunnel-{}", index));
19+
let status_color = if tunnel.enabled {
20+
colors::success()
21+
} else {
22+
gpui::rgba(0x888888ff)
23+
};
24+
25+
div()
26+
.id(id)
27+
.flex()
28+
.items_center()
29+
.justify_between()
30+
.px_4()
31+
.py_3()
32+
.mb_2()
33+
.bg(gpui::rgba(0x2a2a2aff))
34+
.rounded_md()
35+
.hover(|div| div.bg(gpui::rgba(0x333333ff)))
36+
.child(
37+
div()
38+
.flex()
39+
.items_center()
40+
.gap_3()
41+
.child(
42+
div()
43+
.w_3()
44+
.h_3()
45+
.rounded_full()
46+
.bg(status_color)
47+
)
48+
.child(
49+
div()
50+
.flex()
51+
.flex_col()
52+
.gap_1()
53+
.child(
54+
div()
55+
.text_color(colors::foreground())
56+
.child(tunnel.name.clone())
57+
)
58+
.child(
59+
div()
60+
.text_sm()
61+
.text_color(gpui::rgba(0xaaaaaaff))
62+
.child(format!("{} → {}", tunnel.local_addr, tunnel.remote_addr))
63+
)
64+
)
65+
)
66+
.child(
67+
div()
68+
.text_sm()
69+
.text_color(gpui::rgba(0xaaaaaaff))
70+
.child(if tunnel.enabled { "Enabled" } else { "Disabled" })
71+
)
72+
}
73+
74+
fn render_empty_state(&self) -> impl IntoElement {
75+
div()
76+
.flex()
77+
.flex_col()
78+
.items_center()
79+
.justify_center()
80+
.flex_1()
81+
.gap_4()
82+
.child(
83+
div()
84+
.text_xl()
85+
.text_color(gpui::rgba(0xaaaaaaff))
86+
.child("No tunnels configured")
87+
)
88+
.child(
89+
div()
90+
.text_color(gpui::rgba(0x888888ff))
91+
.child("Click the + button to create your first tunnel")
92+
)
1193
}
1294
}
1395

@@ -21,16 +103,40 @@ impl Render for ConnectionsView {
21103
.p_4()
22104
.child(
23105
div()
24-
.text_xl()
25-
.text_color(colors::foreground())
106+
.flex()
107+
.items_center()
108+
.justify_between()
26109
.mb_4()
27-
.child("Connections")
110+
.child(
111+
div()
112+
.text_xl()
113+
.text_color(colors::foreground())
114+
.child("Connections")
115+
)
116+
.child(
117+
div()
118+
.id("add-tunnel-button")
119+
.px_4()
120+
.py_2()
121+
.bg(colors::accent())
122+
.rounded_md()
123+
.cursor_pointer()
124+
.hover(|div| div.bg(gpui::rgba(0x0088ddff)))
125+
.child("+ Add Tunnel")
126+
)
28127
)
29-
.child(
128+
.child(if self.tunnels.is_empty() {
129+
self.render_empty_state().into_any_element()
130+
} else {
131+
let elements: Vec<_> = self.tunnels.iter().enumerate()
132+
.map(|(i, tunnel)| self.render_tunnel_item(tunnel, i))
133+
.collect();
30134
div()
31-
.text_color(colors::foreground())
32-
.child("No tunnels configured yet")
33-
)
135+
.flex()
136+
.flex_col()
137+
.gap_2()
138+
.children(elements).into_any_element()
139+
})
34140
}
35141
}
36142

Lines changed: 142 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,166 @@
11
// Network Logs view - Display real-time logs
2-
use gpui::{Context, Render, Window, div, prelude::*};
2+
use gpui::{Context, Render, Window, div, prelude::*, SharedString};
33
use crate::styles::colors;
4+
use crate::models::{LogEntry, LogLevel};
5+
use std::collections::VecDeque;
46

57
pub struct NetworkLogsView {
8+
logs: VecDeque<LogEntry>,
69
}
710

811
impl NetworkLogsView {
912
pub fn new(_window: &mut Window, _cx: &mut Context<Self>) -> Self {
10-
Self {}
13+
Self {
14+
logs: VecDeque::new(),
15+
}
16+
}
17+
18+
fn log_level_color(&self, level: LogLevel) -> gpui::Rgba {
19+
match level {
20+
LogLevel::Debug => gpui::rgba(0x888888ff),
21+
LogLevel::Info => colors::foreground(),
22+
LogLevel::Warn => colors::warning(),
23+
LogLevel::Error => colors::error(),
24+
}
25+
}
26+
27+
fn log_level_text(&self, level: LogLevel) -> &'static str {
28+
match level {
29+
LogLevel::Debug => "DEBUG",
30+
LogLevel::Info => "INFO",
31+
LogLevel::Warn => "WARN",
32+
LogLevel::Error => "ERROR",
33+
}
34+
}
35+
36+
fn render_log_entry(&self, entry: &LogEntry, index: usize) -> impl IntoElement {
37+
let id = SharedString::from(format!("log-entry-{}", index));
38+
let level_color = self.log_level_color(entry.level);
39+
let level_text = self.log_level_text(entry.level);
40+
41+
div()
42+
.id(id)
43+
.flex()
44+
.items_start()
45+
.gap_3()
46+
.px_4()
47+
.py_2()
48+
.border_b_1()
49+
.border_color(gpui::rgba(0x2a2a2aff))
50+
.hover(|div| div.bg(gpui::rgba(0x00000020)))
51+
.child(
52+
div()
53+
.flex()
54+
.items_center()
55+
.gap_2()
56+
.min_w_32()
57+
.child(
58+
div()
59+
.text_sm()
60+
.text_color(gpui::rgba(0xaaaaaaff))
61+
.child(entry.timestamp.clone())
62+
)
63+
)
64+
.child(
65+
div()
66+
.text_sm()
67+
.text_color(level_color)
68+
.min_w_16()
69+
.child(level_text)
70+
)
71+
.child(
72+
div()
73+
.text_sm()
74+
.text_color(gpui::rgba(0x888888ff))
75+
.min_w_32()
76+
.child(entry.target.clone())
77+
)
78+
.child(
79+
div()
80+
.flex_1()
81+
.text_sm()
82+
.text_color(colors::foreground())
83+
.child(entry.message.clone())
84+
)
85+
}
86+
87+
fn render_empty_state(&self) -> impl IntoElement {
88+
div()
89+
.flex()
90+
.flex_col()
91+
.items_center()
92+
.justify_center()
93+
.flex_1()
94+
.gap_4()
95+
.child(
96+
div()
97+
.text_xl()
98+
.text_color(gpui::rgba(0xaaaaaaff))
99+
.child("No logs yet")
100+
)
101+
.child(
102+
div()
103+
.text_color(gpui::rgba(0x888888ff))
104+
.child("Logs will appear here when connections are active")
105+
)
11106
}
12107
}
13108

14109
impl Render for NetworkLogsView {
15-
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
110+
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
16111
div()
17112
.flex()
18113
.flex_col()
19114
.w_full()
20115
.h_full()
21-
.p_4()
22116
.child(
23117
div()
24-
.text_xl()
25-
.text_color(colors::foreground())
26-
.mb_4()
27-
.child("Network Logs")
118+
.flex()
119+
.items_center()
120+
.justify_between()
121+
.px_4()
122+
.py_3()
123+
.border_b_1()
124+
.border_color(gpui::rgba(0x2a2a2aff))
125+
.child(
126+
div()
127+
.text_xl()
128+
.text_color(colors::foreground())
129+
.child("Network Logs")
130+
)
131+
.child(
132+
div()
133+
.flex()
134+
.gap_2()
135+
.child(
136+
div()
137+
.id("clear-logs-button")
138+
.px_3()
139+
.py_1()
140+
.text_sm()
141+
.bg(gpui::rgba(0x444444ff))
142+
.rounded_md()
143+
.cursor_pointer()
144+
.hover(|div| div.bg(gpui::rgba(0x555555ff)))
145+
.on_click(cx.listener(|this, _event, _window, cx| {
146+
this.logs.clear();
147+
cx.notify();
148+
}))
149+
.child("Clear")
150+
)
151+
)
28152
)
29-
.child(
153+
.child(if self.logs.is_empty() {
154+
self.render_empty_state().into_any_element()
155+
} else {
156+
let elements: Vec<_> = self.logs.iter().enumerate()
157+
.map(|(i, log)| self.render_log_entry(log, i))
158+
.collect();
30159
div()
31-
.text_color(colors::foreground())
32-
.child("No logs yet")
33-
)
160+
.flex()
161+
.flex_col()
162+
.children(elements).into_any_element()
163+
})
34164
}
35165
}
36166

crates/wsrx-desktop-gpui/src/views/root.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ impl RootView {
2222
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
2323
let current_page = Page::GetStarted;
2424

25-
let mut root = Self {
25+
let root = Self {
2626
current_page,
2727
sidebar: cx.new(|cx| SidebarView::new(window, cx, current_page)),
2828
get_started: cx.new(|cx| GetStartedView::new(window, cx)),

0 commit comments

Comments
 (0)