Skip to content

Commit d4c5cb2

Browse files
committed
bit of magic
1 parent d8491f1 commit d4c5cb2

5 files changed

Lines changed: 65 additions & 34 deletions

File tree

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>uno.anahata</groupId>
88
<artifactId>gemini-java-client</artifactId>
9-
<version>1.0.15</version>
9+
<version>1.0.16</version>
1010
<packaging>jar</packaging>
1111

1212
<name>gemini-java-client</name>

src/main/java/uno/anahata/ai/media/functions/spi/RadioTool.java

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* Licensed under the Apache License, Version 2.0 */
22
package uno.anahata.ai.media.functions.spi;
33

4+
import java.awt.KeyboardFocusManager;
5+
import java.awt.Window;
46
import java.io.InputStream;
57
import java.net.URL;
68
import java.net.URLConnection;
@@ -63,30 +65,6 @@ public class RadioTool {
6365
//not working
6466
//stations.put("The Jazz Groove", "http://west-mp3-128.jazzgroove.org");
6567

66-
/**
67-
* Others to try
68-
* [WORKING] http://ice.somafm.com/thetrip (HTTP 200)
69-
* Content-Type: audio/mpeg Read 1024 bytes.
70-
* • [WORKING] http://ice.somafm.com/lush (HTTP 200) Content-Type: audio/mpeg Read
71-
* 1024 bytes. • [WORKING] http://ice.somafm.com/u80s (HTTP 200)
72-
* Content-Type: audio/mpeg Read 1024 bytes. • [WORKING]
73-
* http://ice.somafm.com/deepspaceone (HTTP 200) Content-Type:
74-
* audio/mpeg Read 1024 bytes. • [WORKING]
75-
* http://ice.somafm.com/dronezone (HTTP 200) Content-Type: audio/mpeg
76-
* Read 1024 bytes. • [WORKING] http://ice.somafm.com/secretagent (HTTP
77-
* 200) Content-Type: audio/mpeg Read 1024 bytes. • [WORKING]
78-
* http://ice.somafm.com/groovesalad (HTTP 200) Content-Type: audio/mpeg
79-
* Read 1024 bytes. • [WORKING] http://stream.dotpoint.nl:8000/sst (HTTP
80-
* 200) Content-Type: audio/mpeg Read 1024 bytes. • [WORKING]
81-
* http://server17.streamserver24.com:44220/mp3-256 (HTTP 200)
82-
* Content-Type: audio/mpeg Read 1024 bytes. • [WORKING]
83-
* https://npr-ice.streamguys1.com/live.mp3 (HTTP 200) Content-Type:
84-
* audio/mpeg Read 1024 bytes. • [WORKING]
85-
* https://npr-ice.streamguys1.com/live.aac (HTTP 200) Content-Type:
86-
* audio/aac Read 1024 bytes. • [WORKING]
87-
* http://sc8.radiocaroline.net:8020/ (HTTP 200) Content-Type:
88-
* audio/mpeg Read 1024 bytes.
89-
*/
9068
STATIONS = Collections.unmodifiableMap(stations);
9169
}
9270

@@ -133,6 +111,7 @@ public static String start(String stationUrl) throws Exception {
133111
ui = new RadioToolPanel();
134112
ui.setVisible(true);
135113
}
114+
ui.toFront();
136115
ui.updatePlaybackState(name, true);
137116
});
138117

src/main/java/uno/anahata/ai/swing/ChatPanel.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,8 @@ public Component getListCellRendererComponent(JList<?> list, Object value, int i
264264
mainSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, tabbedPane, mainSouthPanel);
265265
mainSplitPane.setResizeWeight(0.8); // Give more space to the chat history initially
266266
mainSplitPane.setOneTouchExpandable(true);
267+
mainSplitPane.setDividerSize(10); // Make it easier to grab
268+
mainSplitPane.putClientProperty("JSplitPane.dividerStyle", "grip"); // Force the grip style for visibility
267269

268270
add(mainSplitPane, BorderLayout.CENTER);
269271

src/main/java/uno/anahata/ai/swing/InputPanel.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import javax.swing.KeyStroke;
2828
import javax.swing.SwingWorker;
2929
import javax.swing.filechooser.FileNameExtensionFilter;
30+
import javax.swing.undo.UndoManager;
3031
import lombok.Getter;
3132
import lombok.extern.slf4j.Slf4j;
3233
import org.jdesktop.swingx.JXTextArea;
@@ -48,6 +49,8 @@ public class InputPanel extends JPanel {
4849
private JButton screenshotButton;
4950
private JButton captureFramesButton;
5051
private AttachmentsPanel attachmentsPanel;
52+
53+
private final UndoManager undoManager = new UndoManager();
5154

5255
public InputPanel(ChatPanel parentPanel) {
5356
// Use a simple BorderLayout. The JScrollPane in the CENTER will automatically fill available space.
@@ -67,6 +70,29 @@ private void initComponents() {
6770
inputTextArea = new JXTextArea("Type your message here (Ctrl+Enter to send)");
6871
inputTextArea.setLineWrap(true);
6972
inputTextArea.setWrapStyleWord(true);
73+
74+
// Undo/Redo Support
75+
inputTextArea.getDocument().addUndoableEditListener(e -> undoManager.addEdit(e.getEdit()));
76+
77+
inputTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, KeyEvent.CTRL_DOWN_MASK), "Undo");
78+
inputTextArea.getActionMap().put("Undo", new AbstractAction() {
79+
@Override
80+
public void actionPerformed(ActionEvent e) {
81+
if (undoManager.canUndo()) {
82+
undoManager.undo();
83+
}
84+
}
85+
});
86+
87+
inputTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, KeyEvent.CTRL_DOWN_MASK), "Redo");
88+
inputTextArea.getActionMap().put("Redo", new AbstractAction() {
89+
@Override
90+
public void actionPerformed(ActionEvent e) {
91+
if (undoManager.canRedo()) {
92+
undoManager.redo();
93+
}
94+
}
95+
});
7096

7197
// Ctrl+Enter to send
7298
KeyStroke ctrlEnter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.CTRL_DOWN_MASK);

src/main/java/uno/anahata/ai/swing/SwingFunctionPrompter.java

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import com.google.genai.types.Part;
66
import java.awt.BorderLayout;
77
import java.awt.FlowLayout;
8+
import java.awt.Window;
9+
import java.awt.event.ActionEvent;
10+
import java.awt.event.KeyEvent;
811
import java.awt.event.WindowAdapter;
912
import java.awt.event.WindowEvent;
1013
import java.lang.reflect.InvocationTargetException;
@@ -13,22 +16,23 @@
1316
import java.util.LinkedHashMap;
1417
import java.util.List;
1518
import java.util.Map;
19+
import javax.swing.AbstractAction;
1620
import javax.swing.JButton;
1721
import javax.swing.JComponent;
1822
import javax.swing.JDialog;
19-
import javax.swing.JFrame;
2023
import javax.swing.JPanel;
2124
import javax.swing.JScrollPane;
2225
import javax.swing.JTextArea;
26+
import javax.swing.KeyStroke;
2327
import javax.swing.SwingUtilities;
2428
import javax.swing.border.TitledBorder;
29+
import javax.swing.undo.UndoManager;
2530
import lombok.extern.slf4j.Slf4j;
2631
import uno.anahata.ai.Chat;
2732
import uno.anahata.ai.ChatMessage;
2833
import uno.anahata.ai.config.ChatConfig;
2934
import uno.anahata.ai.tools.FunctionConfirmation;
3035
import uno.anahata.ai.tools.FunctionPrompter;
31-
import uno.anahata.ai.swing.SwingChatConfig.UITheme;
3236
import uno.anahata.ai.swing.render.ContentRenderer;
3337
import uno.anahata.ai.swing.render.InteractiveFunctionCallRenderer;
3438

@@ -52,16 +56,17 @@ public class SwingFunctionPrompter extends JDialog implements FunctionPrompter {
5256
* @param chatPanel The ChatPanel that initiated the prompt.
5357
*/
5458
public SwingFunctionPrompter(ChatPanel chatPanel) {
55-
// We use null as the owner initially because the ChatPanel might not be added to a window yet.
56-
// The title is updated in the prompt() method once the Chat instance is fully initialized.
57-
super((JFrame) null, "Confirm Proposed Actions", true);
59+
// We use the window ancestor of the chatPanel as the owner to prevent the "modal hang"
60+
super(SwingUtilities.getWindowAncestor(chatPanel), "Confirm Proposed Actions", ModalityType.APPLICATION_MODAL);
5861
this.chatPanel = chatPanel;
5962
}
6063

6164
@Override
6265
public PromptResult prompt(ChatMessage modelMessage, Chat chat) {
6366
try {
6467
SwingUtilities.invokeAndWait(() -> {
68+
Window owner = SwingUtilities.getWindowAncestor(chatPanel);
69+
6570
setTitle("Confirm Proposed Actions - " + chat.getDisplayName());
6671
interactiveRenderers.clear();
6772
functionConfirmations.clear();
@@ -71,7 +76,10 @@ public PromptResult prompt(ChatMessage modelMessage, Chat chat) {
7176
initComponents(modelMessage);
7277
pack();
7378
setSize(1024, 768);
74-
setLocationRelativeTo(SwingUtilities.getWindowAncestor(chatPanel));
79+
setLocationRelativeTo(owner);
80+
81+
// Ensure it's visible and focused
82+
toFront();
7583
setVisible(true); // This blocks until the dialog is closed
7684
});
7785
} catch (InterruptedException | InvocationTargetException e) {
@@ -116,6 +124,25 @@ private void initComponents(ChatMessage modelMessage) {
116124

117125
JPanel bottomPanel = new JPanel(new BorderLayout(0, 5));
118126
JTextArea commentTextArea = new JTextArea(3, 60);
127+
128+
// Undo/Redo Support for the comment area
129+
UndoManager undoManager = new UndoManager();
130+
commentTextArea.getDocument().addUndoableEditListener(e -> undoManager.addEdit(e.getEdit()));
131+
commentTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, KeyEvent.CTRL_DOWN_MASK), "Undo");
132+
commentTextArea.getActionMap().put("Undo", new AbstractAction() {
133+
@Override
134+
public void actionPerformed(ActionEvent e) {
135+
if (undoManager.canUndo()) undoManager.undo();
136+
}
137+
});
138+
commentTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y, KeyEvent.CTRL_DOWN_MASK), "Redo");
139+
commentTextArea.getActionMap().put("Redo", new AbstractAction() {
140+
@Override
141+
public void actionPerformed(ActionEvent e) {
142+
if (undoManager.canRedo()) undoManager.redo();
143+
}
144+
});
145+
119146
JPanel commentPanel = new JPanel(new BorderLayout());
120147
commentPanel.setBorder(new TitledBorder("Add Comment (Optional)"));
121148
commentPanel.add(new JScrollPane(commentTextArea), BorderLayout.CENTER);
@@ -136,7 +163,6 @@ private void initComponents(ChatMessage modelMessage) {
136163
cancelButton.addActionListener(e -> {
137164
this.userComment = commentTextArea.getText();
138165
this.cancelled = true;
139-
// Do NOT collect results, the cancellation overrides individual choices.
140166
setVisible(false);
141167
dispose();
142168
});
@@ -145,7 +171,6 @@ private void initComponents(ChatMessage modelMessage) {
145171
addWindowListener(new WindowAdapter() {
146172
@Override
147173
public void windowClosing(WindowEvent e) {
148-
// Treat closing the dialog as a cancellation
149174
userComment = commentTextArea.getText();
150175
cancelled = true;
151176
setVisible(false);
@@ -165,7 +190,6 @@ private Map<FunctionCall, FunctionConfirmation> collectResultsFromInteractiveRen
165190
FunctionCall functionCall = renderer.getFunctionCall();
166191
FunctionConfirmation state = renderer.getSelectedState();
167192

168-
// Persist the user's choice for the next time this function is called.
169193
config.setFunctionConfirmation(functionCall, state);
170194
results.put(functionCall, state);
171195
}

0 commit comments

Comments
 (0)