Skip to content

Commit afdaf54

Browse files
authored
Merge pull request #149 from arsi-apli/ADD_TO_SESSION_CONTEXT
Resolves #148 Action to add files to session context
2 parents 8754762 + 23beb0a commit afdaf54

2 files changed

Lines changed: 158 additions & 27 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/**
2+
* Copyright 2025 the original author or authors from the Jeddict project (https://jeddict.github.io/).
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package io.github.jeddict.ai.actions;
17+
18+
import io.github.jeddict.ai.components.AssistantChat;
19+
import io.github.jeddict.ai.hints.AssistantChatManager;
20+
import java.awt.event.ActionEvent;
21+
import java.lang.ref.WeakReference;
22+
import java.util.List;
23+
import java.util.ArrayList;
24+
import java.util.Set;
25+
import javax.swing.AbstractAction;
26+
import javax.swing.Action;
27+
import org.openide.awt.*;
28+
import org.openide.filesystems.FileObject;
29+
import org.openide.util.*;
30+
import org.openide.util.NbBundle.Messages;
31+
import org.openide.windows.Mode;
32+
import org.openide.windows.TopComponent;
33+
import org.openide.windows.WindowManager;
34+
35+
@ActionID(
36+
category = "Project",
37+
id = "io.github.jeddict.ai.actions.AddToChatContextAction"
38+
)
39+
@ActionRegistration(
40+
displayName = "#CTL_AddToChatContextAction",
41+
lazy = false,
42+
asynchronous = false
43+
)
44+
@ActionReferences({
45+
@ActionReference(path = "Projects/package/Actions", position = 101),
46+
@ActionReference(path = "Loaders/text/x-java/Actions", position = 101),
47+
@ActionReference(path = "Loaders/folder/any/Actions", position = 301)
48+
})
49+
@Messages({"CTL_AddToChatContextAction=Add to Chat Session Context"})
50+
public final class AddToChatContextAction extends AbstractAction implements ContextAwareAction {
51+
52+
@Override
53+
public void actionPerformed(ActionEvent e) {
54+
// nikdy nebude volaná priamo - len cez ContextAction
55+
}
56+
57+
@Override
58+
public Action createContextAwareInstance(Lookup context) {
59+
List<FileObject> files = new ArrayList<>(context.lookupAll(FileObject.class));
60+
boolean en = !files.isEmpty() && isAssistantChatOpened();
61+
return new ContextAction(en, files);
62+
}
63+
64+
@Override
65+
public boolean isEnabled() {
66+
return isAssistantChatOpened();
67+
}
68+
69+
private static boolean isAssistantChatOpened() {
70+
Set<? extends Mode> modes = WindowManager.getDefault().getModes();
71+
for (Mode mode : modes) {
72+
TopComponent[] openedTopComponents = WindowManager.getDefault().getOpenedTopComponents(mode);
73+
for (TopComponent tc : openedTopComponents) {
74+
if (tc instanceof AssistantChat) {
75+
if (tc.isOpened() && tc.isShowing()) {
76+
return true;
77+
}
78+
}
79+
}
80+
}
81+
return false;
82+
}
83+
84+
private static TopComponent getOpenedAssistantChat() {
85+
Set<? extends Mode> modes = WindowManager.getDefault().getModes();
86+
for (Mode mode : modes) {
87+
TopComponent[] openedTopComponents = WindowManager.getDefault().getOpenedTopComponents(mode);
88+
for (TopComponent tc : openedTopComponents) {
89+
if (tc instanceof AssistantChat) {
90+
if (tc.isOpened() && tc.isShowing()) {
91+
return tc;
92+
}
93+
}
94+
}
95+
}
96+
return null;
97+
}
98+
99+
private static final class ContextAction extends AbstractAction {
100+
101+
private final List<FileObject> files;
102+
103+
private ContextAction(boolean enabled, List<FileObject> files) {
104+
super(Bundle.CTL_AddToChatContextAction());
105+
this.files = files;
106+
this.putValue(DynamicMenuContent.HIDE_WHEN_DISABLED, true);
107+
setEnabled(enabled);
108+
}
109+
110+
@Override
111+
public void actionPerformed(ActionEvent e) {
112+
TopComponent openedAssistantChat = getOpenedAssistantChat();
113+
if (openedAssistantChat != null) {
114+
Object clientProperty = openedAssistantChat.getClientProperty(AssistantChatManager.ASSISTANT_CHAT_MANAGER_KEY);
115+
if (clientProperty instanceof WeakReference) {
116+
Object tmp = ((WeakReference) clientProperty).get();
117+
if (tmp instanceof AssistantChatManager) {
118+
((AssistantChatManager) tmp).addToSessionContext(files);
119+
}
120+
}
121+
}
122+
}
123+
124+
@Override
125+
public boolean isEnabled() {
126+
return isAssistantChatOpened();
127+
}
128+
129+
}
130+
}

src/main/java/io/github/jeddict/ai/hints/AssistantChatManager.java

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,12 @@
5858
import static io.github.jeddict.ai.util.EditorUtil.getHTMLContent;
5959
import static io.github.jeddict.ai.util.Icons.ICON_ATTACH;
6060
import static io.github.jeddict.ai.util.Icons.ICON_CONTEXT;
61-
import static io.github.jeddict.ai.util.Icons.ICON_COPY;
6261
import static io.github.jeddict.ai.util.Icons.ICON_NEW_CHAT;
6362
import static io.github.jeddict.ai.util.Icons.ICON_NEXT;
6463
import static io.github.jeddict.ai.util.Icons.ICON_PREV;
65-
import static io.github.jeddict.ai.util.Icons.ICON_SAVE;
6664
import static io.github.jeddict.ai.util.Icons.ICON_SEND;
6765
import static io.github.jeddict.ai.util.Icons.ICON_SETTINGS;
6866
import static io.github.jeddict.ai.util.Icons.ICON_STATS;
69-
import static io.github.jeddict.ai.util.Icons.ICON_UPDATE;
7067
import static io.github.jeddict.ai.util.Icons.ICON_WEB;
7168
import io.github.jeddict.ai.util.Labels;
7269
import static io.github.jeddict.ai.util.MimeUtil.MIME_PLAIN_TEXT;
@@ -79,14 +76,12 @@
7976
import java.awt.Dimension;
8077
import java.awt.EventQueue;
8178
import java.awt.FlowLayout;
82-
import java.awt.Toolkit;
83-
import java.awt.datatransfer.Clipboard;
84-
import java.awt.datatransfer.StringSelection;
8579
import java.awt.event.ComponentAdapter;
8680
import java.awt.event.ComponentEvent;
8781
import java.io.File;
8882
import java.io.FileWriter;
8983
import java.io.IOException;
84+
import java.lang.ref.WeakReference;
9085
import java.util.ArrayList;
9186
import java.util.HashSet;
9287
import java.util.List;
@@ -138,9 +133,9 @@
138133
* @author Shiwani Gupta
139134
*/
140135
public class AssistantChatManager extends JavaFix {
141-
142136

143137
private static final ExecutorService executorService = Executors.newSingleThreadExecutor();
138+
public static final String ASSISTANT_CHAT_MANAGER_KEY = "ASSISTANT_CHAT_MANAGER_KEY";
144139

145140
private TreePath treePath;
146141
private final Action action;
@@ -149,7 +144,7 @@ public class AssistantChatManager extends JavaFix {
149144
private JComboBox<String> models;
150145
private JComboBox<AssistantAction> actionComboBox;
151146
private Timer timer;
152-
private JButton prevButton, nextButton, openInBrowserButton, submitButton;//copyButton, saveButton,
147+
private JButton prevButton, nextButton, openInBrowserButton, submitButton;//copyButton, saveButton,
153148
private AssistantChat topComponent;
154149
private JEditorPane questionPane;
155150
private JScrollPane questionScrollPane;
@@ -258,7 +253,7 @@ protected void performRewrite(JavaFix.TransformationContext tc) throws Exception
258253
@Override
259254
public void onComplete(String textResponse) {
260255
Response response = new Response(null, textResponse, messageContextCopy);
261-
sourceCode = EditorUtil.updateEditors(null, getProject(),topComponent, response, getContextFiles());
256+
sourceCode = EditorUtil.updateEditors(null, getProject(), topComponent, response, getContextFiles());
262257
responseHistory.add(response);
263258
currentResponseIndex = responseHistory.size() - 1;
264259
}
@@ -298,7 +293,7 @@ public void askQueryForProjectCommit(Project project, String commitChanges, Stri
298293
this.commitMessage = true;
299294
handleQuestion(intitalCommitMessage, messageContext, true);
300295
}
301-
296+
302297
private boolean commitMessage, codeReview;
303298

304299
public void askQueryForCodeReview(Project project, String commitChanges, String contextMessage) {
@@ -314,6 +309,7 @@ public void displayHtmlContent(String filename, String title) {
314309
Preferences prefs = Preferences.userNodeForPackage(AssistantChat.class);
315310
prefs.putBoolean(AssistantChat.PREFERENCE_KEY, true);
316311
topComponent = new AssistantChat(title, null, getProject());
312+
topComponent.putClientProperty(ASSISTANT_CHAT_MANAGER_KEY, new WeakReference<>(AssistantChatManager.this));
317313
JScrollPane scrollPane = new JScrollPane(topComponent.getParentPanel());
318314
topComponent.add(scrollPane, BorderLayout.CENTER);
319315
topComponent.add(createBottomPanel(null, filename, null), BorderLayout.SOUTH);
@@ -335,6 +331,7 @@ public void openChat(String type, final String query, String fileName, String ti
335331
prefs.putBoolean(AssistantChat.PREFERENCE_KEY, true);
336332
topComponent = new AssistantChat(title, type, getProject());
337333
topComponent.setLayout(new BorderLayout());
334+
topComponent.putClientProperty(ASSISTANT_CHAT_MANAGER_KEY, new WeakReference<>(AssistantChatManager.this));
338335
JScrollPane scrollPane = new JScrollPane(topComponent.getParentPanel());
339336
Color bgColor = getBackgroundColorFromMimeType(MIME_PLAIN_TEXT);
340337
boolean isDark = ColorUtil.isDarkColor(bgColor);
@@ -391,7 +388,7 @@ private void initialMessage() {
391388
} catch (Exception ex) {
392389
Exceptions.printStackTrace(ex);
393390
}
394-
}else if ("tweet".equals(link)) {
391+
} else if ("tweet".equals(link)) {
395392
try {
396393
java.awt.Desktop.getDesktop().browse(java.net.URI.create(RandomTweetSelector.getRandomTweet()));
397394
} catch (Exception ex) {
@@ -467,7 +464,7 @@ private JPanel createBottomPanel(String type, String fileName, Consumer<String>
467464
openInBrowserButton.setVisible(topComponent.getAllEditorCount() > 0);
468465
leftButtonPanel.add(openInBrowserButton);
469466

470-
Set<String> modelsByProvider = getModelsByProvider(pm.getProvider()) ;
467+
Set<String> modelsByProvider = getModelsByProvider(pm.getProvider());
471468
modelsByProvider.add(pm.getModelName());
472469
models = createStyledComboBox(modelsByProvider.toArray(new String[0]));
473470
models.setSelectedItem(pm.getChatModel() != null ? pm.getChatModel() : pm.getModel());
@@ -479,7 +476,7 @@ private JPanel createBottomPanel(String type, String fileName, Consumer<String>
479476
}
480477
});
481478
leftButtonPanel.add(models);
482-
479+
483480
AssistantAction[] options = AssistantAction.values();
484481
actionComboBox = createStyledComboBox(options);
485482
actionComboBox.setSelectedItem(AssistantAction.ASK);
@@ -490,7 +487,7 @@ private JPanel createBottomPanel(String type, String fileName, Consumer<String>
490487
if (getProject() == null) {
491488
Project[] openProjects = org.netbeans.api.project.ui.OpenProjects.getDefault().getOpenProjects();
492489
if (openProjects.length == 1) {
493-
project = openProjects[0];
490+
project = openProjects[0];
494491
DialogDisplayer.getDefault().notify(
495492
new NotifyDescriptor.Message(
496493
"Connected chat to project: " + ProjectUtils.getInformation(project).getDisplayName(),
@@ -557,12 +554,10 @@ public java.awt.Component getListCellRendererComponent(javax.swing.JList<? exten
557554
// saveButton.setToolTipText("Save as");
558555
// saveButton.setVisible(javaEditorCount == 1);
559556
// leftButtonPanel.add(saveButton);
560-
561557
// JButton saveToEditorButton = createIconButton(Labels.UPDATE + " " + fileName, ICON_UPDATE);
562558
// saveToEditorButton.setToolTipText("Update " + fileName);
563559
// saveToEditorButton.setVisible(fileName != null);
564560
// leftButtonPanel.add(saveToEditorButton);
565-
566561
JButton showChartsButton = createIconButton(Labels.STATS, ICON_STATS);
567562
showChartsButton.setToolTipText("Show Token Usage Charts");
568563
rightButtonPanel.add(showChartsButton);
@@ -618,15 +613,16 @@ public void componentResized(ComponentEvent e) {
618613
updateButton(messageContextButton, showOnlyIcons, ICON_ATTACH, Labels.MESSAGE_CONTEXT + " " + ICON_ATTACH);
619614
updateButton(sessionContextButton, showOnlyIcons, ICON_CONTEXT, Labels.SESSION_CONTEXT + " " + ICON_CONTEXT);
620615
updateButton(submitButton, showOnlyIcons, ICON_SEND, Labels.SEND + " " + ICON_SEND);
621-
updateCombobox(models, showOnlyIcons);
622-
updateCombobox(actionComboBox, showOnlyIcons);
616+
updateCombobox(models, showOnlyIcons);
617+
updateCombobox(actionComboBox, showOnlyIcons);
623618
topComponent.updateUserPaneButtons(showOnlyIcons);
624619

625620
}
626621

627622
private void updateButton(JButton button, boolean iconOnly, String iconText, String fullText) {
628623
button.setText(iconOnly ? iconText : fullText);
629624
}
625+
630626
private <T> void updateCombobox(JComboBox<T> comboBox, boolean iconOnly) {
631627
comboBox.putClientProperty("minimal", iconOnly);
632628
}
@@ -694,7 +690,7 @@ public void changedUpdate(DocumentEvent e) {
694690
try {
695691
File latestTempFile = File.createTempFile("gen-ai", ".html");
696692
latestTempFile.deleteOnExit();
697-
try (FileWriter writer = new FileWriter(latestTempFile)) {
693+
try ( FileWriter writer = new FileWriter(latestTempFile)) {
698694
writer.write(topComponent.getAllEditorText());
699695
}
700696
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
@@ -749,7 +745,7 @@ public void changedUpdate(DocumentEvent e) {
749745
if (currentResponseIndex > 0) {
750746
currentResponseIndex--;
751747
Response historyResponse = responseHistory.get(currentResponseIndex);
752-
sourceCode = EditorUtil.updateEditors(queryUpdate, getProject(),topComponent, historyResponse, getContextFiles());
748+
sourceCode = EditorUtil.updateEditors(queryUpdate, getProject(), topComponent, historyResponse, getContextFiles());
753749
updateButtons(prevButton, nextButton);
754750
}
755751
});
@@ -758,7 +754,7 @@ public void changedUpdate(DocumentEvent e) {
758754
if (currentResponseIndex < responseHistory.size() - 1) {
759755
currentResponseIndex++;
760756
Response historyResponse = responseHistory.get(currentResponseIndex);
761-
sourceCode = EditorUtil.updateEditors(queryUpdate, getProject(),topComponent, historyResponse, getContextFiles());
757+
sourceCode = EditorUtil.updateEditors(queryUpdate, getProject(), topComponent, historyResponse, getContextFiles());
762758
updateButtons(prevButton, nextButton);
763759
}
764760
});
@@ -801,7 +797,7 @@ private void startLoading() {
801797
private void stopLoading() {
802798
timer.stop();
803799
}
804-
800+
805801
private void updateHeight() {
806802
int lineHeight = questionPane.getFontMetrics(questionPane.getFont()).getHeight();
807803
String text = questionPane.getText().trim();
@@ -860,6 +856,7 @@ private void showFilePathPopup() {
860856

861857
private Future result;
862858
private JeddictStreamHandler handler;
859+
863860
private void handleQuestion(String question, Set<FileObject> messageContext, boolean newQuery) {
864861
result = executorService.submit(() -> {
865862
try {
@@ -903,7 +900,7 @@ public void onComplete(String textResponse) {
903900
topComponent.setReviews(reviews);
904901
response.getBlocks().clear();
905902
response.getBlocks().add(new Block("web", web));
906-
}
903+
}
907904
sourceCode = EditorUtil.updateEditors(queryUpdate, getProject(), topComponent, response, getContextFiles());
908905

909906
stopLoading();
@@ -988,10 +985,10 @@ public void onComplete(String textResponse) {
988985
}
989986
});
990987
}
991-
988+
992989
private String getModelName() {
993-
String modelName = (String)models.getSelectedItem();
994-
if(modelName == null || modelName.isEmpty()) {
990+
String modelName = (String) models.getSelectedItem();
991+
if (modelName == null || modelName.isEmpty()) {
995992
return pm.getModel();
996993
}
997994
return modelName;
@@ -1008,4 +1005,8 @@ private void updateButtons(JButton prevButton, JButton nextButton) {
10081005
openInBrowserButton.setVisible(topComponent.getAllEditorCount() > 0);
10091006
}
10101007

1011-
}
1008+
public void addToSessionContext(List<FileObject> files) {
1009+
threadContext.addAll(files);
1010+
}
1011+
1012+
}

0 commit comments

Comments
 (0)