Skip to content

Commit 0fffa2e

Browse files
committed
fix: defer toolwindow tab creation and allow closing first/last tab (relates #865)
1 parent 00775d3 commit 0fffa2e

File tree

15 files changed

+426
-184
lines changed

15 files changed

+426
-184
lines changed

src/main/java/ee/carlrobert/codegpt/actions/toolwindow/RenameSessionAction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class RenameSessionAction {
99
private static final int MAX_NAME_LENGTH = 50;
1010

1111
public static void renameSession(ChatToolWindowTabbedPane tabbedPane, int tabIndex) {
12-
if (tabIndex <= 0) {
12+
if (tabIndex < 0) {
1313
return;
1414
}
1515

@@ -44,4 +44,4 @@ public boolean canClose(String inputString) {
4444
return checkInput(inputString);
4545
}
4646
}
47-
}
47+
}

src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowContentManager.java

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
import ee.carlrobert.codegpt.Icons;
1616
import ee.carlrobert.codegpt.completions.ConversationType;
1717
import ee.carlrobert.codegpt.conversations.Conversation;
18-
import ee.carlrobert.codegpt.conversations.ConversationService;
19-
import ee.carlrobert.codegpt.conversations.ConversationsState;
2018
import ee.carlrobert.codegpt.conversations.message.Message;
2119
import ee.carlrobert.codegpt.settings.prompts.PromptsSettings;
2220
import java.util.Arrays;
@@ -48,13 +46,12 @@ public void sendMessage(Message message, ConversationType conversationType) {
4846
.getState()
4947
.getChatActions()
5048
.getStartInNewWindow();
51-
if (startInNewWindow || ConversationsState.getCurrentConversation() == null) {
49+
if (startInNewWindow) {
5250
createNewTabPanel().sendMessage(message, conversationType);
5351
return;
5452
}
5553

56-
tryFindChatTabbedPane()
57-
.map(tabbedPane -> tabbedPane.tryFindActiveTabPanel().orElseGet(this::createNewTabPanel))
54+
tryFindActiveChatTabPanel()
5855
.orElseGet(this::createNewTabPanel)
5956
.sendMessage(message, conversationType);
6057
}
@@ -65,23 +62,18 @@ public Optional<ChatToolWindowTabPanel> tryFindActiveChatTabPanel() {
6562

6663
public void displayConversation(@NotNull Conversation conversation) {
6764
displayChatTab();
68-
tryFindChatTabbedPane()
69-
.ifPresent(tabbedPane -> tabbedPane.tryFindTabTitle(conversation.getId())
70-
.ifPresentOrElse(
71-
title -> tabbedPane.setSelectedIndex(tabbedPane.indexOfTab(title)),
72-
() -> tabbedPane.addNewTab(new ChatToolWindowTabPanel(project, conversation))));
65+
tryFindChatToolWindowPanel().ifPresent(chatPanel -> chatPanel.getChatTabbedPane()
66+
.tryFindTabTitle(conversation.getId())
67+
.ifPresentOrElse(
68+
title -> chatPanel.getChatTabbedPane()
69+
.setSelectedIndex(chatPanel.getChatTabbedPane().indexOfTab(title)),
70+
() -> chatPanel.createAndSelectConversationTab(conversation)));
7371
}
7472

7573
public ChatToolWindowTabPanel createNewTabPanel() {
7674
displayChatTab();
77-
return tryFindChatTabbedPane()
78-
.map(item -> {
79-
var panel = new ChatToolWindowTabPanel(
80-
project,
81-
ConversationService.getInstance().startConversation(project));
82-
item.addNewTab(panel);
83-
return panel;
84-
})
75+
return tryFindChatToolWindowPanel()
76+
.map(ChatToolWindowPanel::createAndSelectNewTabPanel)
8577
.orElseThrow();
8678
}
8779

@@ -113,12 +105,7 @@ public Optional<ChatToolWindowPanel> tryFindChatToolWindowPanel() {
113105
}
114106

115107
public void resetAll() {
116-
tryFindChatTabbedPane().ifPresent(tabbedPane -> {
117-
tabbedPane.clearAll();
118-
tabbedPane.addNewTab(new ChatToolWindowTabPanel(
119-
project,
120-
ConversationService.getInstance().startConversation(project)));
121-
});
108+
tryFindChatTabbedPane().ifPresent(ChatToolWindowTabbedPane::clearAll);
122109
}
123110

124111
public @NotNull ToolWindow getToolWindow() {
@@ -142,4 +129,4 @@ private Optional<Content> tryFindFirstChatTabContent() {
142129
public void clearAllTags() {
143130
tryFindActiveChatTabPanel().ifPresent(ChatToolWindowTabPanel::clearAllTags);
144131
}
145-
}
132+
}

src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowPanel.java

Lines changed: 77 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
import ee.carlrobert.codegpt.actions.toolwindow.ClearChatWindowAction;
2323
import ee.carlrobert.codegpt.actions.toolwindow.CreateNewConversationAction;
2424
import ee.carlrobert.codegpt.actions.toolwindow.OpenInEditorAction;
25+
import ee.carlrobert.codegpt.completions.ConversationType;
2526
import ee.carlrobert.codegpt.conversations.Conversation;
2627
import ee.carlrobert.codegpt.conversations.ConversationService;
27-
import ee.carlrobert.codegpt.conversations.ConversationsState;
28+
import ee.carlrobert.codegpt.conversations.message.Message;
29+
import ee.carlrobert.codegpt.psistructure.models.ClassStructure;
2830
import ee.carlrobert.codegpt.settings.service.FeatureType;
2931
import ee.carlrobert.codegpt.settings.models.ModelSettings;
3032
import ee.carlrobert.codegpt.settings.prompts.PersonaPromptDetailsState;
@@ -35,16 +37,25 @@
3537
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTUserDetailsNotifier;
3638
import ee.carlrobert.codegpt.toolwindow.chat.ui.ToolWindowFooterNotification;
3739
import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.AttachImageNotifier;
40+
import java.awt.CardLayout;
3841
import java.nio.file.Path;
42+
import java.util.Set;
3943
import javax.swing.JComponent;
44+
import javax.swing.JPanel;
4045
import org.jetbrains.annotations.NotNull;
4146

4247
public class ChatToolWindowPanel extends SimpleToolWindowPanel {
4348

49+
private static final String LANDING_CARD = "LANDING";
50+
private static final String TABS_CARD = "TABS";
51+
4452
private final ToolWindowFooterNotification imageFileAttachmentNotification;
4553
private final ActionLink upgradePlanLink;
4654
private final ChatToolWindowTabbedPane tabbedPane;
55+
private final JPanel centerPanel;
56+
private final CardLayout centerLayout;
4757
private final Project project;
58+
private ChatToolWindowTabPanel landingPanel;
4859

4960
public ChatToolWindowPanel(
5061
@NotNull Project project,
@@ -60,22 +71,16 @@ public ChatToolWindowPanel(
6071
upgradePlanLink.setExternalLinkIcon();
6172
upgradePlanLink.setVisible(false);
6273

63-
var tabPanel = new ChatToolWindowTabPanel(project, getConversation());
6474
tabbedPane = new ChatToolWindowTabbedPane(parentDisposable);
65-
tabbedPane.addNewTab(tabPanel);
75+
tabbedPane.setTabLifecycleCallbacks(this::showTabsView, this::showLandingView);
76+
centerLayout = new CardLayout();
77+
centerPanel = new JPanel(centerLayout);
78+
centerPanel.add(tabbedPane, TABS_CARD);
6679

6780
initToolWindowPanel(project);
6881
initializeEventListeners(project);
69-
70-
Disposer.register(parentDisposable, tabPanel);
71-
}
72-
73-
private Conversation getConversation() {
74-
var conversation = ConversationsState.getCurrentConversation();
75-
if (conversation == null) {
76-
return ConversationService.getInstance().startConversation(project);
77-
}
78-
return conversation;
82+
showLandingView();
83+
Disposer.register(parentDisposable, this::disposeLandingPanel);
7984
}
8085

8186
private void initializeEventListeners(Project project) {
@@ -107,6 +112,63 @@ public ChatToolWindowTabbedPane getChatTabbedPane() {
107112
return tabbedPane;
108113
}
109114

115+
public ChatToolWindowTabPanel createAndSelectNewTabPanel() {
116+
return createAndSelectConversationTab(ConversationService.getInstance().startConversation(project));
117+
}
118+
119+
public ChatToolWindowTabPanel createAndSelectConversationTab(Conversation conversation) {
120+
var panel = new ChatToolWindowTabPanel(project, conversation);
121+
tabbedPane.addNewTab(panel);
122+
showTabsView();
123+
return panel;
124+
}
125+
126+
public void showTabsView() {
127+
centerLayout.show(centerPanel, TABS_CARD);
128+
}
129+
130+
public void requestFocusForInput() {
131+
tabbedPane.tryFindActiveTabPanel()
132+
.ifPresentOrElse(
133+
ChatToolWindowTabPanel::requestFocusForTextArea,
134+
() -> {
135+
if (landingPanel != null) {
136+
landingPanel.requestFocusForTextArea();
137+
}
138+
});
139+
}
140+
141+
public void showLandingView() {
142+
disposeLandingPanel();
143+
landingPanel = createLandingPanel();
144+
centerPanel.add(landingPanel.getContent(), LANDING_CARD);
145+
centerLayout.show(centerPanel, LANDING_CARD);
146+
landingPanel.requestFocusForTextArea();
147+
centerPanel.revalidate();
148+
centerPanel.repaint();
149+
}
150+
151+
private ChatToolWindowTabPanel createLandingPanel() {
152+
var conversation = ConversationService.getInstance().createConversation();
153+
conversation.setProjectPath(project.getBasePath());
154+
return new ChatToolWindowTabPanel(project, conversation, this::promoteLandingDraftToTab);
155+
}
156+
157+
private void promoteLandingDraftToTab(Message message, Set<ClassStructure> psiStructure) {
158+
var tabPanel = createAndSelectNewTabPanel();
159+
tabPanel.sendMessage(message, ConversationType.DEFAULT, psiStructure);
160+
}
161+
162+
private void disposeLandingPanel() {
163+
if (landingPanel == null) {
164+
return;
165+
}
166+
167+
centerPanel.remove(landingPanel.getContent());
168+
Disposer.dispose(landingPanel);
169+
landingPanel = null;
170+
}
171+
110172
public void clearImageNotifications(Project project) {
111173
imageFileAttachmentNotification.hideNotification();
112174

@@ -115,9 +177,7 @@ public void clearImageNotifications(Project project) {
115177

116178
private void initToolWindowPanel(Project project) {
117179
Runnable onAddNewTab = () -> {
118-
tabbedPane.addNewTab(new ChatToolWindowTabPanel(
119-
project,
120-
ConversationService.getInstance().startConversation(project)));
180+
createAndSelectNewTabPanel();
121181
repaint();
122182
revalidate();
123183
};
@@ -127,7 +187,7 @@ private void initToolWindowPanel(Project project) {
127187
.addToLeft(createActionToolbar(project, tabbedPane, onAddNewTab).getComponent())
128188
.addToRight(upgradePlanLink));
129189
setContent(new BorderLayoutPanel()
130-
.addToCenter(tabbedPane)
190+
.addToCenter(centerPanel)
131191
.addToBottom(imageFileAttachmentNotification));
132192
});
133193
}

src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,23 @@ public class ChatToolWindowTabPanel implements Disposable {
9999
private final PsiStructureRepository psiStructureRepository;
100100
private final TagManager tagManager;
101101
private final JPanel mcpApprovalContainer;
102+
private final DraftSubmitHandler draftSubmitHandler;
102103
private @Nullable ToolwindowChatCompletionRequestHandler requestHandler;
103104
private final JBLabel loadingLabel;
104105
private final JPanel queuedMessageContainer;
105106

106107
public ChatToolWindowTabPanel(@NotNull Project project, @NotNull Conversation conversation) {
108+
this(project, conversation, null);
109+
}
110+
111+
public ChatToolWindowTabPanel(
112+
@NotNull Project project,
113+
@NotNull Conversation conversation,
114+
@Nullable DraftSubmitHandler draftSubmitHandler
115+
) {
107116
this.project = project;
108117
this.conversation = conversation;
118+
this.draftSubmitHandler = draftSubmitHandler;
109119
this.chatSession = new ChatSession();
110120
conversationService = ConversationService.getInstance();
111121
toolWindowScrollablePanel = new ChatToolWindowScrollablePanel();
@@ -614,7 +624,12 @@ private Unit handleSubmit(String text) {
614624
}
615625

616626
application.invokeLater(() -> {
617-
sendMessage(messageBuilder.build(), ConversationType.DEFAULT, psiStructure);
627+
var message = messageBuilder.build();
628+
if (draftSubmitHandler != null) {
629+
draftSubmitHandler.onDraftSubmit(message, psiStructure);
630+
} else {
631+
sendMessage(message, ConversationType.DEFAULT, psiStructure);
632+
}
618633
});
619634
});
620635
return Unit.INSTANCE;
@@ -772,4 +787,10 @@ private JPanel createRootPanel() {
772787
rootPanel.add(createSouthPanel(createUserPromptPanel()), BorderLayout.SOUTH);
773788
return rootPanel;
774789
}
790+
791+
@FunctionalInterface
792+
public interface DraftSubmitHandler {
793+
794+
void onDraftSubmit(Message message, Set<ClassStructure> psiStructure);
795+
}
775796
}

0 commit comments

Comments
 (0)