From de4595febcb3ce64eed3e40575c5c110a282efdb Mon Sep 17 00:00:00 2001
From: Firas Moussa <149446898+fmazmz@users.noreply.github.com>
Date: Tue, 18 Nov 2025 15:06:12 +0100
Subject: [PATCH] Revert "Initial implementation of JavaFX Chat App with ntfy
integration"
---
.env.example | 2 -
.gitignore | 1 -
README.md | 16 +-
pom.xml | 35 ---
.../java/com/example/HelloController.java | 224 +-----------------
src/main/java/com/example/HelloFX.java | 44 +---
.../com/example/client/ChatNetworkClient.java | 17 --
.../example/client/HttpClientProvider.java | 15 --
.../com/example/client/NtfyHttpClient.java | 131 ----------
.../java/com/example/domain/ChatModel.java | 36 ---
.../com/example/domain/NtfyEventResponse.java | 26 --
.../java/com/example/domain/NtfyMessage.java | 72 ------
.../java/com/example/utils/EnvLoader.java | 27 ---
src/main/java/module-info.java | 9 -
.../resources/com/example/hello-view.fxml | 50 +---
src/main/resources/com/example/styles.css | 86 -------
src/test/java/com/example/ChatModelTest.java | 43 ----
.../java/com/example/TestFxInitializer.java | 21 --
18 files changed, 23 insertions(+), 832 deletions(-)
delete mode 100644 .env.example
delete mode 100644 src/main/java/com/example/client/ChatNetworkClient.java
delete mode 100644 src/main/java/com/example/client/HttpClientProvider.java
delete mode 100644 src/main/java/com/example/client/NtfyHttpClient.java
delete mode 100644 src/main/java/com/example/domain/ChatModel.java
delete mode 100644 src/main/java/com/example/domain/NtfyEventResponse.java
delete mode 100644 src/main/java/com/example/domain/NtfyMessage.java
delete mode 100644 src/main/java/com/example/utils/EnvLoader.java
delete mode 100644 src/main/resources/com/example/styles.css
delete mode 100644 src/test/java/com/example/ChatModelTest.java
delete mode 100644 src/test/java/com/example/TestFxInitializer.java
diff --git a/.env.example b/.env.example
deleted file mode 100644
index 1ba9c1af..00000000
--- a/.env.example
+++ /dev/null
@@ -1,2 +0,0 @@
-NTFY_BASE_URL=https://ntfy.sh
-NTFY_TOPIC= //Add your topic here
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 5a54815d..6ac465db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,2 @@
target/
/.idea/
-.env
\ No newline at end of file
diff --git a/README.md b/README.md
index 6df13fda..5fdc622f 100644
--- a/README.md
+++ b/README.md
@@ -11,20 +11,8 @@ A JavaFX-based chat client using [ntfy](https://docs.ntfy.sh/) for backend messa
- Unit tests for `Model` class
- (Advanced) Send files via "Attach local file" option
-
-## Requirements
-
-- **Java**
- - **Version**: `25`
-
-- **Maven Compiler Plugin**
- - **Version**: `3.11.0`
- - **Configuration**:
- - **Release**: `25`
-
-## Usage
-1. Set `JAVA_HOME` to JDK 25.
-2. Create a **.env** file with the required variables. You can also clone and fill **.env.example** and rename it to `.env`.
+## 🚀 Run Instructions
+1. Set `JAVA_HOME` to JDK 25
2. Start with:
```bash
./mvnw clean javafx:run
diff --git a/pom.xml b/pom.xml
index 177d3c50..c40f667e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,21 +17,6 @@
25
-
- org.slf4j
- slf4j-api
- 2.0.9
-
-
- org.slf4j
- slf4j-simple
- 2.0.9
-
-
- com.fasterxml.jackson.core
- jackson-databind
- 2.17.2
-
org.junit.jupiter
junit-jupiter
@@ -60,18 +45,6 @@
javafx-fxml
${javafx.version}
-
- org.testfx
- testfx-junit5
- 4.0.17
- test
-
-
- org.openjfx
- javafx-swing
- ${javafx.version}
- test
-
@@ -90,14 +63,6 @@
true
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.11.0
-
- 25
-
-
diff --git a/src/main/java/com/example/HelloController.java b/src/main/java/com/example/HelloController.java
index 7474c4ff..fdd160a0 100644
--- a/src/main/java/com/example/HelloController.java
+++ b/src/main/java/com/example/HelloController.java
@@ -1,230 +1,22 @@
package com.example;
-import com.example.client.ChatNetworkClient;
-import com.example.domain.ChatModel;
-import com.example.domain.NtfyEventResponse;
-import com.example.domain.NtfyMessage;
-import javafx.animation.KeyFrame;
-import javafx.animation.Timeline;
-import javafx.event.ActionEvent;
import javafx.fxml.FXML;
-import javafx.scene.control.*;
import javafx.scene.control.Label;
-import javafx.scene.control.TextField;
-import javafx.scene.image.Image;
-import javafx.scene.image.ImageView;
-import javafx.scene.layout.VBox;
-import javafx.stage.FileChooser;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.awt.*;
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.time.Instant;
-import java.time.LocalTime;
-import java.time.ZoneId;
-import java.util.Arrays;
-import java.util.List;
-import java.util.UUID;
+/**
+ * Controller layer: mediates between the view (FXML) and the model.
+ */
public class HelloController {
- private static final Logger log = LoggerFactory.getLogger(HelloController.class);
- private ChatNetworkClient client;
- private String baseUrl;
- private String topic;
- private File selectedFile = null;
-
- @FXML
- private Label messageLabel;
-
- @FXML
- private ListView messagesList;
-
- @FXML
- private TextField messageInput;
-
- @FXML
- private TextField titleInput;
-
- @FXML
- private TextField tagsInput;
-
- public void setClient(ChatNetworkClient client, String baseUrl, String topic) {
- this.client = client;
- this.baseUrl = baseUrl;
- this.topic = topic;
- }
-
- public void setModel(ChatModel model) {
- messagesList.setItems(model.getMessages());
- messagesList.setCellFactory(list -> new MessageCell());
- }
-
- private static String formatTime(long epochSeconds) {
- Instant instant = Instant.ofEpochSecond(epochSeconds);
- LocalTime time = LocalTime.ofInstant(instant, ZoneId.systemDefault());
- return time.toString();
- }
-
- private void showStatus(String text) {
- messageLabel.setText(text);
- Timeline t = new Timeline(new KeyFrame(javafx.util.Duration.seconds(3),
- ev -> messageLabel.setText("")));
- t.setCycleCount(1);
- t.play();
- }
+ private final HelloModel model = new HelloModel();
@FXML
- private void onPickAttachment() {
- FileChooser chooser = new FileChooser();
- chooser.setTitle("Select attachment");
- File file = chooser.showOpenDialog(messageInput.getScene().getWindow());
-
- if (file != null) {
- selectedFile = file;
- messageLabel.setText("Attachment selected: " + file.getName());
- }
- }
+ private Label messageLabel;
@FXML
- private void onSend() {
- String txt = messageInput.getText();
-
- if ((txt == null || txt.isBlank()) && selectedFile == null) {
- showStatus("Nothing to send");
- return;
- }
-
- String title = titleInput.getText();
- if (title != null && title.isBlank()) title = null;
-
- String tagsRaw = tagsInput.getText();
- List tags = null;
-
- if (tagsRaw != null && !tagsRaw.isBlank()) {
- tags = java.util.Arrays.stream(tagsRaw.split(","))
- .map(String::trim)
- .filter(s -> !s.isEmpty())
- .toList();
- }
-
- NtfyMessage msg = new NtfyMessage.Builder()
- .id(UUID.randomUUID().toString())
- .time(System.currentTimeMillis())
- .event("message")
- .topic(topic)
- .message(txt)
- .title(title)
- .tags(tags)
- .attach(null)
- .filename(null)
- .build();
-
- try {
- client.send(baseUrl, msg, selectedFile);
- showStatus(selectedFile == null ? "Message sent" : "Attachment sent");
- } catch (InterruptedException | IOException e) {
- showStatus("Error sending: " + e.getMessage());
- }
-
- messageInput.clear();
- titleInput.clear();
- tagsInput.clear();
- selectedFile = null;
- }
-
-
- private static final class MessageCell extends ListCell {
- @Override
- protected void updateItem(NtfyEventResponse msg, boolean empty) {
- super.updateItem(msg, empty);
-
- if (empty || msg == null) {
- setText(null);
- setGraphic(null);
- return;
- }
-
- setText(null);
-
- VBox container = new VBox();
- container.setSpacing(6);
- container.getStyleClass().add("message-bubble");
-
- container.setStyle("-fx-alignment: CENTER_LEFT;");
- if (msg.title() != null) {
- Label titleLabel = new Label(msg.title());
- titleLabel.getStyleClass().add("message-title");
- container.getChildren().add(titleLabel);
- }
-
- if (msg.attachment() != null) {
- NtfyEventResponse.Attachment att = msg.attachment();
-
- if (att.type() != null && att.type().startsWith("image")) {
- Image image = new Image(att.url(), 300, 0, true, true);
- ImageView imageView = new ImageView(image);
- container.getChildren().add(imageView);
- } else {
- Label fileLabel = getFileLabel(att);
- container.getChildren().add(fileLabel);
- }
- }
-
- if (msg.message() != null && !msg.message().isBlank()) {
- Label messageLabel = new Label(msg.message());
- messageLabel.setWrapText(true);
- messageLabel.getStyleClass().add("message-text");
- container.getChildren().add(messageLabel);
- }
-
- if (msg.tags() != null && !msg.tags().isEmpty()) {
- Label tagsLabel = new Label(String.join(", ", msg.tags()));
- tagsLabel.getStyleClass().add("message-tags");
- container.getChildren().add(tagsLabel);
- }
-
- if (msg.time() != null) {
- Label timeLabel = new Label(formatTime(msg.time()));
- timeLabel.getStyleClass().add("message-time");
- container.getChildren().add(timeLabel);
- }
-
- setGraphic(container);
- }
-
- // Helper method to allow user to open file
- private static Label getFileLabel(NtfyEventResponse.Attachment att) {
- Label fileLabel = new Label("Open file: " + (att.name() != null ? att.name() : att.url()));
- fileLabel.setStyle("-fx-text-fill: #2c75ff; -fx-underline: true;");
- fileLabel.setOnMouseClicked(ev -> {
- try {
- String url = att.url();
-
- // method that works on linux as Desktop is not always supported and crashes application
- if (System.getProperty("os.name").toLowerCase().contains("linux")) {
- try {
- new ProcessBuilder("xdg-open", url).start();
- return;
- }catch (IOException e) {
- log.error("Error opening file: {}", url, e);
- }
- }
-
- if (Desktop.isDesktopSupported()) {
- Desktop.getDesktop().browse(new URI(url));
- }
-
- } catch (IOException | URISyntaxException ex) {
- log.error("Failed to open attachment: {}", ex.getMessage());
- log.error(Arrays.toString(ex.getStackTrace()));
- }
- });
- return fileLabel;
+ private void initialize() {
+ if (messageLabel != null) {
+ messageLabel.setText(model.getGreeting());
}
}
}
diff --git a/src/main/java/com/example/HelloFX.java b/src/main/java/com/example/HelloFX.java
index 6d9a8345..96bdc5ca 100644
--- a/src/main/java/com/example/HelloFX.java
+++ b/src/main/java/com/example/HelloFX.java
@@ -1,59 +1,25 @@
package com.example;
-import com.example.client.ChatNetworkClient;
-import com.example.client.NtfyHttpClient;
-import com.example.domain.ChatModel;
-import com.example.domain.NtfyMessage;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Objects;
-import java.util.Properties;
-
-import static com.example.utils.EnvLoader.loadEnv;
public class HelloFX extends Application {
- private static final Logger log = LoggerFactory.getLogger("MAIN");
- static final ChatModel model = new ChatModel();
@Override
public void start(Stage stage) throws Exception {
- Properties env = loadEnv();
- String baseUrl = env.getProperty("NTFY_BASE_URL", "https://ntfy.sh");
- String topic = env.getProperty("NTFY_TOPIC");
-
- if (topic == null || topic.isBlank()) {
- throw new IllegalStateException("NTFY_TOPIC is not set");
- }
-
- FXMLLoader loader = new FXMLLoader(HelloFX.class.getResource("hello-view.fxml"));
- Parent root = loader.load();
-
- HelloController controller = loader.getController();
- controller.setModel(model);
-
- ChatNetworkClient client = new NtfyHttpClient(model);
- controller.setClient(client, baseUrl, topic);
-
- client.subscribe(baseUrl, topic);
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(
- Objects.requireNonNull(HelloFX.class.getResource("styles.css")).toExternalForm()
- );
-
+ FXMLLoader fxmlLoader = new FXMLLoader(HelloFX.class.getResource("hello-view.fxml"));
+ Parent root = fxmlLoader.load();
+ Scene scene = new Scene(root, 640, 480);
+ stage.setTitle("Hello MVC");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
- launch(args);
-
+ launch();
}
}
\ No newline at end of file
diff --git a/src/main/java/com/example/client/ChatNetworkClient.java b/src/main/java/com/example/client/ChatNetworkClient.java
deleted file mode 100644
index 815418e4..00000000
--- a/src/main/java/com/example/client/ChatNetworkClient.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.example.client;
-
-
-import com.example.domain.NtfyMessage;
-
-import java.io.File;
-import java.io.IOException;
-
-public interface ChatNetworkClient {
- Subscription subscribe(String baseUrl, String topic);
- void send(String baseUrl, NtfyMessage message, File file) throws IOException, InterruptedException;
- interface Subscription extends AutoCloseable {
- @Override
- void close();
- boolean isOpen();
- }
-}
diff --git a/src/main/java/com/example/client/HttpClientProvider.java b/src/main/java/com/example/client/HttpClientProvider.java
deleted file mode 100644
index a0e86184..00000000
--- a/src/main/java/com/example/client/HttpClientProvider.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.example.client;
-
-import java.net.http.HttpClient;
-
-public final class HttpClientProvider {
-
- private static final HttpClient INSTANCE = HttpClient.newHttpClient();
-
- private HttpClientProvider() {
- }
-
- public static HttpClient get() {
- return INSTANCE;
- }
-}
diff --git a/src/main/java/com/example/client/NtfyHttpClient.java b/src/main/java/com/example/client/NtfyHttpClient.java
deleted file mode 100644
index e0cb8f5f..00000000
--- a/src/main/java/com/example/client/NtfyHttpClient.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package com.example.client;
-
-import com.example.domain.ChatModel;
-import com.example.domain.NtfyEventResponse;
-import com.example.domain.NtfyMessage;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Stream;
-
-public record NtfyHttpClient(ChatModel model) implements ChatNetworkClient {
-
- private static final ObjectMapper mapper = new ObjectMapper();
- private static final Logger log = LoggerFactory.getLogger("NtfyClient");
-
- @Override
- public Subscription subscribe(String baseUrl, String topic) {
-
- HttpRequest req = HttpRequest.newBuilder()
- .uri(URI.create(baseUrl).resolve(topic + "/json"))
- .header("accept", "application/json")
- .GET()
- .build();
-
- CompletableFuture>> future =
- HttpClientProvider.get().sendAsync(req, HttpResponse.BodyHandlers.ofLines());
-
- AtomicBoolean open = new AtomicBoolean(true);
-
- future.thenAccept(response -> {
- response.body().forEach(line -> {
-
- log.debug("Raw event received: {}", line);
-
- if (!open.get()) return;
-
- try {
- NtfyEventResponse msg = mapper.readValue(line, NtfyEventResponse.class);
- if (msg.event().equals("message")) {
- model.addMessage(msg);
- log.info("Message added: {}", msg);
- }
- } catch (JsonProcessingException e) {
- log.error("Error parsing event: {}", line, e);
- }
- });
- }).exceptionally(ex -> {
- log.error("Error while subscribing to topic {}", topic, ex);
- open.set(false);
- return null;
- });
- log.info("Subscribing to topic: {}", topic);
-
- return new Subscription() {
- @Override
- public void close() {
- open.set(false);
- future.cancel(true);
- }
-
- @Override
- public boolean isOpen() {
- return open.get();
- }
- };
- }
-
- @Override
- public void send(String baseUrl, NtfyMessage msg, File attachment) throws IOException, InterruptedException {
-
- if (attachment != null) {
- sendWithAttachment(baseUrl, msg, attachment);
- return;
- }
-
- sendJsonOnly(baseUrl, msg);
- }
-
- private void sendJsonOnly(String baseUrl, NtfyMessage msg) throws IOException, InterruptedException {
- String json = mapper.writeValueAsString(msg);
-
- HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(baseUrl))
- .header("Content-Type", "application/json")
- .POST(HttpRequest.BodyPublishers.ofString(json))
- .build();
-
- var response = HttpClientProvider.get().send(request, HttpResponse.BodyHandlers.ofString());
- int statusCode = response.statusCode();
-
- if (statusCode >= 200 && statusCode < 300) {
- log.debug("Sent message payload: {}", json);
- log.info("Message sent");
- }
- log.error("Failed to send message payload: {}", json);
- throw new IOException("Failed to send message payload: " + statusCode);
- }
-
- private void sendWithAttachment(String baseUrl, NtfyMessage msg, File file)
- throws IOException, InterruptedException {
-
- HttpRequest request = HttpRequest.newBuilder()
- .uri(URI.create(baseUrl).resolve((msg.topic())))
- .header("Filename", file.getName())
- .PUT(HttpRequest.BodyPublishers.ofFile(file.toPath()))
- .build();
-
-
- var response = HttpClientProvider.get().send(request, HttpResponse.BodyHandlers.ofString());
-
- int statusCode = response.statusCode();
- if (200 <= statusCode && statusCode < 300) {
- log.debug("Attachment sent: {}", statusCode);
- log.info("status: {}", statusCode);
- }
- log.error("Failed to send attachment: {}", statusCode);
- throw new IOException("Failed to send attachment: " + statusCode);
-
- }
-
-
-}
diff --git a/src/main/java/com/example/domain/ChatModel.java b/src/main/java/com/example/domain/ChatModel.java
deleted file mode 100644
index 3524a777..00000000
--- a/src/main/java/com/example/domain/ChatModel.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.example.domain;
-
-import javafx.application.Platform;
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
-
-
-public class ChatModel {
- private final ObservableList messages = FXCollections.observableArrayList();
-
-
- public void addMessage(NtfyEventResponse msg) {
- runOnFx(() -> messages.add(msg));
- }
-
- public ObservableList getMessages() {
- return messages;
- }
-
- private static void runOnFx(Runnable task) {
- try {
- if (Platform.isFxApplicationThread()) {
- task.run();
- } else if (!Platform.isImplicitExit()) {
- Platform.runLater(task);
- } else {
- // execute test case immediately
- task.run();
- }
- } catch (IllegalStateException notInitialized) {
- task.run();
- }
- }
-
-
-}
diff --git a/src/main/java/com/example/domain/NtfyEventResponse.java b/src/main/java/com/example/domain/NtfyEventResponse.java
deleted file mode 100644
index 603f2a8d..00000000
--- a/src/main/java/com/example/domain/NtfyEventResponse.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.example.domain;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-
-import java.util.List;
-
-@JsonIgnoreProperties(ignoreUnknown = true)
-public record NtfyEventResponse(
- String id,
- Long time,
- String event,
- String topic,
- String message,
- String title,
- List tags,
- Attachment attachment
-) {
- @JsonIgnoreProperties(ignoreUnknown = true)
- public record Attachment(
- String name,
- String type,
- Long size,
- Long expires,
- String url
- ) {}
-}
\ No newline at end of file
diff --git a/src/main/java/com/example/domain/NtfyMessage.java b/src/main/java/com/example/domain/NtfyMessage.java
deleted file mode 100644
index d838d5fc..00000000
--- a/src/main/java/com/example/domain/NtfyMessage.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.example.domain;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-
-import java.util.List;
-
-@JsonIgnoreProperties(ignoreUnknown = true)
-public record NtfyMessage(
- String id,
- Long time,
- String event,
- String topic,
- String message,
- String title,
- List tags,
- String attach,
- String filename
-) {
- public static class Builder {
- private String id;
- private Long time;
- private String event;
- private String topic;
- private String message;
- private String title;
- private List tags;
- private String attach;
- private String filename;
-
- public Builder id(String id) {
- this.id = id;
- return this;
- }
-
- public Builder time(long time) {
- this.time = time;
- return this;
- }
-
- public Builder event(String event) {
- this.event = event;
- return this;
- }
-
- public Builder topic(String topic) {
- this.topic = topic;
- return this;
- }
-
- public Builder message(String message) {
- this.message = message;
- return this;
- }
-
- public Builder title(String title) {
- this.title = title;
- return this;
- }
-
- public Builder tags(List tags) {
- this.tags = tags;
- return this;
- }
-
- public Builder attach(String attach) { this.attach = attach; return this; }
- public Builder filename(String filename) { this.filename = filename; return this; }
-
- public NtfyMessage build() {
- return new NtfyMessage(id, time, event, topic, message, title, tags, attach, filename);
- }
- }
-}
diff --git a/src/main/java/com/example/utils/EnvLoader.java b/src/main/java/com/example/utils/EnvLoader.java
deleted file mode 100644
index 73ad87be..00000000
--- a/src/main/java/com/example/utils/EnvLoader.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.example.utils;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Properties;
-
-public class EnvLoader {
- private static final Logger log = LoggerFactory.getLogger(EnvLoader.class);
-
- public static Properties loadEnv() {
- Properties props = new Properties();
-
- try (FileInputStream fis = new FileInputStream(".env")) {
- props.load(fis);
- } catch (FileNotFoundException e) {
- log.error("Could not load .env file", e);
- } catch (IOException e) {
- log.error("Failed to load env file: ", e);
- }
-
- return props;
- }
-}
\ No newline at end of file
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 0c455103..71574a27 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -1,16 +1,7 @@
module hellofx {
requires javafx.controls;
requires javafx.fxml;
- requires java.net.http;
- requires com.fasterxml.jackson.annotation;
- requires com.fasterxml.jackson.databind;
- requires java.logging;
- requires org.slf4j;
- requires java.desktop;
opens com.example to javafx.fxml;
- opens com.example.domain to com.fasterxml.jackson.databind;
exports com.example;
- exports com.example.domain;
- exports com.example.client;
}
\ No newline at end of file
diff --git a/src/main/resources/com/example/hello-view.fxml b/src/main/resources/com/example/hello-view.fxml
index b080e6d3..20a7dc82 100644
--- a/src/main/resources/com/example/hello-view.fxml
+++ b/src/main/resources/com/example/hello-view.fxml
@@ -1,43 +1,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/example/styles.css b/src/main/resources/com/example/styles.css
deleted file mode 100644
index 5c592f9d..00000000
--- a/src/main/resources/com/example/styles.css
+++ /dev/null
@@ -1,86 +0,0 @@
-/* Global dark background */
-.root {
- -fx-background-color: #1b1b1d;
-}
-
-/* ListView container */
-.list-view {
- -fx-background-color: #1b1b1d;
- -fx-padding: 10;
-}
-
-/* Remove default selection white flash */
-.list-cell:filled:selected,
-.list-cell:filled:selected:focused {
- -fx-background-color: transparent;
-}
-
-/* Remove default hover flash */
-.list-cell:hover {
- -fx-background-color: transparent;
-}
-
-/* Base cell styling */
-.list-cell {
- -fx-background-color: transparent;
- -fx-padding: 6 0 6 0;
-}
-
-/* Dark message bubble */
-.message-bubble {
- -fx-background-color: #2a2a2d;
- -fx-background-radius: 10;
- -fx-padding: 10 14 10 14;
- -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.30), 10, 0, 0, 2);
-}
-
-/* Hover effect for message bubble */
-.list-cell:hover .message-bubble {
- -fx-background-color: #353538;
- -fx-effect: dropshadow(gaussian, rgba(255,255,255,0.05), 16, 0, 0, 1);
-}
-
-/* Title text */
-.message-title {
- -fx-font-size: 13px;
- -fx-font-weight: bold;
- -fx-text-fill: #e8e8e8;
-}
-
-/* Message text */
-.message-text {
- -fx-font-size: 14px;
- -fx-text-fill: #f5f5f5;
-}
-
-/* Tags text */
-.message-tags {
- -fx-font-size: 11px;
- -fx-text-fill: #6aa0ff;
-}
-
-/* Timestamp */
-.message-time {
- -fx-font-size: 10px;
- -fx-text-fill: #a0a0a0;
-}
-
-/* Inputs styling */
-.text-field {
- -fx-background-color: #2a2a2d;
- -fx-text-fill: #f5f5f5;
- -fx-background-radius: 6;
- -fx-border-radius: 6;
- -fx-prompt-text-fill: #777777;
-}
-
-/* Send button */
-.button {
- -fx-background-color: #3d66ff;
- -fx-text-fill: white;
- -fx-background-radius: 6;
-}
-
-.button:hover {
- -fx-background-color: #5176ff;
-}
diff --git a/src/test/java/com/example/ChatModelTest.java b/src/test/java/com/example/ChatModelTest.java
deleted file mode 100644
index 776f2f77..00000000
--- a/src/test/java/com/example/ChatModelTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.example;
-
-import com.example.TestFxInitializer;
-import com.example.domain.ChatModel;
-import com.example.domain.NtfyEventResponse;
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.*;
-
-public class ChatModelTest extends TestFxInitializer {
-
- @Test
- void addMessageShouldAppend() {
- ChatModel model = new ChatModel();
-
- NtfyEventResponse msg = new NtfyEventResponse(
- "1", 100L, "message", "topic", "Hello", null, null, null
- );
-
- model.addMessage(msg);
-
- assertEquals(1, model.getMessages().size());
- assertEquals(msg, model.getMessages().get(0));
- }
-
- @Test
- void addMessageFromBackgroundThreadShouldWork() throws InterruptedException {
- ChatModel model = new ChatModel();
-
- NtfyEventResponse msg = new NtfyEventResponse(
- "2", 200L, "message", "topic", "Background", null, null, null
- );
-
- Thread t = new Thread(() -> model.addMessage(msg));
- t.start();
- t.join();
-
- // Allow queue to be processed before proceeding to assert
- Thread.sleep(1000);
-
- assertEquals(1, model.getMessages().size());
- }
-}
diff --git a/src/test/java/com/example/TestFxInitializer.java b/src/test/java/com/example/TestFxInitializer.java
deleted file mode 100644
index 38ca8fc1..00000000
--- a/src/test/java/com/example/TestFxInitializer.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.example;
-
-import javafx.application.Platform;
-import org.junit.jupiter.api.BeforeAll;
-
-public class TestFxInitializer {
-
- private static boolean initialized = false;
-
- @BeforeAll
- static void initFx() {
- if (!initialized) {
- try {
- Platform.startup(() -> {});
- } catch (IllegalStateException ignored) {
- // FX already started
- }
- initialized = true;
- }
- }
-}