diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/BaseChatToolWindowTabPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/BaseChatToolWindowTabPanel.java index 78e31129..335a7e4f 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/BaseChatToolWindowTabPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/BaseChatToolWindowTabPanel.java @@ -125,8 +125,7 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan (!youUserManager.isAuthenticated() || !youUserManager.isSubscribed())) { scrollablePanel.add(new ResponsePanel().addContent(createYouCouponTextPane())); } - scrollablePanel.repaint(); - scrollablePanel.revalidate(); + revalidateScrollablePanel(); } @Override @@ -142,11 +141,7 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan } var messageWrapper = createNewMessageWrapper(message.getId()); - messageWrapper.add(new UserMessagePanel( - project, - message, - message.getUserMessage() != null, - this)); + messageWrapper.add(new UserMessagePanel(project, message, this)); var responsePanel = new ResponsePanel() .withReloadAction(() -> reloadMessage(message, conversation)) .withDeleteAction(() -> removeMessage(message.getId(), messageWrapper, conversation)) @@ -168,8 +163,7 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan .filter(component -> component instanceof ResponsePanel) .findFirst().orElseThrow(); ((ChatMessageResponseBody) responsePanel.getContent()).clear(); - scrollablePanel.revalidate(); - scrollablePanel.repaint(); + revalidateScrollablePanel(); } catch (Exception e) { throw new RuntimeException("Couldn't delete the existing message component", e); } finally { @@ -187,10 +181,14 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan } } - protected void removeMessage(UUID messageId, JPanel messageWrapper, Conversation conversation) { - scrollablePanel.remove(messageWrapper); + private void revalidateScrollablePanel() { scrollablePanel.repaint(); scrollablePanel.revalidate(); + } + + protected void removeMessage(UUID messageId, JPanel messageWrapper, Conversation conversation) { + scrollablePanel.remove(messageWrapper); + revalidateScrollablePanel(); visibleMessagePanels.remove(messageId); conversation.removeMessage(messageId); @@ -207,16 +205,14 @@ public abstract class BaseChatToolWindowTabPanel implements ChatToolWindowTabPan var messageWrapper = new JPanel(); messageWrapper.setLayout(new BoxLayout(messageWrapper, BoxLayout.PAGE_AXIS)); scrollablePanel.add(messageWrapper); - scrollablePanel.repaint(); - scrollablePanel.revalidate(); + revalidateScrollablePanel(); visibleMessagePanels.put(messageId, messageWrapper); return messageWrapper; } protected void clearWindow() { scrollablePanel.removeAll(); - scrollablePanel.revalidate(); - scrollablePanel.repaint(); + revalidateScrollablePanel(); } private void call( diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ChatMessageResponseBody.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ChatMessageResponseBody.java index 7a9e5be9..30e347d0 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ChatMessageResponseBody.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ChatMessageResponseBody.java @@ -44,6 +44,7 @@ public class ChatMessageResponseBody extends JPanel { private final Project project; private final Disposable parentDisposable; private final StreamParser streamParser; + private final boolean readOnly; private JPanel currentlyProcessedElement; private ResponseEditor currentlyProcessedEditor; private JTextPane currentlyProcessedTextPane; @@ -65,10 +66,20 @@ public class ChatMessageResponseBody extends JPanel { Color backgroundColor, boolean withGhostText, Disposable parentDisposable) { + this(project, backgroundColor, withGhostText, false, parentDisposable); + } + + public ChatMessageResponseBody( + Project project, + Color backgroundColor, + boolean withGhostText, + boolean readOnly, + Disposable parentDisposable) { super(new BorderLayout()); this.project = project; this.parentDisposable = parentDisposable; this.streamParser = new StreamParser(); + this.readOnly = readOnly; setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); setBackground(backgroundColor); @@ -237,6 +248,8 @@ public class ChatMessageResponseBody extends JPanel { project, code, markdownLanguage, + readOnly, + getPanelBackgroundColor(), parentDisposable); currentlyProcessedElement = new ResponseWrapper(); diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserMessagePanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserMessagePanel.java index 17045496..a08687b4 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserMessagePanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserMessagePanel.java @@ -14,13 +14,19 @@ import javax.swing.JPanel; public class UserMessagePanel extends JPanel { - public UserMessagePanel(Project project, Message message, boolean displayEditorMessage, Disposable parentDisposable) { + public UserMessagePanel(Project project, Message message, Disposable parentDisposable) { super(new BorderLayout()); setBorder(JBUI.Borders.compound( JBUI.Borders.customLine(JBColor.border(), 0, 0, 1, 0), JBUI.Borders.empty(12, 8, 8, 8))); add(createDisplayNameWrapper(), BorderLayout.NORTH); - add(createUserMessageTextPane(project, message, displayEditorMessage, parentDisposable), BorderLayout.SOUTH); + add(new ChatMessageResponseBody( + project, + UIUtil.getPanelBackground(), + false, + true, + parentDisposable).withResponse(message.getPrompt()), + BorderLayout.SOUTH); } private JPanel createDisplayNameWrapper() { @@ -30,12 +36,4 @@ public class UserMessagePanel extends JPanel { .setAllowAutoWrapping(true) .withFont(JBFont.label().asBold())); } - - private JPanel createUserMessageTextPane(Project project, Message message, boolean displayEditorMessage, Disposable parentDisposable) { - var prompt = message.getPrompt(); - if (displayEditorMessage && message.getUserMessage() != null) { - prompt = message.getUserMessage(); - } - return new ChatMessageResponseBody(project, UIUtil.getPanelBackground(), false, parentDisposable).withResponse(prompt); - } } diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditor.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditor.java index d8f04735..3103185b 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditor.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditor.java @@ -1,7 +1,9 @@ package ee.carlrobert.codegpt.toolwindow.chat.editor; import static ee.carlrobert.codegpt.util.file.FileUtils.findLanguageExtensionMapping; +import static java.lang.String.format; +import com.intellij.icons.AllIcons.General; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; @@ -15,8 +17,10 @@ import com.intellij.openapi.editor.impl.ContextMenuPopupHandler; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.ui.JBColor; +import com.intellij.ui.components.ActionLink; import com.intellij.ui.components.JBLabel; import com.intellij.util.ui.JBUI; +import ee.carlrobert.codegpt.CodeGPTBundle; import ee.carlrobert.codegpt.actions.toolwindow.ReplaceCodeInMainEditorAction; import ee.carlrobert.codegpt.toolwindow.chat.components.IconActionButton; import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.CopyAction; @@ -26,6 +30,7 @@ import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.NewFileAction; import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.ReplaceSelectionAction; import ee.carlrobert.codegpt.util.EditorUtils; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.FlowLayout; import javax.swing.Box; import javax.swing.JPanel; @@ -40,6 +45,8 @@ public class ResponseEditor extends JPanel implements Disposable { Project project, String code, String markdownLanguage, + boolean readOnly, + Color backgroundColor, Disposable disposableParent) { super(new BorderLayout()); @@ -60,6 +67,10 @@ public class ResponseEditor extends JPanel implements Disposable { } var editorEx = ((EditorEx) editor); + if (readOnly) { + editorEx.setOneLineMode(true); + editorEx.setHorizontalScrollbarVisible(false); + } editorEx.installPopupHandler(new ContextMenuPopupHandler.Simple(group)); editorEx.setColorsScheme(EditorColorsManager.getInstance().getSchemeForCurrentUITheme()); @@ -72,8 +83,9 @@ public class ResponseEditor extends JPanel implements Disposable { settings.setLineMarkerAreaShown(false); settings.setGutterIconsShown(false); - add(createHeaderComponent(), BorderLayout.NORTH); - add(editor.getComponent(), BorderLayout.SOUTH); + add(createHeaderComponent(readOnly), BorderLayout.NORTH); + add(editor.getComponent(), BorderLayout.CENTER); + add(createFooterComponent(readOnly ? getBackground() : backgroundColor), BorderLayout.SOUTH); Disposer.register(disposableParent, this); } @@ -87,16 +99,50 @@ public class ResponseEditor extends JPanel implements Disposable { return editor; } - private JPanel createHeaderComponent() { + private JPanel createHeaderComponent(boolean readOnly) { var headerComponent = new JPanel(new BorderLayout()); headerComponent.setBorder(JBUI.Borders.compound( JBUI.Borders.customLine(JBColor.border(), 1, 1, 1, 1), - JBUI.Borders.empty(8))); + JBUI.Borders.empty(4, 8))); headerComponent.add(new JBLabel(language), BorderLayout.LINE_START); - headerComponent.add(createHeaderActions(), BorderLayout.LINE_END); + if (!readOnly) { + headerComponent.add(createHeaderActions(), BorderLayout.LINE_END); + } return headerComponent; } + private String getLinkText(boolean expanded) { + return expanded ? + format( + CodeGPTBundle.get("toolwindow.chat.editor.action.expand"), + ((EditorEx) editor).getDocument().getLineCount() - 1) : + CodeGPTBundle.get("toolwindow.chat.editor.action.collapse"); + } + + private JPanel createFooterComponent(Color backgroundColor) { + var editorEx = ((EditorEx) editor); + var linkText = getLinkText(editorEx.isOneLineMode()); + var expandLink = new ActionLink( + linkText, + event -> { + var oneLineMode = editorEx.isOneLineMode(); + var source = (ActionLink) event.getSource(); + source.setText(getLinkText(!oneLineMode)); + source.setIcon(oneLineMode ? General.ArrowUp : General.ArrowDown, true); + + editorEx.setOneLineMode(!oneLineMode); + editorEx.setHorizontalScrollbarVisible(oneLineMode); + editorEx.getContentComponent().revalidate(); + editorEx.getContentComponent().repaint(); + }); + expandLink.setIcon(editorEx.isOneLineMode() ? General.ArrowDown : General.ArrowUp, true); + + var panel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + panel.setBackground(backgroundColor); + panel.add(expandLink); + return panel; + } + private JPanel createHeaderActions() { var wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); wrapper.add(new IconActionButton(new DiffAction(editor))); diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabPanel.java index 1f78350f..28aa9158 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabPanel.java @@ -76,7 +76,7 @@ public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel { } var messageWrapper = createNewMessageWrapper(message.getId()); - messageWrapper.add(new UserMessagePanel(project, message, false, this)); + messageWrapper.add(new UserMessagePanel(project, message, this)); messageWrapper.add(new ResponsePanel() .withReloadAction(() -> reloadMessage(message, conversation)) .withDeleteAction(() -> removeMessage(message.getId(), messageWrapper, conversation)) diff --git a/src/main/resources/messages/codegpt.properties b/src/main/resources/messages/codegpt.properties index cf9e61fe..31b6ff40 100644 --- a/src/main/resources/messages/codegpt.properties +++ b/src/main/resources/messages/codegpt.properties @@ -99,6 +99,8 @@ toolwindow.chat.editor.action.newFile.title=New File toolwindow.chat.editor.action.newFile.description=Create new file from generated code toolwindow.chat.editor.action.replaceSelection.title=Replace Selection toolwindow.chat.editor.action.replaceSelection.description=Replace main editor selected code +toolwindow.chat.editor.action.expand=Show More (+%s rows) +toolwindow.chat.editor.action.collapse=Show Less toolwindow.chat.response.action.reloadResponse.text=Reload Response toolwindow.chat.response.action.reloadResponse.description=Reload response description toolwindow.chat.response.action.deleteResponse.text=Delete Response