diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml
deleted file mode 100644
index 78f34d3a..00000000
--- a/.github/workflows/qodana_code_quality.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-#-------------------------------------------------------------------------------#
-# Discover all capabilities of Qodana in our documentation #
-# https://www.jetbrains.com/help/qodana/about-qodana.html #
-#-------------------------------------------------------------------------------#
-
-name: Qodana
-on:
- workflow_dispatch:
- pull_request:
- push:
- branches:
- - master
-
-jobs:
- qodana:
- runs-on: ubuntu-latest
- permissions:
- contents: write
- pull-requests: write
- checks: write
- steps:
- - uses: actions/checkout@v4
- with:
- ref: ${{ github.event.pull_request.head.sha }}
- fetch-depth: 0
- - name: 'Qodana Scan'
- uses: JetBrains/qodana-action@v2025.2
- env:
- QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
- with:
- # When pr-mode is set to true, Qodana analyzes only the files that have been changed
- pr-mode: false
- use-caches: true
- post-pr-comment: true
- use-annotations: true
- # Upload Qodana results (SARIF, other artifacts, logs) as an artifact to the job
- upload-result: false
- # quick-fixes available in Ultimate and Ultimate Plus plans
- push-fixes: 'none'
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index b13cc9aa..eeaa66b5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
me.piitex.app
character-chat-app
- 1.1.3.1105
+ 1.1.4
character-chat-app
@@ -25,7 +25,7 @@
org.apache.logging.log4j
log4j-api
- 2.25.2
+ 2.25.4
org.apache.logging.log4j
@@ -42,7 +42,7 @@
me.piitex.engine
ren-engine
- 1.0.7-SNAPSHOT
+ 1.0.8-SNAPSHOT
org.junit.jupiter
diff --git a/qodana.yaml b/qodana.yaml
deleted file mode 100644
index 6d050d82..00000000
--- a/qodana.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-#-------------------------------------------------------------------------------#
-# Qodana analysis is configured by qodana.yaml file #
-# https://www.jetbrains.com/help/qodana/qodana-yaml.html #
-#-------------------------------------------------------------------------------#
-#################################################################################
-# WARNING: Do not store sensitive information in this file, #
-# as its contents will be included in the Qodana report. #
-#################################################################################
-version: '1.0'
-#Specify inspection profile for code analysis
-profile:
- name: qodana.starter
-#Enable inspections
-#include:
-# - name:
-#Disable inspections
-#exclude:
-# - name:
-# paths:
-# -
-projectJDK: '25' #(Applied in CI/CD pipeline)
-#Execute shell command before Qodana execution (Applied in CI/CD pipeline)
-#bootstrap: sh ./prepare-qodana.sh
-#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
-#plugins:
-# - id: #(plugin id can be found at https://plugins.jetbrains.com)
-# Quality gate. Will fail the CI/CD pipeline if any condition is not met
-# severityThresholds - configures maximum thresholds for different problem severities
-# testCoverageThresholds - configures minimum code coverage on a whole project and newly added code
-# Code Coverage is available in Ultimate and Ultimate Plus plans
-#failureConditions:
-# severityThresholds:
-# any: 15
-# critical: 5
-# testCoverageThresholds:
-# fresh: 70
-# total: 50
-#Qodana supports other languages, for example, Python, JavaScript, TypeScript, Go, C#, PHP
-#For all supported languages see https://www.jetbrains.com/help/qodana/linters.html
-linter: 'jetbrains/qodana-jvm-community:2025.2'
-include:
- - name: UNUSED_IMPORT
- - name: ThrowablePrintStackTrace
- - name: GroovyUnnecessaryReturn
- - name: FieldMayBeFinal
- - name: UnnecessaryLocalVariable
diff --git a/src/main/java/me/piitex/app/App.java b/src/main/java/me/piitex/app/App.java
index a63cd397..46ce51ba 100644
--- a/src/main/java/me/piitex/app/App.java
+++ b/src/main/java/me/piitex/app/App.java
@@ -13,20 +13,19 @@
import me.piitex.app.backend.User;
import me.piitex.app.backend.server.DeviceProcess;
import me.piitex.app.backend.server.ServerProcess;
-import me.piitex.app.backend.server.ServerSettings;
+import me.piitex.app.configuration.ServerSettings;
import me.piitex.app.configuration.AppSettings;
-import me.piitex.app.updater.ApplicationUpdater;
import me.piitex.app.updater.LLamaBackendUpdater;
import me.piitex.app.views.HomeView;
import me.piitex.app.views.Positions;
import me.piitex.engine.WindowBuilder;
+import me.piitex.engine.loaders.image.BaseImageLoader;
import me.piitex.os.OSPathing;
import me.piitex.os.OSUtil;
import me.piitex.os.configurations.InfoFile;
import me.piitex.engine.Window;
import me.piitex.engine.containers.EmptyContainer;
import me.piitex.engine.fxloader.FXLoad;
-import me.piitex.engine.loaders.ImageLoader;
import me.piitex.engine.overlays.AlertOverlay;
import me.piitex.engine.overlays.ButtonBuilder;
import me.piitex.engine.overlays.ButtonOverlay;
@@ -41,8 +40,11 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URISyntaxException;
import java.util.*;
import java.util.List;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Future;
public class App extends FXLoad {
private ServerSettings settings;
@@ -70,6 +72,7 @@ public class App extends FXLoad {
public static final Logger logger = LogManager.getLogger(App.class);
+ private final ConcurrentLinkedQueue characterLoadQueue = new ConcurrentLinkedQueue<>();
private volatile boolean loading = true;
private volatile boolean error = false;
@@ -122,13 +125,12 @@ public void preInitialization() {
loadUserTemplates();
loadCharacters();
App.logger.info("Finished pre-initialization.");
- loading = false;
});
threadPoolManager.submitTask(this::loadBackendServer);
}
@Override
- public void initialization(Stage initialStage) {
+ public void initialization() {
// Error will pass if another instance is running,
if (error) return;
App.logger.info("Loading app from '{}'", getAppDirectory().getAbsolutePath());
@@ -207,7 +209,7 @@ public void initialization(Stage initialStage) {
logger.info("Screen Size ({},{})", dimension.width, dimension.height);
File logo = new File(getExecutedDirectory(), "logo.png");
- window = new WindowBuilder("Chat App").setIcon(new ImageLoader(logo)).setScale((appSettings.isWindowScaling()) && !mobile).setAntiAliasing(false).setDimensions(setWidth, setHeight).build();
+ window = new WindowBuilder("Chat App").setIcon(new BaseImageLoader(logo)).setScale((appSettings.isWindowScaling()) && !mobile).setAntiAliasing(false).setDimensions(setWidth, setHeight).build();
// Initialize global positions. Needed for the rendering process.
Positions.initialize();
@@ -342,20 +344,44 @@ public void loadCharacters() {
logger.error("Could not initialize characters directory. Program may lack permission to access file system.");
return;
}
+
+ Collection> tasks = new HashSet<>();
+
for (File file : files) {
- logger.info("Loading character '{}'...", file.getName());
if (file.isDirectory()) {
- String id = file.getName();
- // Check if info file exists
- File info = new File(file, "character.info");
- if (info.exists()) {
- InfoFile infoFile = new InfoFile(info, true);
- characters.put(id, new Character(id, infoFile));
- } else {
- logger.error("Character file does not exist for '{}'", file.getName());
- }
+ tasks.add(App.getThreadPoolManager().submitTask(() -> {
+ characterLoadQueue.add(file.getName());
+ logger.info("Loading character '{}'...", file.getName());
+ String id = file.getName();
+ // Check if info file exists
+ File info = new File(file, "character.info");
+ if (info.exists()) {
+ InfoFile infoFile = new InfoFile(info, true);
+ characters.put(id, new Character(id, infoFile));
+ } else {
+ logger.error("Character file does not exist for '{}'", file.getName());
+ }
+
+ characterLoadQueue.remove(file.getName());
+ }));
}
}
+
+ int total = tasks.size();
+ Collection> completed;
+ do {
+ completed = new HashSet<>();
+ for (Future> task : tasks) {
+ if (task.isDone()) {
+ completed.add(task);
+ }
+ }
+
+ if (completed.size() == total) {
+ loading = false;
+ }
+ } while (loading);
+
}
public void loadUserTemplates() {
@@ -399,26 +425,21 @@ public void performUpdates() {
logger.info("Model list updated.");
downloader.shutdown();
- } catch (IOException e) {
+ } catch (IOException | URISyntaxException e) {
App.logger.error("Failed to fetch download size.", e);
}
- // Microslop, the multi trillion dollar company that can't handle more than 50 API requests.
- App.logger.info("Checking for application updates...");
- ApplicationUpdater applicationUpdater = new ApplicationUpdater(getVersion());
- //applicationUpdater.checkForUpdates();
-
App.logger.info("Checking for backend version...");
-
if (settings.getDevice().equals("error")) {
App.logger.info("Could not load backend devices. Force checking updates...");
settings.setDevice("Auto");
lLamaBackendUpdater = new LLamaBackendUpdater("0");
} else {
- File backendVersionFile = Arrays.stream(getBackendDirectory().listFiles()).filter(file -> file.getName().endsWith(".txt")).findAny().orElse(null);
+ File backendVersionFile = Arrays.stream(Objects.requireNonNull(getBackendDirectory().listFiles())).filter(file -> file.getName().endsWith(".txt")).findAny().orElse(null);
if (backendVersionFile != null) {
lLamaBackendUpdater = new LLamaBackendUpdater(backendVersionFile.getName().split(".txt")[0]);
} else {
+ App.logger.info("No update file found.");
lLamaBackendUpdater = new LLamaBackendUpdater("0");
}
}
@@ -469,7 +490,7 @@ public Window buildErrorWindow(String message) {
Application.setUserAgentStylesheet(new PrimerDark().getUserAgentStylesheet());
File logo = new File(getExecutedDirectory(), "logo.png");
- window = new WindowBuilder("Error").setDimensions(400, 150).setIcon(new ImageLoader(logo)).build();
+ window = new WindowBuilder("Error").setDimensions(400, 150).setIcon(new BaseImageLoader(logo)).build();
EmptyContainer emptyContainer = new EmptyContainer(window.getWidth(), window.getHeight());
window.addContainer(emptyContainer);
@@ -484,15 +505,19 @@ public Window buildErrorWindow(String message) {
emptyContainer.addElement(kill);
kill.onClick(event -> {
App.logger.info("Killing old process.");
- if (ProcessUtil.killProcess(Long.parseLong(settings.getInfoFile().get("main-pid")))) {
- App.logger.info("Old process was destroyed gracefully.");
- Platform.exit();
- System.exit(0);
- } else {
- App.logger.info("Forcefully killing old process.");
- ProcessUtil.terminateProcess(Long.parseLong(settings.getInfoFile().get("main-pid")));
- }
-
+ Optional handle = ProcessUtil.getRunningProcess(Long.parseLong(settings.getInfoFile().get("main-pid")));
+ handle.ifPresent(processHandle -> {
+ if (processHandle.info().toString().contains("java.exe") || processHandle.info().toString().contains("javaw.exe") || processHandle.info().toString().contains("CCA.exe")) {
+ if (ProcessUtil.killProcess(Long.parseLong(settings.getInfoFile().get("main-pid")))) {
+ App.logger.info("Old process was destroyed gracefully.");
+ Platform.exit();
+ System.exit(0);
+ } else {
+ App.logger.info("Forcefully killing old process.");
+ ProcessUtil.terminateProcess(Long.parseLong(settings.getInfoFile().get("main-pid")));
+ }
+ }
+ });
appSettings.getInfoFile().set("main-pid", "");
});
diff --git a/src/main/java/me/piitex/app/Main.java b/src/main/java/me/piitex/app/Main.java
index 6b97cfda..7c77bf29 100644
--- a/src/main/java/me/piitex/app/Main.java
+++ b/src/main/java/me/piitex/app/Main.java
@@ -21,7 +21,6 @@ public static void main(String[] args) {
if (Arrays.asList(args).contains("--force-updates")) {
forceUpdate = true;
}
- new App();
Application.launch(App.class);
}
}
diff --git a/src/main/java/me/piitex/app/backend/Character.java b/src/main/java/me/piitex/app/backend/Character.java
index ae92abba..8b22d4fc 100644
--- a/src/main/java/me/piitex/app/backend/Character.java
+++ b/src/main/java/me/piitex/app/backend/Character.java
@@ -118,10 +118,8 @@ private void loadChats() {
if (getChatDirectory() == null || !getChatDirectory().exists()) return;
for (File file : getChatDirectory().listFiles()) {
if (file.isDirectory()) continue;
- App.getThreadPoolManager().submitTask(() -> {
- Chat chat = new Chat(file);
- chats.add(chat);
- });
+ Chat chat = new Chat(file);
+ chats.add(chat);
}
}
diff --git a/src/main/java/me/piitex/app/backend/User.java b/src/main/java/me/piitex/app/backend/User.java
index ef9a9153..28cd0bfa 100644
--- a/src/main/java/me/piitex/app/backend/User.java
+++ b/src/main/java/me/piitex/app/backend/User.java
@@ -1,9 +1,10 @@
package me.piitex.app.backend;
+import me.piitex.engine.loaders.image.BaseImageLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import org.jetbrains.annotations.Nullable;
import me.piitex.app.App;
import me.piitex.os.configurations.InfoFile;
-import me.piitex.engine.loaders.ImageLoader;
import me.piitex.engine.overlays.ImageOverlay;
import java.io.File;
@@ -141,7 +142,7 @@ public static ImageOverlay getUserAvatar(String iconPath, double width, double h
}
- ImageLoader loader = new ImageLoader(file);
+ ImageLoader loader = new BaseImageLoader(file);
loader.setWidth(width);
loader.setHeight(height);
diff --git a/src/main/java/me/piitex/app/backend/server/ModelTestProcess.java b/src/main/java/me/piitex/app/backend/server/ModelTestProcess.java
index 3e89a2ee..9a6b273e 100644
--- a/src/main/java/me/piitex/app/backend/server/ModelTestProcess.java
+++ b/src/main/java/me/piitex/app/backend/server/ModelTestProcess.java
@@ -2,6 +2,7 @@
import me.piitex.app.App;
import me.piitex.app.backend.Model;
+import me.piitex.app.configuration.ServerSettings;
import me.piitex.os.OSUtil;
import java.io.File;
@@ -136,6 +137,8 @@ private LinkedList getParameters(File server, ServerSettings settings) {
parameters.add("-no-cnv");
parameters.add("--no-warmup");
+ parameters.add("-lv");
+ parameters.add("4");
return parameters;
}
@@ -188,15 +191,19 @@ protected void processOutput() {
try (Scanner scanner = new Scanner(new FileInputStream(output))) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
+ if (line.contains(" ")) {
+ line = line.split(" ", 2)[1];
+ }
+ line = line.replace("I ", "").trim();
if (line.contains("cleaning up before exit...") || line.contains("failed to load model") || line.contains("error while handling") || line.startsWith("error:") || line.startsWith("ROCm error:")) {
App.logger.error("ERROR: Could not start backend server.");
error = true;
break;
}
if (line.startsWith("print_info: n_layer") && model.getSettings().getTotalLayers() == 0) {
- line = line.split("=")[1].trim();
- App.logger.info("Total Model Layers: {}", line);
- model.getSettings().setTotalLayers(Integer.parseInt(line));
+ String layers = line.substring(15).split("=")[1].trim();
+ App.logger.info("Total Model Layers: {}", layers);
+ model.getSettings().setTotalLayers(Integer.parseInt(layers));
}
if (line.contains("common_memory_breakdown_print:") && line.contains("|") && line.contains("=")) {
String[] parts = line.split("\\|");
@@ -264,10 +271,12 @@ protected void processOutput() {
if (computeBufferMiB > 0.0) {
model.getSettings().setComputeBufferSize(computeBufferMiB);
}
- } catch (FileNotFoundException e) {
+ } catch (Exception e) {
error = true;
- stop();
+ App.logger.error("Err while testing model.", e);
}
+
+ App.logger.info("Model data processed!");
stop();
}
diff --git a/src/main/java/me/piitex/app/backend/server/Server.java b/src/main/java/me/piitex/app/backend/server/Server.java
index bfdd58cd..ab4124af 100644
--- a/src/main/java/me/piitex/app/backend/server/Server.java
+++ b/src/main/java/me/piitex/app/backend/server/Server.java
@@ -6,6 +6,7 @@
import me.piitex.app.backend.ChatMessage;
import me.piitex.app.backend.Response;
import me.piitex.app.configuration.ModelSettings;
+import me.piitex.app.configuration.ServerSettings;
import me.piitex.app.utils.Placeholder;
import me.piitex.engine.Element;
import me.piitex.engine.containers.CardContainer;
@@ -106,6 +107,14 @@ private static HttpPost prepareOAIStreamRequest(Response response) throws JSONEx
*/
private static void addModelSettingsToJSON(JSONObject toPost, ModelSettings settings) throws JSONException {
toPost.put("stream", true);
+
+ if (settings.isReasoning()) {
+ JSONObject chatTemplateKwargs = new JSONObject();
+ chatTemplateKwargs.put("enable_thinking", true);
+ chatTemplateKwargs.put("thinking", true);
+ toPost.put("chat_template_kwargs", chatTemplateKwargs);
+ }
+
toPost.put("temperature", settings.getTemperature());
toPost.put("dynatemp_range", settings.getDynamicTempRage());
toPost.put("dynatemp_exponent", settings.getDynamicExponent());
@@ -215,8 +224,9 @@ private static boolean processStreamChunk(String content, StringBuilder appender
}
JSONObject delta = arrayObject.getJSONObject("delta");
+
String line = delta.optString("content", "");
- if (line.equalsIgnoreCase("null")) continue;
+ if (line.isEmpty() || line.equalsIgnoreCase("null")) continue;
appender.append(line);
diff --git a/src/main/java/me/piitex/app/backend/server/ServerProcess.java b/src/main/java/me/piitex/app/backend/server/ServerProcess.java
index 8b14639e..4298c7bb 100644
--- a/src/main/java/me/piitex/app/backend/server/ServerProcess.java
+++ b/src/main/java/me/piitex/app/backend/server/ServerProcess.java
@@ -5,9 +5,13 @@
import javafx.application.Platform;
import me.piitex.app.App;
import me.piitex.app.backend.Model;
+import me.piitex.app.configuration.ServerSettings;
import me.piitex.engine.PopupPosition;
import me.piitex.engine.overlays.MessageOverlay;
import me.piitex.os.OSUtil;
+import oshi.SystemInfo;
+import oshi.hardware.GraphicsCard;
+import oshi.hardware.HardwareAbstractionLayer;
import java.io.File;
import java.io.FileInputStream;
@@ -176,6 +180,24 @@ private LinkedList getParameters(File server, ServerSettings settings) {
// The usage is a percentage of the total layers (model.getGpuLayers());
double TOTAL_AVAILABLE_VRAM_MIB = App.getInstance().getAppSettings().getTotalGpuVram();
+ if (TOTAL_AVAILABLE_VRAM_MIB <= 0) {
+ App.logger.warn("VRAM is set to 0. Attempting to fetch VRAM...");
+ // Fallback to Oshi
+ SystemInfo systemInfo = new SystemInfo();
+ HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware();
+ GraphicsCard graphicsCard = hardwareAbstractionLayer.getGraphicsCards().getFirst();
+ if (graphicsCard != null && graphicsCard.getVRam() > 0) {
+ App.logger.info("Using GPU memory...");
+ TOTAL_AVAILABLE_VRAM_MIB = graphicsCard.getVRam() / (1024.0 * 1024.0);
+ } else {
+ App.logger.error("Could not find dedicated GPU. Using global memory pool...");
+ TOTAL_AVAILABLE_VRAM_MIB = systemInfo.getHardware().getMemory().getTotal() / (1024.0 * 1024.0);
+ App.logger.info("VRAM: {} MiB", TOTAL_AVAILABLE_VRAM_MIB);
+ }
+
+ App.getInstance().getAppSettings().setTotalGpuVram(TOTAL_AVAILABLE_VRAM_MIB);
+ }
+
double KV_CACHE = model.getSettings().getKvCacheSize();
double COMPUTED_BUFFER_SIZE = model.getSettings().getComputeBufferSize();
double FIXED_OVERHEAD_MIB = KV_CACHE + COMPUTED_BUFFER_SIZE;
@@ -207,8 +229,8 @@ private LinkedList getParameters(File server, ServerSettings settings) {
if (VRAM_PER_LAYER_MIB > 0.0) {
layers = (int) Math.floor(LAYER_ALLOC_BUDGET / VRAM_PER_LAYER_MIB);
} else {
- App.logger.warn("VRAM per layer (dataPerLayer) is 0.0, defaulting layers to 0.");
- layers = 0;
+ App.logger.warn("VRAM per layer (dataPerLayer) is 0.0, defaulting layers to {}.", model.getSettings().getTotalLayers());
+ layers = model.getSettings().getTotalLayers();
}
// Cap the offloaded layers
@@ -242,6 +264,19 @@ private LinkedList getParameters(File server, ServerSettings settings) {
parameters.add(model.getSettings().getReasoningTemplate());
}
+ if (model.getSettings().isForceDisableReasoning()) {
+ App.logger.info("Force disable reasoning....");
+ parameters.add("--no-prefill-assistant");
+ parameters.add("-rea");
+ parameters.add("off");
+ } else {
+ if (model.getSettings().isReasoning()) {
+ App.logger.info("Enabling thinking mode....");
+ parameters.add("-rea");
+ parameters.add("on");
+ }
+ }
+
if (!model.getSettings().getChatTemplate().equalsIgnoreCase("default")) {
App.logger.debug("Setting chat template...");
parameters.add("--chat-template");
@@ -265,6 +300,8 @@ private LinkedList getParameters(File server, ServerSettings settings) {
parameters.add("--port");
parameters.add("8187");
parameters.add("--no-webui");
+ parameters.add("-lv");
+ parameters.add("4");
if (settings.isHost()) {
App.logger.info("Server is listening on 0.0.0.0");
@@ -291,7 +328,7 @@ protected void waitForServer() {
process.destroy();
break;
}
- if (line.contains("starting the main loop")) {
+ if (line.contains("starting the main loop") || line.contains("model loaded") || line.contains("server is listening on")) {
App.logger.info("Backend server stated!");
started = true;
break;
diff --git a/src/main/java/me/piitex/app/configuration/ModelSettings.java b/src/main/java/me/piitex/app/configuration/ModelSettings.java
index 29ae48aa..cc4ef16e 100644
--- a/src/main/java/me/piitex/app/configuration/ModelSettings.java
+++ b/src/main/java/me/piitex/app/configuration/ModelSettings.java
@@ -26,9 +26,12 @@ public class ModelSettings {
private int dryPenaltyTokens = -1;
private String mmProj = "None / Disabled";
private String chatTemplate = "default";
- private String reasoningTemplate = "disabled";
+ private String reasoningTemplate = "auto";
private boolean useDefault;
private boolean jinja = false;
+ private boolean reasoning = false;
+ private int reasoningBudget = -1;
+ private boolean forceDisableReasoning = false;
private int totalLayers = 0;
private double dataPerLayer;
private double kvCacheSize;
@@ -184,6 +187,15 @@ public ModelSettings(InfoFile infoFile) {
if (infoFile.hasKey("change")) {
this.change = infoFile.getBoolean("change");
}
+ if (infoFile.hasKey("force-disable-reason")) {
+ this.forceDisableReasoning = infoFile.getBoolean("force-disable-reason");
+ }
+ if (infoFile.hasKey("reasoning")) {
+ this.reasoning = infoFile.getBoolean("reasoning");
+ }
+ if (infoFile.hasKey("reasoning-budget")) {
+ this.reasoningBudget = infoFile.getInteger("reasoning-budget");
+ }
}
public String getModelInstructions() {
@@ -474,6 +486,33 @@ public void setChange(boolean change) {
infoFile.set("change", change);
}
+ public int getReasoningBudget() {
+ return reasoningBudget;
+ }
+
+ public boolean isReasoning() {
+ return reasoning;
+ }
+
+ public void setReasoning(boolean reasoning) {
+ this.reasoning = reasoning;
+ infoFile.set("reasoning", reasoning);
+ }
+
+ public void setReasoningBudget(int reasoningBudget) {
+ this.reasoningBudget = reasoningBudget;
+ infoFile.set("reasoning-budget", reasoningBudget);
+ }
+
+ public boolean isForceDisableReasoning() {
+ return forceDisableReasoning;
+ }
+
+ public void setForceDisableReasoning(boolean forceDisableReasoning) {
+ this.forceDisableReasoning = forceDisableReasoning;
+ infoFile.set("force-disable-reason", forceDisableReasoning);
+ }
+
@Nullable
public InfoFile getInfoFile() {
return infoFile;
diff --git a/src/main/java/me/piitex/app/backend/server/ServerSettings.java b/src/main/java/me/piitex/app/configuration/ServerSettings.java
similarity index 99%
rename from src/main/java/me/piitex/app/backend/server/ServerSettings.java
rename to src/main/java/me/piitex/app/configuration/ServerSettings.java
index db26c3a6..92485ffd 100644
--- a/src/main/java/me/piitex/app/backend/server/ServerSettings.java
+++ b/src/main/java/me/piitex/app/configuration/ServerSettings.java
@@ -1,4 +1,4 @@
-package me.piitex.app.backend.server;
+package me.piitex.app.configuration;
import me.piitex.app.App;
import me.piitex.app.backend.Model;
diff --git a/src/main/java/me/piitex/app/updater/ApplicationUpdater.java b/src/main/java/me/piitex/app/updater/ApplicationUpdater.java
deleted file mode 100644
index b99c4a3a..00000000
--- a/src/main/java/me/piitex/app/updater/ApplicationUpdater.java
+++ /dev/null
@@ -1,141 +0,0 @@
-package me.piitex.app.updater;
-
-import javafx.application.Platform;
-import me.piitex.app.App;
-import me.piitex.app.backend.server.ServerProcess;
-import me.piitex.engine.Window;
-import me.piitex.engine.WindowBuilder;
-import me.piitex.engine.containers.Container;
-import me.piitex.engine.containers.EmptyContainer;
-import me.piitex.engine.loaders.ImageLoader;
-import me.piitex.engine.overlays.ButtonBuilder;
-import me.piitex.engine.overlays.ButtonOverlay;
-import me.piitex.engine.overlays.ProgressBarOverlay;
-import me.piitex.engine.overlays.TextOverlay;
-import me.piitex.os.*;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URISyntaxException;
-
-public class ApplicationUpdater {
- private String currentVersion;
- private Window window;
- private Container container;
-
- public ApplicationUpdater(String currentVersion) {
- this.currentVersion = currentVersion;
- }
-
- public void checkForUpdates() {
- App.logger.info("Checking version: {}", currentVersion);
- GitHubUtil gitHubUtil = new GitHubUtil("https://api.github.com/repos/HackusatePvP/character-chat-app/");
- try {
- String latestVersionString = gitHubUtil.getLatestReleaseJson().getString("tag_name");
- Version latestVersion = VersionUtil.parseVersion(latestVersionString);
- App.logger.info("Latest tag is {}", latestVersionString);
-
- if (!currentVersion.startsWith("v")) {
- currentVersion = "v" + currentVersion;
- }
- Version version = VersionUtil.parseVersion(currentVersion);
-
- if (version.compareTo(latestVersion) < 0) {
- App.logger.info("New application version available.");
- Platform.runLater(() -> {
- buildAndDisplayUpdateWindow(gitHubUtil);
- });
- } else {
- App.logger.info("No update available.");
- }
-
- } catch (IOException | URISyntaxException e) {
- App.logger.error("Could not fetch latest app release!", e);
- }
- }
-
- public void buildAndDisplayUpdateWindow(GitHubUtil gitHubUtil) {
- window = new WindowBuilder("Update").setDimensions(400, 150).setIcon(new ImageLoader(new File(App.getAppDirectory(), "logo.png"))).build();
- container = new EmptyContainer(400, 150);
- window.addContainer(container);
-
- TextOverlay textOverlay = new TextOverlay("App updates available. Click 'Update' to start.");
- textOverlay.setY(20);
- textOverlay.setX(10);
- container.addElement(textOverlay);
-
- ButtonOverlay buttonOverlay = new ButtonBuilder("update").setText("Update").build();
- container.addElement(buttonOverlay);
- buttonOverlay.setX(150);
- buttonOverlay.setY(70);
-
- buttonOverlay.onClick(_ -> {
- App.window.close(false);
- App.getInstance().getCharacters().clear();
- App.getInstance().getUserTemplates().clear();
- App.reloadModelList();
- if (ServerProcess.getCurrentServer() != null) {
- ServerProcess serverProcess = ServerProcess.getCurrentServer();
- serverProcess.stop();
- }
-
- container.removeAllElements();
- TextOverlay updateInfo = new TextOverlay("Preparing for installation...");
- updateInfo.setY(20);
- updateInfo.setX(10);
- container.addElement(updateInfo);
-
- ProgressBarOverlay progressBarOverlay = new ProgressBarOverlay();
- progressBarOverlay.setX(150);
- progressBarOverlay.setY(70);
- container.addElement(progressBarOverlay);
- });
- App.window.getStage().getScene().getRoot().setDisable(true);
- window.getStage().setAlwaysOnTop(true);
- window.render();
- }
-
- private void downloadUpdate(GitHubUtil gitHubUtil, TextOverlay textOverlay, ProgressBarOverlay progressBarOverlay) {
- textOverlay.setText("Download updates...");
- progressBarOverlay.getProgressBar().setProgress(0);
-
- App.getThreadPoolManager().submitTask(() -> {
- try {
- gitHubUtil.downloadAsset(gitHubUtil.getLatestReleaseID(), new File("download.jar"), new DownloadListener() {
- @Override
- public void onDownloadStart(DownloadInfo info) {
- App.logger.info("Starting download...");
- }
-
- @Override
- public void onDownloadProgress(DownloadInfo info) {
- progressBarOverlay.getProgressBar().progressProperty().set(info.getDownloadProgress());
- }
-
- @Override
- public void onDownloadComplete(DownloadInfo info, File outputFile) {
- App.logger.info("Update completed! Shutting down...");
-
- // Call the file to be moved after the runtime shuts down.
-
- Platform.exit();
- System.exit(1);
- }
-
- @Override
- public void onDownloadError(DownloadInfo info, Exception e) {
-
- }
-
- @Override
- public void onDownloadCancel(DownloadInfo info) {
-
- }
-
- });
- } catch (IOException | URISyntaxException e) {
- // TOOD: Display error window
- }
- });
- }
-}
diff --git a/src/main/java/me/piitex/app/updater/LLamaBackendUpdater.java b/src/main/java/me/piitex/app/updater/LLamaBackendUpdater.java
index 74788457..b4d5b51a 100644
--- a/src/main/java/me/piitex/app/updater/LLamaBackendUpdater.java
+++ b/src/main/java/me/piitex/app/updater/LLamaBackendUpdater.java
@@ -4,6 +4,8 @@
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.control.ProgressIndicator;
+import javafx.scene.paint.Color;
+import javafx.scene.text.TextAlignment;
import me.piitex.app.App;
import me.piitex.app.backend.Model;
import me.piitex.app.backend.server.ServerProcess;
@@ -13,11 +15,8 @@
import me.piitex.engine.containers.DownloadContainer;
import me.piitex.engine.containers.EmptyContainer;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
-import me.piitex.engine.overlays.ButtonBuilder;
-import me.piitex.engine.overlays.ButtonOverlay;
-import me.piitex.engine.overlays.ProgressBarOverlay;
-import me.piitex.engine.overlays.TextOverlay;
+import me.piitex.engine.loaders.image.BaseImageLoader;
+import me.piitex.engine.overlays.*;
import me.piitex.os.*;
import org.apache.commons.io.FileUtils;
import org.json.JSONObject;
@@ -64,10 +63,8 @@ private void fetchLatestRelease() {
public synchronized boolean isUpdateAvailable() {
if (current != null && latest != null) {
- System.out.println("Not null");
return current.compareTo(latest) < 0;
}
- System.out.println("False");
return false;
}
@@ -82,7 +79,7 @@ public boolean startUpdate() {
}
public void buildAndDisplayUpdateWindow(GitHubUtil gitHubUtil) {
- window = new WindowBuilder("Update").setDimensions(450, 200).setIcon(new ImageLoader(new File(App.getAppDirectory(), "logo.png"))).build();
+ window = new WindowBuilder("Update").setDimensions(450, 200).setIcon(new BaseImageLoader(new File(App.getExecutedDirectory(), "logo.png"))).build();
container = new EmptyContainer(window.getWidth(), window.getHeight());
window.addContainer(container);
window.getStage().setOnHidden(windowEvent -> {
@@ -115,9 +112,21 @@ public void buildAndDisplayUpdateWindow(GitHubUtil gitHubUtil) {
container.removeAllElements();
if (OSUtil.getOS().contains("Windows")) {
- downloadCudaBackendNew(gitHubUtil);
+ try {
+ downloadCudaBackendNew(gitHubUtil);
+ } catch (Exception ignored) {
+ VerticalLayout errorBox = new VerticalLayout(container.getWidth() - 20, container.getHeight() - 20);
+ errorBox.setMaxSize(errorBox.getWidth(), errorBox.getHeight());
+ container.addElement(errorBox);
+
+ TextFlowOverlay error = new TextFlowOverlay("Error occurred while fetching update. Please try updating at a later point.", errorBox.getWidth(), errorBox.getHeight());
+ error.setTextAlignment(TextAlignment.CENTER);
+ error.setMaxSize(error.getWidth(), error.getHeight());
+ error.setTextFillColor(Color.RED);
+ errorBox.addElement(error);
+ }
} else {
-
+ downloadVulkanBackendNew(gitHubUtil, null);
}
});
diff --git a/src/main/java/me/piitex/app/views/HomeView.java b/src/main/java/me/piitex/app/views/HomeView.java
index 7b346fb5..39a7bbd2 100644
--- a/src/main/java/me/piitex/app/views/HomeView.java
+++ b/src/main/java/me/piitex/app/views/HomeView.java
@@ -23,7 +23,7 @@ public class HomeView extends EmptyContainer {
private final SidebarView sidebarView;
public HomeView() {
- int height = App.getInstance().getAppSettings().getHeight() - 50;
+ double height = App.window.getDrawHeight();
super(600, height);
this.sidebarView = new SidebarView();
if (App.mobile) {
@@ -49,8 +49,8 @@ public void init() {
boolean loading = App.getInstance().isLoading();
while (loading) {
loading = App.getInstance().isLoading();
- if (!loading) break;
}
+
Platform.runLater(() -> {
root.removeElement(1);
buildBody();
diff --git a/src/main/java/me/piitex/app/views/LoadingView.java b/src/main/java/me/piitex/app/views/LoadingView.java
index cf5620d0..74b9c640 100644
--- a/src/main/java/me/piitex/app/views/LoadingView.java
+++ b/src/main/java/me/piitex/app/views/LoadingView.java
@@ -1,6 +1,5 @@
package me.piitex.app.views;
-import atlantafx.base.theme.Styles;
import javafx.geometry.Pos;
import me.piitex.engine.layouts.VerticalLayout;
diff --git a/src/main/java/me/piitex/app/views/Positions.java b/src/main/java/me/piitex/app/views/Positions.java
index 5ef69d22..6b672c8d 100644
--- a/src/main/java/me/piitex/app/views/Positions.java
+++ b/src/main/java/me/piitex/app/views/Positions.java
@@ -14,7 +14,7 @@ public class Positions {
*/
public static int SIDEBAR_WIDTH;
public static int SIDEBAR_WIDTH_COLLAPSE;
- public static int SIDEBAR_HEIGHT;
+ public static double SIDEBAR_HEIGHT;
/*
###########################
@@ -80,7 +80,7 @@ private static void initializeDesktop() {
SIDEBAR_WIDTH = 200;
SIDEBAR_WIDTH_COLLAPSE = 50;
- SIDEBAR_HEIGHT = (int) window.getHeight();
+ SIDEBAR_HEIGHT = window.getDrawHeight();
MODEL_CONFIGURATION_SCROLL_HEIGHT = window.getHeight() - 100;
MODEL_CONFIGURATION_LAYOUT_WIDTH = window.getWidth() - SIDEBAR_WIDTH - 25; // -25 to account for spacing
diff --git a/src/main/java/me/piitex/app/views/SidebarView.java b/src/main/java/me/piitex/app/views/SidebarView.java
index b9826e8e..94971f67 100644
--- a/src/main/java/me/piitex/app/views/SidebarView.java
+++ b/src/main/java/me/piitex/app/views/SidebarView.java
@@ -194,29 +194,4 @@ private VerticalLayout buildVersionLayout() {
return layout;
}
-}
-
-// ButtonOverlay users = new ButtonBuilder("users").setText("User Templates").setIcon(new FontIcon(Material2MZ.MEMORY)).build();
-// users.addStyle(appSettings.getGlobalTextSize());
-// users.setWidth(rootWidth);
-// users.setAlignment(Pos.BASELINE_LEFT);
-// addElement(users);
-// users.onClick(event -> {
-// MessageOverlay warning = new MessageOverlay("Development", "User templates are still in development.");
-// warning.addStyle(Styles.WARNING);
-// App.window.renderPopup(warning, PopupPosition.BOTTOM_CENTER, 400, 100, true);
-//
-// App.window.clearContainers();
-// App.window.addContainer(new UsersView());
-// });
-//
-// ButtonOverlay characters = new ButtonBuilder("characters").setText("New Character").setIcon(new FontIcon(Material2MZ.PERSON)).build();
-// characters.addStyle(appSettings.getGlobalTextSize());
-// characters.setWidth(rootWidth);
-// addElement(characters);
-// characters.setAlignment(Pos.BASELINE_LEFT);
-// characters.onClick(event -> {
-// App.window.clearContainers();
-// App.window.addContainer(new CharacterEditView(null).getRoot());
-// });
-
+}
\ No newline at end of file
diff --git a/src/main/java/me/piitex/app/views/characters/CharactersView.java b/src/main/java/me/piitex/app/views/characters/CharactersView.java
index 8fc95c5f..7b7e2677 100644
--- a/src/main/java/me/piitex/app/views/characters/CharactersView.java
+++ b/src/main/java/me/piitex/app/views/characters/CharactersView.java
@@ -23,7 +23,7 @@
import me.piitex.engine.layouts.FlowLayout;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import me.piitex.engine.overlays.*;
import org.apache.commons.io.FileUtils;
import org.kordamp.ikonli.material2.Material2AL;
@@ -269,7 +269,7 @@ private void deleteCharacter(FlowLayout base, CardContainer card, Character char
App.getThreadPoolManager().submitSchedule(() -> {
try {
App.logger.info("Removing image from cache '{}'", character.getIconPath());
- ImageLoader.imageCache.remove(character.getIconPath()); // Clear image from cache.
+ ImageLoader.clearCache();
App.logger.info("Deleting Character: {}", character.getId());
FileUtils.deleteDirectory(character.getCharacterDirectory());
} catch (IOException e) {
diff --git a/src/main/java/me/piitex/app/views/characters/tabs/CharacterTab.java b/src/main/java/me/piitex/app/views/characters/tabs/CharacterTab.java
index 737c8e07..2cd0349a 100644
--- a/src/main/java/me/piitex/app/views/characters/tabs/CharacterTab.java
+++ b/src/main/java/me/piitex/app/views/characters/tabs/CharacterTab.java
@@ -20,7 +20,8 @@
import me.piitex.engine.containers.tabs.Tab;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
+import me.piitex.engine.loaders.image.BaseImageLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import me.piitex.engine.overlays.*;
import me.piitex.app.backend.Character;
import org.json.JSONObject;
@@ -104,7 +105,7 @@ private CardContainer buildCharacterDisplay() {
currentIconPath = new File(App.getAppDirectory(), "icons/character.png");
}
- ImageLoader loader = new ImageLoader(currentIconPath);
+ ImageLoader loader = new BaseImageLoader(currentIconPath);
loader.setWidth(256);
loader.setHeight(256);
@@ -134,7 +135,7 @@ private CardContainer buildCharacterDisplay() {
parentView.updateInfoData();
- ImageLoader imageLoader = new ImageLoader(selectedFile);
+ ImageLoader imageLoader = new BaseImageLoader(selectedFile);
imageLoader.setWidth(256);
imageLoader.setHeight(256);
@@ -207,7 +208,7 @@ private VerticalLayout buildCharacterInput(@Nullable Character character, boolea
parentView.setCharacterIconPath(file);
parentView.getInfoFile().set("icon-path", file.getAbsolutePath());
- image.setImage(new ImageLoader(file));
+ image.setImage(new BaseImageLoader(file));
parentView.updateInfoData();
diff --git a/src/main/java/me/piitex/app/views/characters/tabs/UserTab.java b/src/main/java/me/piitex/app/views/characters/tabs/UserTab.java
index 942609bf..60e9deab 100644
--- a/src/main/java/me/piitex/app/views/characters/tabs/UserTab.java
+++ b/src/main/java/me/piitex/app/views/characters/tabs/UserTab.java
@@ -10,6 +10,8 @@
import me.piitex.app.configuration.AppSettings;
import me.piitex.app.utils.ImageCardExporter;
import me.piitex.app.utils.UserCardImporter;
+import me.piitex.engine.loaders.image.BaseImageLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import me.piitex.os.configurations.InfoFile;
import me.piitex.app.views.characters.CharacterEditView;
import me.piitex.engine.containers.CardContainer;
@@ -17,7 +19,6 @@
import me.piitex.engine.containers.tabs.Tab;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
import me.piitex.engine.overlays.*;
import org.json.JSONObject;
@@ -103,7 +104,7 @@ private CardContainer buildUserDisplay() {
currentIconPath = new File(App.getAppDirectory(), "icons/character.png");
}
- ImageLoader loader = new ImageLoader(currentIconPath);
+ ImageLoader loader = new BaseImageLoader(currentIconPath);
loader.setWidth(256);
loader.setHeight(256);
@@ -133,7 +134,7 @@ private CardContainer buildUserDisplay() {
parentView.updateInfoData();
- ImageLoader imageLoader = new ImageLoader(selectedFile);
+ ImageLoader imageLoader = new BaseImageLoader(selectedFile);
imageLoader.setWidth(256);
imageLoader.setHeight(256);
@@ -189,7 +190,7 @@ private VerticalLayout buildUserInput() {
if (template.getIconPath() != null && !template.getIconPath().isEmpty()) {
parentView.setUserIconPath(new File(template.getIconPath()));
- image.setImage(new ImageLoader(parentView.getUserIconPath()));
+ image.setImage(new BaseImageLoader(parentView.getUserIconPath()));
}
if (parentView.getUser() != null) {
@@ -228,7 +229,7 @@ private VerticalLayout buildUserInput() {
parentView.setUserIconPath(file);
parentView.getInfoFile().set("icon-path-user", file.getAbsolutePath());
- image.setImage(new ImageLoader(file));
+ image.setImage(new BaseImageLoader(file));
parentView.updateInfoData();
diff --git a/src/main/java/me/piitex/app/views/chats/ChatPageView.java b/src/main/java/me/piitex/app/views/chats/ChatPageView.java
index 5ccbe9db..5adb3528 100644
--- a/src/main/java/me/piitex/app/views/chats/ChatPageView.java
+++ b/src/main/java/me/piitex/app/views/chats/ChatPageView.java
@@ -12,6 +12,7 @@
import me.piitex.app.backend.Response;
import me.piitex.app.backend.Role;
import me.piitex.app.backend.server.Server;
+import me.piitex.app.backend.server.ServerProcess;
import me.piitex.app.configuration.AppSettings;
import me.piitex.app.utils.Placeholder;
import me.piitex.app.views.chats.components.ControlBarView;
@@ -20,7 +21,8 @@
import me.piitex.engine.containers.ScrollContainer;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
+import me.piitex.engine.loaders.image.BaseImageLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import me.piitex.engine.overlays.*;
import org.kordamp.ikonli.material2.Material2MZ;
@@ -114,7 +116,7 @@ public VerticalLayout buildMessageBox(ChatMessage chatMessage) {
layout.addElement(separator);
//TODO: Make Avatar circular
- int avatarSize = 128;
+ int avatarSize = 196;
if (chatMessage.getSender() == Role.ASSISTANT) {
displayBox.setAlignment(Pos.CENTER_RIGHT);
@@ -123,9 +125,10 @@ public VerticalLayout buildMessageBox(ChatMessage chatMessage) {
iconPath = new File(App.getExecutedDirectory(), "icons/charater.png").getAbsolutePath();
}
- ImageLoader imageLoader = new ImageLoader(new File(iconPath));
+ ImageLoader imageLoader = new BaseImageLoader(new File(iconPath));
imageLoader.setWidth(avatarSize);
imageLoader.setHeight(avatarSize);
+ imageLoader.setSmoothing(true);
ImageOverlay avatar = new ImageOverlay(imageLoader);
avatar.setFitWidth(avatarSize);
avatar.setFitHeight(avatarSize);
@@ -143,9 +146,10 @@ public VerticalLayout buildMessageBox(ChatMessage chatMessage) {
iconPath = new File(App.getExecutedDirectory(), "icons/charater.png").getAbsolutePath();
}
- ImageLoader imageLoader = new ImageLoader(new File(iconPath));
+ ImageLoader imageLoader = new BaseImageLoader(new File(iconPath));
imageLoader.setWidth(avatarSize);
imageLoader.setHeight(avatarSize);
+ imageLoader.setSmoothing(true);
ImageOverlay avatar = new ImageOverlay(imageLoader);
avatar.setFitWidth(avatarSize);
avatar.setFitHeight(avatarSize);
@@ -171,6 +175,8 @@ public VerticalLayout buildMessageBox(ChatMessage chatMessage) {
public void generateResponse(String prompt, boolean update) {
Chat chat = parent.getChat();
+ if (ServerProcess.getCurrentServer() == null) return;
+
// Remove regenerate button from last message
if (!chatRoot.getElements().isEmpty()) {
VerticalLayout lastMessageBox = (VerticalLayout) chatRoot.getLastElement();
diff --git a/src/main/java/me/piitex/app/views/chats/ChatView.java b/src/main/java/me/piitex/app/views/chats/ChatView.java
index f14f6bfa..b615edf2 100644
--- a/src/main/java/me/piitex/app/views/chats/ChatView.java
+++ b/src/main/java/me/piitex/app/views/chats/ChatView.java
@@ -21,7 +21,7 @@ public class ChatView extends EmptyContainer {
private static final AppSettings APP_SETTINGS = App.getInstance().getAppSettings();
public ChatView(@NotNull Character character, @Nullable Chat chat) {
- super(APP_SETTINGS.getWidth(), APP_SETTINGS.getHeight());
+ super(APP_SETTINGS.getWidth(), App.window.getDrawHeight());
this.character = character;
if (chat == null) {
// Create a new chat
@@ -34,7 +34,7 @@ public ChatView(@NotNull Character character, @Nullable Chat chat) {
}
public void init() {
- layout = new HorizontalLayout(APP_SETTINGS.getWidth() - 100, APP_SETTINGS.getHeight());
+ layout = new HorizontalLayout(getWidth() - 100, getHeight());
layout.setMaxSize(layout.getWidth(), layout.getHeight());
layout.setSpacing(5);
layout.addStyle(Styles.BG_INSET);
diff --git a/src/main/java/me/piitex/app/views/chats/ChatViewSidebar.java b/src/main/java/me/piitex/app/views/chats/ChatViewSidebar.java
index fa8c83d6..937ab754 100644
--- a/src/main/java/me/piitex/app/views/chats/ChatViewSidebar.java
+++ b/src/main/java/me/piitex/app/views/chats/ChatViewSidebar.java
@@ -5,10 +5,12 @@
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.paint.Color;
+import javafx.stage.FileChooser;
import me.piitex.app.App;
import me.piitex.app.backend.Character;
import me.piitex.app.backend.Chat;
import me.piitex.app.configuration.AppSettings;
+import me.piitex.app.utils.ChatUtil;
import me.piitex.app.views.HomeView;
import me.piitex.app.views.LoadingView;
import me.piitex.engine.containers.BorderContainer;
@@ -20,6 +22,7 @@
import me.piitex.engine.overlays.*;
import org.kordamp.ikonli.material2.Material2AL;
+import javax.crypto.IllegalBlockSizeException;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -31,6 +34,7 @@
public class ChatViewSidebar extends EmptyContainer {
private final ChatView parent;
private final VerticalLayout root;
+ private ChoiceBoxOverlay chatSelection;
private static final AppSettings APP_SETTINGS = App.getInstance().getAppSettings();
@@ -100,16 +104,70 @@ private StackContainer buildCurrentChatSelection(Layout layout) {
downloadIcon.setColor(Color.LIGHTBLUE);
ButtonOverlay downloadChat = new ButtonBuilder("download").addStyle(Styles.FLAT).setIcon(downloadIcon).build();
downloadChat.setTooltip("Download Chat");
+ downloadIcon.onClick(_ -> {
+ FileChooser chooser = new FileChooser();
+ chooser.setTitle("Save Chat File");
+ chooser.setInitialFileName(currentChat.getCurrentText() + ".dat");
+ chooser.setSelectedExtensionFilter(new FileChooser.ExtensionFilter("Chat Message", ".dat"));
+ File file = chooser.showSaveDialog(App.window.getStage());
+
+ if (file.isDirectory()) return;
+ if (!file.getParentFile().exists()) return;
+
+ // Now save the data inside the file.
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ App.logger.error("Could not create chat file!", e);
+ }
+
+ try {
+ ChatUtil.exportChat(parent.getChat(), file);
+ } catch (IOException e) {
+ App.logger.error("Could not export chat file!", e);
+ }
+ });
IconOverlay importIcon = new IconOverlay(Material2AL.IMPORT_EXPORT);
importIcon.setColor(Color.LIGHTYELLOW);
ButtonOverlay importChat = new ButtonBuilder("import").addStyle(Styles.FLAT).setIcon(importIcon).build();
importChat.setTooltip("Import Chat");
+ importChat.onClick(_ -> {
+ FileChooser chooser = new FileChooser();
+ chooser.setTitle("Save Chat File");
+ chooser.setSelectedExtensionFilter(new FileChooser.ExtensionFilter("Chat Message", ".dat"));
+
+ File file = chooser.showOpenDialog(App.window.getStage());
+ if (!file.exists() || file.isDirectory()) return;
+
+ try {
+ ChatUtil.importChat(parent.getCharacter(), file);
+ App.logger.info("Imported Chat: '{}'", file.getAbsolutePath());
+
+ Chat chat = parent.getCharacter().getChat("import-" + file.getName());
+ parent.getCharacter().setLastChat(chat);
+ App.window.clearContainers();
+ EmptyContainer progressContainer = new EmptyContainer(APP_SETTINGS.getWidth(), APP_SETTINGS.getHeight());
+ progressContainer.addElement(new LoadingView("Loading chat...", progressContainer.getWidth(), progressContainer.getHeight()));
+ App.window.addContainer(progressContainer);
+
+ App.getThreadPoolManager().submitTask(() -> {
+ ChatView chatView = new ChatView(parent.getCharacter(), chat);
+ Node assemble = chatView.assemble();
+ Platform.runLater(() -> {
+ App.window.clearContainers();
+ App.window.addContainer(chatView, assemble);
+ });
+ });
+ } catch (IOException | IllegalBlockSizeException e) {
+ App.logger.error("Could not import chat file!", e);
+ }
+ });
IconOverlay renameIcon = new IconOverlay(Material2AL.EDIT);
renameIcon.setColor(Color.LIGHTGREEN);
ButtonOverlay renameChat = new ButtonBuilder("rename").addStyle(Styles.FLAT).setIcon(renameIcon).build();
- renameChat.setTooltip("Rename Chat");
+ renameChat.setTooltip("Enter the new name in the text box. Press this button to apply.");
renameIcon.onClick(_ -> {
Character character = parent.getCharacter();
Chat chat = parent.getChat();
@@ -154,7 +212,7 @@ private StackContainer buildCurrentChatSelection(Layout layout) {
chats.add(chat.getFile().getName());
}
- ChoiceBoxOverlay chatSelection = new ChoiceBoxOverlay(chats);
+ chatSelection = new ChoiceBoxOverlay(chats);
chatSelection.setWidth(layout.getWidth());
chatSelection.setDefaultItem(parent.getChat().getFile().getName());
layout.addElement(chatSelection);
diff --git a/src/main/java/me/piitex/app/views/creator/CreatorView.java b/src/main/java/me/piitex/app/views/creator/CreatorView.java
index 24111192..02ecaff0 100644
--- a/src/main/java/me/piitex/app/views/creator/CreatorView.java
+++ b/src/main/java/me/piitex/app/views/creator/CreatorView.java
@@ -10,7 +10,6 @@
import me.piitex.app.views.SidebarView;
import me.piitex.app.views.creator.characters.CharacterCreator;
import me.piitex.app.views.creator.users.UserCreator;
-import me.piitex.app.views.users.UsersView;
import me.piitex.engine.containers.EmptyContainer;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
diff --git a/src/main/java/me/piitex/app/views/creator/characters/CharacterCustomizationView.java b/src/main/java/me/piitex/app/views/creator/characters/CharacterCustomizationView.java
index 899c169f..ded08e91 100644
--- a/src/main/java/me/piitex/app/views/creator/characters/CharacterCustomizationView.java
+++ b/src/main/java/me/piitex/app/views/creator/characters/CharacterCustomizationView.java
@@ -16,7 +16,8 @@
import me.piitex.engine.containers.TileContainer;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
+import me.piitex.engine.loaders.image.BaseImageLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import me.piitex.engine.overlays.*;
import me.piitex.os.configurations.InfoFile;
import org.json.JSONObject;
@@ -56,7 +57,7 @@ public CharacterCustomizationView(CharacterCreator parent, InfoFile infoFile, do
root.setAlignment(Pos.CENTER);
addProperties("progress", "Character");
- scrollContainer = new ScrollContainer(root, width - 5, height - 40);
+ scrollContainer = new ScrollContainer(root, width - 15, height - 40);
scrollContainer.setMaxSize(scrollContainer.getWidth(), scrollContainer.getHeight());
scrollContainer.setHorizontalScroll(false);
scrollContainer.setScrollWhenNeeded(false);
@@ -178,7 +179,7 @@ private VerticalLayout buildSettingsLayout() {
characterIdInput.setCurrentText(id);
characterDisplayInput.setCurrentText(displayName);
characterPersonaInput.setCurrentText(persona);
- characterImage.setImage(new ImageLoader(file));
+ characterImage.setImage(new BaseImageLoader(file));
loreLayout.removeAllElements();
loreItems.forEach((s, s2) -> loreLayout.addElement(buildLoreEntry(s, s2)));
} catch (ImageProcessingException | IOException e) {
@@ -257,9 +258,9 @@ private VerticalLayout buildImagesLayout() {
ImageLoader imageLoader;
if (infoFile.hasKey("icon-path")) {
- imageLoader = new ImageLoader(new File(infoFile.get("icon-path")));
+ imageLoader = new BaseImageLoader(new File(infoFile.get("icon-path")));
} else {
- imageLoader = new ImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
+ imageLoader = new BaseImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
}
imageLoader.setWidth(imageSize);
imageLoader.setHeight(imageSize);
@@ -277,7 +278,7 @@ private VerticalLayout buildImagesLayout() {
if (file != null && file.isFile() && file.exists()) {
App.logger.info("Updating image to '{}'", file.getAbsoluteFile());
- ImageLoader newImage = new ImageLoader(file);
+ ImageLoader newImage = new BaseImageLoader(file);
newImage.setWidth(imageSize);
newImage.setHeight(imageSize);
characterImage.setImage(newImage);
@@ -290,7 +291,7 @@ private VerticalLayout buildImagesLayout() {
ButtonOverlay reset = new ButtonBuilder("rst").setText("Reset Image").addStyle(Styles.FLAT).build();
layout.addElement(reset);
reset.onClick(_ -> {
- ImageLoader loader = new ImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
+ ImageLoader loader = new BaseImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
characterImage.setImage(loader);
infoFile.set("icon-path", imageLoader.getFile().getAbsolutePath());
});
diff --git a/src/main/java/me/piitex/app/views/creator/characters/CharacterUserCustomizationView.java b/src/main/java/me/piitex/app/views/creator/characters/CharacterUserCustomizationView.java
index 13480229..26817a29 100644
--- a/src/main/java/me/piitex/app/views/creator/characters/CharacterUserCustomizationView.java
+++ b/src/main/java/me/piitex/app/views/creator/characters/CharacterUserCustomizationView.java
@@ -16,7 +16,8 @@
import me.piitex.engine.containers.TileContainer;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
+import me.piitex.engine.loaders.image.BaseImageLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import me.piitex.engine.overlays.*;
import me.piitex.os.configurations.InfoFile;
import org.json.JSONObject;
@@ -48,7 +49,7 @@ public CharacterUserCustomizationView(CharacterCreator parent, InfoFile infoFile
root.setAlignment(Pos.CENTER);
addProperties("progress", "User");
- ScrollContainer scrollContainer = new ScrollContainer(root, width, height);
+ ScrollContainer scrollContainer = new ScrollContainer(root, width - 15, height - 40);
scrollContainer.setHorizontalScroll(false);
scrollContainer.setScrollWhenNeeded(false);
scrollContainer.setMaxSize(width, height);
@@ -112,7 +113,7 @@ private VerticalLayout buildSettingsLayout() {
tempLore.clear();
userDisplayInput.setCurrentText(user.getDisplayName());
userPersonaInput.setCurrentText(user.getPersona());
- userImage.setImage(new ImageLoader(new File(user.getIconPath())));
+ userImage.setImage(new BaseImageLoader(new File(user.getIconPath())));
loreLayout.removeAllElements();
loreItems.forEach((s, s2) -> loreLayout.addElement(buildLoreEntry(s, s2)));
});
@@ -183,7 +184,7 @@ private VerticalLayout buildSettingsLayout() {
tempLore.clear();
userDisplayInput.setCurrentText(displayName);
userPersonaInput.setCurrentText(persona);
- userImage.setImage(new ImageLoader(file));
+ userImage.setImage(new BaseImageLoader(file));
loreLayout.removeAllElements();
loreItems.forEach((s, s2) -> loreLayout.addElement(buildLoreEntry(s, s2)));
} catch (ImageProcessingException | IOException e) {
@@ -266,7 +267,7 @@ private VerticalLayout buildImagesLayout() {
image = new File(App.getExecutedDirectory(), "icons/character.png");
}
- ImageLoader imageLoader = new ImageLoader(image);
+ ImageLoader imageLoader = new BaseImageLoader(image);
imageLoader.setWidth(imageSize);
imageLoader.setHeight(imageSize);
@@ -284,7 +285,7 @@ private VerticalLayout buildImagesLayout() {
if (file != null && !file.isDirectory() && file.exists()) {
App.logger.info("Updating image to '{}'", file.getAbsoluteFile());
- ImageLoader newImage = new ImageLoader(file);
+ ImageLoader newImage = new BaseImageLoader(file);
newImage.setWidth(imageSize);
newImage.setHeight(imageSize);
userImage.setImage(newImage);
@@ -297,7 +298,7 @@ private VerticalLayout buildImagesLayout() {
ButtonOverlay reset = new ButtonBuilder("rst").setText("Reset Image").addStyle(Styles.FLAT).build();
layout.addElement(reset);
reset.onClick(_ -> {
- ImageLoader loader = new ImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
+ ImageLoader loader = new BaseImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
userImage.setImage(loader);
infoFile.set("user-icon-path", loader.getFile().getAbsolutePath());
});
diff --git a/src/main/java/me/piitex/app/views/creator/characters/ChatCustomizationView.java b/src/main/java/me/piitex/app/views/creator/characters/ChatCustomizationView.java
index 38aba3b9..a5373048 100644
--- a/src/main/java/me/piitex/app/views/creator/characters/ChatCustomizationView.java
+++ b/src/main/java/me/piitex/app/views/creator/characters/ChatCustomizationView.java
@@ -31,7 +31,7 @@ public ChatCustomizationView(CharacterCreator parent, InfoFile infoFile, double
root.setSpacing(50);
addProperties("progress", "User");
- ScrollContainer scrollContainer = new ScrollContainer(root, width, height);
+ ScrollContainer scrollContainer = new ScrollContainer(root, width - 15, height - 40);
scrollContainer.setHorizontalScroll(false);
scrollContainer.setScrollWhenNeeded(false);
scrollContainer.setMaxSize(width, height);
diff --git a/src/main/java/me/piitex/app/views/creator/characters/FinishCharacterCreatorView.java b/src/main/java/me/piitex/app/views/creator/characters/FinishCharacterCreatorView.java
index f62cbf81..da996062 100644
--- a/src/main/java/me/piitex/app/views/creator/characters/FinishCharacterCreatorView.java
+++ b/src/main/java/me/piitex/app/views/creator/characters/FinishCharacterCreatorView.java
@@ -14,7 +14,7 @@
import me.piitex.engine.containers.ScrollContainer;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import me.piitex.engine.overlays.*;
import me.piitex.os.configurations.InfoFile;
import org.kordamp.ikonli.material2.Material2AL;
@@ -23,7 +23,6 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
-import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
public class FinishCharacterCreatorView extends EmptyContainer {
@@ -44,7 +43,7 @@ public FinishCharacterCreatorView(CharacterCreator parent, InfoFile infoFile, do
root.setSpacing(50);
addProperties("progress", "User");
- ScrollContainer scrollContainer = new ScrollContainer(root, width, height);
+ ScrollContainer scrollContainer = new ScrollContainer(root, width - 15, height - 40);
scrollContainer.setHorizontalScroll(false);
scrollContainer.setScrollWhenNeeded(false);
scrollContainer.setMaxSize(width, height);
diff --git a/src/main/java/me/piitex/app/views/creator/users/UserCustomizationView.java b/src/main/java/me/piitex/app/views/creator/users/UserCustomizationView.java
index e5d0c657..3550fdef 100644
--- a/src/main/java/me/piitex/app/views/creator/users/UserCustomizationView.java
+++ b/src/main/java/me/piitex/app/views/creator/users/UserCustomizationView.java
@@ -13,7 +13,8 @@
import me.piitex.engine.containers.ScrollContainer;
import me.piitex.engine.containers.TileContainer;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
+import me.piitex.engine.loaders.image.BaseImageLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import me.piitex.engine.overlays.*;
import me.piitex.os.configurations.InfoFile;
import org.json.JSONObject;
@@ -172,7 +173,7 @@ private VerticalLayout buildSettingsLayout() {
userIdInput.setCurrentText(id);
userDisplayInput.setCurrentText(displayName);
userPersonaInput.setCurrentText(persona);
- userImage.setImage(new ImageLoader(file));
+ userImage.setImage(new BaseImageLoader(file));
parent.getUserLoreCustomizationView().getLoreLayout().removeAllElements();
loreItems.forEach((s, s2) -> parent.getUserLoreCustomizationView().getLoreLayout().addElement(parent.getUserLoreCustomizationView().buildLoreEntry(s, s2)));
@@ -253,9 +254,9 @@ private VerticalLayout buildImagesLayout() {
ImageLoader imageLoader;
if (infoFile.hasKey("icon-path")) {
- imageLoader = new ImageLoader(new File(infoFile.get("icon-path")));
+ imageLoader = new BaseImageLoader(new File(infoFile.get("icon-path")));
} else {
- imageLoader = new ImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
+ imageLoader = new BaseImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
}
imageLoader.setWidth(imageSize);
imageLoader.setHeight(imageSize);
@@ -273,7 +274,7 @@ private VerticalLayout buildImagesLayout() {
if (file != null && file.isFile() && file.exists()) {
App.logger.info("Updating image to '{}'", file.getAbsoluteFile());
- ImageLoader newImage = new ImageLoader(file);
+ ImageLoader newImage = new BaseImageLoader(file);
newImage.setWidth(imageSize);
newImage.setHeight(imageSize);
userImage.setImage(newImage);
@@ -286,7 +287,7 @@ private VerticalLayout buildImagesLayout() {
ButtonOverlay reset = new ButtonBuilder("rst").setText("Reset Image").addStyle(Styles.FLAT).build();
layout.addElement(reset);
reset.onClick(_ -> {
- ImageLoader loader = new ImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
+ ImageLoader loader = new BaseImageLoader(new File(App.getExecutedDirectory(), "icons/character.png"));
userImage.setImage(loader);
infoFile.set("icon-path", imageLoader.getFile().getAbsolutePath());
});
diff --git a/src/main/java/me/piitex/app/views/models/ModelEditView.java b/src/main/java/me/piitex/app/views/models/ModelEditView.java
index 96795a60..b287b170 100644
--- a/src/main/java/me/piitex/app/views/models/ModelEditView.java
+++ b/src/main/java/me/piitex/app/views/models/ModelEditView.java
@@ -42,7 +42,10 @@ public class ModelEditView extends EmptyContainer {
private int dryPenaltyTokens = -1;
private String mmProj = "None / Disabled";
private String chatTemplate = "default";
- private String reasoningTemplate = "disabled";
+ private String reasoningTemplate = "auto";
+ private int reasoningBudget = 1;
+ private boolean reasoning = false;
+ private boolean forceDisableReasoning = false;
private boolean jinja = false;
public ModelEditView(ModelSettings settings) {
@@ -90,7 +93,10 @@ public ModelEditView(ModelSettings settings) {
layout.addElement(buildDryAllowedLength());
layout.addElement(buildDryPenaltyToken());
layout.addElement(buildChatTemplates());
+ layout.addElement(buildReasoningEnable());
+ layout.addElement(buildReasoningBudget());
layout.addElement(buildReasoningTemplate());
+ layout.addElement(buildForceReasoning());
layout.addElement(buildJinjaTemplate());
page.addElement(buildSubmitBox());
@@ -119,6 +125,9 @@ private void initializeSettings() {
this.dryPenaltyTokens = settings.getDryPenaltyTokens();
this.chatTemplate = settings.getChatTemplate();
this.reasoningTemplate = settings.getReasoningTemplate();
+ this.reasoning = settings.isReasoning();
+ this.reasoningBudget = settings.getReasoningBudget();
+ this.forceDisableReasoning = settings.isForceDisableReasoning();
this.jinja = settings.isJinja();
}
@@ -676,6 +685,7 @@ private TileContainer buildReasoningTemplate() {
items.add("deepseek");
items.add("none");
items.add("disabled");
+ items.add("auto");
ComboBoxOverlay selection = new ComboBoxOverlay(items, 400, 50);
selection.setDefaultItem(reasoningTemplate);
@@ -687,6 +697,72 @@ private TileContainer buildReasoningTemplate() {
return tileContainer;
}
+ private TileContainer buildReasoningEnable() {
+ TileContainer tileContainer = new TileContainer(0, 0);
+ tileContainer.addStyle(Styles.BORDER_DEFAULT);
+ tileContainer.addStyle(Styles.BG_DEFAULT);
+ tileContainer.addStyle(appSettings.getGlobalTextSize());
+ tileContainer.setMaxSize(appSettings.getWidth() - 300, 150);
+ tileContainer.setTitle("Enable Reasoning");
+ tileContainer.setDescription("Enables thinking mode.");
+
+ IconOverlay info = new IconOverlay(Material2AL.INFO);
+ info.setTooltip("Only enable if the model is capable of reasoning.");
+ tileContainer.setGraphic(info);
+
+ ToggleSwitchOverlay switchOverlay = new ToggleSwitchOverlay(reasoning);
+ switchOverlay.onToggle(event -> {
+ this.reasoning = event.getNewValue();
+ });
+ tileContainer.setAction(switchOverlay);
+
+ return tileContainer;
+ }
+
+ private TileContainer buildReasoningBudget() {
+ TileContainer tileContainer = new TileContainer(0, 0);
+ tileContainer.addStyle(Styles.BORDER_DEFAULT);
+ tileContainer.addStyle(Styles.BG_DEFAULT);
+ tileContainer.addStyle(appSettings.getGlobalTextSize());
+ tileContainer.setMaxSize(appSettings.getWidth() - 300, 150);
+ tileContainer.setTitle("Reasoning Budget");
+ tileContainer.setDescription("Set the maximum tokens allowed for reasoning.");
+
+ IconOverlay info = new IconOverlay(Material2AL.INFO);
+ info.setTooltip("Default is -1 which disables the limit.");
+ tileContainer.setGraphic(info);
+
+ SpinnerNumberOverlay input = new SpinnerNumberOverlay(-1, Double.MAX_VALUE, reasoningBudget);
+ input.onValueChange(event -> {
+ this.reasoningBudget = event.getNewValue().intValue();
+ });
+ tileContainer.setAction(input);
+
+ return tileContainer;
+ }
+
+ private TileContainer buildForceReasoning() {
+ TileContainer tileContainer = new TileContainer(0, 0);
+ tileContainer.addStyle(Styles.BORDER_DEFAULT);
+ tileContainer.addStyle(Styles.BG_DEFAULT);
+ tileContainer.addStyle(appSettings.getGlobalTextSize());
+ tileContainer.setMaxSize(appSettings.getWidth() - 300, 150);
+ tileContainer.setTitle("Force Disable Reasoning");
+ tileContainer.setDescription("Forcefully disables reasoning.");
+
+ IconOverlay info = new IconOverlay(Material2AL.INFO);
+ info.setTooltip("Only enable if the reasoning is incompatible with llama.cpp.");
+ tileContainer.setGraphic(info);
+
+ ToggleSwitchOverlay switchOverlay = new ToggleSwitchOverlay(forceDisableReasoning);
+ switchOverlay.onToggle(event -> {
+ this.forceDisableReasoning = event.getNewValue();
+ });
+ tileContainer.setAction(switchOverlay);
+
+ return tileContainer;
+ }
+
private TileContainer buildJinjaTemplate() {
TileContainer tileContainer = new TileContainer(0, 0);
tileContainer.addStyle(Styles.BORDER_DEFAULT);
@@ -751,6 +827,9 @@ public HorizontalLayout buildSubmitBox() {
settings.setMmProj(mmProj);
settings.setChatTemplate(chatTemplate);
settings.setReasoningTemplate(reasoningTemplate);
+ settings.setReasoning(reasoning);
+ settings.setReasoningBudget(reasoningBudget);
+ settings.setForceDisableReasoning(forceDisableReasoning);
settings.setJinja(jinja);
settings.setChange(true);
diff --git a/src/main/java/me/piitex/app/views/models/tabs/ConfigurationTab.java b/src/main/java/me/piitex/app/views/models/tabs/ConfigurationTab.java
index 4609d6be..b8d18fd1 100644
--- a/src/main/java/me/piitex/app/views/models/tabs/ConfigurationTab.java
+++ b/src/main/java/me/piitex/app/views/models/tabs/ConfigurationTab.java
@@ -11,6 +11,8 @@
import me.piitex.app.backend.Model;
import me.piitex.app.backend.server.*;
import me.piitex.app.configuration.AppSettings;
+import me.piitex.app.configuration.ServerSettings;
+import me.piitex.app.views.HomeView;
import me.piitex.engine.Element;
import me.piitex.engine.PopupPosition;
import me.piitex.engine.containers.CardContainer;
@@ -23,11 +25,14 @@
import me.piitex.engine.layouts.VerticalLayout;
import me.piitex.engine.overlays.*;
import me.piitex.os.OSUtil;
+import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.TimeUnit;
import static me.piitex.app.views.Positions.*;
@@ -59,17 +64,15 @@ public ConfigurationTab(TabsContainer tabsContainer) {
scrollContainer.setScrollWhenNeeded(false);
addElement(scrollContainer); // Adds the scroll container
- // TODO: Allow remote server routing.
- // When enabled it allows the user to remotely connect to an endpoint.
- // Not sure how control of the server would work.
layout.addElement(buildServerZone());
+ layout.addElement(buildBackendReset());
layout.addElement(buildHostTile());
layout.addElement(buildRemoteModeTile());
layout.addElement(buildBackend());
layout.addElement(buildGpuDevice());
+ layout.addElement(buildCurrentModel());
layout.addElement(buildRunningModel());
layout.addElement(buildModelPathTile());
- layout.addElement(buildCurrentModel());
layout.addElement(buildGpuLayers());
layout.addElement(buildMemoryLock());
layout.addElement(buildFlashAttention());
@@ -77,16 +80,67 @@ public ConfigurationTab(TabsContainer tabsContainer) {
Platform.runLater(this::handleServerLoad);
}
+ public TileContainer buildBackendReset() {
+ TileContainer container = new TileContainer(layout.getWidth(), -1);
+ container.setMaxSize(container.getWidth(), container.getHeight());
+ container.setTitle("Reset backend files.");
+ container.setDescription("Deletes all backend files and prompts a re-installation.");
+ container.addStyle(Styles.BG_DEFAULT);
+ container.addStyle(Styles.BORDER_DEFAULT);
+ container.addStyle(appSettings.getGlobalTextSize());
+
+ VerticalLayout configLayout = new VerticalLayout(400, container.getHeight());
+ configLayout.setSpacing(10);
+ configLayout.setAlignment(Pos.CENTER_RIGHT);
+
+ ButtonOverlay reset = new ButtonOverlay(new ButtonBuilder("reset").setText("Reset").addStyle(Styles.DANGER).addStyle(Styles.BUTTON_OUTLINED));
+ reset.onClick(_ -> {
+ if (ServerProcess.getCurrentServer() == null) return;
+ ServerProcess.getCurrentServer().stop();
+ File backendDir = App.getBackendDirectory();
+ for (File dir : backendDir.listFiles()) {
+ if (dir.isDirectory()) {
+ App.logger.info("Deleted '{}'", dir.getAbsolutePath());
+ try {
+ FileUtils.deleteDirectory(dir);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ File backendVersionFile = Arrays.stream(Objects.requireNonNull(backendDir.listFiles())).filter(file -> file.getName().endsWith(".txt")).findAny().orElse(null);
+ if (backendVersionFile != null) {
+ try {
+ FileUtils.delete(backendVersionFile);
+ App.logger.info("Deleted backend version file...");
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ App.getInstance().performUpdates();
+
+ App.window.clearContainers();
+ App.window.addContainer(new HomeView());
+ });
+ configLayout.addElement(reset);
+
+ container.setAction(configLayout);
+
+ return container;
+ }
+
public TileContainer buildHostTile() {
- TileContainer container = new TileContainer(0, -1);
- container.setMaxSize(layout.getWidth(), 180);
+ TileContainer container = new TileContainer(layout.getWidth(), -1);
+ container.setMaxSize(container.getWidth(), container.getHeight());
container.setTitle("Set device as host.");
container.setDescription("Allows other devices to connect to this devices backend server.");
container.addStyle(Styles.BG_DEFAULT);
container.addStyle(Styles.BORDER_DEFAULT);
container.addStyle(appSettings.getGlobalTextSize());
- VerticalLayout configLayout = new VerticalLayout(400, 150);
+ VerticalLayout configLayout = new VerticalLayout(400, container.getHeight());
configLayout.setSpacing(10);
configLayout.setAlignment(Pos.CENTER_RIGHT);
@@ -100,15 +154,15 @@ public TileContainer buildHostTile() {
}
public TileContainer buildRemoteModeTile() {
- TileContainer container = new TileContainer(0, -1);
- container.setMaxSize(layout.getWidth(), 180);
+ TileContainer container = new TileContainer(layout.getWidth(), -1);
+ container.setMaxSize(container.getWidth(), container.getHeight());
container.setTitle("Remote Server Mode");
container.setDescription("Setup a remote connection to use a different device to run models.");
container.addStyle(Styles.BG_DEFAULT);
container.addStyle(Styles.BORDER_DEFAULT);
container.addStyle(appSettings.getGlobalTextSize());
- VerticalLayout configLayout = new VerticalLayout(400, 150);
+ VerticalLayout configLayout = new VerticalLayout(400, container.getHeight());
configLayout.setSpacing(10);
configLayout.setAlignment(Pos.CENTER_RIGHT);
@@ -136,8 +190,8 @@ public TileContainer buildRemoteModeTile() {
}
public TileContainer buildModelPathTile() {
- TileContainer container = new TileContainer(0, -1);
- container.setMaxSize(layout.getWidth(), 100);
+ TileContainer container = new TileContainer(layout.getWidth(), -1);
+ container.setMaxSize(container.getWidth(), container.getHeight());
container.setTitle("Model Path");
container.setDescription("Select the folder for your models.");
container.addStyle(Styles.BG_DEFAULT);
@@ -186,8 +240,8 @@ private ButtonOverlay actionButton(TileContainer container) {
public TileContainer buildCurrentModel() {
- TileContainer container = new TileContainer(0, -1);
- container.setMaxSize(layout.getWidth(), 100);
+ TileContainer container = new TileContainer(layout.getWidth(), -1);
+ container.setMaxSize(container.getWidth(), container.getHeight());
container.setTitle("Model Selection");
container.setDescription("Select a model to use. Will require a \"reload\".");
container.addStyle(Styles.BG_DEFAULT);
@@ -226,15 +280,15 @@ public TileContainer buildCurrentModel() {
}
public TileContainer buildGpuLayers() {
- TileContainer container = new TileContainer(0, -1);
- container.setMaxSize(layout.getWidth(), 100);
+ TileContainer container = new TileContainer(layout.getWidth(), -1);
+ container.setMaxSize(container.getWidth(), container.getHeight());
container.setTitle("GPU Usage");
container.setDescription("Percentage of total VRAM to use. Recommended to keep below 80%.");
container.addStyle(Styles.BG_DEFAULT);
container.addStyle(Styles.BORDER_DEFAULT);
container.addStyle(appSettings.getGlobalTextSize());
- VerticalLayout action = new VerticalLayout(200, 100);
+ VerticalLayout action = new VerticalLayout(200, container.getHeight());
action.setAlignment(Pos.CENTER);
SliderOverlay input = new SliderOverlay(0, 100, settings.getGpuUsage());
@@ -269,8 +323,8 @@ public Double fromString(String string) {
}
public TileContainer buildMemoryLock() {
- TileContainer container = new TileContainer(0, -1);
- container.setMaxSize(layout.getWidth(), 100);
+ TileContainer container = new TileContainer(layout.getWidth(), -1);
+ container.setMaxSize(container.getWidth(), container.getHeight());
container.setTitle("Memory Lock");
container.setDescription("Locks model in RAM. Can improve generation times. Disables model swapping.");
container.addStyle(Styles.BG_DEFAULT);
@@ -287,8 +341,8 @@ public TileContainer buildMemoryLock() {
}
public TileContainer buildFlashAttention() {
- TileContainer container = new TileContainer(0, -1);
- container.setMaxSize(layout.getWidth(), 100);
+ TileContainer container = new TileContainer(layout.getWidth(), -1);
+ container.setMaxSize(container.getWidth(), container.getHeight());
container.setTitle("Flash Attention");
container.setDescription("Toggles flash attention. Designed to speed up training and inference while reducing memory usage. In some rare cases it can greatly reduce quality.");
container.addStyle(Styles.BG_DEFAULT);
@@ -305,8 +359,8 @@ public TileContainer buildFlashAttention() {
}
public TileContainer buildRunningModel() {
- TileContainer container = new TileContainer(0, -1);
- container.setMaxSize(layout.getWidth(), 100);
+ TileContainer container = new TileContainer(layout.getWidth(), -1);
+ container.setMaxSize(container.getWidth(), container.getHeight());
container.setTitle("Current Model");
container.setDescription("The current running model that is loaded. Will be null if no model is active.");
container.addStyle(Styles.BG_DEFAULT);
@@ -328,8 +382,8 @@ public TileContainer buildRunningModel() {
}
public CardContainer buildServerZone() {
- CardContainer card = new CardContainer(0, 0, layout.getWidth(), 200);
- card.setMaxSize(layout.getWidth(), 200);
+ CardContainer card = new CardContainer(0, 0, layout.getWidth(), -1);
+ card.setMaxSize(card.getWidth(), card.getHeight());
TextOverlay text = new TextOverlay("Server Zone");
text.addStyle(Styles.TITLE_3);
@@ -341,11 +395,14 @@ public CardContainer buildServerZone() {
desc.addStyle(appSettings.getGlobalTextSize());
card.setBody(desc);
- HorizontalLayout layout = new HorizontalLayout(0, 0);
+
+ HorizontalLayout layout = new HorizontalLayout(-1, 80);
+ layout.setMaxSize(layout.getWidth(), layout.getHeight());
layout.setSpacing(20);
- layout.setAlignment(Pos.CENTER);
+ layout.setAlignment(Pos.BOTTOM_CENTER);
card.setFooter(layout);
+
start = new ButtonBuilder("start").setText("Start").build();
start.setEnabled(true);
start.addStyle(Styles.SUCCESS);
@@ -427,7 +484,7 @@ public CardContainer buildServerZone() {
});
- stop.onClick(event -> {
+ stop.onClick(_ -> {
if (ServerProcess.getCurrentServer() == null) {
return;
}
@@ -506,13 +563,11 @@ public void onServerLoadingComplete(boolean success) {
Platform.runLater(() -> {
if (App.window.getCurrentPopup() != null) { // Check if popup still exists
App.window.removeContainer(App.window.getCurrentPopup());
-
- start.getNode().setDisable(false);
- stop.getNode().setDisable(false);
- reload.getNode().setDisable(false);
+ start.setEnabled(true);
+ stop.setEnabled(true);
+ reload.setEnabled(true);
}
});
- // Crucial: Remove the listener if it's a one-time event, to prevent memory leaks
serverProcess.removeServerLoadingListener(this);
}
});
diff --git a/src/main/java/me/piitex/app/views/models/tabs/DownloadTab.java b/src/main/java/me/piitex/app/views/models/tabs/DownloadTab.java
index bb9c890d..2579fa0b 100644
--- a/src/main/java/me/piitex/app/views/models/tabs/DownloadTab.java
+++ b/src/main/java/me/piitex/app/views/models/tabs/DownloadTab.java
@@ -30,6 +30,7 @@
import java.io.File;
import java.io.IOException;
import java.net.SocketTimeoutException;
+import java.net.URISyntaxException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -233,7 +234,7 @@ private void setupFileInfoFetch(HorizontalLayout tileLayout, String key, String
long fileSize = 0;
try {
fileSize = downloader.getRemoteFileSize(url);
- } catch (IOException e) {
+ } catch (IOException | URISyntaxException e) {
App.logger.warn("Failed to fetch download size. Connection could not be established: '{}'", url);
}
String fileName = url.substring(url.lastIndexOf('/') + 1);
@@ -315,7 +316,7 @@ private void createDownloadInputs(HorizontalLayout tileLayout, ButtonOverlay dow
DownloadInfo existingInfo = downloader.getDownloadInfo(url);
RingProgressIndicator progressIndicator = new RingProgressIndicator(0, false);
- IconOverlay stopButton = createStopButton(url, destinationFile, downloadIcon, tileLayout, fileInfoRef);
+ IconOverlay stopButton = createStopButton(url, destinationFile, existingInfo, fileInfoRef);
TextField downloadSpeed = new TextField("");
downloadSpeed.setMaxWidth(100);
@@ -411,7 +412,7 @@ public void onDownloadCancel(DownloadInfo info) {
}
}
- private IconOverlay createStopButton(String url, File fileToDelete, ButtonOverlay downloadIcon, HorizontalLayout tileLayout, AtomicReference fileInfoRef) {
+ private IconOverlay createStopButton(String url, File fileToDelete, DownloadInfo info, AtomicReference fileInfoRef) {
IconOverlay stop = new IconOverlay(Material2MZ.STOP_CIRCLE);
stop.setColor(Color.RED);
stop.onClick(event -> {
diff --git a/src/main/java/me/piitex/app/views/models/tabs/ListTab.java b/src/main/java/me/piitex/app/views/models/tabs/ListTab.java
index 7fecfec0..ad019616 100644
--- a/src/main/java/me/piitex/app/views/models/tabs/ListTab.java
+++ b/src/main/java/me/piitex/app/views/models/tabs/ListTab.java
@@ -4,6 +4,7 @@
import atlantafx.base.theme.Tweaks;
import javafx.application.Platform;
import javafx.geometry.Pos;
+import javafx.scene.paint.Color;
import me.piitex.app.App;
import me.piitex.app.backend.Model;
import me.piitex.app.configuration.AppSettings;
@@ -74,7 +75,7 @@ public void buildModelCards(Layout layout) {
TitledLayout root = new TitledLayout(model.getFile().getName() + " (" + formattedFileSize + "GB)", scrollContainer.getWidth() - 100, -1);
root.setMaxSize(root.getWidth(), -1);
root.addStyle(Styles.DENSE);
- root.setSpacing(50);
+ root.setSpacing(25);
root.setAlignment(Pos.TOP_CENTER);
root.addStyle(Tweaks.ALT_ICON);
root.setExpanded(model.getSettings().isDefault());
@@ -100,37 +101,16 @@ public void buildModelCards(Layout layout) {
location.setEnabled(false);
body.addElement(location);
- HorizontalLayout footer = new HorizontalLayout(800, 50);
+ HorizontalLayout footer = new HorizontalLayout(400, 50);
+ footer.setAlignment(Pos.CENTER_LEFT);
root.addElement(footer);
-
- CheckBoxOverlay defaultModel = new CheckBoxOverlay(model.getSettings().isDefault(),"Set as default");
- footer.addElement(defaultModel);
- defaultModel.onSet(event -> {
- // If set to true scan all models and set default to false
- // Then set this one to true
- // Also, re-render the view
-
- // Reloop models and disable any defaults
- for (Model m : App.getModels("exclude")) {
- if (m == model) continue;
- if (m.getSettings().isDefault()) {
- m.getSettings().setDefault(false);
- }
- }
- model.getSettings().setDefault(event.getNewValue());
-
- tabsContainer.replaceTab(tabsContainer.getTabs().get("List"), new ListTab(tabsContainer));
- tabsContainer.setSelectedTab("List");
- });
-
- HorizontalLayout subFooter = new HorizontalLayout(400, 50);
- subFooter.setX(20);
- subFooter.setSpacing(40);
- footer.addElement(subFooter);
+ footer.setSpacing(30);
IconOverlay settings = new IconOverlay(Material2MZ.SETTINGS);
+ settings.setIconSize(18);
+ settings.setColor(Color.BLUE);
settings.setTooltip("Go to model settings.");
- subFooter.addElement(settings);
+ footer.addElement(settings);
settings.addStyle(Styles.ACCENT);
settings.addStyle(Styles.LARGE);
settings.onClick(event -> {
@@ -139,9 +119,11 @@ public void buildModelCards(Layout layout) {
});
IconOverlay delete = new IconOverlay(Material2AL.DELETE_FOREVER);
+ delete.setIconSize(18);
+ delete.setColor(Color.RED);
delete.setTooltip("Delete the model.");
delete.addStyle(Styles.DANGER);
- subFooter.addElement(delete);
+ footer.addElement(delete);
delete.onClick(event -> {
// Confirm the model deletion.
diff --git a/src/main/java/me/piitex/app/views/settings/SettingsView.java b/src/main/java/me/piitex/app/views/settings/SettingsView.java
index 98f219fa..ede18af4 100644
--- a/src/main/java/me/piitex/app/views/settings/SettingsView.java
+++ b/src/main/java/me/piitex/app/views/settings/SettingsView.java
@@ -79,7 +79,7 @@ public TileContainer buildWindowScaling() {
App.window.clear();
App.window.close(false);
appSettings.setWindowScaling(event.getNewValue());
- App.getInstance().initialization(App.window.getStage());
+ App.getInstance().initialization();
});
tileContainer.setAction(toggleSwitchOverlay);
@@ -175,7 +175,7 @@ public TileContainer buildChatSize() {
App.getInstance().getCharacters().values().forEach(character -> character.getChatViewCachedNodes().clear());
App.window.clear();
App.window.close(false);
- App.getInstance().initialization(App.window.getStage());
+ App.getInstance().initialization();
});
tileContainer.setAction(selection);
@@ -230,7 +230,7 @@ public TileContainer buildGlobalChatSize() {
App.getInstance().getCharacters().values().forEach(character -> character.getChatViewCachedNodes().clear());
App.window.clear();
App.window.close(false);
- App.getInstance().initialization(App.window.getStage());
+ App.getInstance().initialization();
});
diff --git a/src/main/java/me/piitex/app/views/setup/ConfigureBackendView.java b/src/main/java/me/piitex/app/views/setup/ConfigureBackendView.java
index f109c785..28f4d5d4 100644
--- a/src/main/java/me/piitex/app/views/setup/ConfigureBackendView.java
+++ b/src/main/java/me/piitex/app/views/setup/ConfigureBackendView.java
@@ -6,7 +6,7 @@
import javafx.scene.text.TextAlignment;
import javafx.util.StringConverter;
import me.piitex.app.App;
-import me.piitex.app.backend.server.ServerSettings;
+import me.piitex.app.configuration.ServerSettings;
import me.piitex.app.configuration.AppSettings;
import me.piitex.app.views.Positions;
import me.piitex.engine.layouts.VerticalLayout;
diff --git a/src/main/java/me/piitex/app/views/setup/ConfigureModelView.java b/src/main/java/me/piitex/app/views/setup/ConfigureModelView.java
index 9f5f37ac..a1277c59 100644
--- a/src/main/java/me/piitex/app/views/setup/ConfigureModelView.java
+++ b/src/main/java/me/piitex/app/views/setup/ConfigureModelView.java
@@ -3,13 +3,11 @@
import atlantafx.base.theme.Styles;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
-import javafx.scene.paint.Color;
import javafx.stage.DirectoryChooser;
import me.piitex.app.App;
-import me.piitex.app.backend.server.ServerSettings;
+import me.piitex.app.configuration.ServerSettings;
import me.piitex.app.configuration.AppSettings;
import me.piitex.app.views.Positions;
-import me.piitex.engine.containers.TileContainer;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
import me.piitex.engine.overlays.*;
diff --git a/src/main/java/me/piitex/app/views/setup/GPUSetupView.java b/src/main/java/me/piitex/app/views/setup/GPUSetupView.java
index 85a17bc0..f050103d 100644
--- a/src/main/java/me/piitex/app/views/setup/GPUSetupView.java
+++ b/src/main/java/me/piitex/app/views/setup/GPUSetupView.java
@@ -5,7 +5,7 @@
import javafx.geometry.Pos;
import me.piitex.app.App;
import me.piitex.app.backend.server.DeviceProcess;
-import me.piitex.app.backend.server.ServerSettings;
+import me.piitex.app.configuration.ServerSettings;
import me.piitex.app.configuration.AppSettings;
import me.piitex.app.views.Positions;
import me.piitex.engine.layouts.VerticalLayout;
@@ -63,6 +63,12 @@ private void init() {
backendBox.addElement(backendSelection);
backendSelection.onItemSelect(event -> {
settings.setBackend(event.getNewValue());
+
+ try {
+ new DeviceProcess(event.getNewValue());
+ } catch (IOException e) {
+ App.logger.error("Could not load devices for '" + event.getNewValue() + "'!", e);
+ }
});
VerticalLayout gpuBox = new VerticalLayout(-1, -1);
diff --git a/src/main/java/me/piitex/app/views/setup/SetupModelView.java b/src/main/java/me/piitex/app/views/setup/SetupModelView.java
index b1145200..4c849a13 100644
--- a/src/main/java/me/piitex/app/views/setup/SetupModelView.java
+++ b/src/main/java/me/piitex/app/views/setup/SetupModelView.java
@@ -7,7 +7,7 @@
import javafx.scene.text.TextAlignment;
import me.piitex.app.App;
import me.piitex.app.backend.server.ServerProcess;
-import me.piitex.app.backend.server.ServerSettings;
+import me.piitex.app.configuration.ServerSettings;
import me.piitex.app.configuration.AppSettings;
import me.piitex.app.views.Positions;
import me.piitex.app.views.creator.characters.CharacterCreator;
@@ -18,7 +18,6 @@
import me.piitex.os.FileDownloader;
import me.piitex.os.OSUtil;
-import java.awt.*;
import java.io.File;
import java.io.IOException;
diff --git a/src/main/java/me/piitex/app/views/setup/SetupRunnerView.java b/src/main/java/me/piitex/app/views/setup/SetupRunnerView.java
index 450b6963..75e4af8f 100644
--- a/src/main/java/me/piitex/app/views/setup/SetupRunnerView.java
+++ b/src/main/java/me/piitex/app/views/setup/SetupRunnerView.java
@@ -2,16 +2,14 @@
import atlantafx.base.theme.Styles;
import javafx.application.Platform;
-import javafx.beans.binding.Bindings;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.control.ProgressBar;
import javafx.scene.paint.Color;
import me.piitex.app.App;
import me.piitex.app.backend.Model;
-import me.piitex.app.backend.server.DeviceProcess;
import me.piitex.app.backend.server.ServerProcess;
-import me.piitex.app.backend.server.ServerSettings;
+import me.piitex.app.configuration.ServerSettings;
import me.piitex.app.configuration.AppSettings;
import me.piitex.app.views.Positions;
import me.piitex.engine.layouts.VerticalLayout;
@@ -92,6 +90,12 @@ private void startRunner() {
}
private void downloadSmallModel() {
+ File model = new File(App.getModelsDirectory(), "gemma-3-270m-it-UD-IQ2_M.gguf");
+ if (model.exists()) {
+ startLLama(model);
+ return;
+ }
+
FileDownloader fileDownloader = new FileDownloader();
fileDownloader.addDownloadListener(new DownloadListener() {
@Override
diff --git a/src/main/java/me/piitex/app/views/users/UserEditView.java b/src/main/java/me/piitex/app/views/users/UserEditView.java
deleted file mode 100644
index f49bb7aa..00000000
--- a/src/main/java/me/piitex/app/views/users/UserEditView.java
+++ /dev/null
@@ -1,227 +0,0 @@
-package me.piitex.app.views.users;
-
-import atlantafx.base.theme.Styles;
-import javafx.geometry.Pos;
-import javafx.scene.control.TextField;
-import me.piitex.app.App;
-import me.piitex.app.backend.User;
-import me.piitex.app.configuration.AppSettings;
-import me.piitex.app.views.SidebarView;
-import me.piitex.app.views.users.tabs.UserLoreBookTab;
-import me.piitex.app.views.users.tabs.UserTab;
-import me.piitex.engine.PopupPosition;
-import me.piitex.engine.containers.DialogueContainer;
-import me.piitex.engine.containers.EmptyContainer;
-import me.piitex.engine.containers.tabs.TabsContainer;
-import me.piitex.engine.layouts.HorizontalLayout;
-import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.overlays.ButtonBuilder;
-import me.piitex.engine.overlays.ButtonOverlay;
-import me.piitex.engine.overlays.MessageOverlay;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
-import java.util.TreeMap;
-
-public class UserEditView extends EmptyContainer {
- private User user;
- private String userId = "";
- private String userDisplay = "";
- private String userPersona = "";
- private File userIconPath;
-
- // Lore Map
- private TreeMap loreBook;
-
- private TabsContainer tabsContainer;
- private UserTab userTab;
- private UserLoreBookTab userLoreBookTab;
-
- private static final AppSettings appSettings = App.getInstance().getAppSettings();
-
- public UserEditView() {
- super(appSettings.getWidth(), appSettings.getHeight());
- this.loreBook = new TreeMap<>();
- init();
- }
-
- public UserEditView(User user) {
- super(appSettings.getWidth(), appSettings.getHeight());
- this.user = user;
- this.userId = user.getId();
- this.userDisplay = user.getDisplayName();
- this.userPersona = user.getPersona();
- this.userIconPath = new File(user.getIconPath());
- this.loreBook = user.getLorebook();
- init();
- }
-
- public void init() {
- addStyle(Styles.BG_INSET);
-
- HorizontalLayout root = new HorizontalLayout(getWidth(), getHeight());
- root.setMaxSize(root.getWidth(), root.getHeight());
- addElement(root);
-
- SidebarView sidebarView = new SidebarView();
- root.addElement(sidebarView);
-
- VerticalLayout mainPage = new VerticalLayout(appSettings.getWidth() - 200, 0);
- mainPage.setMaxSize(mainPage.getWidth(), mainPage.getHeight());
- root.addElement(mainPage);
-
- tabsContainer = new TabsContainer(0, 0, mainPage.getWidth(), appSettings.getHeight());
- mainPage.addElement(tabsContainer);
-
- userTab = new UserTab("User", this);
- tabsContainer.addTab(userTab);
-
- userLoreBookTab = new UserLoreBookTab("Lorebook", this);
- tabsContainer.addTab(userLoreBookTab);
- }
-
- public HorizontalLayout buildSubmitBox() {
- HorizontalLayout layout = new HorizontalLayout(appSettings.getWidth() - 300, 50);
- layout.setY(appSettings.getHeight() - 150);
- layout.setSpacing(20);
- layout.setAlignment(Pos.CENTER);
-
- ButtonOverlay cancel = new ButtonBuilder("cancel").setText("Cancel").build();
- cancel.addStyle(Styles.DANGER);
- cancel.addStyle(Styles.BUTTON_OUTLINED);
- layout.addElement(cancel);
-
- ButtonOverlay submit = new ButtonBuilder("submit").setText("Submit").build();
- submit.addStyle(Styles.SUCCESS);
- submit.addStyle(Styles.BUTTON_OUTLINED);
- layout.addElements(submit);
-
- cancel.onClick(event -> {
- DialogueContainer dialogueContainer = new DialogueContainer("Do you want to exit without saving?", 500, 500);
-
- ButtonOverlay stay = new ButtonBuilder("stay").setText("Stay").build();
- stay.setWidth(150);
- stay.addStyle(Styles.SUCCESS);
- stay.onClick(_ -> App.window.removeContainer(dialogueContainer));
-
- ButtonOverlay leave = new ButtonBuilder("leave").setText("Leave").build();
- leave.setWidth(150);
- leave.addStyle(Styles.DANGER);
- leave.onClick(_ -> {
- App.window.clearContainers();
- App.window.addContainer(new UsersView());
- });
-
- dialogueContainer.setCancelButton(stay);
- dialogueContainer.setConfirmButton(leave);
-
- App.window.renderPopup(dialogueContainer, event.getHandler().getSceneX(), event.getHandler().getSceneY() - 100, 500, 500);
- });
-
- submit.onClick(event -> {
- if (!validate()) {
- return;
- }
-
- if (user == null) {
- user = new User(userId);
- }
- user.setDisplayName(userDisplay);
- user.setPersona(userPersona);
- user.setLorebook(loreBook);
- if (userIconPath == null || !userIconPath.exists()) {
- userIconPath = new File(App.getAppDirectory(), "icons/character.png");
- }
- try {
- File output = new File(user.getUserDirectory(), "user.png");
- Files.copy(userIconPath.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING);
- user.setIconPath(output.getAbsolutePath());
- } catch (IOException e) {
- App.logger.error("Failed to move image to user.", e);
- }
-
- App.getInstance().getUserTemplates().putIfAbsent(userId, user);
-
- App.window.clearContainers();
- App.window.addContainer(new UsersView());
- });
-
- return layout;
- }
-
- public boolean validate() {
- if (userId.isEmpty() || ((TextField) userTab.getUserIdInput().getNode()).getText().isEmpty()) {
- tabsContainer.getTabPane().getSelectionModel().select(userTab.getJfxTab());
- MessageOverlay required = new MessageOverlay(0, 0, 600, 100, "User ID", "ID is required.");
- required.addStyle(Styles.WARNING);
- required.addStyle(Styles.BG_DEFAULT);
- App.window.renderPopup(required, PopupPosition.CENTER, 600, 100, true);
- userTab.getUserIdInput().getNode().requestFocus();
- return false;
- } else if (userDisplay.isEmpty() || ((TextField) userTab.getUserDisplayNameInput().getNode()).getText().isEmpty()) {
- tabsContainer.getTabPane().getSelectionModel().select(userTab.getJfxTab());
- MessageOverlay required = new MessageOverlay(0, 0, 600, 100, "Display Name", "Display name is required.");
- required.addStyle(Styles.WARNING);
- required.addStyle(Styles.BG_DEFAULT);
- App.window.renderPopup(required, PopupPosition.CENTER, 600, 100, true);
- userTab.getUserIdInput().getNode().requestFocus();
- return false;
- } else {
- return true;
- }
- }
-
- public UserTab getUserTab() {
- return userTab;
- }
-
- public UserLoreBookTab getUserLoreBookTab() {
- return userLoreBookTab;
- }
-
- public User getUser() {
- return user;
- }
-
- public String getUserId() {
- return userId;
- }
-
- public File getUserIconPath() {
- return userIconPath;
- }
-
- public String getUserDisplay() {
- return userDisplay;
- }
-
- public String getUserPersona() {
- return userPersona;
- }
-
- public TreeMap getLoreBook() {
- return loreBook;
- }
-
- public void setUserId(String userId) {
- this.userId = userId;
- }
-
- public void setUserDisplay(String userDisplay) {
- this.userDisplay = userDisplay;
- }
-
- public void setUserPersona(String userPersona) {
- this.userPersona = userPersona;
- }
-
- public void setUserIconPath(File userIconPath) {
- this.userIconPath = userIconPath;
- }
-
- public void setLoreBook(TreeMap loreBook) {
- this.loreBook = loreBook;
- }
-}
diff --git a/src/main/java/me/piitex/app/views/users/UserTemplateView.java b/src/main/java/me/piitex/app/views/users/UserTemplateView.java
index 8e48e8b3..f164127f 100644
--- a/src/main/java/me/piitex/app/views/users/UserTemplateView.java
+++ b/src/main/java/me/piitex/app/views/users/UserTemplateView.java
@@ -7,6 +7,7 @@
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.paint.Color;
+import javafx.scene.text.Font;
import javafx.stage.FileChooser;
import me.piitex.app.App;
import me.piitex.app.backend.User;
@@ -23,7 +24,8 @@
import me.piitex.engine.layouts.FlowLayout;
import me.piitex.engine.layouts.HorizontalLayout;
import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
+import me.piitex.engine.loaders.FontLoader;
+import me.piitex.engine.loaders.image.ImageLoader;
import me.piitex.engine.overlays.*;
import org.apache.commons.io.FileUtils;
import org.kordamp.ikonli.material2.Material2AL;
@@ -58,21 +60,34 @@ public void init() {
root.addElement(sidebarView);
root.setSpacing(35);
- if (App.getInstance().isLoading()) {
- root.addElement(new LoadingView("Loading data...", root.getWidth(), 650));
- App.getThreadPoolManager().submitSchedule(() -> {
- boolean loading = App.getInstance().isLoading();
- while (loading) {
- loading = App.getInstance().isLoading();
- if (!loading) break;
- }
- Platform.runLater(() -> {
- root.removeElement(1);
- buildUsers();
- });
- }, 1, TimeUnit.SECONDS);
+ if (!App.getInstance().getUserTemplates().isEmpty()) {
+ if (App.getInstance().isLoading()) {
+ root.addElement(new LoadingView("Loading data...", root.getWidth(), 650));
+ App.getThreadPoolManager().submitSchedule(() -> {
+ boolean loading = App.getInstance().isLoading();
+ while (loading) {
+ loading = App.getInstance().isLoading();
+ if (!loading) break;
+ }
+ Platform.runLater(() -> {
+ root.removeElement(1);
+ buildUsers();
+ });
+ }, 1, TimeUnit.SECONDS);
+ } else {
+ buildUsers();
+ }
} else {
- buildUsers();
+ VerticalLayout layout = new VerticalLayout(appSettings.getWidth() - Positions.SIDEBAR_WIDTH - 25, -1);
+ layout.setMaxSize(layout.getWidth(), layout.getHeight());
+ layout.setSpacing(20);
+ layout.setAlignment(Pos.CENTER);
+ layout.addStyle(Styles.BORDER_DEFAULT);
+ root.addElement(layout);
+
+ TextOverlay body = new TextOverlay("Create your first user in the creator page.");
+ body.setFont(new FontLoader(Font.getDefault(), 24));
+ layout.addElement(body);
}
}
@@ -102,7 +117,7 @@ public void buildUsers() {
imageWidth = 256;
imageHeight = 256;
cardWidth = 280;
- cardHeight = 380;
+ cardHeight = 350;
}
body.setScrollWhenNeeded(false);
body.setHorizontalScroll(false);
@@ -119,16 +134,11 @@ public void buildUsers() {
CardContainer card = new CardContainer(0,0, cardWidth, cardHeight);
card.setMaxSize(cardWidth, cardHeight);
- VerticalLayout displayBox = new VerticalLayout(0, 330);
+ VerticalLayout displayBox = new VerticalLayout(0, cardHeight - 50);
displayBox.setSpacing(15);
displayBox.setAlignment(Pos.TOP_CENTER);
- TextOverlay helper = new TextOverlay("Click to chat");
- helper.setUnderline(true);
- displayBox.addElement(helper);
-
ContextMenu contextMenu = new ContextMenu();
-
MenuItem edit = new MenuItem("Edit");
edit.setOnAction(_ -> editUser(user));
MenuItem copy = new MenuItem("Copy");
@@ -270,7 +280,7 @@ private void deleteUser(FlowLayout base, CardContainer card, me.piitex.app.backe
// Cleanup image usage
VerticalLayout verticalLayout = (VerticalLayout) card.getBody();
- ImageOverlay imageOverlay = (ImageOverlay) verticalLayout.getElementAt(1);
+ ImageOverlay imageOverlay = (ImageOverlay) verticalLayout.getElementAt(0);
// When setting to null the engine will dispose of the image and the JVM will call gc.
imageOverlay.setImage(null);
@@ -281,9 +291,10 @@ private void deleteUser(FlowLayout base, CardContainer card, me.piitex.app.backe
App.getThreadPoolManager().submitSchedule(() -> {
try {
App.logger.info("Removing image from cache '{}'", user.getIconPath());
- ImageLoader.imageCache.remove(user.getIconPath()); // Clear image from cache.
+ ImageLoader.clearCache();
App.logger.info("Deleting User: {}", user.getId());
FileUtils.deleteDirectory(user.getUserDirectory());
+ App.getInstance().getUserTemplates().remove(user.getId());
} catch (IOException e) {
App.logger.error("Could not delete directory!", e);
}
diff --git a/src/main/java/me/piitex/app/views/users/UsersView.java b/src/main/java/me/piitex/app/views/users/UsersView.java
deleted file mode 100644
index af780630..00000000
--- a/src/main/java/me/piitex/app/views/users/UsersView.java
+++ /dev/null
@@ -1,254 +0,0 @@
-package me.piitex.app.views.users;
-
-import atlantafx.base.theme.Styles;
-import javafx.application.Platform;
-import javafx.geometry.Pos;
-import javafx.scene.Node;
-import javafx.scene.control.ContextMenu;
-import javafx.scene.control.MenuItem;
-import javafx.scene.paint.Color;
-import me.piitex.app.App;
-import me.piitex.app.backend.User;
-import me.piitex.app.configuration.AppSettings;
-import me.piitex.app.views.LoadingView;
-import me.piitex.app.views.SidebarView;
-import me.piitex.engine.containers.Container;
-import me.piitex.engine.containers.CardContainer;
-import me.piitex.engine.containers.DialogueContainer;
-import me.piitex.engine.containers.EmptyContainer;
-import me.piitex.engine.containers.ScrollContainer;
-import me.piitex.engine.layouts.FlowLayout;
-import me.piitex.engine.layouts.HorizontalLayout;
-import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.overlays.*;
-import org.apache.commons.io.FileUtils;
-import org.kordamp.ikonli.material2.Material2AL;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-public class UsersView extends EmptyContainer {
- private static final AppSettings appSettings = App.getInstance().getAppSettings();
- private final VerticalLayout mainPage;
-
- public UsersView() {
- super(appSettings.getWidth(), appSettings.getHeight());
- addStyle(Styles.BG_INSET);
- HorizontalLayout root = new HorizontalLayout(getWidth(), getHeight());
- root.setMaxSize(root.getWidth(), root.getHeight());
- addElement(root);
-
- SidebarView sidebarView = new SidebarView();
- root.addElement(sidebarView);
-
- mainPage = new VerticalLayout(appSettings.getWidth() - 200, 0);
- mainPage.setMaxSize(mainPage.getWidth(), mainPage.getHeight());
- root.addElement(mainPage);
-
- init();
- }
-
- public void init() {
- VerticalLayout header = new VerticalLayout(appSettings.getWidth() - 200, 100);
- header.addStyle(Styles.BG_DEFAULT);
- header.setMaxSize(header.getWidth(), header.getHeight());
- header.setAlignment(Pos.CENTER);
- mainPage.addElement(header);
-
- ButtonOverlay newUser = new ButtonBuilder("new").setText("New User").build();
- newUser.addStyle(Styles.SUCCESS);
- newUser.addStyle(Styles.BUTTON_OUTLINED);
- newUser.onClick(_ -> {
- App.window.clearContainers();
- App.window.addContainer(new UserEditView());
- });
- header.addElement(newUser);
-
- buildFlowLayout();
- }
-
- public void buildFlowLayout() {
- VerticalLayout flowRoot = new VerticalLayout(appSettings.getWidth() - 200, 0);
-
- ScrollContainer base = new ScrollContainer(flowRoot, 10, 10, mainPage.getMaxWidth(), -1);
- base.setMaxSize(base.getWidth(), base.getHeight());
-
- FlowLayout flowLayout = new FlowLayout(appSettings.getWidth() - 200, 0);
- flowLayout.setX(10);
- flowLayout.setMaxSize(flowLayout.getWidth(), flowLayout.getHeight());
- mainPage.addElement(flowLayout);
- flowLayout.addStyle(Styles.BORDER_DEFAULT);
-
- for (User user : App.getInstance().getUserTemplates().values()) {
- CardContainer card = new CardContainer(0,0, 280, 380);
- card.setMaxSize(card.getWidth(), card.getHeight());
-
- VerticalLayout displayBox = new VerticalLayout(0, 330);
- displayBox.setSpacing(15);
- displayBox.setAlignment(Pos.TOP_CENTER);
-
- TextOverlay id = new TextOverlay(user.getId());
- displayBox.addElement(id);
-
-
- ContextMenu contextMenu = new ContextMenu();
-
- MenuItem edit = new MenuItem("Edit");
- edit.setOnAction(_ -> editUser(user));
- MenuItem copy = new MenuItem("Copy");
- copy.setOnAction(_ -> duplicateUser(user));
- MenuItem delete = new MenuItem("Delete");
- delete.setOnAction(_ -> deleteUser(user, flowLayout, card));
-
- contextMenu.getItems().add(edit);
- contextMenu.getItems().add(copy);
- contextMenu.getItems().add(delete);
-
- ImageOverlay icon = User.getUserAvatar(user.getIconPath(), 256, 256);
- if (icon != null && icon.getImage() != null) {
- icon.setPreserveRatio(false);
- displayBox.addElement(icon);
- }
-
- TextOverlay name = new TextOverlay(user.getDisplayName());
- displayBox.addElement(name);
-
- card.setBody(displayBox);
-
- card.setFooter(buildControlBox(flowLayout, card, user));
-
- flowLayout.addElement(card);
- }
- }
-
- public HorizontalLayout buildControlBox(FlowLayout base, CardContainer card, User user) {
- HorizontalLayout root = new HorizontalLayout(200, 25);
- root.setIndex(10);
- root.setSpacing(20);
- if (!App.mobile) {
- root.setAlignment(Pos.BASELINE_CENTER);
- }
-
- IconOverlay edit = new IconOverlay(Material2AL.EDIT);
- edit.setTooltip("Edit the user");
- edit.setColor(Color.GREEN);
- edit.onClick(event -> {
- editUser(user);
- });
- root.addElement(edit);
-
- IconOverlay duplicate = new IconOverlay(Material2AL.FILE_COPY);
- duplicate.setTooltip("Duplicate the user.");
- duplicate.setColor(Color.YELLOW);
- duplicate.onClick(event -> {
- duplicateUser(user);
- });
- root.addElement(duplicate);
-
- IconOverlay delete = new IconOverlay(Material2AL.DELETE_FOREVER);
- delete.setColor(Color.RED);
- delete.setTooltip("Delete the user.");
- delete.onClick(event -> {
- deleteUser(base, card, user, event.getHandler().getSceneX(), event.getHandler().getSceneY());
- });
- root.addElement(delete);
- return root;
- }
-
- private void editUser(User user) {
- App.window.clearContainers();
- EmptyContainer progressContainer = new EmptyContainer(App.getInstance().getAppSettings().getWidth(), App.getInstance().getAppSettings().getHeight());
- progressContainer.addElement(new LoadingView("Loading User data...", progressContainer.getWidth(), progressContainer.getHeight()));
- App.window.addContainer(progressContainer);
-
- App.getThreadPoolManager().submitTask(() -> {
- Container container = new UserEditView(user);
- Node assemble = container.assemble();
- Platform.runLater(() -> {
- App.window.clearContainers();
- App.window.addContainer(container, assemble);
- });
- });
- }
-
- private void duplicateUser(User user) {
- // Duplicate the User.
- String newId = user.getId() + " (copy)";
- while (App.getInstance().getUser(newId) != null) {
- newId += " (copy)";
- }
-
- // Edit the User in the edit view rather than duplicating the files
- // Allow the id to be edited and changed.
-
- // Create a copy of the User.
- App.window.clearContainers();
- EmptyContainer progressContainer = new EmptyContainer(App.getInstance().getAppSettings().getWidth(), App.getInstance().getAppSettings().getHeight());
- progressContainer.addElement(new LoadingView("Loading User data...", progressContainer.getWidth(), progressContainer.getHeight()));
- App.window.addContainer(progressContainer);
-
- User duplicated = new User(newId, null);
- App.getThreadPoolManager().submitTask(() -> {
- duplicated.copy(user);
- UserEditView editView = new UserEditView(duplicated);
- Node assemble = editView.assemble();
- Platform.runLater(() -> {
- App.window.clearContainers();
- App.window.addContainer(editView, assemble);
- });
- });
- }
-
- private void deleteUser(FlowLayout base, CardContainer card, User user, double x, double y) {
- DialogueContainer dialogueContainer = new DialogueContainer("Delete '" + user.getId() + "'?", 500, 500);
-
- ButtonOverlay cancel = new ButtonBuilder("cancel").setText("Keep").build();
- cancel.setWidth(150);
- cancel.addStyle(Styles.SUCCESS);
- cancel.onClick(_ -> {
- App.window.removeContainer(dialogueContainer);
- });
-
- ButtonOverlay confirm = new ButtonBuilder("confirm").setText("Delete").build();
- confirm.setWidth(150);
- confirm.addStyle(Styles.DANGER);
- confirm.onClick(_ -> {
- App.getInstance().getUserTemplates().remove(user.getId());
- App.window.removeContainer(dialogueContainer);
-
- // Cleanup image usage
- VerticalLayout verticalLayout = (VerticalLayout) card.getBody();
- ImageOverlay imageOverlay = (ImageOverlay) verticalLayout.getElementAt(1);
-
- // When setting to null the engine will dispose of the image and the JVM will call gc.
- imageOverlay.setImage(null);
-
- deleteUserDirectory(user);
- base.removeElement(card);
- });
-
- dialogueContainer.setCancelButton(cancel);
- dialogueContainer.setConfirmButton(confirm);
-
- // Render this on top
- App.window.renderPopup(dialogueContainer, x, y, 500, 500);
- }
-
- private void deleteUser(User user, FlowLayout base, CardContainer card) {
- App.getInstance().getUserTemplates().remove(user.getId());
- deleteUserDirectory(user);
- base.removeElement(card);
- }
-
- private void deleteUserDirectory(User user) {
- // Add a delay to ensure all io operations are completed.
- App.getThreadPoolManager().submitSchedule(() -> {
- App.logger.info("Deleting User: {}", user.getId());
- try {
- FileUtils.deleteDirectory(user.getUserDirectory());
- } catch (IOException e) {
- App.logger.error("Could not delete directory!", e);
- }
- }, 1, TimeUnit.SECONDS);
- }
-}
diff --git a/src/main/java/me/piitex/app/views/users/tabs/UserLoreBookTab.java b/src/main/java/me/piitex/app/views/users/tabs/UserLoreBookTab.java
deleted file mode 100644
index 7e0afe72..00000000
--- a/src/main/java/me/piitex/app/views/users/tabs/UserLoreBookTab.java
+++ /dev/null
@@ -1,134 +0,0 @@
-package me.piitex.app.views.users.tabs;
-
-import atlantafx.base.theme.Styles;
-import javafx.geometry.Pos;
-import me.piitex.app.App;
-import me.piitex.app.configuration.AppSettings;
-import me.piitex.app.views.users.UserEditView;
-import me.piitex.engine.containers.CardContainer;
-import me.piitex.engine.containers.ScrollContainer;
-import me.piitex.engine.containers.tabs.Tab;
-import me.piitex.engine.layouts.HorizontalLayout;
-import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.overlays.*;
-import org.kordamp.ikonli.javafx.FontIcon;
-import org.kordamp.ikonli.material2.Material2AL;
-
-import java.util.Map;
-
-public class UserLoreBookTab extends Tab {
- private final UserEditView userEditView;
- private TextFieldOverlay addKeyInput;
- private TextAreaOverlay addValueInput;
- private ScrollContainer scrollLoreContainer;
-
- private static final AppSettings appSettings = App.getInstance().getAppSettings();
-
- public UserLoreBookTab(String text, UserEditView userEditView) {
- super(text);
- this.userEditView = userEditView;
- buildLorebookTabContent();
- }
-
- public void buildLorebookTabContent() {
- removeAllElements();
-
- VerticalLayout rootLayout = new VerticalLayout(appSettings.getWidth() - 300, appSettings.getHeight());
- rootLayout.setSpacing(50);
- rootLayout.setAlignment(Pos.TOP_CENTER);
- this.addElement(rootLayout);
-
- IconOverlay info = new IconOverlay(Material2AL.INFO);
- info.setTooltip("Use the following placeholders; {char}, {{char}}, {chara}, {{chara}}, {character}, {{character}}, {user}, {{user}}, {usr}, {{usr}}");
- rootLayout.addElement(info);
-
- HorizontalLayout displayBox = new HorizontalLayout(0, -1);
- displayBox.setAlignment(Pos.TOP_CENTER);
- displayBox.setSpacing(100);
- rootLayout.addElement(displayBox);
-
- CardContainer addContainer = new CardContainer(0, 0, 400, 400);
- addContainer.setMaxSize(400, 400);
-
- addKeyInput = new TextFieldOverlay("", "Separate multiple keys with a comma (,)", 0, 0, 200, 50);
- addContainer.setHeader(addKeyInput);
-
- addValueInput = new TextAreaOverlay("", "Enter the lore info", 0, 0, 400, 200);
- addContainer.setBody(addValueInput);
-
- HorizontalLayout buttonBox = new HorizontalLayout(200, 50);
- buttonBox.setAlignment(Pos.CENTER);
-
- ButtonOverlay add = new ButtonBuilder("add").setText("Add").build();
- add.addStyle(Styles.SUCCESS);
- add.addStyle(Styles.BUTTON_OUTLINED);
- buttonBox.addElement(add);
- addContainer.setFooter(buttonBox);
-
- scrollLoreContainer = getLoreItems();
- add.onClick(_ -> {
- String keyText = addKeyInput.getCurrentText();
- String valueText = addValueInput.getCurrentText();
-
- if (keyText.isEmpty() || valueText.isEmpty()) {
- return;
- }
-
- userEditView.getLoreBook().put(keyText, valueText);
-
- VerticalLayout scrollLayout = (VerticalLayout) scrollLoreContainer.getLayout();
- CardContainer newLoreEntryCard = buildLoreEntry(keyText, scrollLayout);
- scrollLayout.addElement(newLoreEntryCard);
-
- addKeyInput.setCurrentText("");
- addValueInput.setCurrentText("");
- });
-
- displayBox.addElement(addContainer);
- displayBox.addElement(scrollLoreContainer);
-
- this.addElement(userEditView.buildSubmitBox());
- }
-
- private ScrollContainer getLoreItems() {
- VerticalLayout scrollLayout = new VerticalLayout(0, -1);
-
- ScrollContainer root = new ScrollContainer(scrollLayout, 0, 0, 450, appSettings.getHeight() - 300);
- root.setMaxSize(450, appSettings.getHeight() - 300);
- root.setVerticalScroll(true);
- root.setScrollWhenNeeded(false);
- root.setHorizontalScroll(false);
- root.setPannable(true);
-
- App.logger.info("Building lore cache...");
- for (Map.Entry entry : userEditView.getLoreBook().entrySet()) {
- scrollLayout.addElement(buildLoreEntry(entry.getKey(), scrollLayout));
- }
- return root;
- }
-
- private CardContainer buildLoreEntry(String key, VerticalLayout scrollContainer) {
- CardContainer card = new CardContainer(0, 0, 400, 300);
-
- TextFieldOverlay entryKey = new TextFieldOverlay(key, 0, 0, 400, 50);
- entryKey.setEnabled(false); // Make key read-only
- card.setHeader(entryKey);
-
- TextAreaOverlay entryValue = new TextAreaOverlay(userEditView.getLoreBook().get(key), "", 400, 200);
- entryValue.onInputSetEvent(event -> userEditView.getLoreBook().put(key, event.getInput()));
- card.setBody(entryValue);
-
- ButtonOverlay remove = new ButtonBuilder("remove").setText("Remove").build();
- remove.setX(170);
- remove.addStyle(Styles.DANGER);
- remove.addStyle(Styles.BUTTON_OUTLINED);
- card.setFooter(remove);
-
- remove.onClick(_ -> {
- userEditView.getLoreBook().remove(key);
- scrollContainer.removeElement(card);
- });
-
- return card;
- }
-}
diff --git a/src/main/java/me/piitex/app/views/users/tabs/UserTab.java b/src/main/java/me/piitex/app/views/users/tabs/UserTab.java
deleted file mode 100644
index 0033b8a0..00000000
--- a/src/main/java/me/piitex/app/views/users/tabs/UserTab.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package me.piitex.app.views.users.tabs;
-
-import atlantafx.base.theme.Styles;
-import com.drew.imaging.ImageProcessingException;
-import javafx.application.Platform;
-import javafx.geometry.Pos;
-import javafx.stage.FileChooser;
-import me.piitex.app.App;
-import me.piitex.app.configuration.AppSettings;
-import me.piitex.app.utils.UserCardImporter;
-import me.piitex.app.views.users.UserEditView;
-import me.piitex.engine.containers.CardContainer;
-import me.piitex.engine.containers.ScrollContainer;
-import me.piitex.engine.containers.tabs.Tab;
-import me.piitex.engine.layouts.HorizontalLayout;
-import me.piitex.engine.layouts.VerticalLayout;
-import me.piitex.engine.loaders.ImageLoader;
-import me.piitex.engine.overlays.*;
-import org.json.JSONObject;
-
-import java.io.File;
-import java.io.IOException;
-
-public class UserTab extends Tab {
- private final UserEditView userEditView;
-
- private TextFieldOverlay userIdInput;
- private TextFieldOverlay userDisplayNameInput;
- private RichTextAreaOverlay userDescription;
-
- private ImageOverlay image;
-
- private static final AppSettings appSettings = App.getInstance().getAppSettings();
-
- public UserTab(String text, UserEditView userEditView) {
- super(text);
- this.userEditView = userEditView;
- init();
- }
-
- public void init() {
- VerticalLayout rootLayout = new VerticalLayout(appSettings.getWidth() - 315, 0);
- rootLayout.setSpacing(40);
- rootLayout.setAlignment(Pos.TOP_CENTER);
-
- ScrollContainer scrollContainer = new ScrollContainer(rootLayout, 0, 0, appSettings.getWidth() - 300, appSettings.getHeight() - 200);
- scrollContainer.setMaxSize(scrollContainer.getWidth(), scrollContainer.getHeight());
- scrollContainer.setHorizontalScroll(false);
- scrollContainer.setPannable(true);
- addElement(scrollContainer);
-
- HorizontalLayout displayBox = new HorizontalLayout(600, 320);
- displayBox.setMaxSize(600, 320);
- displayBox.addStyle(Styles.BORDER_SUBTLE);
- displayBox.setSpacing(20);
- rootLayout.addElement(displayBox);
-
- CardContainer displayCard = buildUserDisplay();
- displayBox.addElement(displayCard);
- displayBox.addElement(buildUserInput());
-
- double scaleFactor = (double) appSettings.getWidth() / 1920.0;
- userDescription = new RichTextAreaOverlay(userEditView.getUserPersona(), 600, 400 * scaleFactor);
- userDescription.setBackgroundColor(appSettings.getThemeDefaultColor(appSettings.getTheme()));
- userDescription.setBorderColor(appSettings.getThemeBorderColor(appSettings.getTheme()));
- userDescription.setTextFill(appSettings.getThemeTextColor(appSettings.getTheme()));
- userDescription.setMaxHeight(400 * scaleFactor);
- userDescription.setMaxWidth(600);
- userDescription.onInputSetEvent(event -> {
- userEditView.setUserPersona(event.getInput());
- });
- userDescription.addStyle(Styles.BG_DEFAULT);
- userDescription.addStyle(appSettings.getChatTextSize());
- userDescription.addStyle(Styles.TEXT_ON_EMPHASIS);
-
- rootLayout.addElement(userDescription);
-
- addElement(userEditView.buildSubmitBox());
- }
-
- private CardContainer buildUserDisplay() {
- CardContainer root = new CardContainer(0, 0, 300, 320);
-
- VerticalLayout layout = new VerticalLayout(300, 320);
- root.setBody(layout);
- layout.setAlignment(Pos.TOP_CENTER);
- layout.setSpacing(25);
-
- // Use parentView's userIconPath
- File currentIconPath = userEditView.getUserIconPath();
- if (currentIconPath == null || !currentIconPath.exists() || currentIconPath.isDirectory()) {
- currentIconPath = new File(App.getAppDirectory(), "icons/character.png");
- }
-
- ImageLoader loader = new ImageLoader(currentIconPath);
- loader.setWidth(256);
- loader.setHeight(256);
-
- image = new ImageOverlay(loader);
- image.setFitWidth(256);
- image.setFitHeight(256);
- image.setPreserveRatio(false);
-
- layout.addElement(image);
-
- TextOverlay upload = new TextOverlay("Click to upload image");
- upload.setTextFill(javafx.scene.paint.Color.WHITE);
- upload.setUnderline(true);
- layout.addElement(upload);
-
- root.onClick(event -> {
- FileChooser chooser = new FileChooser();
- chooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Select an image.", "*.img", "*.png", "*.webp", "*.jpg", "*.gif"));
- if (appSettings.getImagesPath() != null && !appSettings.getImagesPath().isEmpty()) {
- chooser.setInitialDirectory(new File(appSettings.getImagesPath()));
- }
- File selectedFile = chooser.showOpenDialog(App.window.getStage());
- if (selectedFile != null) {
- userEditView.setUserIconPath(selectedFile);
- appSettings.setImagesPath(selectedFile.getParent());
- userEditView.setUserIconPath(selectedFile.getAbsoluteFile());
-
- ImageLoader imageLoader = new ImageLoader(selectedFile);
- imageLoader.setWidth(256);
- imageLoader.setHeight(256);
-
- image.setImage(imageLoader);
- }
- });
-
- return root;
- }
-
- private VerticalLayout buildUserInput() {
- VerticalLayout root = new VerticalLayout(250, 150);
- root.setAlignment(Pos.BASELINE_CENTER);
- root.setMaxSize(250, 200);
- root.setSpacing(10);
-
- userIdInput = new TextFieldOverlay(userEditView.getUserId(), 0, 0, 200, 50);
- userIdInput.setEnabled(true);
- userIdInput.setHintText("Unique Identifier");
- userIdInput.onInputSetEvent(event -> {
- userEditView.setUserId(event.getInput());
- });
- if (userEditView.getUser() != null) {
- userIdInput.setEnabled(false);
- }
- root.addElement(userIdInput);
-
- userDisplayNameInput = new TextFieldOverlay(userEditView.getUserDisplay(), 0, 0, 200, 50);
- userDisplayNameInput.setEnabled(true);
- userDisplayNameInput.setHintText("Display Name");
- userDisplayNameInput.onInputSetEvent(event -> {
- userEditView.setUserDisplay(event.getInput());
- });
- root.addElement(userDisplayNameInput);
-
- ButtonOverlay importCard = new ButtonBuilder("import").setText("Import Character Card").build();
- if (userEditView.getUser() != null) {
- importCard.setEnabled(false);
- }
- importCard.addStyle(Styles.ACCENT);
- importCard.addStyle(Styles.BUTTON_OUTLINED);
- importCard.setWidth(200);
- importCard.setHeight(50);
-
- FileChooserOverlay fileSelector = new FileChooserOverlay(App.window, importCard);
- root.addElement(fileSelector);
- fileSelector.onFileSelect(event -> {
- File file = event.getDirectory();
- try {
- JSONObject metadata = UserCardImporter.getImageMetaData(file);
- userDisplayNameInput.setCurrentText(UserCardImporter.getUserDisplay(metadata));
- userDescription.setCurrentText(UserCardImporter.getUserPersona(metadata));
-
- userEditView.getLoreBook().clear();
- userEditView.getLoreBook().putAll(UserCardImporter.getLoreItems(metadata));
- userEditView.getUserLoreBookTab().buildLorebookTabContent();
-
- userEditView.setUserIconPath(file);
- image.setImage(new ImageLoader(file));
- } catch (ImageProcessingException | IOException e) {
- App.logger.error("Error importing character card: ", e);
- Platform.runLater(() -> {
- MessageOverlay errorOverlay = new MessageOverlay(0, 0, 500, 50, "Import Failed", "Could not import character card: " + e.getMessage());
- errorOverlay.addStyle(Styles.DANGER);
- errorOverlay.addStyle(Styles.BG_DEFAULT);
- App.window.renderPopup(errorOverlay, 650, 870, 500, 50, false, null);
- });
- }
- });
-
- return root;
- }
-
- public TextFieldOverlay getUserIdInput() {
- return userIdInput;
- }
-
- public TextFieldOverlay getUserDisplayNameInput() {
- return userDisplayNameInput;
- }
-
- public RichTextAreaOverlay getUserDescription() {
- return userDescription;
- }
-}
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 8c8647ed..42635b48 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -22,6 +22,7 @@
requires org.apache.commons.lang3;
requires org.apache.commons.compress;
requires javafx.base;
+ requires com.github.oshi;
opens me.piitex.app to javafx.fxml;
exports me.piitex.app;