From 1df20ccb8698452b44809ce68989c801c19de930 Mon Sep 17 00:00:00 2001 From: Carl-Robert Date: Sun, 26 Nov 2023 10:52:47 +0200 Subject: [PATCH] Update toolwindow UI (#290) --- .../codegpt.java-conventions.gradle.kts | 2 +- .../java/ee/carlrobert/codegpt/Icons.java | 18 +- .../GenerateGitCommitMessageAction.java | 2 +- .../codegpt/actions/editor/AskAction.java | 2 +- .../DeleteAllConversationsAction.java | 4 +- .../CompletionRequestProvider.java | 4 +- .../completions/MethodNameLookupListener.java | 2 +- .../configuration/ConfigurationState.java | 2 +- .../codegpt/toolwindow/ModelIconLabel.java | 10 +- .../chat/BaseChatToolWindowTabPanel.java | 20 ++- .../chat/ChatToolWindowScrollablePanel.java | 11 +- .../toolwindow/chat/ResponseNodeRenderer.java | 40 ++++- .../components/ChatMessageResponseBody.java | 91 ++++------ .../chat/components/ResponsePanel.java | 11 +- .../chat/components/TotalTokensPanel.java | 2 + .../chat/components/UserMessagePanel.java | 39 +++-- .../chat/components/UserPromptTextArea.java | 6 +- .../components/UserPromptTextAreaHeader.java | 27 ++- .../ContextualChatToolWindowLandingPanel.java | 9 +- .../chat/editor/ResponseEditorPanel.java | 114 ++++++++----- .../chat/editor/actions/DiffAction.java | 30 ++-- .../chat/editor/actions/EditAction.java | 44 +++-- .../chat/editor/actions/NewFileAction.java | 27 ++- .../chat/standard/EditorAction.java | 20 +-- .../chat/standard/ModelComboBoxAction.java | 158 ++++++++++++++++++ .../StandardChatToolWindowLandingPanel.java | 100 +++++------ .../standard/StandardChatToolWindowPanel.java | 34 ++-- .../StandardChatToolWindowTabPanel.java | 4 +- .../StandardChatToolWindowTabbedPane.java | 7 +- .../conversations/ConversationPanel.java | 6 - .../carlrobert/codegpt/util/OverlayUtil.java | 15 +- .../ee/carlrobert/codegpt/util/UIUtil.java | 9 +- src/main/resources/META-INF/plugin.xml | 4 +- src/main/resources/icons/user.svg | 7 + src/main/resources/icons/user_dark.svg | 7 + src/main/resources/icons/you_small.png | Bin 0 -> 1194 bytes .../resources/messages/codegpt.properties | 2 +- .../DefaultCompletionRequestHandlerTest.java | 4 +- 38 files changed, 538 insertions(+), 356 deletions(-) create mode 100644 src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/ModelComboBoxAction.java create mode 100644 src/main/resources/icons/user.svg create mode 100644 src/main/resources/icons/user_dark.svg create mode 100644 src/main/resources/icons/you_small.png diff --git a/buildSrc/src/main/kotlin/codegpt.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/codegpt.java-conventions.gradle.kts index 44700eac..ff1924e6 100644 --- a/buildSrc/src/main/kotlin/codegpt.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/codegpt.java-conventions.gradle.kts @@ -23,7 +23,7 @@ checkstyle { } dependencies { - implementation("ee.carlrobert:llm-client:0.0.11") + implementation("ee.carlrobert:llm-client:0.0.12") } tasks { diff --git a/src/main/java/ee/carlrobert/codegpt/Icons.java b/src/main/java/ee/carlrobert/codegpt/Icons.java index 238018c3..b61dcb04 100644 --- a/src/main/java/ee/carlrobert/codegpt/Icons.java +++ b/src/main/java/ee/carlrobert/codegpt/Icons.java @@ -5,13 +5,15 @@ import javax.swing.Icon; public final class Icons { - public static final Icon DefaultIcon = IconLoader.getIcon("/icons/codegpt.svg", Icons.class); - public static final Icon DefaultSmallIcon = + public static final Icon Default = IconLoader.getIcon("/icons/codegpt.svg", Icons.class); + public static final Icon DefaultSmall = IconLoader.getIcon("/icons/codegpt-small.svg", Icons.class); - public static final Icon AzureIcon = IconLoader.getIcon("/icons/azure.svg", Icons.class); - public static final Icon LlamaIcon = IconLoader.getIcon("/icons/llama.svg", Icons.class); - public static final Icon OpenAIIcon = IconLoader.getIcon("/icons/openai.svg", Icons.class); - public static final Icon SendIcon = IconLoader.getIcon("/icons/send.svg", Icons.class); - public static final Icon SparkleIcon = IconLoader.getIcon("/icons/sparkle.svg", Icons.class); - public static final Icon YouIcon = IconLoader.getIcon("/icons/you.svg", Icons.class); + public static final Icon Azure = IconLoader.getIcon("/icons/azure.svg", Icons.class); + public static final Icon Llama = IconLoader.getIcon("/icons/llama.svg", Icons.class); + public static final Icon OpenAI = IconLoader.getIcon("/icons/openai.svg", Icons.class); + public static final Icon Send = IconLoader.getIcon("/icons/send.svg", Icons.class); + public static final Icon Sparkle = IconLoader.getIcon("/icons/sparkle.svg", Icons.class); + public static final Icon You = IconLoader.getIcon("/icons/you.svg", Icons.class); + public static final Icon YouSmall = IconLoader.getIcon("/icons/you_small.png", Icons.class); + public static final Icon User = IconLoader.getIcon("/icons/user.svg", Icons.class); } diff --git a/src/main/java/ee/carlrobert/codegpt/actions/GenerateGitCommitMessageAction.java b/src/main/java/ee/carlrobert/codegpt/actions/GenerateGitCommitMessageAction.java index f50fffe2..fc31c954 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/GenerateGitCommitMessageAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/GenerateGitCommitMessageAction.java @@ -44,7 +44,7 @@ public class GenerateGitCommitMessageAction extends AnAction { super( CodeGPTBundle.get("action.generateCommitMessage.title"), CodeGPTBundle.get("action.generateCommitMessage.description"), - Icons.SparkleIcon); + Icons.Sparkle); encodingManager = EncodingManager.getInstance(); } diff --git a/src/main/java/ee/carlrobert/codegpt/actions/editor/AskAction.java b/src/main/java/ee/carlrobert/codegpt/actions/editor/AskAction.java index 527cc1f9..3b8fcd0b 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/editor/AskAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/editor/AskAction.java @@ -10,7 +10,7 @@ import org.jetbrains.annotations.NotNull; public class AskAction extends AnAction { public AskAction() { - super("New Chat", "Chat with CodeGPT", Icons.SparkleIcon); + super("New Chat", "Chat with CodeGPT", Icons.Sparkle); EditorActionsUtil.registerOrReplaceAction(this); } diff --git a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteAllConversationsAction.java b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteAllConversationsAction.java index b9b4b396..dab31ba8 100644 --- a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteAllConversationsAction.java +++ b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/DeleteAllConversationsAction.java @@ -1,6 +1,6 @@ package ee.carlrobert.codegpt.actions.toolwindow; -import static ee.carlrobert.codegpt.Icons.DefaultIcon; +import static ee.carlrobert.codegpt.Icons.Default; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnAction; @@ -37,7 +37,7 @@ public class DeleteAllConversationsAction extends AnAction { int answer = Messages.showYesNoDialog( "Are you sure you want to delete all conversations?", "Clear History", - DefaultIcon); + Default); if (answer == Messages.YES) { var project = event.getProject(); if (project != null) { diff --git a/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestProvider.java b/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestProvider.java index 47a34bbb..da08ec2b 100644 --- a/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestProvider.java +++ b/src/main/java/ee/carlrobert/codegpt/completions/CompletionRequestProvider.java @@ -100,8 +100,10 @@ public class CompletionRequestProvider { COMPLETION_SYSTEM_PROMPT, message.getPrompt(), conversation.getMessages()); + var configuration = ConfigurationState.getInstance(); return new LlamaCompletionRequest.Builder(prompt) - .setN_predict(512) + .setN_predict(configuration.getMaxTokens()) + .setTemperature(configuration.getTemperature()) .build(); } diff --git a/src/main/java/ee/carlrobert/codegpt/completions/MethodNameLookupListener.java b/src/main/java/ee/carlrobert/codegpt/completions/MethodNameLookupListener.java index 89b82a7b..fdb6fb58 100644 --- a/src/main/java/ee/carlrobert/codegpt/completions/MethodNameLookupListener.java +++ b/src/main/java/ee/carlrobert/codegpt/completions/MethodNameLookupListener.java @@ -56,7 +56,7 @@ public class MethodNameLookupListener implements LookupManagerListener { for (var value : response.split(",")) { application.runReadAction(() -> { lookup.addItem( - LookupElementBuilder.create(value.trim()).withIcon(Icons.SparkleIcon), + LookupElementBuilder.create(value.trim()).withIcon(Icons.Sparkle), PrefixMatcher.ALWAYS_TRUE); }); application.invokeLater(() -> lookup.refreshUi(true, true)); diff --git a/src/main/java/ee/carlrobert/codegpt/settings/configuration/ConfigurationState.java b/src/main/java/ee/carlrobert/codegpt/settings/configuration/ConfigurationState.java index 7a36675b..02cfb369 100644 --- a/src/main/java/ee/carlrobert/codegpt/settings/configuration/ConfigurationState.java +++ b/src/main/java/ee/carlrobert/codegpt/settings/configuration/ConfigurationState.java @@ -19,7 +19,7 @@ public class ConfigurationState implements PersistentStateComponent\n" + "\n" + "

Use CodeGPT coupon for free month of GPT-4.

\n" @@ -81,10 +79,7 @@ public class ChatToolWindowScrollablePanel extends ScrollablePanel { + " Sign up here\n" + "

\n" + "\n" - + "" - ); - textPane.setBackground(getPanelBackgroundColor()); - textPane.setFocusable(false); - return textPane; + + "", + false); } } diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ResponseNodeRenderer.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ResponseNodeRenderer.java index 21930696..45c2b071 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ResponseNodeRenderer.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ResponseNodeRenderer.java @@ -4,6 +4,8 @@ import com.intellij.ui.ColorUtil; import com.intellij.ui.JBColor; import com.vladsch.flexmark.ast.BulletListItem; import com.vladsch.flexmark.ast.Code; +import com.vladsch.flexmark.ast.CodeBlock; +import com.vladsch.flexmark.ast.Heading; import com.vladsch.flexmark.ast.OrderedListItem; import com.vladsch.flexmark.ast.Paragraph; import com.vladsch.flexmark.html.HtmlWriter; @@ -21,12 +23,23 @@ public class ResponseNodeRenderer implements NodeRenderer { public Set> getNodeRenderingHandlers() { return Set.of( new NodeRenderingHandler<>(Paragraph.class, this::renderParagraph), - new NodeRenderingHandler<>(Code.class, this::renderCode) + new NodeRenderingHandler<>(Code.class, this::renderCode), + new NodeRenderingHandler<>(CodeBlock.class, this::renderCodeBlock), + new NodeRenderingHandler<>(BulletListItem.class, this::renderBulletListItem), + new NodeRenderingHandler<>(Heading.class, this::renderHeading), + new NodeRenderingHandler<>(OrderedListItem.class, this::renderOrderedListItem) ); } - private void renderCode(Code node, NodeRendererContext context, HtmlWriter html) { - html.attr("style", "color: " + ColorUtil.toHex(new JBColor(0x00627A, 0xCC7832))); + private void renderCodeBlock(CodeBlock node, NodeRendererContext context, HtmlWriter html) { + html.attr("style", "white-space: pre-wrap;"); + context.delegateRender(); + } + + private void renderHeading(Heading node, NodeRendererContext context, HtmlWriter html) { + if (node.getLevel() == 3) { + html.attr("style", "margin-top: 4px; margin-bottom: 4px;"); + } context.delegateRender(); } @@ -39,6 +52,27 @@ public class ResponseNodeRenderer implements NodeRenderer { context.delegateRender(); } + private void renderCode(Code node, NodeRendererContext context, HtmlWriter html) { + html.attr("style", "color: " + ColorUtil.toHex(new JBColor(0x00627A, 0xCC7832))); + context.delegateRender(); + } + + private void renderBulletListItem( + BulletListItem node, + NodeRendererContext context, + HtmlWriter html) { + html.attr("style", "margin-bottom: 4px;"); + context.delegateRender(); + } + + private void renderOrderedListItem( + OrderedListItem node, + NodeRendererContext context, + HtmlWriter html) { + html.attr("style", "margin-bottom: 4px;"); + context.delegateRender(); + } + public static class Factory implements NodeRendererFactory { @NotNull 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 ff252d2c..8af06774 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 @@ -1,6 +1,5 @@ package ee.carlrobert.codegpt.toolwindow.chat.components; -import static ee.carlrobert.codegpt.util.UIUtil.getPanelBackgroundColor; import static java.lang.String.format; import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED; @@ -13,6 +12,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.JBColor; import com.intellij.util.ui.JBUI; import com.vladsch.flexmark.ast.FencedCodeBlock; import com.vladsch.flexmark.html.HtmlRenderer; @@ -36,7 +36,6 @@ import java.util.stream.Collectors; import javax.swing.BoxLayout; import javax.swing.JPanel; import javax.swing.JTextPane; -import javax.swing.UIManager; public class ChatMessageResponseBody extends JPanel { @@ -44,33 +43,23 @@ public class ChatMessageResponseBody extends JPanel { private final Disposable parentDisposable; private final StreamParser streamParser; private final boolean readOnly; - private JPanel currentlyProcessedElement; private ResponseEditorPanel currentlyProcessedEditor; private JTextPane currentlyProcessedTextPane; private boolean responseReceived; public ChatMessageResponseBody(Project project, Disposable parentDisposable) { - this(project, getPanelBackgroundColor(), false, parentDisposable); + this(project, false, parentDisposable); } public ChatMessageResponseBody( Project project, boolean withGhostText, Disposable parentDisposable) { - this(project, getPanelBackgroundColor(), withGhostText, parentDisposable); + this(project, withGhostText, false, parentDisposable); } public ChatMessageResponseBody( Project project, - Color backgroundColor, - boolean withGhostText, - Disposable parentDisposable) { - this(project, backgroundColor, withGhostText, false, parentDisposable); - } - - public ChatMessageResponseBody( - Project project, - Color backgroundColor, boolean withGhostText, boolean readOnly, Disposable parentDisposable) { @@ -80,15 +69,13 @@ public class ChatMessageResponseBody extends JPanel { this.streamParser = new StreamParser(); this.readOnly = readOnly; setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); - setBackground(backgroundColor); + setOpaque(false); if (withGhostText) { prepareProcessingTextResponse(!readOnly); currentlyProcessedTextPane.setText( "

"); } - - UIManager.addPropertyChangeListener(propertyChangeEvent -> setBackground(backgroundColor)); } public ChatMessageResponseBody withResponse(String response) { @@ -146,7 +133,7 @@ public class ChatMessageResponseBody extends JPanel { "

%s

", message); if (responseReceived) { - add(new ResponseWrapper().add(createTextPane(errorText, false))); + add(createTextPane(errorText, false)); } else { currentlyProcessedTextPane.setText(errorText); } @@ -159,24 +146,12 @@ public class ChatMessageResponseBody extends JPanel { public void displaySerpResults(List serpResults) { var html = getSearchResultsHtml(serpResults); if (responseReceived) { - add(new ResponseWrapper().add(createTextPane(html, false))); + add(createTextPane(html, false)); } else { currentlyProcessedTextPane.setText(html); } } - private String getSearchResultsHtml(List serpResults) { - var titles = serpResults.stream() - .map(result -> format("
  • %s
  • ", - result.getUrl(), result.getName())) - .collect(Collectors.joining()); - return format( - "" - + "

    Search results:

    " - + "
      %s
    " - + "", titles); - } - public void clear() { removeAll(); @@ -190,6 +165,18 @@ public class ChatMessageResponseBody extends JPanel { revalidate(); } + private String getSearchResultsHtml(List serpResults) { + var titles = serpResults.stream() + .map(result -> format("
  • %s
  • ", + result.getUrl(), result.getName())) + .collect(Collectors.joining()); + return format( + "" + + "

    Search results:

    " + + "
      %s
    " + + "", titles); + } + private void processResponse(String markdownInput, boolean codeResponse, boolean caretVisible) { responseReceived = true; @@ -225,24 +212,17 @@ public class ChatMessageResponseBody extends JPanel { private void prepareProcessingTextResponse(boolean caretVisible) { currentlyProcessedEditor = null; currentlyProcessedTextPane = createTextPane("", caretVisible); - currentlyProcessedElement = new ResponseWrapper(); - currentlyProcessedElement.add(currentlyProcessedTextPane); - add(currentlyProcessedElement); + add(currentlyProcessedTextPane); } private void prepareProcessingCodeResponse(String code, String markdownLanguage) { - currentlyProcessedTextPane.getCaret().setVisible(false); + if (currentlyProcessedTextPane != null) { + currentlyProcessedTextPane.getCaret().setVisible(false); + } currentlyProcessedTextPane = null; - currentlyProcessedEditor = new ResponseEditorPanel( - project, - code, - markdownLanguage, - readOnly, - getPanelBackgroundColor(), - parentDisposable); - currentlyProcessedElement = new ResponseWrapper(); - currentlyProcessedElement.add(currentlyProcessedEditor); - add(currentlyProcessedElement); + currentlyProcessedEditor = + new ResponseEditorPanel(project, code, markdownLanguage, readOnly, parentDisposable); + add(currentlyProcessedEditor); } private void updateEditorDocument(String code) { @@ -250,8 +230,11 @@ public class ChatMessageResponseBody extends JPanel { var document = editor.getDocument(); var application = ApplicationManager.getApplication(); Runnable updateDocumentRunnable = () -> application.runWriteAction(() -> - WriteCommandAction.runWriteCommandAction(project, () -> - document.replaceString(0, document.getTextLength(), code))); + WriteCommandAction.runWriteCommandAction(project, () -> { + document.replaceString(0, document.getTextLength(), code); + editor.getComponent().repaint(); + editor.getComponent().revalidate(); + })); if (application.isUnitTestMode()) { application.invokeAndWait(updateDocumentRunnable); @@ -261,7 +244,7 @@ public class ChatMessageResponseBody extends JPanel { } private JTextPane createTextPane(String text, boolean caretVisible) { - var textPane = UIUtil.createTextPane(text, event -> { + var textPane = UIUtil.createTextPane(text, false, event -> { if (FileUtil.exists(event.getDescription()) && ACTIVATED.equals(event.getEventType())) { VirtualFile file = LocalFileSystem.getInstance().findFileByPath(event.getDescription()); FileEditorManager.getInstance(project).openFile(Objects.requireNonNull(file), true); @@ -275,7 +258,6 @@ public class ChatMessageResponseBody extends JPanel { textPane.setCaretPosition(textPane.getDocument().getLength()); } textPane.setBorder(JBUI.Borders.empty()); - textPane.setBackground(getBackground()); return textPane; } @@ -287,15 +269,4 @@ public class ChatMessageResponseBody extends JPanel { .build() .render(document); } - - private static class ResponseWrapper extends JPanel { - - ResponseWrapper() { - super(new BorderLayout()); - setBorder(JBUI.Borders.empty()); - setBackground(getBackground()); - - UIManager.addPropertyChangeListener(propertyChangeEvent -> setBackground(getBackground())); - } - } } diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ResponsePanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ResponsePanel.java index 853b0cdc..897acd16 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ResponsePanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/ResponsePanel.java @@ -1,11 +1,8 @@ package ee.carlrobert.codegpt.toolwindow.chat.components; -import static ee.carlrobert.codegpt.util.UIUtil.getPanelBackgroundColor; - import com.intellij.icons.AllIcons.Actions; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.ui.JBColor; import com.intellij.ui.components.JBLabel; import com.intellij.util.ui.JBFont; import com.intellij.util.ui.JBUI; @@ -66,7 +63,6 @@ public class ResponsePanel extends JPanel { Header() { super(new BorderLayout()); - setBackground(getPanelBackgroundColor()); setBorder(JBUI.Borders.empty(12, 8, 4, 8)); add(getIconLabel(), BorderLayout.LINE_START); @@ -118,7 +114,7 @@ public class ResponsePanel extends JPanel { private JBLabel getIconLabel() { return new JBLabel( CodeGPTBundle.get("project.label"), - Icons.DefaultIcon, + Icons.Default, SwingConstants.LEADING) .setAllowAutoWrapping(true) .withFont(JBFont.label().asBold()); @@ -131,10 +127,7 @@ public class ResponsePanel extends JPanel { Body() { super(new BorderLayout()); - setBackground(getPanelBackgroundColor()); - setBorder(JBUI.Borders.compound( - JBUI.Borders.customLine(JBColor.border(), 0, 0, 1, 0), - JBUI.Borders.empty(4, 8, 8, 8))); + setBorder(JBUI.Borders.empty(4, 8, 8, 8)); } public void addContent(JComponent content) { diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/TotalTokensPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/TotalTokensPanel.java index 5616502e..80808ad5 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/TotalTokensPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/TotalTokensPanel.java @@ -10,6 +10,7 @@ import com.intellij.openapi.editor.event.EditorFactoryListener; import com.intellij.openapi.editor.event.SelectionEvent; import com.intellij.openapi.editor.event.SelectionListener; import com.intellij.ui.components.JBLabel; +import com.intellij.util.ui.JBUI; import ee.carlrobert.codegpt.EncodingManager; import ee.carlrobert.codegpt.conversations.Conversation; import ee.carlrobert.codegpt.toolwindow.chat.TokenDetails; @@ -35,6 +36,7 @@ public class TotalTokensPanel extends JPanel { @Nullable String highlightedText, Disposable parentDisposable) { super(new FlowLayout(FlowLayout.LEADING, 0, 0)); + setBorder(JBUI.Borders.empty(4)); this.encodingManager = EncodingManager.getInstance(); this.tokenDetails = createTokenDetails(conversation, highlightedText); this.label = getLabel(tokenDetails); 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 a08687b4..964ea7ae 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 @@ -2,38 +2,45 @@ package ee.carlrobert.codegpt.toolwindow.chat.components; 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 com.intellij.util.ui.UIUtil; +import ee.carlrobert.codegpt.Icons; import ee.carlrobert.codegpt.conversations.message.Message; import ee.carlrobert.codegpt.settings.state.SettingsState; import java.awt.BorderLayout; import javax.swing.JPanel; +import javax.swing.SwingConstants; public class UserMessagePanel extends JPanel { 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.customLine(JBColor.border(), 1, 0, 1, 0), JBUI.Borders.empty(12, 8, 8, 8))); - add(createDisplayNameWrapper(), BorderLayout.NORTH); - add(new ChatMessageResponseBody( - project, - UIUtil.getPanelBackground(), - false, - true, - parentDisposable).withResponse(message.getPrompt()), - BorderLayout.SOUTH); + setBackground(ColorUtil.brighter(getBackground(), 2)); + add(createDisplayNameLabel(), BorderLayout.NORTH); + add(createResponseBody(project, message, parentDisposable), BorderLayout.SOUTH); } - private JPanel createDisplayNameWrapper() { - return JBUI.Panels.simplePanel() - .withBorder(JBUI.Borders.emptyBottom(6)) - .addToLeft(new JBLabel(SettingsState.getInstance().getDisplayName()) - .setAllowAutoWrapping(true) - .withFont(JBFont.label().asBold())); + private ChatMessageResponseBody createResponseBody( + Project project, + Message message, + Disposable parentDisposable) { + return new ChatMessageResponseBody(project, false, true, parentDisposable) + .withResponse(message.getPrompt()); + } + + private JBLabel createDisplayNameLabel() { + return new JBLabel( + SettingsState.getInstance().getDisplayName(), + Icons.User, + SwingConstants.LEADING) + .setAllowAutoWrapping(true) + .withFont(JBFont.label().asBold()) + .withBorder(JBUI.Borders.emptyBottom(6)); } } diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserPromptTextArea.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserPromptTextArea.java index d3d442fe..62f014ee 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserPromptTextArea.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserPromptTextArea.java @@ -50,6 +50,7 @@ public class UserPromptTextArea extends JPanel { private boolean submitEnabled = true; public UserPromptTextArea(Consumer onSubmit, TotalTokensPanel totalTokensPanel) { + super(new BorderLayout()); this.onSubmit = onSubmit; textArea = new JBTextArea(); @@ -160,7 +161,6 @@ public class UserPromptTextArea extends JPanel { private void init() { setOpaque(false); - setLayout(new BorderLayout()); add(textArea, BorderLayout.CENTER); stopButton = createIconButton(AllIcons.Actions.Suspend, null); @@ -168,9 +168,9 @@ public class UserPromptTextArea extends JPanel { var flowLayout = new FlowLayout(FlowLayout.RIGHT); flowLayout.setHgap(8); iconsPanel = new JPanel(flowLayout); - iconsPanel.add(createIconButton(Icons.SendIcon, this::handleSubmit)); + iconsPanel.add(createIconButton(Icons.Send, this::handleSubmit)); iconsPanel.add(stopButton); - add(JBUI.Panels.simplePanel().addToBottom(iconsPanel), BorderLayout.EAST); + add(iconsPanel, BorderLayout.EAST); } private void updateFont() { diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserPromptTextAreaHeader.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserPromptTextAreaHeader.java index 2f713d6b..0b6aef46 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserPromptTextAreaHeader.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/components/UserPromptTextAreaHeader.java @@ -1,26 +1,27 @@ package ee.carlrobert.codegpt.toolwindow.chat.components; -import static ee.carlrobert.codegpt.util.UIUtil.getPanelBackgroundColor; - +import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.application.ApplicationManager; import com.intellij.ui.components.JBCheckBox; import com.intellij.util.messages.MessageBusConnection; import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.JBUI.Borders; import ee.carlrobert.codegpt.completions.you.YouSubscriptionNotifier; import ee.carlrobert.codegpt.completions.you.auth.SignedOutNotifier; -import ee.carlrobert.codegpt.settings.state.SettingsState; -import ee.carlrobert.codegpt.toolwindow.ModelIconLabel; +import ee.carlrobert.codegpt.settings.service.ServiceType; +import ee.carlrobert.codegpt.toolwindow.chat.standard.ModelComboBoxAction; import java.awt.BorderLayout; import javax.swing.JPanel; public class UserPromptTextAreaHeader extends JPanel { - public UserPromptTextAreaHeader(SettingsState settings, TotalTokensPanel totalTokensPanel) { + public UserPromptTextAreaHeader( + ServiceType selectedService, + TotalTokensPanel totalTokensPanel, + Runnable onAddNewTab) { super(new BorderLayout()); - setBackground(getPanelBackgroundColor()); - setBorder(JBUI.Borders.emptyBottom(8)); - switch (settings.getSelectedService()) { + setOpaque(false); + setBorder(JBUI.Borders.emptyBottom(4)); + switch (selectedService) { case OPENAI: case AZURE: add(totalTokensPanel, BorderLayout.LINE_START); @@ -32,12 +33,8 @@ public class UserPromptTextAreaHeader extends JPanel { break; default: } - add(JBUI.Panels - .simplePanel(new ModelIconLabel( - settings.getSelectedService().getCompletionCode(), - settings.getModel())) - .withBorder(Borders.emptyRight(4)) - .withBackground(getPanelBackgroundColor()), BorderLayout.LINE_END); + add(new ModelComboBoxAction(onAddNewTab, selectedService) + .createCustomComponent(ActionPlaces.UNKNOWN), BorderLayout.LINE_END); } private void subscribeToYouTopics(JBCheckBox gpt4CheckBox) { diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/contextual/ContextualChatToolWindowLandingPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/contextual/ContextualChatToolWindowLandingPanel.java index 6f201832..b2bdaded 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/contextual/ContextualChatToolWindowLandingPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/contextual/ContextualChatToolWindowLandingPanel.java @@ -1,7 +1,6 @@ package ee.carlrobert.codegpt.toolwindow.chat.contextual; import static com.intellij.openapi.ui.DialogWrapper.OK_EXIT_CODE; -import static ee.carlrobert.codegpt.util.UIUtil.getPanelBackgroundColor; import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED; import com.intellij.openapi.diagnostic.Logger; @@ -37,7 +36,7 @@ class ContextualChatToolWindowLandingPanel extends ResponsePanel { } private JTextPane createContent() { - var description = createTextPane(); + var description = UIUtil.createTextPane("", false, this::handleHyperlinkClicked); if (VectorStore.getInstance(CodeGPTPlugin.getPluginBasePath()).isIndexExists()) { description.setText("" + "

    " @@ -76,12 +75,6 @@ class ContextualChatToolWindowLandingPanel extends ResponsePanel { return description; } - private JTextPane createTextPane() { - var textPane = UIUtil.createTextPane("", this::handleHyperlinkClicked); - textPane.setBackground(getPanelBackgroundColor()); - return textPane; - } - private void handleHyperlinkClicked(HyperlinkEvent event) { if (ACTIVATED.equals(event.getEventType())) { if (event.getURL() == null) { diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.java index c4d2442a..1bfd4dc8 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.java @@ -7,18 +7,23 @@ import com.intellij.icons.AllIcons.General; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionGroup; import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.DefaultCompactActionGroup; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.impl.ContextMenuPopupHandler; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.JBMenuItem; +import com.intellij.openapi.ui.JBPopupMenu; import com.intellij.openapi.util.Disposer; -import com.intellij.ui.JBColor; +import com.intellij.ui.ColorUtil; +import com.intellij.ui.IdeBorderFactory; 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; @@ -30,34 +35,32 @@ import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.NewFileAction; import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.ReplaceSelectionAction; import ee.carlrobert.codegpt.util.EditorUtil; import java.awt.BorderLayout; -import java.awt.Color; import java.awt.FlowLayout; import javax.swing.Box; import javax.swing.JPanel; +import org.jetbrains.annotations.NotNull; public class ResponseEditorPanel extends JPanel implements Disposable { private final Editor editor; - private final String language; - private final String extension; public ResponseEditorPanel( Project project, String code, String markdownLanguage, boolean readOnly, - Color backgroundColor, Disposable disposableParent) { super(new BorderLayout()); + setBorder(JBUI.Borders.empty(8, 0)); + setOpaque(false); - var extensionMapping = findLanguageExtensionMapping(markdownLanguage); - language = extensionMapping.getKey(); - extension = extensionMapping.getValue(); - editor = EditorUtil.createEditor(project, extension, code); + editor = EditorUtil.createEditor( + project, + findLanguageExtensionMapping(markdownLanguage).getValue(), + code); - DefaultActionGroup group = new DefaultActionGroup(); + var group = new DefaultActionGroup(); group.add(new ReplaceCodeInMainEditorAction()); - String originalGroupId = ((EditorEx) editor).getContextMenuGroupId(); if (originalGroupId != null) { AnAction originalGroup = ActionManager.getInstance().getAction(originalGroupId); @@ -65,12 +68,12 @@ public class ResponseEditorPanel extends JPanel implements Disposable { group.addAll(((ActionGroup) originalGroup).getChildren(null)); } } - - configureEditor((EditorEx) editor, readOnly, new ContextMenuPopupHandler.Simple(group)); - - add(createHeaderComponent(readOnly), BorderLayout.NORTH); + configureEditor( + (EditorEx) editor, + readOnly, + new ContextMenuPopupHandler.Simple(group), + findLanguageExtensionMapping(markdownLanguage).getValue()); add(editor.getComponent(), BorderLayout.CENTER); - add(createFooterComponent(readOnly ? getBackground() : backgroundColor), BorderLayout.SOUTH); Disposer.register(disposableParent, this); } @@ -87,7 +90,8 @@ public class ResponseEditorPanel extends JPanel implements Disposable { private void configureEditor( EditorEx editorEx, boolean readOnly, - ContextMenuPopupHandler popupHandler) { + ContextMenuPopupHandler popupHandler, + String extension) { if (readOnly) { editorEx.setOneLineMode(true); editorEx.setHorizontalScrollbarVisible(false); @@ -97,23 +101,32 @@ public class ResponseEditorPanel extends JPanel implements Disposable { var settings = editorEx.getSettings(); settings.setAdditionalColumnsCount(0); - settings.setAdditionalLinesCount(1); + settings.setAdditionalLinesCount(0); settings.setAdditionalPageAtBottom(false); settings.setVirtualSpace(false); settings.setUseSoftWraps(false); settings.setLineMarkerAreaShown(false); settings.setGutterIconsShown(false); + settings.setLineNumbersShown(false); + + editorEx.getGutterComponentEx().setVisible(true); + editorEx.getGutterComponentEx().getParent().setVisible(false); + editorEx.setVerticalScrollbarVisible(false); + editorEx.getContentComponent().setBorder(JBUI.Borders.emptyLeft(4)); + editorEx.setBorder(IdeBorderFactory.createBorder(ColorUtil.fromHex("#48494b"))); + editorEx.setPermanentHeaderComponent(createHeaderComponent(editorEx, extension)); + editorEx.setHeaderComponent(null); } - private JPanel createHeaderComponent(boolean readOnly) { + private JPanel createHeaderComponent(EditorEx editorEx, String extension) { var headerComponent = new JPanel(new BorderLayout()); - headerComponent.setBorder(JBUI.Borders.compound( - JBUI.Borders.customLine(JBColor.border(), 1, 1, 1, 1), - JBUI.Borders.empty(4, 8))); - headerComponent.add(new JBLabel(language), BorderLayout.LINE_START); - if (!readOnly) { - headerComponent.add(createHeaderActions(), BorderLayout.LINE_END); - } + headerComponent.setBorder( + JBUI.Borders.compound( + JBUI.Borders.customLine(ColorUtil.fromHex("#48494b"), 1, 1, 0, 1), + JBUI.Borders.empty(4))); + headerComponent.add(createExpandLink(editorEx), BorderLayout.LINE_START); + headerComponent.add(createHeaderActions(extension, editorEx).getComponent(), + BorderLayout.LINE_END); return headerComponent; } @@ -125,8 +138,7 @@ public class ResponseEditorPanel extends JPanel implements Disposable { : CodeGPTBundle.get("toolwindow.chat.editor.action.collapse"); } - private JPanel createFooterComponent(Color backgroundColor) { - var editorEx = ((EditorEx) editor); + private ActionLink createExpandLink(EditorEx editorEx) { var linkText = getLinkText(editorEx.isOneLineMode()); var expandLink = new ActionLink( linkText, @@ -134,32 +146,44 @@ public class ResponseEditorPanel extends JPanel implements Disposable { var oneLineMode = editorEx.isOneLineMode(); var source = (ActionLink) event.getSource(); source.setText(getLinkText(!oneLineMode)); - source.setIcon(oneLineMode ? General.ArrowUp : General.ArrowDown, true); + source.setIcon(oneLineMode ? General.ArrowDown : General.ArrowRight); 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; + expandLink.setIcon(editorEx.isOneLineMode() ? General.ArrowRight : General.ArrowDown); + return expandLink; } - private JPanel createHeaderActions() { + private ActionToolbar createHeaderActions(String extension, EditorEx editorEx) { + var actionGroup = new DefaultCompactActionGroup("EDITOR_TOOLBAR_ACTION_GROUP", false); + actionGroup.add(new CopyAction(editor)); + actionGroup.add(new ReplaceSelectionAction(editor)); + actionGroup.addSeparator(); + var wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - wrapper.add(new IconActionButton(new DiffAction(editor))); - wrapper.add(Box.createHorizontalStrut(8)); - wrapper.add(new IconActionButton(new EditAction(editor))); - wrapper.add(Box.createHorizontalStrut(8)); - wrapper.add(new IconActionButton(new NewFileAction(editor, extension))); - wrapper.add(Box.createHorizontalStrut(8)); wrapper.add(new IconActionButton(new CopyAction(editor))); - wrapper.add(Box.createHorizontalStrut(8)); + wrapper.add(Box.createHorizontalStrut(4)); wrapper.add(new IconActionButton(new ReplaceSelectionAction(editor))); - return wrapper; + + var menu = new JBPopupMenu(); + menu.add(new JBMenuItem(new DiffAction(editorEx, menu.getLocation()))); + menu.add(new JBMenuItem(new EditAction(editorEx))); + menu.add(new JBMenuItem(new NewFileAction(editorEx, extension))); + + var toolbar = ActionManager.getInstance() + .createActionToolbar("NAVIGATION_BAR_TOOLBAR", actionGroup, true); + actionGroup.add(new AnAction("Editor Actions", "Editor Actions", General.GearPlain) { + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + menu.show(e.getInputEvent().getComponent(), 0, 0); + } + }); + toolbar.setLayoutPolicy(ActionToolbar.NOWRAP_LAYOUT_POLICY); + toolbar.setTargetComponent(editorEx.getComponent()); + toolbar.getComponent().setBorder(JBUI.Borders.empty()); + return toolbar; } } diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/DiffAction.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/DiffAction.java index f081cefb..502eaeb7 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/DiffAction.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/DiffAction.java @@ -10,35 +10,35 @@ import com.intellij.diff.util.DiffUserDataKeys; import com.intellij.diff.util.DiffUtil; import com.intellij.diff.util.Side; import com.intellij.icons.AllIcons.Actions; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.util.Pair; import ee.carlrobert.codegpt.CodeGPTBundle; -import ee.carlrobert.codegpt.actions.ActionType; -import ee.carlrobert.codegpt.actions.TrackableAction; import ee.carlrobert.codegpt.util.EditorUtil; import ee.carlrobert.codegpt.util.OverlayUtil; import ee.carlrobert.codegpt.util.file.FileUtil; +import java.awt.Point; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; import org.jetbrains.annotations.NotNull; -public class DiffAction extends TrackableAction { +public class DiffAction extends AbstractAction { - public DiffAction(@NotNull Editor editor) { - super( - editor, - CodeGPTBundle.get("toolwindow.chat.editor.action.diff.title"), - CodeGPTBundle.get("toolwindow.chat.editor.action.diff.description"), - Actions.DiffWithClipboard, - ActionType.DIFF_CODE); + private final EditorEx editor; + private final Point locationOnScreen; + + public DiffAction(@NotNull EditorEx editor, @NotNull Point locationOnScreen) { + super("Diff", Actions.DiffWithClipboard); + this.editor = editor; + this.locationOnScreen = locationOnScreen; } @Override - public void handleAction(@NotNull AnActionEvent event) { - var project = requireNonNull(event.getProject()); + public void actionPerformed(ActionEvent event) { + var project = requireNonNull(editor.getProject()); var selectedTextEditor = FileEditorManager.getInstance(project).getSelectedTextEditor(); if (!EditorUtil.hasSelection(selectedTextEditor)) { - OverlayUtil.showSelectedEditorSelectionWarning(event); + OverlayUtil.showSelectedEditorSelectionWarning(project, locationOnScreen); return; } diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/EditAction.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/EditAction.java index ee2ac3e0..b6ce3cac 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/EditAction.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/EditAction.java @@ -1,44 +1,38 @@ package ee.carlrobert.codegpt.toolwindow.chat.editor.actions; import com.intellij.icons.AllIcons.Actions; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.editor.Editor; +import com.intellij.icons.AllIcons.Diff; import com.intellij.openapi.editor.ex.EditorEx; +import com.intellij.openapi.ui.JBMenuItem; import ee.carlrobert.codegpt.CodeGPTBundle; -import ee.carlrobert.codegpt.actions.ActionType; -import ee.carlrobert.codegpt.actions.TrackableAction; -import java.awt.event.MouseEvent; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; import org.jetbrains.annotations.NotNull; -public class EditAction extends TrackableAction { +public class EditAction extends AbstractAction { - public EditAction(@NotNull Editor editor) { - super( - editor, - CodeGPTBundle.get("toolwindow.chat.editor.action.edit.title"), - CodeGPTBundle.get("toolwindow.chat.editor.action.edit.description"), - Actions.EditSource, - ActionType.EDIT_CODE); + private final EditorEx editor; + + public EditAction(@NotNull EditorEx editor) { + super("Edit Source", Actions.EditSource); + this.editor = editor; } @Override - public void handleAction(@NotNull AnActionEvent event) { - var editorEx = ((EditorEx) editor); - editorEx.setViewer(!editorEx.isViewer()); + public void actionPerformed(@NotNull ActionEvent event) { + editor.setViewer(!editor.isViewer()); - var viewer = editorEx.isViewer(); - editorEx.setCaretVisible(!viewer); - editorEx.setCaretEnabled(!viewer); + var viewer = editor.isViewer(); + editor.setCaretVisible(!viewer); + editor.setCaretEnabled(!viewer); - var settings = editorEx.getSettings(); + var settings = editor.getSettings(); settings.setCaretRowShown(!viewer); - event.getPresentation().setIcon(viewer ? Actions.EditSource : Actions.Show); - event.getPresentation().setText(viewer + var menuItem = (JBMenuItem) event.getSource(); + menuItem.setText(viewer ? CodeGPTBundle.get("toolwindow.chat.editor.action.edit.title") : CodeGPTBundle.get("toolwindow.chat.editor.action.disableEditing.title")); - - var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen(); - locationOnScreen.y = locationOnScreen.y - 16; + menuItem.setIcon(viewer ? Actions.EditSource : Diff.Lock); } } diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/NewFileAction.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/NewFileAction.java index f2eeefbe..4ef16c78 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/NewFileAction.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/NewFileAction.java @@ -1,11 +1,11 @@ package ee.carlrobert.codegpt.toolwindow.chat.editor.actions; import static com.intellij.openapi.ui.DialogWrapper.OK_EXIT_CODE; +import static java.util.Objects.requireNonNull; import com.intellij.icons.AllIcons.Actions; import com.intellij.ide.util.EditorHelper; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogBuilder; @@ -15,30 +15,25 @@ import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.psi.PsiManager; import com.intellij.ui.components.JBTextField; import com.intellij.util.ui.FormBuilder; -import ee.carlrobert.codegpt.CodeGPTBundle; -import ee.carlrobert.codegpt.actions.ActionType; -import ee.carlrobert.codegpt.actions.TrackableAction; import ee.carlrobert.codegpt.util.file.FileUtil; -import java.util.Objects; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; import org.jetbrains.annotations.NotNull; -public class NewFileAction extends TrackableAction { +public class NewFileAction extends AbstractAction { private final String fileExtension; + private final EditorEx editor; - public NewFileAction(@NotNull Editor editor, String fileExtension) { - super( - editor, - CodeGPTBundle.get("toolwindow.chat.editor.action.newFile.title"), - CodeGPTBundle.get("toolwindow.chat.editor.action.newFile.description"), - Actions.AddFile, - ActionType.CREATE_NEW_FILE); + public NewFileAction(@NotNull EditorEx editor, String fileExtension) { + super("Create New File", Actions.AddFile); this.fileExtension = fileExtension; + this.editor = editor; } @Override - public void handleAction(@NotNull AnActionEvent e) { - var project = Objects.requireNonNull(e.getProject()); + public void actionPerformed(@NotNull ActionEvent event) { + var project = requireNonNull(editor.getProject()); var fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); fileChooserDescriptor.setForcedToUseIdeaFileChooser(true); var textFieldWithBrowseButton = new TextFieldWithBrowseButton(); diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/EditorAction.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/EditorAction.java index 597de764..5baf396d 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/EditorAction.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/EditorAction.java @@ -3,24 +3,24 @@ package ee.carlrobert.codegpt.toolwindow.chat.standard; enum EditorAction { FIND_BUGS( "Find Bugs", - "Find bugs in the following code", - "Find bugs and output code with bugs fixed in the following code: {{selectedCode}}"), + "Find bugs in the selected code", + "Find bugs and output code with bugs fixed in the selected code: {{selectedCode}}"), WRITE_TESTS( "Write Tests", - "Write Tests for the following code", - "Write Tests for the following code: {{selectedCode}}"), + "Write unit tests for the selected code", + "Write unit tests for the selected code: {{selectedCode}}"), EXPLAIN( "Explain", - "Explain the following code", - "Explain the following code: {{selectedCode}}"), + "Explain the selected code", + "Explain the selected code: {{selectedCode}}"), REFACTOR( "Refactor", - "Refactor the following code", - "Refactor the following code: {{selectedCode}}"), + "Refactor the selected code", + "Refactor the selected code: {{selectedCode}}"), OPTIMIZE( "Optimize", - "Optimize the following code", - "Optimize the following code: {{selectedCode}}"); + "Optimize the selected code", + "Optimize the selected code: {{selectedCode}}"); private final String label; private final String userMessage; diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/ModelComboBoxAction.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/ModelComboBoxAction.java new file mode 100644 index 00000000..51a2ddc9 --- /dev/null +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/ModelComboBoxAction.java @@ -0,0 +1,158 @@ +package ee.carlrobert.codegpt.toolwindow.chat.standard; + +import static java.lang.String.format; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DefaultActionGroup; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.actionSystem.ex.ComboBoxAction; +import ee.carlrobert.codegpt.Icons; +import ee.carlrobert.codegpt.completions.llama.LlamaModel; +import ee.carlrobert.codegpt.settings.service.ServiceType; +import ee.carlrobert.codegpt.settings.state.LlamaSettingsState; +import ee.carlrobert.codegpt.settings.state.OpenAISettingsState; +import ee.carlrobert.codegpt.settings.state.SettingsState; +import ee.carlrobert.llm.client.openai.completion.chat.OpenAIChatCompletionModel; +import java.util.List; +import javax.swing.Icon; +import javax.swing.JComponent; +import org.jetbrains.annotations.NotNull; + +public class ModelComboBoxAction extends ComboBoxAction { + + private final Runnable onAddNewTab; + private final SettingsState settings; + private final OpenAISettingsState openAISettings; + + public ModelComboBoxAction(Runnable onAddNewTab, ServiceType selectedService) { + this.onAddNewTab = onAddNewTab; + settings = SettingsState.getInstance(); + openAISettings = OpenAISettingsState.getInstance(); + updateTemplatePresentation(selectedService); + } + + public JComponent createCustomComponent(@NotNull String place) { + return createCustomComponent(getTemplatePresentation(), place); + } + + @NotNull + @Override + public JComponent createCustomComponent( + @NotNull Presentation presentation, + @NotNull String place) { + ComboBoxButton button = createComboBoxButton(presentation); + button.setBorder(null); + return button; + } + + @Override + protected @NotNull DefaultActionGroup createPopupActionGroup(JComponent button) { + var presentation = ((ComboBoxButton) button).getPresentation(); + var actionGroup = new DefaultActionGroup(); + actionGroup.addSeparator("OpenAI"); + List.of( + OpenAIChatCompletionModel.GPT_4_1106_128k, + OpenAIChatCompletionModel.GPT_3_5_1106_16k, + OpenAIChatCompletionModel.GPT_4_32k, + OpenAIChatCompletionModel.GPT_4, + OpenAIChatCompletionModel.GPT_3_5) + .forEach( + model -> actionGroup.add(createOpenAIModelAction(model, presentation))); + actionGroup.addSeparator(); + actionGroup.add( + createModelAction(ServiceType.AZURE, "Azure OpenAI", Icons.Azure, presentation)); + actionGroup.addSeparator(); + actionGroup.add(createModelAction( + ServiceType.LLAMA_CPP, + getSelectedHuggingFace(), + Icons.Llama, + presentation)); + actionGroup.addSeparator(); + actionGroup.add(createModelAction(ServiceType.YOU, "You.com", Icons.YouSmall, presentation)); + return actionGroup; + } + + @Override + protected boolean shouldShowDisabledActions() { + return true; + } + + private void updateTemplatePresentation(ServiceType selectedService) { + var templatePresentation = getTemplatePresentation(); + switch (selectedService) { + case OPENAI: + templatePresentation.setIcon(Icons.OpenAI); + templatePresentation.setText( + OpenAIChatCompletionModel.findByCode(openAISettings.getModel()).getDescription()); + break; + case AZURE: + templatePresentation.setIcon(Icons.Azure); + templatePresentation.setText("Azure OpenAI"); + break; + case YOU: + templatePresentation.setIcon(Icons.YouSmall); + templatePresentation.setText("You.com"); + break; + case LLAMA_CPP: + templatePresentation.setText(getSelectedHuggingFace()); + templatePresentation.setIcon(Icons.Llama); + break; + default: + } + } + + private String getSelectedHuggingFace() { + var huggingFaceModel = LlamaSettingsState.getInstance().getHuggingFaceModel(); + return format( + "%s %dB (Q%d)", + LlamaModel.findByHuggingFaceModel(huggingFaceModel).getLabel(), + huggingFaceModel.getParameterSize(), + huggingFaceModel.getQuantization()); + } + + private AnAction createModelAction( + ServiceType serviceType, + String label, + Icon icon, + Presentation comboBoxPresentation) { + return new AnAction(label, "", icon) { + @Override + public void update(@NotNull AnActionEvent event) { + var presentation = event.getPresentation(); + presentation.setEnabled(!presentation.getText().equals(comboBoxPresentation.getText())); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + settings.setSelectedService(serviceType); + comboBoxPresentation.setIcon(icon); + comboBoxPresentation.setText(label); + onAddNewTab.run(); + } + }; + } + + private AnAction createOpenAIModelAction( + OpenAIChatCompletionModel model, + Presentation comboBoxPresentation) { + createModelAction(ServiceType.OPENAI, model.getDescription(), Icons.OpenAI, + comboBoxPresentation); + return new AnAction(model.getDescription(), "", Icons.OpenAI) { + @Override + public void update(@NotNull AnActionEvent event) { + var presentation = event.getPresentation(); + presentation.setEnabled(!presentation.getText().equals(comboBoxPresentation.getText())); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + settings.setSelectedService(ServiceType.OPENAI); + openAISettings.setModel(model.getCode()); + comboBoxPresentation.setIcon(Icons.OpenAI); + comboBoxPresentation.setText(model.getDescription()); + onAddNewTab.run(); + } + }; + } +} \ No newline at end of file diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowLandingPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowLandingPanel.java index ff2f60b5..17ec256d 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowLandingPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowLandingPanel.java @@ -1,86 +1,70 @@ package ee.carlrobert.codegpt.toolwindow.chat.standard; -import static ee.carlrobert.codegpt.util.UIUtil.getPanelBackgroundColor; import static java.lang.String.format; -import static javax.swing.event.HyperlinkEvent.EventType.ACTIVATED; -import com.intellij.openapi.diagnostic.Logger; +import com.intellij.ui.components.ActionLink; +import com.intellij.util.ui.JBUI; +import ee.carlrobert.codegpt.Icons; import ee.carlrobert.codegpt.settings.state.SettingsState; import ee.carlrobert.codegpt.toolwindow.chat.components.ResponsePanel; import ee.carlrobert.codegpt.util.UIUtil; -import java.awt.event.MouseEvent; -import javax.swing.JTextPane; -import javax.swing.event.HyperlinkEvent; +import java.awt.BorderLayout; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JPanel; class StandardChatToolWindowLandingPanel extends ResponsePanel { - private static final Logger LOG = Logger.getInstance(StandardChatToolWindowLandingPanel.class); - - private final EditorActionEvent onAction; - StandardChatToolWindowLandingPanel(EditorActionEvent onAction) { - this.onAction = onAction; - addContent(createContent()); + addContent(createContent(onAction)); } - private JTextPane createContent() { - var textPane = UIUtil.createTextPane( + private ActionLink createEditorActionLink(EditorAction action, EditorActionEvent onAction) { + var link = new ActionLink(action.getUserMessage(), event -> { + onAction.handleAction(action, ((ActionLink) event.getSource()).getLocationOnScreen()); + }); + link.setIcon(Icons.Sparkle); + return link; + } + + private JPanel createContent(EditorActionEvent onAction) { + var panel = new JPanel(new BorderLayout()); + panel.add(UIUtil.createTextPane( "" + format( - "

    " - + "Welcome %s, I'm your intelligent code companion, here to be" - + " your partner-in-crime for getting things done in a flash. Together, we'll " - + "tackle tasks swiftly and efficiently, making your coding experience a joy." - + "

    ", - SettingsState.getInstance().getDisplayName()) + "

    " + + "Welcome %s, I'm your intelligent code companion, here to be" + + " your partner-in-crime for getting things done in a flash." + + "

    ", SettingsState.getInstance().getDisplayName()) + "

    " + "Feel free to ask me anything you'd like, but my true superpower lies in assisting " + "you with your code! Here are a few examples of how I can assist you:" + "

    " - - + "
      " - + "
    • " - + "Generate unit tests for the selected code" - + "
    • " - + "
    • " - + "Explain the selected code" - + "
    • " - + "
    • " - + "Find bugs in the selected code" - + "
    • " - + "", + false), BorderLayout.NORTH); + panel.add(createEditorActionsListPanel(onAction), BorderLayout.CENTER); + panel.add(UIUtil.createTextPane( + "" + "

      " + "Being an AI-powered assistant, I may occasionally have surprises or make mistakes. " + "Therefore, it's wise to double-check any code or suggestions I provide." + "

      " + "", - this::handleHyperlinkClicked); - textPane.setBackground(getPanelBackgroundColor()); - return textPane; + false), BorderLayout.SOUTH); + return panel; } - private void handleHyperlinkClicked(HyperlinkEvent event) { - if (ACTIVATED.equals(event.getEventType())) { - if (event.getURL() == null) { - var mouseLocation = ((MouseEvent) event.getInputEvent()).getLocationOnScreen(); - mouseLocation.y = mouseLocation.y - 10; - switch (event.getDescription()) { - case "GENERATE_UNIT_TESTS": - onAction.handleAction(EditorAction.WRITE_TESTS, mouseLocation); - break; - case "EXPLAIN_CODE": - onAction.handleAction(EditorAction.EXPLAIN, mouseLocation); - break; - case "FIND_BUGS": - onAction.handleAction(EditorAction.FIND_BUGS, mouseLocation); - break; - default: - LOG.error("Could not trigger action {}", event.getDescription()); - } - } else { - UIUtil.handleHyperlinkClicked(event); - } - } + private JPanel createEditorActionsListPanel(EditorActionEvent onAction) { + var listPanel = new JPanel(); + listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.PAGE_AXIS)); + listPanel.setBorder(JBUI.Borders.emptyLeft(4)); + listPanel.add(Box.createVerticalStrut(4)); + listPanel.add(createEditorActionLink(EditorAction.WRITE_TESTS, onAction)); + listPanel.add(Box.createVerticalStrut(4)); + listPanel.add(createEditorActionLink(EditorAction.EXPLAIN, onAction)); + listPanel.add(Box.createVerticalStrut(4)); + listPanel.add(createEditorActionLink(EditorAction.FIND_BUGS, onAction)); + listPanel.add(Box.createVerticalStrut(4)); + return listPanel; } } diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowPanel.java index 3b0eea95..64b94d63 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowPanel.java @@ -12,7 +12,8 @@ import ee.carlrobert.codegpt.actions.toolwindow.CreateNewConversationAction; import ee.carlrobert.codegpt.actions.toolwindow.OpenInEditorAction; import ee.carlrobert.codegpt.conversations.ConversationService; import ee.carlrobert.codegpt.conversations.ConversationsState; -import java.awt.FlowLayout; +import java.awt.BorderLayout; +import javax.swing.JPanel; import org.jetbrains.annotations.NotNull; public class StandardChatToolWindowPanel extends SimpleToolWindowPanel { @@ -29,11 +30,22 @@ public class StandardChatToolWindowPanel extends SimpleToolWindowPanel { if (conversation == null) { conversation = ConversationService.getInstance().startConversation(); } + var tabPanel = new StandardChatToolWindowTabPanel(project, conversation); var tabbedPane = createTabbedPane(tabPanel, parentDisposable); - var toolbarComponent = createActionToolbar(project, tabbedPane).getComponent(); - toolbarComponent.setLayout(new FlowLayout()); - setToolbar(toolbarComponent); + Runnable onAddNewTab = () -> { + tabbedPane.addNewTab(new StandardChatToolWindowTabPanel( + project, + ConversationService.getInstance().startConversation())); + repaint(); + revalidate(); + }; + var actionToolbarPanel = new JPanel(new BorderLayout()); + actionToolbarPanel.add( + createActionToolbar(project, tabbedPane, onAddNewTab).getComponent(), + BorderLayout.LINE_START); + + setToolbar(actionToolbarPanel); setContent(tabbedPane); Disposer.register(parentDisposable, tabPanel); @@ -41,15 +53,10 @@ public class StandardChatToolWindowPanel extends SimpleToolWindowPanel { private ActionToolbar createActionToolbar( Project project, - StandardChatToolWindowTabbedPane tabbedPane) { + StandardChatToolWindowTabbedPane tabbedPane, + Runnable onAddNewTab) { var actionGroup = new DefaultCompactActionGroup("TOOLBAR_ACTION_GROUP", false); - actionGroup.add(new CreateNewConversationAction(() -> { - tabbedPane.addNewTab(new StandardChatToolWindowTabPanel( - project, - ConversationService.getInstance().startConversation())); - repaint(); - revalidate(); - })); + actionGroup.add(new CreateNewConversationAction(onAddNewTab)); actionGroup.add( new ClearChatWindowAction(() -> tabbedPane.resetCurrentlyActiveTabPanel(project))); actionGroup.addSeparator(); @@ -61,7 +68,8 @@ public class StandardChatToolWindowPanel extends SimpleToolWindowPanel { return toolbar; } - private StandardChatToolWindowTabbedPane createTabbedPane(StandardChatToolWindowTabPanel tabPanel, + private StandardChatToolWindowTabbedPane createTabbedPane( + StandardChatToolWindowTabPanel tabPanel, Disposable parentDisposable) { var tabbedPane = new StandardChatToolWindowTabbedPane(parentDisposable); tabbedPane.addNewTab(tabPanel); 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 03c00cee..1c3633fc 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 @@ -56,8 +56,8 @@ public class StandardChatToolWindowTabPanel extends BaseChatToolWindowTabPanel { private void displayConversation(@NotNull Conversation conversation) { clearWindow(); conversation.getMessages().forEach(message -> { - var messageResponseBody = new ChatMessageResponseBody(project, this) - .withResponse(message.getResponse()); + var messageResponseBody = + new ChatMessageResponseBody(project, this).withResponse(message.getResponse()); var serpResults = message.getSerpResults(); if (YouSettingsState.getInstance().isDisplayWebSearchResults() diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabbedPane.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabbedPane.java index de5233ee..775d1612 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabbedPane.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/standard/StandardChatToolWindowTabbedPane.java @@ -134,11 +134,10 @@ public class StandardChatToolWindowTabbedPane extends JBTabbedPane { button.setToolTipText("Close Chat"); button.setRolloverIcon(AllIcons.Actions.CloseHovered); - var panel = JBUI.Panels.simplePanel(4, 0) + return JBUI.Panels.simplePanel(4, 0) .addToLeft(new JBLabel(title)) - .addToRight(button); - panel.setOpaque(false); - return panel; + .addToRight(button) + .andTransparent(); } class CloseActionListener implements ActionListener { diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/conversations/ConversationPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/conversations/ConversationPanel.java index 88752ae7..c3a01f2e 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/conversations/ConversationPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/conversations/ConversationPanel.java @@ -1,7 +1,5 @@ package ee.carlrobert.codegpt.toolwindow.conversations; -import static ee.carlrobert.codegpt.util.UIUtil.getPanelBackgroundColor; - import com.intellij.openapi.project.Project; import com.intellij.ui.JBColor; import com.intellij.ui.components.JBLabel; @@ -62,7 +60,6 @@ class ConversationPanel extends JPanel { var border = isSelected ? JBUI.Borders.customLine(JBUI.CurrentTheme.ActionButton.focusedBorder(), 2, 2, 2, 2) : JBUI.Borders.customLine(JBColor.border(), 1, 0, 1, 0); - setBackground(getPanelBackgroundColor()); setBorder(JBUI.Borders.compound(border, JBUI.Borders.empty(8))); setLayout(new GridBagLayout()); setCursor(new Cursor(Cursor.HAND_CURSOR)); @@ -85,7 +82,6 @@ class ConversationPanel extends JPanel { gbc.weightx = 1.0; gbc.gridx = 0; - headerPanel.setBackground(getPanelBackgroundColor()); headerPanel.add(new JBLabel(getFirstPrompt(conversation)) .withFont(JBFont.label().asBold()), gbc); @@ -94,7 +90,6 @@ class ConversationPanel extends JPanel { headerPanel.add(new IconActionButton(new DeleteConversationAction(onDelete)), gbc); var bottomPanel = new JPanel(new BorderLayout()); - bottomPanel.setBackground(getPanelBackgroundColor()); bottomPanel.add(new JLabel(conversation.getUpdatedOn() .format(DateTimeFormatter.ofPattern("M/d/yyyy, h:mm:ss a"))), BorderLayout.WEST); if (conversation.getModel() != null) { @@ -104,7 +99,6 @@ class ConversationPanel extends JPanel { } var textPanel = new JPanel(new BorderLayout()); - textPanel.setBackground(getPanelBackgroundColor()); textPanel.add(headerPanel, BorderLayout.NORTH); textPanel.add(bottomPanel, BorderLayout.SOUTH); return textPanel; diff --git a/src/main/java/ee/carlrobert/codegpt/util/OverlayUtil.java b/src/main/java/ee/carlrobert/codegpt/util/OverlayUtil.java index c54f9c09..550e8ddf 100644 --- a/src/main/java/ee/carlrobert/codegpt/util/OverlayUtil.java +++ b/src/main/java/ee/carlrobert/codegpt/util/OverlayUtil.java @@ -2,7 +2,7 @@ package ee.carlrobert.codegpt.util; import static com.intellij.openapi.ui.Messages.CANCEL; import static com.intellij.openapi.ui.Messages.OK; -import static ee.carlrobert.codegpt.Icons.DefaultIcon; +import static ee.carlrobert.codegpt.Icons.Default; import static java.lang.String.format; import static java.util.Objects.requireNonNull; @@ -68,7 +68,7 @@ public class OverlayUtil { return Messages.showYesNoDialog( CodeGPTBundle.get("dialog.deleteConversation.description"), CodeGPTBundle.get("dialog.deleteConversation.title"), - DefaultIcon); + Default); } public static int showTokenLimitExceededDialog() { @@ -77,7 +77,7 @@ public class OverlayUtil { CodeGPTBundle.get("dialog.tokenLimitExceeded.description")) .yesText(CodeGPTBundle.get("dialog.continue")) .noText(CodeGPTBundle.get("dialog.cancel")) - .icon(DefaultIcon) + .icon(Default) .doNotAsk(new DoNotAskOption.Adapter() { @Override public void rememberChoice(boolean isSelected, int exitCode) { @@ -106,7 +106,7 @@ public class OverlayUtil { format(CodeGPTBundle.get("dialog.tokenSoftLimitExceeded.description"), tokenCount)) .yesText(CodeGPTBundle.get("dialog.continue")) .noText(CodeGPTBundle.get("dialog.cancel")) - .icon(DefaultIcon) + .icon(Default) .doNotAsk(new DoNotAskOption.Adapter() { @Override public void rememberChoice(boolean isSelected, int exitCode) { @@ -132,9 +132,14 @@ public class OverlayUtil { public static void showSelectedEditorSelectionWarning(AnActionEvent event) { var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen(); locationOnScreen.y = locationOnScreen.y - 16; + showSelectedEditorSelectionWarning(requireNonNull(event.getProject()), locationOnScreen); + } + public static void showSelectedEditorSelectionWarning( + @NotNull Project project, + Point locationOnScreen) { showWarningBalloon( - EditorUtil.getSelectedEditor(requireNonNull(event.getProject())) == null + EditorUtil.getSelectedEditor(project) == null ? "Unable to locate a selected editor" : "Please select a target code before proceeding", locationOnScreen); diff --git a/src/main/java/ee/carlrobert/codegpt/util/UIUtil.java b/src/main/java/ee/carlrobert/codegpt/util/UIUtil.java index afaa33f7..be769289 100644 --- a/src/main/java/ee/carlrobert/codegpt/util/UIUtil.java +++ b/src/main/java/ee/carlrobert/codegpt/util/UIUtil.java @@ -31,16 +31,21 @@ import javax.swing.event.HyperlinkListener; public class UIUtil { public static JTextPane createTextPane(String text) { - return createTextPane(text, UIUtil::handleHyperlinkClicked); + return createTextPane(text, true); } - public static JTextPane createTextPane(String text, HyperlinkListener listener) { + public static JTextPane createTextPane(String text, boolean opaque) { + return createTextPane(text, opaque, UIUtil::handleHyperlinkClicked); + } + + public static JTextPane createTextPane(String text, boolean opaque, HyperlinkListener listener) { var textPane = new JTextPane(); textPane.putClientProperty(JTextPane.HONOR_DISPLAY_PROPERTIES, true); textPane.addHyperlinkListener(listener); textPane.setContentType("text/html"); textPane.setEditable(false); textPane.setText(text); + textPane.setOpaque(opaque); return textPane; } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 1e8d60c9..c41d17c4 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -32,7 +32,7 @@ - @@ -45,7 +45,7 @@ text="CodeGPT" class="com.intellij.openapi.actionSystem.DefaultActionGroup" popup="true" - icon="ee.carlrobert.codegpt.Icons.DefaultSmallIcon"> + icon="ee.carlrobert.codegpt.Icons.DefaultSmall"> diff --git a/src/main/resources/icons/user.svg b/src/main/resources/icons/user.svg new file mode 100644 index 00000000..800972a1 --- /dev/null +++ b/src/main/resources/icons/user.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/icons/user_dark.svg b/src/main/resources/icons/user_dark.svg new file mode 100644 index 00000000..63382036 --- /dev/null +++ b/src/main/resources/icons/user_dark.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/main/resources/icons/you_small.png b/src/main/resources/icons/you_small.png new file mode 100644 index 0000000000000000000000000000000000000000..08ed4f31a28e59db362283ccc01eb25f3999476b GIT binary patch literal 1194 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4uAB#T}@sR2?f>5jgR3=A9lx&I`x0{IHb9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg(UKbBnda-upao=eFt9QT zF)#yJj6lf1D8&FW4aj2fVw8rngBUfSYM2-p+A|qgplYIkGzfSAF-Q-DW?sOEFmVAB zTs7|kW&|6gE$Tz*S|G();1OBOz`!jG!i)^F=12fdi_8p(D2ed(u}aR*)k{ptPfFFR z$SnZrVz8;O0y1+`OA-|-a&z*EttxDlz$&bOY>=?Nk^)#sNw%$0gl~X?bAC~(f|;Iy zo`I4bmx6+VO;JjkRgjAtR6CGotCUevQedU8UtV6WS8lAAUzDzIXlZGwZ(yWvWTXpJ zp<7&;SCUwvn^&w1F$89gOKNd)QD#9&W`3Rm$lS!F{L&IzB_)tWZ~$>KW+6%?4_<0f}1q7iFdbZ3dZdXJ`Xfi6REI$3`DyIg(=_J_U;cy=up0 zqYn=@J1)t%hwKatOxm6Z6iz3*zY!=vf`I4LUX_2m_3nEr{Y>8MTw0OmW*=uI|mVJMB@Aw&uS&fGe zJpBL6zPPS1ZC`b?=gt@0+0IK_y-bRav#Ou}wWt2x;<&RCQsQPjts2u@r?~u@mz7s} z-KIulrU$d*FYZ~F4GiWiHdzt*chbThQO|oxyR}L#EXvxNqG8AWnQZQOds0sH3(kylCX|@jrOfTQ2cMjIsHxXvd#3 z+#mFOu3GLDq!72q>+L?Yg{|JYu|m7bcw0^|q~@ zS(G4KbZS$av5xKL68k7Qn^T6?%Q@z=)a%A*>|ag zTnS(P*xF;eVCL=C8%I9BVySE1u*LUAcR-