diff --git a/gui/assets/arrow-right-svgrepo-com.svg b/gui/assets/arrow-right-svgrepo-com.svg
new file mode 100644
index 0000000..4ae7291
--- /dev/null
+++ b/gui/assets/arrow-right-svgrepo-com.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/gui/assets/earth-14-svgrepo-com.svg b/gui/assets/earth-14-svgrepo-com.svg
new file mode 100644
index 0000000..0512760
--- /dev/null
+++ b/gui/assets/earth-14-svgrepo-com.svg
@@ -0,0 +1,135 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/gui/assets/main.scss b/gui/assets/main.scss
index f6054ab..f93216b 100644
--- a/gui/assets/main.scss
+++ b/gui/assets/main.scss
@@ -431,3 +431,128 @@ a:visited {
}
}
}
+
+.server-list-page {
+ display: flex;
+ flex-direction: column;
+ padding: 1.5rem;
+ gap: 1rem;
+}
+
+.server-list-page h1 {
+ text-align: center;
+}
+
+.login_version {
+ font-size: 0.55em;
+ font-weight: 400;
+ color: rgba(255, 255, 255, 0.4);
+ vertical-align: middle;
+}
+
+.server-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ width: 500px;
+ margin: 0 auto;
+}
+
+/* Rounded card */
+.server-card {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding: 1rem 1.25rem;
+ border-radius: 12px;
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.server-card__icon {
+ width: 32px;
+ height: 32px;
+ opacity: 0.65;
+ filter: brightness(0) invert(0.8); /* light gray */
+ flex-shrink: 0;
+}
+
+.server-card__info {
+ display: flex;
+ flex-direction: column;
+ gap: 0.15rem;
+ flex: 1; /* pushes the connect button to the far right */
+ min-width: 0; /* prevents text overflow from breaking flex layout */
+}
+
+.server-card__name {
+ font-weight: 600;
+ font-size: 0.95rem;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.server-card__address {
+ font-size: 0.78rem;
+ opacity: 0.55;
+}
+
+
+.server-card__connect {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 38px;
+ height: 38px;
+ padding: 0;
+ line-height: 0;
+ border-radius: 8px;
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ background: rgba(255, 255, 255, 0.07);
+ cursor: pointer;
+ transition: background 0.15s, border-color 0.15s, transform 0.1s;
+}
+
+.server-card__connect img {
+ width: 20px;
+ height: 20px;
+ filter: brightness(0) invert(0.8); /* light gray */
+ opacity: 0.75;
+ transition: opacity 0.15s;
+}
+
+.server-card__connect:hover {
+ background: rgba(255, 255, 255, 0.15);
+ border-color: rgba(255, 255, 255, 0.35);
+ transform: scale(1.08);
+}
+
+.server-card__connect:hover img {
+ opacity: 1.0;
+}
+
+.server-card__connect:active {
+ transform: scale(0.95);
+}
+
+/* Add server — dashed outline style to distinguish from real cards */
+.add-server-btn {
+ width: 100%;
+ padding: 0.85rem;
+ border-radius: 12px;
+ border: 2px dashed rgba(255, 255, 255, 0.2);
+ background: transparent;
+ color: rgba(255, 255, 255, 0.45);
+ font-size: 0.9rem;
+ cursor: pointer;
+ transition: border-color 0.15s, color 0.15s;
+ width: 500px;
+ margin: 0 auto;
+}
+
+.add-server-btn:hover {
+ border-color: rgba(255, 255, 255, 0.4);
+ color: rgba(255, 255, 255, 0.7);
+}
diff --git a/gui/src/app.rs b/gui/src/app.rs
index a56e647..7534c23 100644
--- a/gui/src/app.rs
+++ b/gui/src/app.rs
@@ -752,83 +752,73 @@ pub fn LoginView(config: Resource) -> Element {
),
Connected => unreachable!(),
};
+
+ struct Server {
+ name: String,
+ username: String,
+ address: String,
+ }
+
+ let servers: [Server; 3] = [
+ Server {
+ name: "name0".to_string(),
+ username: "username0".to_string(),
+ address: "address0".to_string(),
+ },
+ Server {
+ name: "name1".to_string(),
+ username: "username1".to_string(),
+ address: "address1".to_string(),
+ },
+ Server {
+ name: "name2".to_string(),
+ username: "username2".to_string(),
+ address: "address2".to_string(),
+ },
+ ];
+
let version = option_env!("MUMBLE_WEB2_VERSION");
rsx!(
div {
- class: "login",
+ class: "server-list-page",
h1 {
"Mumble Web"
match version {
- Some(v) => rsx!(" " span { class: "login_version", "({v})" }),
+ Some(v) => rsx!(div { class: "login_version", "({v})" }),
None => rsx!(),
}
}
- if config.read().as_ref().is_some_and(|cfg| cfg.any_server) {
- div {
- label {
- for: "address-entry",
- "Server Address:"
- }
- input {
- id: "address-entry",
- placeholder: "address",
- value: "{address.read()}",
- autofocus: "true",
- oninput: move |evt| address_input.set(Some(evt.value().clone())),
+ div {
+ class: "server-list",
+ for server in servers {
+ div {
+ key: "{server.address}", // use the most unique field
+ class: "server-card",
+ img {
+ class: "server-card__icon",
+ src: asset!("assets/earth-14-svgrepo-com.svg"),
+ alt: "Server icon",
+ }
+ div {
+ class: "server-card__info",
+ span { class: "server-card__name", "{server.name}" }
+ span { class: "server-card__address", "{server.address}" }
+ }
+ button {
+ class: "server-card__connect",
+ onclick: move |_| { /* TODO: initiate connection */ },
+ img {
+ src: asset!("assets/arrow-right-svgrepo-com.svg"),
+ alt: "Connect",
+ }
+ }
}
}
}
- div {
- label {
- for: "username-entry",
- "Username:"
- //style: "color: rgba(255, 255, 255, 0.5); font-variation-settings: 'FILL' 1, 'wght' 700, 'GRAD' 0, 'opsz' 48; vertical-align: middle; font-size: 35px; user-select: none;",
- }
- input {
- id: "username-entry",
- placeholder: "username",
- value: "{username.read()}",
- autofocus: "true",
- oninput: move |evt| username.set(evt.value().clone()),
- }
- }
- div {
- match &*last_status.read() {
- None => rsx!(div {
- class: "login_status",
- span {"···"}
- }),
- Some(Ok(ServerStatus { success: false, .. })) => rsx!(div {
- class: "login_status is_error",
- span {
- "Could not reach server"
- }
- }),
- Some(Ok(status)) => rsx!(div {
- class: "login_status",
- if let (Some(users), Some(max_users)) = (status.users, status.max_users) {
- span {"{users}/{max_users} Online"}
- } else {
- span {"Unknown Online"}
- }
- span {"-"}
- if let Some((maj, min, pat)) = status.version {
- span {"Version: {maj}.{min}.{pat}"}
- } else {
- span {"Unknown Version"}
- }
- }),
- Some(Err(_)) => rsx!(div {
- class: "login_status is_error",
- span {
- "Could not reach proxy server"
- }
- }),
- }
- div {
- {bottom}
- }
-
+ button {
+ class: "add-server-btn",
+ onclick: move |_| {},
+ "+ Add Server"
}
}
)
diff --git a/gui/src/imp/desktop.rs b/gui/src/imp/desktop.rs
index 34a59f8..00c2404 100644
--- a/gui/src/imp/desktop.rs
+++ b/gui/src/imp/desktop.rs
@@ -81,8 +81,8 @@ impl super::PlatformInterface for DesktopPlatform {
fn get_config_path() -> std::path::PathBuf {
let strategy = choose_app_strategy(AppStrategyArgs {
- top_level_domain: "com".to_string(),
- author: "Ohea Corp".to_string(),
+ top_level_domain: "xyz".to_string(),
+ author: "ohea".to_string(),
app_name: "Mumble Web2".to_string(),
})
.expect("failed to choose app strategy");