From 68d6f647482521fded5d7123f0ee45d4150500be Mon Sep 17 00:00:00 2001 From: pulkitbajaj Date: Thu, 25 Dec 2025 17:28:09 +0530 Subject: [PATCH 01/12] feat: add right-click copy context menu to AI chat messages --- .../chatmessage/ChatMessageComponent.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index f620938dea3..c45cfaf1abe 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -7,7 +7,11 @@ import javafx.fxml.FXML; import javafx.geometry.NodeOrientation; import javafx.geometry.Pos; +import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; +import javafx.scene.control.MenuItem; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; @@ -24,6 +28,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static javafx.scene.input.MouseEvent.MOUSE_PRESSED; + public class ChatMessageComponent extends HBox { private static final Logger LOGGER = LoggerFactory.getLogger(ChatMessageComponent.class); @@ -53,6 +59,8 @@ public ChatMessageComponent() { markdownContentPane.getChildren().add(markdownTextFlow); markdownContentPane.minHeightProperty().bind(markdownTextFlow.heightProperty()); markdownContentPane.prefHeightProperty().bind(markdownTextFlow.heightProperty()); + + setupContextMenu(); } public ChatMessageComponent(ChatMessage chatMessage, Consumer onDeleteCallback) { @@ -61,6 +69,64 @@ public ChatMessageComponent(ChatMessage chatMessage, Consumer { + int[] selection = (int[]) contextMenu.getProperties().getOrDefault("selection", new int[] {-1, -1}); + int start = selection[0]; + int end = selection[1]; + + String fullText = switch (chatMessage.get()) { + case UserMessage user -> + user.singleText(); + case AiMessage ai -> + ai.text(); + case ErrorMessage err -> + err.getText(); + case null, + default -> + ""; + }; + + if (fullText.isEmpty()) { + return; + } + + String textToCopy; + if (start >= 0 && end > start) { + int safeEnd = Math.min(end, fullText.length()); + textToCopy = fullText.substring(start, safeEnd); + } else { + textToCopy = fullText; + } + + final Clipboard clipboard = Clipboard.getSystemClipboard(); + final ClipboardContent content = new ClipboardContent(); + content.putString(textToCopy); + clipboard.setContent(content); + }); + + contextMenu.getItems().add(copyItem); + + markdownContentPane.addEventFilter(MOUSE_PRESSED, event -> { + if (event.isSecondaryButtonDown()) { + try { + int start = markdownTextFlow.getSelectionStartIndex(); + int end = markdownTextFlow.getSelectionEndIndex(); + contextMenu.getProperties().put("selection", new int[] {start, end}); + } catch (AssertionError | Exception e) { + contextMenu.getProperties().put("selection", new int[] {-1, -1}); + } + } + }); + + markdownContentPane.setOnContextMenuRequested(event -> + contextMenu.show(markdownContentPane, event.getScreenX(), event.getScreenY()) + ); + } + public void setChatMessage(ChatMessage chatMessage) { this.chatMessage.set(chatMessage); } From 0be89019e8409d8f8ebd9adca4e9c862a662b059 Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Sun, 28 Dec 2025 23:06:46 +0530 Subject: [PATCH 02/12] feat: add copy context menu and ChatMessage content utility --- .../chatmessage/ChatMessageComponent.java | 43 +++++++------------ .../logic/ai/util/ChatMessageUtils.java | 32 ++++++++++++++ 2 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 jablib/src/main/java/org/jabref/logic/ai/util/ChatMessageUtils.java diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index c45cfaf1abe..aa5e507d4cb 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -18,6 +18,7 @@ import javafx.scene.layout.VBox; import org.jabref.gui.util.MarkdownTextFlow; +import org.jabref.logic.ai.util.ChatMessageUtils; import org.jabref.logic.ai.util.ErrorMessage; import org.jabref.logic.l10n.Localization; @@ -43,6 +44,7 @@ public class ChatMessageComponent extends HBox { @FXML private VBox buttonsVBox; private final MarkdownTextFlow markdownTextFlow; + private String selectedText; public ChatMessageComponent() { ViewLoader.view(this) @@ -74,32 +76,12 @@ private void setupContextMenu() { MenuItem copyItem = new MenuItem(Localization.lang("Copy")); copyItem.setOnAction(_ -> { - int[] selection = (int[]) contextMenu.getProperties().getOrDefault("selection", new int[] {-1, -1}); - int start = selection[0]; - int end = selection[1]; - - String fullText = switch (chatMessage.get()) { - case UserMessage user -> - user.singleText(); - case AiMessage ai -> - ai.text(); - case ErrorMessage err -> - err.getText(); - case null, - default -> - ""; - }; - - if (fullText.isEmpty()) { - return; - } + String textToCopy = !this.selectedText.isEmpty() + ? this.selectedText + : ChatMessageUtils.getContent(chatMessage.get()).orElse(""); - String textToCopy; - if (start >= 0 && end > start) { - int safeEnd = Math.min(end, fullText.length()); - textToCopy = fullText.substring(start, safeEnd); - } else { - textToCopy = fullText; + if (textToCopy.isEmpty()) { + return; } final Clipboard clipboard = Clipboard.getSystemClipboard(); @@ -115,9 +97,16 @@ private void setupContextMenu() { try { int start = markdownTextFlow.getSelectionStartIndex(); int end = markdownTextFlow.getSelectionEndIndex(); - contextMenu.getProperties().put("selection", new int[] {start, end}); + + String fullText = ChatMessageUtils.getContent(chatMessage.get()).orElse(""); + + if (start >= 0 && end > start && !fullText.isEmpty()) { + this.selectedText = fullText.substring(start, Math.min(end, fullText.length())); + } else { + this.selectedText = ""; + } } catch (AssertionError | Exception e) { - contextMenu.getProperties().put("selection", new int[] {-1, -1}); + this.selectedText = ""; } } }); diff --git a/jablib/src/main/java/org/jabref/logic/ai/util/ChatMessageUtils.java b/jablib/src/main/java/org/jabref/logic/ai/util/ChatMessageUtils.java new file mode 100644 index 00000000000..cd36d330377 --- /dev/null +++ b/jablib/src/main/java/org/jabref/logic/ai/util/ChatMessageUtils.java @@ -0,0 +1,32 @@ +package org.jabref.logic.ai.util; + +import java.util.Optional; + +import dev.langchain4j.data.message.AiMessage; +import dev.langchain4j.data.message.ChatMessage; +import dev.langchain4j.data.message.UserMessage; + +public class ChatMessageUtils { + + private ChatMessageUtils() { + } + + public static Optional getContent(ChatMessage chatMessage) { + if (chatMessage == null) { + return Optional.empty(); + } + + String content = switch (chatMessage) { + case UserMessage user -> + user.singleText(); + case AiMessage ai -> + ai.text(); + case ErrorMessage err -> + err.getText(); + default -> + null; + }; + + return Optional.ofNullable(content); + } +} From 6aef9daa82dfb997526272d7af7b9fad9c014c71 Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Mon, 29 Dec 2025 20:03:16 +0530 Subject: [PATCH 03/12] refactor: use ClipBoardManager and specific exception handling for AI chat copy --- .../chatmessage/ChatMessageComponent.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index aa5e507d4cb..41f16b114a2 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -10,18 +10,19 @@ import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; -import javafx.scene.input.Clipboard; -import javafx.scene.input.ClipboardContent; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; +import org.jabref.gui.StateManager; +import org.jabref.gui.clipboard.ClipBoardManager; import org.jabref.gui.util.MarkdownTextFlow; import org.jabref.logic.ai.util.ChatMessageUtils; import org.jabref.logic.ai.util.ErrorMessage; import org.jabref.logic.l10n.Localization; +import com.airhacks.afterburner.injection.Injector; import com.airhacks.afterburner.views.ViewLoader; import dev.langchain4j.data.message.AiMessage; import dev.langchain4j.data.message.ChatMessage; @@ -44,7 +45,8 @@ public class ChatMessageComponent extends HBox { @FXML private VBox buttonsVBox; private final MarkdownTextFlow markdownTextFlow; - private String selectedText; + private String selectedText = ""; + private final ClipBoardManager clipBoardManager = new ClipBoardManager(Injector.instantiateModelOrService(StateManager.class)); public ChatMessageComponent() { ViewLoader.view(this) @@ -80,14 +82,9 @@ private void setupContextMenu() { ? this.selectedText : ChatMessageUtils.getContent(chatMessage.get()).orElse(""); - if (textToCopy.isEmpty()) { - return; + if (!textToCopy.isEmpty()) { + clipBoardManager.setContent(textToCopy); } - - final Clipboard clipboard = Clipboard.getSystemClipboard(); - final ClipboardContent content = new ClipboardContent(); - content.putString(textToCopy); - clipboard.setContent(content); }); contextMenu.getItems().add(copyItem); @@ -105,7 +102,8 @@ private void setupContextMenu() { } else { this.selectedText = ""; } - } catch (AssertionError | Exception e) { + } catch (AssertionError | IndexOutOfBoundsException e) { + LOGGER.debug("Failed to extract selection indices for copy action", e); this.selectedText = ""; } } From 865076f3251b16168227a7b6dd23289ac4994503 Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Mon, 29 Dec 2025 21:48:39 +0530 Subject: [PATCH 04/12] refactor: use @Inject and early returns for AI chat copy logic --- .../chatmessage/ChatMessageComponent.java | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index 41f16b114a2..f07b45e0e87 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -15,18 +15,17 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; -import org.jabref.gui.StateManager; import org.jabref.gui.clipboard.ClipBoardManager; import org.jabref.gui.util.MarkdownTextFlow; import org.jabref.logic.ai.util.ChatMessageUtils; import org.jabref.logic.ai.util.ErrorMessage; import org.jabref.logic.l10n.Localization; -import com.airhacks.afterburner.injection.Injector; import com.airhacks.afterburner.views.ViewLoader; import dev.langchain4j.data.message.AiMessage; import dev.langchain4j.data.message.ChatMessage; import dev.langchain4j.data.message.UserMessage; +import jakarta.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,7 +45,8 @@ public class ChatMessageComponent extends HBox { private final MarkdownTextFlow markdownTextFlow; private String selectedText = ""; - private final ClipBoardManager clipBoardManager = new ClipBoardManager(Injector.instantiateModelOrService(StateManager.class)); + @Inject + private ClipBoardManager clipBoardManager; public ChatMessageComponent() { ViewLoader.view(this) @@ -78,10 +78,12 @@ private void setupContextMenu() { MenuItem copyItem = new MenuItem(Localization.lang("Copy")); copyItem.setOnAction(_ -> { - String textToCopy = !this.selectedText.isEmpty() - ? this.selectedText - : ChatMessageUtils.getContent(chatMessage.get()).orElse(""); - + String textToCopy; + if (!this.selectedText.isEmpty()) { + textToCopy = selectedText; + } else { + textToCopy = ChatMessageUtils.getContent(chatMessage.get()).orElse(""); + } if (!textToCopy.isEmpty()) { clipBoardManager.setContent(textToCopy); } @@ -90,22 +92,23 @@ private void setupContextMenu() { contextMenu.getItems().add(copyItem); markdownContentPane.addEventFilter(MOUSE_PRESSED, event -> { - if (event.isSecondaryButtonDown()) { - try { - int start = markdownTextFlow.getSelectionStartIndex(); - int end = markdownTextFlow.getSelectionEndIndex(); - - String fullText = ChatMessageUtils.getContent(chatMessage.get()).orElse(""); - - if (start >= 0 && end > start && !fullText.isEmpty()) { - this.selectedText = fullText.substring(start, Math.min(end, fullText.length())); - } else { - this.selectedText = ""; - } - } catch (AssertionError | IndexOutOfBoundsException e) { - LOGGER.debug("Failed to extract selection indices for copy action", e); + if (!event.isSecondaryButtonDown()) { + return; + } + try { + int start = markdownTextFlow.getSelectionStartIndex(); + int end = markdownTextFlow.getSelectionEndIndex(); + + String fullText = ChatMessageUtils.getContent(chatMessage.get()).orElse(""); + + if (start >= 0 && end > start && !fullText.isEmpty()) { + this.selectedText = fullText.substring(start, Math.min(end, fullText.length())); + } else { this.selectedText = ""; } + } catch (AssertionError | IndexOutOfBoundsException e) { + LOGGER.debug("Failed to extract selection indices for copy action", e); + this.selectedText = ""; } }); From 5d6bc2ef00cbfcb588f40124b68e4d3163450795 Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Mon, 29 Dec 2025 22:07:58 +0530 Subject: [PATCH 05/12] refactor: extract copy/selection logic into methods and align style --- .../chatmessage/ChatMessageComponent.java | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index f07b45e0e87..7ba121a425a 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -45,8 +45,7 @@ public class ChatMessageComponent extends HBox { private final MarkdownTextFlow markdownTextFlow; private String selectedText = ""; - @Inject - private ClipBoardManager clipBoardManager; + @Inject private ClipBoardManager clipBoardManager; public ChatMessageComponent() { ViewLoader.view(this) @@ -78,12 +77,7 @@ private void setupContextMenu() { MenuItem copyItem = new MenuItem(Localization.lang("Copy")); copyItem.setOnAction(_ -> { - String textToCopy; - if (!this.selectedText.isEmpty()) { - textToCopy = selectedText; - } else { - textToCopy = ChatMessageUtils.getContent(chatMessage.get()).orElse(""); - } + String textToCopy = getTextToCopy(); if (!textToCopy.isEmpty()) { clipBoardManager.setContent(textToCopy); } @@ -95,21 +89,7 @@ private void setupContextMenu() { if (!event.isSecondaryButtonDown()) { return; } - try { - int start = markdownTextFlow.getSelectionStartIndex(); - int end = markdownTextFlow.getSelectionEndIndex(); - - String fullText = ChatMessageUtils.getContent(chatMessage.get()).orElse(""); - - if (start >= 0 && end > start && !fullText.isEmpty()) { - this.selectedText = fullText.substring(start, Math.min(end, fullText.length())); - } else { - this.selectedText = ""; - } - } catch (AssertionError | IndexOutOfBoundsException e) { - LOGGER.debug("Failed to extract selection indices for copy action", e); - this.selectedText = ""; - } + extractSelectedText(); }); markdownContentPane.setOnContextMenuRequested(event -> @@ -175,4 +155,29 @@ private void onDeleteClick() { private void setColor(String fillColor, String borderColor) { vBox.setStyle("-fx-background-color: " + fillColor + "; -fx-border-radius: 10; -fx-background-radius: 10; -fx-border-color: " + borderColor + "; -fx-border-width: 3;"); } + + private String getTextToCopy() { + if (!this.selectedText.isEmpty()) { + return this.selectedText; + } + return ChatMessageUtils.getContent(chatMessage.get()).orElse(""); + } + + private void extractSelectedText() { + try { + int start = markdownTextFlow.getSelectionStartIndex(); + int end = markdownTextFlow.getSelectionEndIndex(); + + String fullText = ChatMessageUtils.getContent(chatMessage.get()).orElse(""); + + if (start >= 0 && end > start && !fullText.isEmpty()) { + this.selectedText = fullText.substring(start, Math.min(end, fullText.length())); + } else { + this.selectedText = ""; + } + } catch (AssertionError | IndexOutOfBoundsException e) { + LOGGER.debug("Failed to extract selection indices for copy action", e); + this.selectedText = ""; + } + } } From b462ee54378f90f0c2c487452c2b152cad27a798 Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Mon, 29 Dec 2025 23:50:31 +0530 Subject: [PATCH 06/12] docs: add explanation for error handling --- .../ai/components/aichat/chatmessage/ChatMessageComponent.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index 7ba121a425a..a5fcbc8cb74 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -175,6 +175,7 @@ private void extractSelectedText() { } else { this.selectedText = ""; } + // These errors can occur if the UI selection state and message content drift during rendering } catch (AssertionError | IndexOutOfBoundsException e) { LOGGER.debug("Failed to extract selection indices for copy action", e); this.selectedText = ""; From 970fdf616ae79539487160f7d78e9a09e5a7f06f Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Tue, 30 Dec 2025 21:32:44 +0530 Subject: [PATCH 07/12] refactor: preserve AI chat selection using event filters and copySelectedText --- .../chatmessage/ChatMessageComponent.java | 61 ++++++++----------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index a5fcbc8cb74..112bb0f7598 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -44,7 +44,7 @@ public class ChatMessageComponent extends HBox { @FXML private VBox buttonsVBox; private final MarkdownTextFlow markdownTextFlow; - private String selectedText = ""; + // private String selectedText = ""; @Inject private ClipBoardManager clipBoardManager; public ChatMessageComponent() { @@ -75,26 +75,31 @@ public ChatMessageComponent(ChatMessage chatMessage, Consumer { - String textToCopy = getTextToCopy(); - if (!textToCopy.isEmpty()) { - clipBoardManager.setContent(textToCopy); + // 1. Capture and LOCK the selection state + markdownContentPane.addEventFilter(MOUSE_PRESSED, event -> { + if (event.isSecondaryButtonDown() && markdownTextFlow.isSelectionActive()) { + // Consume the event to prevent JavaFX from clearing the selection highlight + event.consume(); + // Manually trigger the context menu since we consumed the event that usually triggers it + contextMenu.show(markdownContentPane, event.getScreenX(), event.getScreenY()); } }); - contextMenu.getItems().add(copyItem); - - markdownContentPane.addEventFilter(MOUSE_PRESSED, event -> { - if (!event.isSecondaryButtonDown()) { - return; + copyItem.setOnAction(_ -> { + if (markdownTextFlow.isSelectionActive()) { + markdownTextFlow.copySelectedText(); + } else { + copyFullMessage(); } - extractSelectedText(); }); - markdownContentPane.setOnContextMenuRequested(event -> - contextMenu.show(markdownContentPane, event.getScreenX(), event.getScreenY()) - ); + markdownContentPane.setOnContextMenuRequested(event -> { + if (!markdownTextFlow.isSelectionActive()) { + contextMenu.show(markdownContentPane, event.getScreenX(), event.getScreenY()); + } + }); } public void setChatMessage(ChatMessage chatMessage) { @@ -156,29 +161,11 @@ private void setColor(String fillColor, String borderColor) { vBox.setStyle("-fx-background-color: " + fillColor + "; -fx-border-radius: 10; -fx-background-radius: 10; -fx-border-color: " + borderColor + "; -fx-border-width: 3;"); } - private String getTextToCopy() { - if (!this.selectedText.isEmpty()) { - return this.selectedText; - } - return ChatMessageUtils.getContent(chatMessage.get()).orElse(""); - } - - private void extractSelectedText() { - try { - int start = markdownTextFlow.getSelectionStartIndex(); - int end = markdownTextFlow.getSelectionEndIndex(); - - String fullText = ChatMessageUtils.getContent(chatMessage.get()).orElse(""); - - if (start >= 0 && end > start && !fullText.isEmpty()) { - this.selectedText = fullText.substring(start, Math.min(end, fullText.length())); - } else { - this.selectedText = ""; + private void copyFullMessage() { + ChatMessageUtils.getContent(chatMessage.get()).ifPresent(content -> { + if (!content.isEmpty()) { + clipBoardManager.setContent(content); } - // These errors can occur if the UI selection state and message content drift during rendering - } catch (AssertionError | IndexOutOfBoundsException e) { - LOGGER.debug("Failed to extract selection indices for copy action", e); - this.selectedText = ""; - } + }); } } From 28489a399f02e8feb88863512a34908ee03c495b Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Wed, 31 Dec 2025 12:32:46 +0530 Subject: [PATCH 08/12] remove dead code and commented-out selection variables --- .../ai/components/aichat/chatmessage/ChatMessageComponent.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index 112bb0f7598..fa2df72888a 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -44,7 +44,6 @@ public class ChatMessageComponent extends HBox { @FXML private VBox buttonsVBox; private final MarkdownTextFlow markdownTextFlow; - // private String selectedText = ""; @Inject private ClipBoardManager clipBoardManager; public ChatMessageComponent() { From 0622be1e6ee4cce9e7eb334b273dfd5829d2358f Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Wed, 31 Dec 2025 18:05:15 +0530 Subject: [PATCH 09/12] style: remove extra blank line in constructor --- .../ai/components/aichat/chatmessage/ChatMessageComponent.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index fa2df72888a..527b355946c 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -61,7 +61,6 @@ public ChatMessageComponent() { markdownContentPane.getChildren().add(markdownTextFlow); markdownContentPane.minHeightProperty().bind(markdownTextFlow.heightProperty()); markdownContentPane.prefHeightProperty().bind(markdownTextFlow.heightProperty()); - setupContextMenu(); } From 9d201c5ed0ff18f51f9e810b7536966dc472f6f1 Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Wed, 31 Dec 2025 22:26:55 +0530 Subject: [PATCH 10/12] add changelog entry for AI chat copy selection --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8689e3349b7..3de8e3d3d01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We introduced a new "Search Engine URL Template" setting in Preferences to allow users to customize their search engine URL templates [#12268](https://github.com/JabRef/jabref/issues/12268) - We added the option to pseudonymize a library using the GUI, via the tools tab in the Main Menu. [#14118](https://github.com/JabRef/jabref/issues/14118) - We added export options (Markdown and JSON) for AI Summary and AI Chat. [#13868](https://github.com/JabRef/jabref/issues/13868) +- We added the ability to copy selected text from AI chat interface. [#14655](https://github.com/JabRef/jabref/issues/14655) ### Changed From fee1f69d82a12e44630b6a595502f9728f61c3f2 Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Wed, 31 Dec 2025 22:53:45 +0530 Subject: [PATCH 11/12] add changelog entry for AI chat copy selection --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 827e0f2457c..98409c2dc4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We added support for selecting citation fetcher in Citations Tab. [#14430](https://github.com/JabRef/jabref/issues/14430) - In the "New Entry" dialog the identifier type is now automatically updated on typing. [#14660](https://github.com/JabRef/jabref/issues/14660) +- We added the ability to copy selected text from AI chat interface. [#14655](https://github.com/JabRef/jabref/issues/14655) ### Changed @@ -55,7 +56,6 @@ Note that this project **does not** adhere to [Semantic Versioning](https://semv - We enabled CLI parameters for customizing citation key generation in JabKit, allowing users to override citation key patterns without modifying GUI settings. [#14361](https://github.com/JabRef/jabref/issues/14361) - We added the option to pseudonymize a library using the GUI, via the tools tab in the Main Menu. [#14118](https://github.com/JabRef/jabref/issues/14118) - We added export options (Markdown and JSON) for AI Summary and AI Chat. [#13868](https://github.com/JabRef/jabref/issues/13868) -- We added the ability to copy selected text from AI chat interface. [#14655](https://github.com/JabRef/jabref/issues/14655) ### Changed From f4ceab140cd7c7d3347c3045a257d37198c81f28 Mon Sep 17 00:00:00 2001 From: Pulkit Bajaj Date: Thu, 1 Jan 2026 11:57:27 +0530 Subject: [PATCH 12/12] replace static import with MouseEvent class import --- .../components/aichat/chatmessage/ChatMessageComponent.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java index 527b355946c..bd90c30dbdb 100644 --- a/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java +++ b/jabgui/src/main/java/org/jabref/gui/ai/components/aichat/chatmessage/ChatMessageComponent.java @@ -10,6 +10,7 @@ import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; @@ -29,8 +30,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static javafx.scene.input.MouseEvent.MOUSE_PRESSED; - public class ChatMessageComponent extends HBox { private static final Logger LOGGER = LoggerFactory.getLogger(ChatMessageComponent.class); @@ -76,7 +75,7 @@ private void setupContextMenu() { contextMenu.getItems().add(copyItem); // 1. Capture and LOCK the selection state - markdownContentPane.addEventFilter(MOUSE_PRESSED, event -> { + markdownContentPane.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> { if (event.isSecondaryButtonDown() && markdownTextFlow.isSelectionActive()) { // Consume the event to prevent JavaFX from clearing the selection highlight event.consume();