Skip to content

Commit 6431d99

Browse files
CopilotGZTimeWalker
andcommitted
Implement Phase 2 and Phase 3: Core models, state management, and basic views
Co-authored-by: GZTimeWalker <28180262+GZTimeWalker@users.noreply.github.com>
1 parent 0adfa0e commit 6431d99

11 files changed

Lines changed: 433 additions & 30 deletions

File tree

crates/wsrx-desktop-gpui/src/main.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,37 @@
11
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
22

33
use anyhow::Result;
4-
use gpui::{App, Application, WindowOptions, WindowBounds, Bounds, Size};
4+
use gpui::{App, AppContext, Application, Bounds, WindowOptions, WindowBounds, px, size};
55

66
mod logging;
7+
mod models;
8+
mod styles;
9+
mod views;
10+
mod components;
11+
mod bridges;
12+
13+
use views::RootView;
714

815
fn main() -> Result<()> {
916
// Initialize logging
1017
let (_console_guard, _file_guard) = logging::setup()?;
1118

1219
// Create and run the GPUI application
13-
Application::new().run(|_cx: &mut App| {
14-
// TODO: Initialize main window and root view
15-
// This will be implemented in the next migration phase
20+
Application::new().run(|cx: &mut App| {
21+
// Create main window with centered bounds
22+
let bounds = Bounds::centered(None, size(px(1200.0), px(800.0)), cx);
23+
24+
cx.open_window(
25+
WindowOptions {
26+
window_bounds: Some(WindowBounds::Windowed(bounds)),
27+
titlebar: None,
28+
..Default::default()
29+
},
30+
|window, cx| cx.new(|cx| RootView::new(window, cx)),
31+
)
32+
.expect("Failed to open window");
33+
34+
cx.activate(true);
1635
});
1736

1837
Ok(())
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Application State - Global application state management
2+
use std::collections::VecDeque;
3+
use super::{Tunnel, Connection, LogEntry, Settings};
4+
5+
/// Current active page in the application
6+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7+
pub enum Page {
8+
GetStarted,
9+
Connections,
10+
NetworkLogs,
11+
Settings,
12+
}
13+
14+
/// Daemon connection status
15+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16+
pub enum DaemonStatus {
17+
Stopped,
18+
Starting,
19+
Running,
20+
Stopping,
21+
Error,
22+
}
23+
24+
/// Global application state
25+
/// This struct holds the main application data that is shared across views
26+
pub struct AppState {
27+
/// Currently active page
28+
pub current_page: Page,
29+
30+
/// List of configured tunnels
31+
pub tunnels: Vec<Tunnel>,
32+
33+
/// Active connections
34+
pub connections: Vec<Connection>,
35+
36+
/// Application settings
37+
pub settings: Settings,
38+
39+
/// Recent log entries (circular buffer)
40+
pub recent_logs: VecDeque<LogEntry>,
41+
42+
/// Maximum number of logs to keep in memory
43+
pub max_logs: usize,
44+
45+
/// Current daemon status
46+
pub daemon_status: DaemonStatus,
47+
}
48+
49+
impl AppState {
50+
/// Create a new AppState with default values
51+
pub fn new() -> Self {
52+
Self {
53+
current_page: Page::GetStarted,
54+
tunnels: Vec::new(),
55+
connections: Vec::new(),
56+
settings: Settings::default(),
57+
recent_logs: VecDeque::new(),
58+
max_logs: 10000,
59+
daemon_status: DaemonStatus::Stopped,
60+
}
61+
}
62+
63+
/// Add a log entry, removing oldest if over capacity
64+
pub fn add_log(&mut self, entry: LogEntry) {
65+
if self.recent_logs.len() >= self.max_logs {
66+
self.recent_logs.pop_front();
67+
}
68+
self.recent_logs.push_back(entry);
69+
}
70+
71+
/// Clear all logs
72+
pub fn clear_logs(&mut self) {
73+
self.recent_logs.clear();
74+
}
75+
76+
/// Add or update a tunnel
77+
pub fn upsert_tunnel(&mut self, tunnel: Tunnel) {
78+
if let Some(pos) = self.tunnels.iter().position(|t| t.id == tunnel.id) {
79+
self.tunnels[pos] = tunnel;
80+
} else {
81+
self.tunnels.push(tunnel);
82+
}
83+
}
84+
85+
/// Remove a tunnel by ID
86+
pub fn remove_tunnel(&mut self, tunnel_id: &str) {
87+
self.tunnels.retain(|t| t.id != tunnel_id);
88+
}
89+
90+
/// Get a tunnel by ID
91+
pub fn get_tunnel(&self, tunnel_id: &str) -> Option<&Tunnel> {
92+
self.tunnels.iter().find(|t| t.id == tunnel_id)
93+
}
94+
}
95+
96+
impl Default for AppState {
97+
fn default() -> Self {
98+
Self::new()
99+
}
100+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Events - Application event definitions for inter-component communication
2+
3+
use super::{Tunnel, Connection, LogEntry};
4+
5+
/// Events that can occur in the application
6+
#[derive(Clone, Debug)]
7+
pub enum AppEvent {
8+
/// Page navigation event
9+
NavigateToPage(super::app_state::Page),
10+
11+
/// Tunnel-related events
12+
TunnelCreated(Tunnel),
13+
TunnelUpdated(Tunnel),
14+
TunnelDeleted(String), // tunnel_id
15+
TunnelEnabled(String),
16+
TunnelDisabled(String),
17+
18+
/// Connection-related events
19+
ConnectionEstablished(Connection),
20+
ConnectionClosed(String), // connection_id
21+
ConnectionError { connection_id: String, error: String },
22+
23+
/// Daemon-related events
24+
DaemonStarted,
25+
DaemonStopped,
26+
DaemonError(String),
27+
28+
/// Log events
29+
LogReceived(LogEntry),
30+
ClearLogs,
31+
32+
/// Settings events
33+
SettingsUpdated,
34+
ThemeChanged,
35+
36+
/// UI events
37+
ShowNotification { title: String, message: String },
38+
ShowError(String),
39+
}

crates/wsrx-desktop-gpui/src/models/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
use serde::{Deserialize, Serialize};
55
use std::net::SocketAddr;
66

7+
pub mod app_state;
8+
pub mod events;
9+
10+
pub use app_state::{AppState, Page, DaemonStatus};
11+
pub use events::AppEvent;
12+
713
/// Represents a WebSocket tunnel configuration
814
#[derive(Clone, Debug, Serialize, Deserialize)]
915
pub struct Tunnel {

crates/wsrx-desktop-gpui/src/styles/mod.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,54 +32,54 @@ pub mod colors {
3232

3333
/// Typography settings
3434
pub mod typography {
35-
use gpui::Pixels;
35+
use gpui::{Pixels, px};
3636

3737
pub fn font_size_xs() -> Pixels {
38-
Pixels(11.0)
38+
px(11.0)
3939
}
4040

4141
pub fn font_size_sm() -> Pixels {
42-
Pixels(12.0)
42+
px(12.0)
4343
}
4444

4545
pub fn font_size_base() -> Pixels {
46-
Pixels(14.0)
46+
px(14.0)
4747
}
4848

4949
pub fn font_size_lg() -> Pixels {
50-
Pixels(16.0)
50+
px(16.0)
5151
}
5252

5353
pub fn font_size_xl() -> Pixels {
54-
Pixels(20.0)
54+
px(20.0)
5555
}
5656
}
5757

5858
/// Spacing constants
5959
pub mod spacing {
60-
use gpui::Pixels;
60+
use gpui::{Pixels, px};
6161

6262
pub fn xs() -> Pixels {
63-
Pixels(2.0)
63+
px(2.0)
6464
}
6565

6666
pub fn sm() -> Pixels {
67-
Pixels(4.0)
67+
px(4.0)
6868
}
6969

7070
pub fn base() -> Pixels {
71-
Pixels(8.0)
71+
px(8.0)
7272
}
7373

7474
pub fn md() -> Pixels {
75-
Pixels(12.0)
75+
px(12.0)
7676
}
7777

7878
pub fn lg() -> Pixels {
79-
Pixels(16.0)
79+
px(16.0)
8080
}
8181

8282
pub fn xl() -> Pixels {
83-
Pixels(24.0)
83+
px(24.0)
8484
}
8585
}
Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
// Connections view - Manage tunnels and connections
2+
use gpui::{Context, Render, Window, div, prelude::*};
3+
use crate::styles::colors;
4+
25
pub struct ConnectionsView {
3-
// TODO: Define state
46
}
57

6-
// Placeholder - Will be implemented in next phase
8+
impl ConnectionsView {
9+
pub fn new(_window: &mut Window, _cx: &mut Context<Self>) -> Self {
10+
Self {}
11+
}
12+
}
13+
14+
impl Render for ConnectionsView {
15+
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
16+
div()
17+
.flex()
18+
.flex_col()
19+
.w_full()
20+
.h_full()
21+
.p_4()
22+
.child(
23+
div()
24+
.text_xl()
25+
.text_color(colors::foreground())
26+
.mb_4()
27+
.child("Connections")
28+
)
29+
.child(
30+
div()
31+
.text_color(colors::foreground())
32+
.child("No tunnels configured yet")
33+
)
34+
}
35+
}
36+
Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,38 @@
11
// Get Started view - Onboarding page
2+
use gpui::{Context, Render, Window, div, prelude::*};
3+
use crate::styles::colors;
4+
25
pub struct GetStartedView {
3-
// TODO: Define state
46
}
57

6-
// Placeholder - Will be implemented in next phase
8+
impl GetStartedView {
9+
pub fn new(_window: &mut Window, _cx: &mut Context<Self>) -> Self {
10+
Self {}
11+
}
12+
}
13+
14+
impl Render for GetStartedView {
15+
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
16+
div()
17+
.flex()
18+
.flex_col()
19+
.items_center()
20+
.justify_center()
21+
.w_full()
22+
.h_full()
23+
.gap_4()
24+
.child(
25+
div()
26+
.text_2xl()
27+
.text_color(colors::foreground())
28+
.child("Welcome to WebSocket Reflector X")
29+
)
30+
.child(
31+
div()
32+
.text_base()
33+
.text_color(colors::foreground())
34+
.child("Get started by creating your first tunnel")
35+
)
36+
}
37+
}
38+
Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
// Network Logs view - Display real-time logs
2+
use gpui::{Context, Render, Window, div, prelude::*};
3+
use crate::styles::colors;
4+
25
pub struct NetworkLogsView {
3-
// TODO: Define state
46
}
57

6-
// Placeholder - Will be implemented in next phase
8+
impl NetworkLogsView {
9+
pub fn new(_window: &mut Window, _cx: &mut Context<Self>) -> Self {
10+
Self {}
11+
}
12+
}
13+
14+
impl Render for NetworkLogsView {
15+
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
16+
div()
17+
.flex()
18+
.flex_col()
19+
.w_full()
20+
.h_full()
21+
.p_4()
22+
.child(
23+
div()
24+
.text_xl()
25+
.text_color(colors::foreground())
26+
.mb_4()
27+
.child("Network Logs")
28+
)
29+
.child(
30+
div()
31+
.text_color(colors::foreground())
32+
.child("No logs yet")
33+
)
34+
}
35+
}
36+

0 commit comments

Comments
 (0)