diff --git a/pom.xml b/pom.xml
index 5c1cef2..6c6c0af 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,7 +36,7 @@
eu.m724
chatapi
- 1.0
+ 1.1.1
diff --git a/src/main/java/eu/m724/chaqt/ChatWindowViewModel.java b/src/main/java/eu/m724/chaqt/ChatWindowViewModel.java
deleted file mode 100644
index a217c5c..0000000
--- a/src/main/java/eu/m724/chaqt/ChatWindowViewModel.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package eu.m724.chaqt;
-
-import eu.m724.chatapi.chat.Chat;
-
-public class ChatWindowViewModel {
- private ChatWindowWidget widget;
-
- private Chat chat;
-
- public ChatWindowViewModel(ChatWindowWidget widget, Chat chat) {
- this.widget = widget;
- this.chat = chat;
- }
-}
diff --git a/src/main/java/eu/m724/chaqt/ChatWindowWidget.java b/src/main/java/eu/m724/chaqt/ChatWindowWidget.java
deleted file mode 100644
index 9178950..0000000
--- a/src/main/java/eu/m724/chaqt/ChatWindowWidget.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package eu.m724.chaqt;
-
-import eu.m724.chatapi.chat.Chat;
-import io.qt.core.Qt;
-import io.qt.widgets.*;
-
-public class ChatWindowWidget extends QWidget {
- private ChatWindowViewModel viewModel;
-
- private QVBoxLayout layout;
- private QTextEdit textEdit;
-
- public ChatWindowWidget(Chat chat) {
- this.viewModel = new ChatWindowViewModel(this, chat);
-
- //
-
- this.layout = new QVBoxLayout();
- layout.setAlignment(Qt.AlignmentFlag.AlignBottom); // or top?
-
- QWidget widget = new QWidget();
- widget.setLayout(layout);
-
- QScrollArea scrollArea = new QScrollArea();
- scrollArea.setWidgetResizable(true);
- scrollArea.setWidget(widget);
-
- //
-
- this.textEdit = new QTextEdit();
- textEdit.setPlaceholderText("Write a message...");
- textEdit.textChanged.connect(this, "handleComposerType()");
-
- QPushButton sendButton = new QPushButton("Send");
-
- QHBoxLayout composerLayout = new QHBoxLayout();
- composerLayout.addWidget(textEdit);
- composerLayout.addWidget(sendButton, 0, Qt.AlignmentFlag.AlignBottom);
-
- QWidget composerWidget = new QWidget();
- composerWidget.setLayout(composerLayout);
-
- handleComposerType(); // TODO make it resizable and fix it
-
- //
-
- QVBoxLayout mainLayout = new QVBoxLayout();
- mainLayout.addWidget(scrollArea, 1);
- mainLayout.addWidget(composerWidget);
- setLayout(mainLayout);
-
- for (int i=0; i<10; i++) {
- QPushButton pushButton = new QPushButton("hello");
- layout.addWidget(pushButton);
- }
- }
-
- public void handleComposerType() {
- int height = (int) textEdit.document().size().height();
- textEdit.setFixedHeight(height + textEdit.contentsMargins().top() + textEdit.contentsMargins().bottom());
- }
-}
diff --git a/src/main/java/eu/m724/chaqt/Main.java b/src/main/java/eu/m724/chaqt/Main.java
index 6d9f28e..796f4bf 100644
--- a/src/main/java/eu/m724/chaqt/Main.java
+++ b/src/main/java/eu/m724/chaqt/Main.java
@@ -1,8 +1,8 @@
package eu.m724.chaqt;
+import eu.m724.chaqt.chatwindow.ChatWindowWidget;
import eu.m724.chatapi.chat.Chat;
import io.qt.widgets.QApplication;
-import io.qt.widgets.QMessageBox;
public class Main {
@@ -11,9 +11,10 @@ public class Main {
Chat chat = new Chat();
- ChatWindowWidget widget = new ChatWindowWidget(chat);
+ ChatWindowWidget widget = new ChatWindowWidget();
widget.show();
QApplication.exec();
+ QApplication.shutdown();
}
}
\ No newline at end of file
diff --git a/src/main/java/eu/m724/chaqt/ObservableChat.java b/src/main/java/eu/m724/chaqt/ObservableChat.java
new file mode 100644
index 0000000..d760ad0
--- /dev/null
+++ b/src/main/java/eu/m724/chaqt/ObservableChat.java
@@ -0,0 +1,22 @@
+package eu.m724.chaqt;
+
+import eu.m724.chatapi.chat.Chat;
+import eu.m724.chatapi.chat.ChatMessage;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public class ObservableChat extends Chat {
+ private Set> watchers = new HashSet<>();
+
+ @Override
+ public void addMessage(ChatMessage message) {
+ super.addMessage(message);
+ watchers.forEach(c -> c.accept(message));
+ }
+
+ public void watch(Consumer callable) {
+ this.watchers.add(callable);
+ }
+}
diff --git a/src/main/java/eu/m724/chaqt/QMessage.java b/src/main/java/eu/m724/chaqt/QMessage.java
new file mode 100644
index 0000000..4ddcdaa
--- /dev/null
+++ b/src/main/java/eu/m724/chaqt/QMessage.java
@@ -0,0 +1,30 @@
+package eu.m724.chaqt;
+
+import io.qt.core.QMargins;
+import io.qt.gui.QPalette;
+import io.qt.widgets.QLabel;
+import io.qt.widgets.QVBoxLayout;
+import io.qt.widgets.QWidget;
+
+// TODO I think we could put Qtextdocument here
+public class QMessage extends QWidget {
+ private final QLabel label;
+
+ public QMessage(boolean assistant, String text) {
+ this.label = new QLabel(text);
+ label.setWordWrap(true);
+
+ QVBoxLayout layout = new QVBoxLayout();
+ layout.addWidget(label);
+
+ if (assistant) {
+ layout.setContentsMargins(5,0,0, 0);
+ }
+
+ setLayout(layout);
+ }
+
+ public void addText(String text) {
+ label.setText(label.text() + text);
+ }
+}
diff --git a/src/main/java/eu/m724/chaqt/chatwindow/ChatWindowViewModel.java b/src/main/java/eu/m724/chaqt/chatwindow/ChatWindowViewModel.java
new file mode 100644
index 0000000..690b7be
--- /dev/null
+++ b/src/main/java/eu/m724/chaqt/chatwindow/ChatWindowViewModel.java
@@ -0,0 +1,69 @@
+package eu.m724.chaqt.chatwindow;
+
+import eu.m724.chatapi.chat.Chat;
+import eu.m724.chatapi.chat.ChatEvent;
+import eu.m724.chatapi.chat.ChatMessage;
+import eu.m724.chatapi.example.ExampleSource;
+import eu.m724.chatapi.source.ChatResponse;
+import eu.m724.chatapi.source.ChatSource;
+import io.qt.core.QObject.Signal1;
+import io.qt.core.QThread;
+
+public class ChatWindowViewModel {
+ private final ChatWindowWidget widget;
+ private final Signal1 chatMessageSignal;
+ private final Signal1 chatEventSignal;
+
+ private Chat chat = new Chat();
+ private ChatSource source = new ExampleSource(); // TODO
+
+
+ public ChatWindowViewModel(ChatWindowWidget widget, Signal1 chatEventSignal, Signal1 chatMessageSignal) {
+ this.widget = widget;
+ this.chatEventSignal = chatEventSignal;
+ this.chatMessageSignal = chatMessageSignal;
+ }
+
+
+ void send(String text) {
+ ChatMessage promptMessage = new ChatMessage(false, text);
+ chatMessageSignal.emit(promptMessage);
+ chat.addMessage(new ChatMessage(false, text));
+
+ ChatResponse response = source.ask(chat);
+ ChatMessage message = response.message();
+
+ chatMessageSignal.emit(message);
+ message.addEventConsumer(chatEventSignal::emit, true);
+
+ /*QThread thread = new QThread() {
+ @Override
+ protected void run() {
+ ChatEvent token;
+ do {
+ try {
+ token = response.eventQueue().take();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e); // TODO
+ }
+
+ chatEventSignal.emit(token);
+
+ if (token.finishReason() != null) {
+ chatMessageSignal.emit(response.message().join()); // TODO ???
+ }
+ /*if (!"error".equals(token.finishReason())) {
+ if (token.text() != null) {
+ chatEventSignal.emit(token.text());
+ }
+ } else {
+ System.out.print("Error: " + token.error().toString());
+ }/
+ } while (token.finishReason() == null);
+ // QApplication.invokeLater(() -> resultField.setText(result));
+ }
+ };
+ thread.start();*/
+ //
+ }
+}
diff --git a/src/main/java/eu/m724/chaqt/chatwindow/ChatWindowWidget.java b/src/main/java/eu/m724/chaqt/chatwindow/ChatWindowWidget.java
new file mode 100644
index 0000000..d28f467
--- /dev/null
+++ b/src/main/java/eu/m724/chaqt/chatwindow/ChatWindowWidget.java
@@ -0,0 +1,133 @@
+package eu.m724.chaqt.chatwindow;
+
+import eu.m724.chaqt.QMessage;
+import eu.m724.chatapi.chat.ChatEvent;
+import eu.m724.chatapi.chat.ChatMessage;
+import io.qt.core.Qt;
+import io.qt.widgets.*;
+
+public class ChatWindowWidget extends QWidget {
+ private ChatWindowViewModel viewModel;
+
+ private QVBoxLayout layout;
+ private QTextEdit textEdit;
+
+ // TODO are both really needed
+ private final Signal1 chatMessageSignal = new Signal1<>();
+ private final Signal1 chatEventSignal = new Signal1<>();
+
+ private QMessage latestMessage;
+
+ public ChatWindowWidget() {
+ this.viewModel = new ChatWindowViewModel(this, chatEventSignal, chatMessageSignal);
+
+ //
+
+ chatEventSignal.connect(this::handleChatEvent);
+ chatMessageSignal.connect(this::handleChatMessage);
+
+ //
+
+ this.layout = new QVBoxLayout();
+ layout.setAlignment(Qt.AlignmentFlag.AlignBottom); // or top?
+
+ QWidget widget = new QWidget();
+ widget.setLayout(layout);
+
+ QScrollArea scrollArea = new QScrollArea();
+ scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff);
+ scrollArea.setWidgetResizable(true);
+ scrollArea.setWidget(widget);
+
+ //
+
+ this.textEdit = new QTextEdit();
+ textEdit.setPlaceholderText("Write a message...");
+ textEdit.textChanged.connect(this, "handleComposerType()");
+
+ QPushButton sendButton = new QPushButton("Send");
+ sendButton.clicked.connect(this::onSend);
+
+ QHBoxLayout composerLayout = new QHBoxLayout();
+ composerLayout.addWidget(textEdit);
+ composerLayout.addWidget(sendButton, 0, Qt.AlignmentFlag.AlignBottom);
+
+ QWidget composerWidget = new QWidget();
+ composerWidget.setLayout(composerLayout);
+
+ handleComposerType(); // TODO make it resizable and fix it
+
+ //
+
+ QVBoxLayout mainLayout = new QVBoxLayout();
+ mainLayout.addWidget(scrollArea, 1);
+ mainLayout.addWidget(composerWidget);
+ setLayout(mainLayout);
+
+ /*for (int i=0; i<10; i++) {
+ QPushButton pushButton = new QPushButton("hello");
+ layout.addWidget(pushButton);
+ }*/
+
+ textEdit.setFocus();
+ }
+
+ public void handleComposerType() {
+ int height = (int) textEdit.document().size().height();
+ textEdit.setFixedHeight(height + textEdit.contentsMargins().top() + textEdit.contentsMargins().bottom());
+ }
+
+ public void onSend() {
+ String prompt = textEdit.getPlainText();
+ if (!prompt.isBlank()) {
+ textEdit.setText("");
+ viewModel.send(prompt);
+
+ textEdit.setEnabled(false);
+ textEdit.setPlaceholderText("Asking...");
+ }
+ }
+
+ private void handleChatMessage(ChatMessage message) {
+ System.out.printf("Widget received a message. Is response: %b\n", message.response());
+
+ QMessage qMessage = new QMessage(false, message.content());
+ layout.addWidget(qMessage);
+
+ // if the message is by assistant, disable composer.
+ // I don't know if this is good, but it works
+ if (message.response()) {
+ textEdit.setEnabled(false);
+ textEdit.setPlaceholderText("Thinking...");
+ }
+ }
+
+ private void handleChatEvent(ChatEvent chatEvent) {
+ String token = chatEvent.text();
+
+ // usually when it's the first token
+ if (latestMessage == null) {
+ latestMessage = new QMessage(true, "");
+ layout.addWidget(latestMessage);
+
+ textEdit.setPlaceholderText("Responding...");
+ }
+
+ if (token != null) {
+ latestMessage.addText(token);
+ }
+
+ // when finished
+ if (chatEvent.finishReason() != null) {
+ // to make sure we won't be streaming to the same message
+ latestMessage = null;
+
+ // enable composer
+ textEdit.setEnabled(true);
+ textEdit.setPlaceholderText("Write a message...");
+ textEdit.setFocus();
+ }
+
+ System.out.printf("Event: %s %s\n", chatEvent.finishReason(), chatEvent.text());
+ } // TODO that's messy so do something about that
+}