mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-23 04:28:32 +00:00
feat: support copying user and response messages (closes #791)
This commit is contained in:
parent
1688394e87
commit
ce46deeb57
17 changed files with 463 additions and 373 deletions
|
|
@ -22,13 +22,14 @@ import ee.carlrobert.codegpt.conversations.Conversation;
|
|||
import ee.carlrobert.codegpt.conversations.ConversationService;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.CopyAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.ChatMessageResponseBody;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.ChatToolWindowScrollablePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.UserMessagePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.TotalTokensDetails;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.TotalTokensPanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.ui.ChatToolWindowLandingPanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.ui.ResponseMessagePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.ui.UserMessagePanel;
|
||||
import ee.carlrobert.codegpt.ui.OverlayUtil;
|
||||
import ee.carlrobert.codegpt.ui.textarea.AppliedActionInlay;
|
||||
import ee.carlrobert.codegpt.ui.textarea.UserInputPanel;
|
||||
|
|
@ -174,12 +175,14 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
totalTokensPanel.updateReferencedFilesTokens(callParameters.getReferencedFiles());
|
||||
}
|
||||
|
||||
var responsePanel = createResponsePanel(callParameters);
|
||||
var messagePanel = toolWindowScrollablePanel.addMessage(message.getId());
|
||||
messagePanel.add(new UserMessagePanel(project, message, this));
|
||||
messagePanel.add(responsePanel);
|
||||
var userMessagePanel = createUserMessagePanel(message, callParameters);
|
||||
var responseMessagePanel = createResponseMessagePanel(callParameters);
|
||||
|
||||
call(callParameters, responsePanel);
|
||||
var messagePanel = toolWindowScrollablePanel.addMessage(message.getId());
|
||||
messagePanel.add(userMessagePanel);
|
||||
messagePanel.add(responseMessagePanel);
|
||||
|
||||
call(callParameters, responseMessagePanel, userMessagePanel);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -193,29 +196,39 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
it -> it.getReferencedFilePaths() != null && !it.getReferencedFilePaths().isEmpty());
|
||||
}
|
||||
|
||||
private ResponsePanel createResponsePanel(ChatCompletionParameters callParameters) {
|
||||
private UserMessagePanel createUserMessagePanel(
|
||||
Message message,
|
||||
ChatCompletionParameters callParameters) {
|
||||
var panel = new UserMessagePanel(project, message, this);
|
||||
panel.addCopyAction(() -> CopyAction.copyToClipboard(message.getPrompt()));
|
||||
panel.addReloadAction(() -> reloadMessage(callParameters, panel));
|
||||
panel.addDeleteAction(() -> removeMessage(message.getId(), conversation));
|
||||
return panel;
|
||||
}
|
||||
|
||||
private ResponseMessagePanel createResponseMessagePanel(ChatCompletionParameters callParameters) {
|
||||
var message = callParameters.getMessage();
|
||||
var fileContextIncluded =
|
||||
hasReferencedFilePaths(message) || hasReferencedFilePaths(conversation);
|
||||
|
||||
return new ResponsePanel()
|
||||
.withReloadAction(() -> reloadMessage(callParameters))
|
||||
.withDeleteAction(() -> removeMessage(message.getId(), conversation))
|
||||
.addContent(
|
||||
new ChatMessageResponseBody(
|
||||
project,
|
||||
true,
|
||||
false,
|
||||
message.isWebSearchIncluded(),
|
||||
fileContextIncluded || message.getDocumentationDetails() != null,
|
||||
this));
|
||||
var panel = new ResponseMessagePanel();
|
||||
panel.addCopyAction(() -> CopyAction.copyToClipboard(message.getResponse()));
|
||||
panel.addContent(new ChatMessageResponseBody(
|
||||
project,
|
||||
true,
|
||||
false,
|
||||
message.isWebSearchIncluded(),
|
||||
fileContextIncluded || message.getDocumentationDetails() != null,
|
||||
this));
|
||||
return panel;
|
||||
}
|
||||
|
||||
private void reloadMessage(ChatCompletionParameters prevParameters) {
|
||||
private void reloadMessage(ChatCompletionParameters prevParameters,
|
||||
UserMessagePanel userMessagePanel) {
|
||||
var prevMessage = prevParameters.getMessage();
|
||||
ResponsePanel responsePanel = null;
|
||||
ResponseMessagePanel responsePanel = null;
|
||||
try {
|
||||
responsePanel = toolWindowScrollablePanel.getMessageResponsePanel(prevMessage.getId());
|
||||
responsePanel = toolWindowScrollablePanel.getResponseMessagePanel(prevMessage.getId());
|
||||
((ChatMessageResponseBody) responsePanel.getContent()).clear();
|
||||
toolWindowScrollablePanel.update();
|
||||
} catch (Exception e) {
|
||||
|
|
@ -226,7 +239,7 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
if (responsePanel != null) {
|
||||
prevMessage.setResponse("");
|
||||
conversationService.saveMessage(conversation, prevMessage);
|
||||
call(prevParameters.toBuilder().retry(true).build(), responsePanel);
|
||||
call(prevParameters.toBuilder().retry(true).build(), responsePanel, userMessagePanel);
|
||||
}
|
||||
|
||||
totalTokensPanel.updateConversationTokens(conversation);
|
||||
|
|
@ -253,8 +266,11 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
totalTokensPanel.updateConversationTokens(conversation);
|
||||
}
|
||||
|
||||
private void call(ChatCompletionParameters callParameters, ResponsePanel responsePanel) {
|
||||
var responseContainer = (ChatMessageResponseBody) responsePanel.getContent();
|
||||
private void call(
|
||||
ChatCompletionParameters callParameters,
|
||||
ResponseMessagePanel responseMessagePanel,
|
||||
UserMessagePanel userMessagePanel) {
|
||||
var responseContainer = (ChatMessageResponseBody) responseMessagePanel.getContent();
|
||||
|
||||
if (!CompletionRequestService.isRequestAllowed()) {
|
||||
responseContainer.displayMissingCredential();
|
||||
|
|
@ -264,15 +280,18 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
requestHandler = new ToolwindowChatCompletionRequestHandler(
|
||||
new ToolWindowCompletionResponseEventListener(
|
||||
conversationService,
|
||||
responsePanel,
|
||||
userMessagePanel,
|
||||
responseMessagePanel,
|
||||
totalTokensPanel,
|
||||
userInputPanel) {
|
||||
@Override
|
||||
public void handleTokensExceededPolicyAccepted() {
|
||||
call(callParameters, responsePanel);
|
||||
call(callParameters, responseMessagePanel, userMessagePanel);
|
||||
}
|
||||
});
|
||||
userInputPanel.setSubmitEnabled(false);
|
||||
userMessagePanel.disableActions(List.of("RELOAD", "DELETE"));
|
||||
responseMessagePanel.disableActions(List.of("COPY"));
|
||||
|
||||
requestHandler.call(callParameters);
|
||||
}
|
||||
|
|
@ -337,30 +356,41 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
private void displayConversation() {
|
||||
clearWindow();
|
||||
conversation.getMessages().forEach(message -> {
|
||||
var response = message.getResponse() == null ? "" : message.getResponse();
|
||||
var messageResponseBody =
|
||||
new ChatMessageResponseBody(project, this).withResponse(response);
|
||||
|
||||
messageResponseBody.hideCaret();
|
||||
|
||||
var userMessagePanel = new UserMessagePanel(project, message, this);
|
||||
var imageFilePath = message.getImageFilePath();
|
||||
if (imageFilePath != null && !imageFilePath.isEmpty()) {
|
||||
userMessagePanel.displayImage(imageFilePath);
|
||||
}
|
||||
|
||||
var messagePanel = toolWindowScrollablePanel.addMessage(message.getId());
|
||||
messagePanel.add(userMessagePanel);
|
||||
messagePanel.add(new ResponsePanel()
|
||||
.withReloadAction(() -> reloadMessage(
|
||||
ChatCompletionParameters.builder(conversation, message)
|
||||
.conversationType(ConversationType.DEFAULT)
|
||||
.build()))
|
||||
.withDeleteAction(() -> removeMessage(message.getId(), conversation))
|
||||
.addContent(messageResponseBody));
|
||||
messagePanel.add(getUserMessagePanel(message));
|
||||
messagePanel.add(getResponseMessagePanel(message));
|
||||
});
|
||||
}
|
||||
|
||||
private UserMessagePanel getUserMessagePanel(Message message) {
|
||||
var userMessagePanel = new UserMessagePanel(project, message, this);
|
||||
userMessagePanel.addCopyAction(() -> CopyAction.copyToClipboard(message.getPrompt()));
|
||||
userMessagePanel.addReloadAction(() -> reloadMessage(
|
||||
ChatCompletionParameters.builder(conversation, message)
|
||||
.conversationType(ConversationType.DEFAULT)
|
||||
.build(),
|
||||
userMessagePanel));
|
||||
userMessagePanel.addDeleteAction(() -> removeMessage(message.getId(), conversation));
|
||||
var imageFilePath = message.getImageFilePath();
|
||||
if (imageFilePath != null && !imageFilePath.isEmpty()) {
|
||||
userMessagePanel.displayImage(imageFilePath);
|
||||
}
|
||||
return userMessagePanel;
|
||||
}
|
||||
|
||||
private ResponseMessagePanel getResponseMessagePanel(Message message) {
|
||||
var response = message.getResponse() == null ? "" : message.getResponse();
|
||||
var messageResponseBody =
|
||||
new ChatMessageResponseBody(project, this).withResponse(response);
|
||||
|
||||
messageResponseBody.hideCaret();
|
||||
|
||||
var responseMessagePanel = new ResponseMessagePanel();
|
||||
responseMessagePanel.addContent(messageResponseBody);
|
||||
responseMessagePanel.addCopyAction(() -> CopyAction.copyToClipboard(message.getResponse()));
|
||||
return responseMessagePanel;
|
||||
}
|
||||
|
||||
private JPanel createRootPanel() {
|
||||
var rootPanel = new JPanel(new BorderLayout());
|
||||
rootPanel.add(createScrollPaneWithSmartScroller(toolWindowScrollablePanel),
|
||||
|
|
|
|||
|
|
@ -37,9 +37,7 @@ public class ResponseNodeRenderer implements NodeRenderer {
|
|||
}
|
||||
|
||||
private void renderHeading(Heading node, NodeRendererContext context, HtmlWriter html) {
|
||||
if (node.getLevel() == 3) {
|
||||
html.attr("style", "margin-top: 4px; margin-bottom: 4px;");
|
||||
}
|
||||
html.attr("style", "margin-top: 8px; margin-bottom: 4px;");
|
||||
context.delegateRender();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,9 @@ import ee.carlrobert.codegpt.conversations.message.Message;
|
|||
import ee.carlrobert.codegpt.events.CodeGPTEvent;
|
||||
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.ChatMessageResponseBody;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.ResponsePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.TotalTokensPanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.ui.ResponseMessagePanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.ui.UserMessagePanel;
|
||||
import ee.carlrobert.codegpt.ui.OverlayUtil;
|
||||
import ee.carlrobert.codegpt.ui.textarea.UserInputPanel;
|
||||
import ee.carlrobert.llm.client.openai.completion.ErrorDetails;
|
||||
|
|
@ -31,7 +32,8 @@ abstract class ToolWindowCompletionResponseEventListener implements
|
|||
private final StringBuilder messageBuilder = new StringBuilder();
|
||||
private final EncodingManager encodingManager;
|
||||
private final ConversationService conversationService;
|
||||
private final ResponsePanel responsePanel;
|
||||
private final ResponseMessagePanel responsePanel;
|
||||
private final UserMessagePanel userMessagePanel;
|
||||
private final ChatMessageResponseBody responseContainer;
|
||||
private final TotalTokensPanel totalTokensPanel;
|
||||
private final UserInputPanel textArea;
|
||||
|
|
@ -43,11 +45,13 @@ abstract class ToolWindowCompletionResponseEventListener implements
|
|||
|
||||
public ToolWindowCompletionResponseEventListener(
|
||||
ConversationService conversationService,
|
||||
ResponsePanel responsePanel,
|
||||
UserMessagePanel userMessagePanel,
|
||||
ResponseMessagePanel responsePanel,
|
||||
TotalTokensPanel totalTokensPanel,
|
||||
UserInputPanel textArea) {
|
||||
this.encodingManager = EncodingManager.getInstance();
|
||||
this.conversationService = conversationService;
|
||||
this.userMessagePanel = userMessagePanel;
|
||||
this.responsePanel = responsePanel;
|
||||
this.responseContainer = (ChatMessageResponseBody) responsePanel.getContent();
|
||||
this.totalTokensPanel = totalTokensPanel;
|
||||
|
|
@ -89,7 +93,7 @@ abstract class ToolWindowCompletionResponseEventListener implements
|
|||
}
|
||||
} finally {
|
||||
LOG.error(error.getMessage(), ex);
|
||||
responsePanel.enableActions();
|
||||
responsePanel.enableAllActions(true);
|
||||
stopStreaming(responseContainer);
|
||||
}
|
||||
});
|
||||
|
|
@ -119,7 +123,7 @@ abstract class ToolWindowCompletionResponseEventListener implements
|
|||
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
try {
|
||||
responsePanel.enableActions();
|
||||
responsePanel.enableAllActions(true);
|
||||
if (!streamResponseReceived && !fullMessage.isEmpty()) {
|
||||
responseContainer.withResponse(fullMessage);
|
||||
}
|
||||
|
|
@ -156,6 +160,8 @@ abstract class ToolWindowCompletionResponseEventListener implements
|
|||
private void stopStreaming(ChatMessageResponseBody responseContainer) {
|
||||
stopped = true;
|
||||
textArea.setSubmitEnabled(true);
|
||||
userMessagePanel.enableAllActions(true);
|
||||
responsePanel.enableAllActions(true);
|
||||
responseContainer.hideCaret();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import ee.carlrobert.codegpt.actions.ActionType;
|
|||
import ee.carlrobert.codegpt.actions.TrackableAction;
|
||||
import ee.carlrobert.codegpt.ui.OverlayUtil;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.MouseEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -19,8 +18,8 @@ public class CopyAction extends TrackableAction {
|
|||
|
||||
public CopyAction(@NotNull Editor toolwindowEditor) {
|
||||
super(
|
||||
CodeGPTBundle.get("shared.copy"),
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.copy.description"),
|
||||
CodeGPTBundle.get("shared.copyCode"),
|
||||
CodeGPTBundle.get("shared.copyToClipboard"),
|
||||
Actions.Copy,
|
||||
ActionType.COPY_CODE);
|
||||
this.toolwindowEditor = toolwindowEditor;
|
||||
|
|
@ -28,17 +27,24 @@ public class CopyAction extends TrackableAction {
|
|||
|
||||
@Override
|
||||
public void handleAction(@NotNull AnActionEvent event) {
|
||||
StringSelection stringSelection = new StringSelection(toolwindowEditor.getDocument().getText());
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
clipboard.setContents(stringSelection, null);
|
||||
copyToClipboard(toolwindowEditor.getDocument().getText());
|
||||
showCopyBalloon(event);
|
||||
}
|
||||
|
||||
public static void copyToClipboard(String text) {
|
||||
Toolkit.getDefaultToolkit()
|
||||
.getSystemClipboard()
|
||||
.setContents(new StringSelection(text), null);
|
||||
}
|
||||
|
||||
public static void showCopyBalloon(AnActionEvent event) {
|
||||
var mouseEvent = (MouseEvent) event.getInputEvent();
|
||||
if (mouseEvent != null) {
|
||||
var locationOnScreen = mouseEvent.getLocationOnScreen();
|
||||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
|
||||
OverlayUtil.showInfoBalloon(
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.copy.success"),
|
||||
CodeGPTBundle.get("shared.copiedToClipboard"),
|
||||
locationOnScreen);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.intellij.icons.AllIcons;
|
|||
import com.intellij.icons.AllIcons.General;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces;
|
||||
import com.intellij.openapi.actionSystem.ActionUpdateThread;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
|
|
@ -37,12 +38,15 @@ import ee.carlrobert.codegpt.settings.GeneralSettingsConfigurable;
|
|||
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.StreamParser;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.editor.ResponseEditorPanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.CopyAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.ui.ResponseBodyProgressPanel;
|
||||
import ee.carlrobert.codegpt.toolwindow.ui.WebpageList;
|
||||
import ee.carlrobert.codegpt.ui.OverlayUtil;
|
||||
import ee.carlrobert.codegpt.ui.UIUtil;
|
||||
import ee.carlrobert.codegpt.util.EditorUtil;
|
||||
import ee.carlrobert.codegpt.util.MarkdownUtil;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Objects;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.DefaultListModel;
|
||||
|
|
@ -331,9 +335,16 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
CodeGPTBundle.get("shared.copy"),
|
||||
CodeGPTBundle.get("shared.copyToClipboard"),
|
||||
AllIcons.Actions.Copy) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
public @NotNull ActionUpdateThread getActionUpdateThread() {
|
||||
return ActionUpdateThread.EDT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent event) {
|
||||
textPane.copy();
|
||||
CopyAction.showCopyBalloon(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import ee.carlrobert.codegpt.credentials.CredentialsStore.CredentialKey;
|
|||
import ee.carlrobert.codegpt.settings.GeneralSettings;
|
||||
import ee.carlrobert.codegpt.settings.service.ServiceType;
|
||||
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceConfigurable;
|
||||
import ee.carlrobert.codegpt.toolwindow.ui.ResponseMessagePanel;
|
||||
import ee.carlrobert.codegpt.ui.UIUtil;
|
||||
import ee.carlrobert.codegpt.util.ApplicationUtil;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -36,35 +37,35 @@ public class ChatToolWindowScrollablePanel extends ScrollablePanel {
|
|||
if (GeneralSettings.isSelected(ServiceType.CODEGPT)
|
||||
&& !CredentialsStore.INSTANCE.isCredentialSet(CredentialKey.CODEGPT_API_KEY)) {
|
||||
|
||||
var panel = new ResponsePanel()
|
||||
.addContent(UIUtil.createTextPane("""
|
||||
<html>
|
||||
<p style="margin-top: 4px; margin-bottom: 4px;">
|
||||
It looks like you haven't configured your API key yet. Visit <a href="#OPEN_SETTINGS">CodeGPT settings</a> to do so.
|
||||
</p>
|
||||
<p style="margin-top: 4px; margin-bottom: 4px;">
|
||||
Don't have an account? <a href="https://codegpt.ee">Sign up</a> to get the most out of CodeGPT.
|
||||
</p>
|
||||
</html>""",
|
||||
false,
|
||||
event -> {
|
||||
if (ACTIVATED.equals(event.getEventType())
|
||||
&& "#OPEN_SETTINGS".equals(event.getDescription())) {
|
||||
ShowSettingsUtil.getInstance().showSettingsDialog(
|
||||
ApplicationUtil.findCurrentProject(),
|
||||
CodeGPTServiceConfigurable.class);
|
||||
} else {
|
||||
UIUtil.handleHyperlinkClicked(event);
|
||||
}
|
||||
}));
|
||||
var panel = new ResponseMessagePanel();
|
||||
panel.addContent(UIUtil.createTextPane("""
|
||||
<html>
|
||||
<p style="margin-top: 4px; margin-bottom: 4px;">
|
||||
It looks like you haven't configured your API key yet. Visit <a href="#OPEN_SETTINGS">CodeGPT settings</a> to do so.
|
||||
</p>
|
||||
<p style="margin-top: 4px; margin-bottom: 4px;">
|
||||
Don't have an account? <a href="https://codegpt.ee">Sign up</a> to get the most out of CodeGPT.
|
||||
</p>
|
||||
</html>""",
|
||||
false,
|
||||
event -> {
|
||||
if (ACTIVATED.equals(event.getEventType())
|
||||
&& "#OPEN_SETTINGS".equals(event.getDescription())) {
|
||||
ShowSettingsUtil.getInstance().showSettingsDialog(
|
||||
ApplicationUtil.findCurrentProject(),
|
||||
CodeGPTServiceConfigurable.class);
|
||||
} else {
|
||||
UIUtil.handleHyperlinkClicked(event);
|
||||
}
|
||||
}));
|
||||
panel.setBorder(JBUI.Borders.customLine(JBColor.border(), 1, 0, 0, 0));
|
||||
add(panel);
|
||||
}
|
||||
}
|
||||
|
||||
public ResponsePanel getMessageResponsePanel(UUID messageId) {
|
||||
return (ResponsePanel) Arrays.stream(visibleMessagePanels.get(messageId).getComponents())
|
||||
.filter(ResponsePanel.class::isInstance)
|
||||
public ResponseMessagePanel getResponseMessagePanel(UUID messageId) {
|
||||
return (ResponseMessagePanel) Arrays.stream(visibleMessagePanels.get(messageId).getComponents())
|
||||
.filter(ResponseMessagePanel.class::isInstance)
|
||||
.findFirst().orElseThrow();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,150 +0,0 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.ui;
|
||||
|
||||
import com.intellij.icons.AllIcons.Actions;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.JBFont;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.Icons;
|
||||
import ee.carlrobert.codegpt.ui.IconActionButton;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ResponsePanel extends JPanel {
|
||||
|
||||
private final Header header;
|
||||
private final Body body;
|
||||
|
||||
public ResponsePanel() {
|
||||
super(new BorderLayout());
|
||||
header = new Header();
|
||||
body = new Body();
|
||||
add(header, BorderLayout.NORTH);
|
||||
add(body, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
public void enableActions() {
|
||||
header.enableActions(true);
|
||||
}
|
||||
|
||||
public ResponsePanel withReloadAction(Runnable onReload) {
|
||||
header.addReloadAction(onReload);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponsePanel withDeleteAction(Runnable onDelete) {
|
||||
header.addDeleteAction(onDelete);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponsePanel addContent(JComponent content) {
|
||||
body.addContent(content);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void updateContent(JComponent content) {
|
||||
body.updateContent(content);
|
||||
}
|
||||
|
||||
public JComponent getContent() {
|
||||
return body.getContent();
|
||||
}
|
||||
|
||||
static class Header extends JPanel {
|
||||
|
||||
private final JPanel iconsWrapper;
|
||||
|
||||
Header() {
|
||||
super(new BorderLayout());
|
||||
setBorder(JBUI.Borders.empty(12, 8, 4, 8));
|
||||
add(getIconLabel(), BorderLayout.LINE_START);
|
||||
|
||||
iconsWrapper = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 0));
|
||||
iconsWrapper.setBackground(getBackground());
|
||||
add(iconsWrapper, BorderLayout.LINE_END);
|
||||
}
|
||||
|
||||
public void enableActions(boolean enabled) {
|
||||
for (var iconButton : iconsWrapper.getComponents()) {
|
||||
iconButton.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
public void addReloadAction(Runnable onReload) {
|
||||
addIconActionButton(new IconActionButton(
|
||||
new AnAction(
|
||||
CodeGPTBundle.get("toolwindow.chat.response.action.reloadResponse.text"),
|
||||
CodeGPTBundle.get("toolwindow.chat.response.action.reloadResponse.description"),
|
||||
Actions.Refresh) {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
enableActions(false);
|
||||
onReload.run();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public void addDeleteAction(Runnable onDelete) {
|
||||
addIconActionButton(new IconActionButton(
|
||||
new AnAction(
|
||||
CodeGPTBundle.get("toolwindow.chat.response.action.deleteResponse.text"),
|
||||
CodeGPTBundle.get("toolwindow.chat.response.action.deleteResponse.description"),
|
||||
Actions.GC) {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
onDelete.run();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private void addIconActionButton(IconActionButton iconActionButton) {
|
||||
if (iconsWrapper.getComponents() != null && iconsWrapper.getComponents().length > 0) {
|
||||
iconsWrapper.add(Box.createHorizontalStrut(8));
|
||||
}
|
||||
iconsWrapper.add(iconActionButton);
|
||||
}
|
||||
|
||||
private JBLabel getIconLabel() {
|
||||
return new JBLabel(
|
||||
CodeGPTBundle.get("project.label"),
|
||||
Icons.Default,
|
||||
SwingConstants.LEADING)
|
||||
.setAllowAutoWrapping(true)
|
||||
.withFont(JBFont.label().asBold());
|
||||
}
|
||||
}
|
||||
|
||||
static class Body extends JPanel {
|
||||
|
||||
private @Nullable JComponent content;
|
||||
|
||||
Body() {
|
||||
super(new BorderLayout());
|
||||
setBorder(JBUI.Borders.empty(4, 8));
|
||||
}
|
||||
|
||||
public void addContent(JComponent content) {
|
||||
this.content = content;
|
||||
add(content);
|
||||
}
|
||||
|
||||
public void updateContent(JComponent content) {
|
||||
removeAll();
|
||||
revalidate();
|
||||
repaint();
|
||||
addContent(content);
|
||||
}
|
||||
|
||||
public @Nullable JComponent getContent() {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.ui;
|
||||
|
||||
import com.intellij.icons.AllIcons.General;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.ui.ColorUtil;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.JBFont;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.CodeGPTKeys;
|
||||
import ee.carlrobert.codegpt.Icons;
|
||||
import ee.carlrobert.codegpt.conversations.message.Message;
|
||||
import ee.carlrobert.codegpt.events.WebSearchEventDetails;
|
||||
import ee.carlrobert.codegpt.settings.GeneralSettings;
|
||||
import ee.carlrobert.codegpt.toolwindow.ui.WebpageList;
|
||||
import java.awt.BorderLayout;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.UUID;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class UserMessagePanel extends JPanel {
|
||||
|
||||
public UserMessagePanel(Project project, Message message, Disposable parentDisposable) {
|
||||
super(new BorderLayout());
|
||||
var headerPanel = new JPanel(new BorderLayout());
|
||||
headerPanel.setOpaque(false);
|
||||
headerPanel.add(createDisplayNameLabel(), BorderLayout.LINE_START);
|
||||
setBorder(JBUI.Borders.compound(
|
||||
JBUI.Borders.customLine(JBColor.border(), 1, 0, 1, 0),
|
||||
JBUI.Borders.empty(12, 8, 8, 8)));
|
||||
setBackground(ColorUtil.brighter(getBackground(), 2));
|
||||
add(headerPanel, BorderLayout.NORTH);
|
||||
|
||||
var additionalContextPanel = getAdditionalContextPanel(project, message);
|
||||
if (additionalContextPanel != null) {
|
||||
add(additionalContextPanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
if (message.getImageFilePath() != null && !message.getImageFilePath().isEmpty()) {
|
||||
displayImage(message.getImageFilePath());
|
||||
}
|
||||
|
||||
add(createResponseBody(
|
||||
project,
|
||||
message.getPrompt(),
|
||||
parentDisposable), BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
public @Nullable JPanel getAdditionalContextPanel(Project project, Message message) {
|
||||
var addedDocumentation = CodeGPTKeys.ADDED_DOCUMENTATION.get(project);
|
||||
var referencedFilePaths = message.getReferencedFilePaths();
|
||||
if (addedDocumentation == null
|
||||
&& (referencedFilePaths == null
|
||||
|| referencedFilePaths.isEmpty())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var panel = new JPanel(new BorderLayout());
|
||||
panel.setOpaque(false);
|
||||
if (addedDocumentation != null) {
|
||||
var listModel = new DefaultListModel<WebSearchEventDetails>();
|
||||
listModel.addElement(
|
||||
new WebSearchEventDetails(UUID.randomUUID(), addedDocumentation.getName(),
|
||||
addedDocumentation.getUrl(), addedDocumentation.getUrl()));
|
||||
panel.add(createWebpageListPanel(new WebpageList(listModel)), BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
if (referencedFilePaths != null && !referencedFilePaths.isEmpty()) {
|
||||
panel.add(new SelectedFilesAccordion(project, referencedFilePaths), BorderLayout.NORTH);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
public void displayImage(String imageFilePath) {
|
||||
try {
|
||||
var path = Paths.get(imageFilePath);
|
||||
add(new ImageAccordion(path.getFileName().toString(), Files.readAllBytes(path)));
|
||||
} catch (IOException e) {
|
||||
add(new JBLabel(
|
||||
"<html><small>Unable to load image %s</small></html>".formatted(imageFilePath),
|
||||
General.Error,
|
||||
SwingConstants.LEFT));
|
||||
}
|
||||
}
|
||||
|
||||
private ChatMessageResponseBody createResponseBody(
|
||||
Project project,
|
||||
String prompt,
|
||||
Disposable parentDisposable) {
|
||||
return new ChatMessageResponseBody(project, false, true, false, false, parentDisposable)
|
||||
.withResponse(prompt);
|
||||
}
|
||||
|
||||
private JBLabel createDisplayNameLabel() {
|
||||
return new JBLabel(
|
||||
GeneralSettings.getCurrentState().getDisplayName(),
|
||||
Icons.User,
|
||||
SwingConstants.LEADING)
|
||||
.setAllowAutoWrapping(true)
|
||||
.withFont(JBFont.label().asBold())
|
||||
.withBorder(JBUI.Borders.emptyBottom(6));
|
||||
}
|
||||
|
||||
private static JPanel createWebpageListPanel(WebpageList webpageList) {
|
||||
var title = new JPanel(new BorderLayout());
|
||||
title.setOpaque(false);
|
||||
title.setBorder(JBUI.Borders.empty(8, 0));
|
||||
title.add(new JBLabel(CodeGPTBundle.get("userMessagePanel.documentation.title"))
|
||||
.withFont(JBUI.Fonts.miniFont()), BorderLayout.LINE_START);
|
||||
var listPanel = new JPanel(new BorderLayout());
|
||||
listPanel.setOpaque(false);
|
||||
listPanel.add(webpageList, BorderLayout.LINE_START);
|
||||
|
||||
var panel = new JPanel(new BorderLayout());
|
||||
panel.setOpaque(false);
|
||||
panel.add(title, BorderLayout.NORTH);
|
||||
panel.add(listPanel, BorderLayout.CENTER);
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ class ConversationPanel extends JPanel {
|
|||
|
||||
gbc.gridx = 1;
|
||||
gbc.weightx = 0;
|
||||
headerPanel.add(new IconActionButton(new DeleteConversationAction(onDelete)), gbc);
|
||||
headerPanel.add(new IconActionButton(new DeleteConversationAction(onDelete), "DELETE"), gbc);
|
||||
|
||||
var bottomPanel = new JPanel(new BorderLayout());
|
||||
bottomPanel.add(new JLabel(conversation.getUpdatedOn()
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ import com.intellij.openapi.actionSystem.impl.ActionButton;
|
|||
|
||||
public class IconActionButton extends ActionButton {
|
||||
|
||||
public IconActionButton(AnAction action) {
|
||||
public IconActionButton(AnAction action, String actionCode) {
|
||||
super(action,
|
||||
getPresentation(action),
|
||||
ActionPlaces.TOOLWINDOW_CONTENT,
|
||||
ActionToolbar.DEFAULT_MINIMUM_BUTTON_SIZE);
|
||||
putClientProperty("actionCode", actionCode);
|
||||
}
|
||||
|
||||
private static Presentation getPresentation(AnAction action) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue