├── ChatGPT ├── .gitignore ├── src │ ├── logo.ico │ ├── logo.png │ ├── upFolder.gif │ ├── FloppyDrive.gif │ ├── AboutFrame.java │ ├── ChatLoader.java │ └── MainFrame.java ├── lib │ └── JTattoo-1.6.13.jar ├── .settings │ ├── org.eclipse.core.resources.prefs │ ├── org.eclipse.m2e.core.prefs │ └── org.eclipse.jdt.core.prefs ├── config.properties ├── .project ├── .classpath └── pom.xml └── README.md /ChatGPT/.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /target/ 3 | -------------------------------------------------------------------------------- /ChatGPT/src/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FrankCYB/JavaGPT/HEAD/ChatGPT/src/logo.ico -------------------------------------------------------------------------------- /ChatGPT/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FrankCYB/JavaGPT/HEAD/ChatGPT/src/logo.png -------------------------------------------------------------------------------- /ChatGPT/src/upFolder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FrankCYB/JavaGPT/HEAD/ChatGPT/src/upFolder.gif -------------------------------------------------------------------------------- /ChatGPT/src/FloppyDrive.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FrankCYB/JavaGPT/HEAD/ChatGPT/src/FloppyDrive.gif -------------------------------------------------------------------------------- /ChatGPT/lib/JTattoo-1.6.13.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FrankCYB/JavaGPT/HEAD/ChatGPT/lib/JTattoo-1.6.13.jar -------------------------------------------------------------------------------- /ChatGPT/.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/MainFrame.java=UTF-8 3 | -------------------------------------------------------------------------------- /ChatGPT/.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /ChatGPT/config.properties: -------------------------------------------------------------------------------- 1 | #JavaGPT Config 2 | #To use JavaGPT, provide a ChatGPT API Key after the "=" on line 4 3 | #Get API key here > https://platform.openai.com/account/api-keys 4 | apikey= 5 | model=gpt-3.5-turbo 6 | maxTokens=1024 7 | timeout=30 8 | proxyip= 9 | proxyport= 10 | proxytype= 11 | autosave=true 12 | autotitle=true 13 | autoscroll=true 14 | EnterToSubmit=true 15 | chat_location_override= 16 | WindowSize=large 17 | FontSize=12 18 | Theme=dark 19 | -------------------------------------------------------------------------------- /ChatGPT/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | ChatGPT 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.m2e.core.maven2Nature 21 | org.eclipse.jdt.core.javanature 22 | 23 | 24 | -------------------------------------------------------------------------------- /ChatGPT/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ChatGPT/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.8 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 12 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 13 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 14 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore 15 | org.eclipse.jdt.core.compiler.release=disabled 16 | org.eclipse.jdt.core.compiler.source=1.8 17 | -------------------------------------------------------------------------------- /ChatGPT/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | ChatGPT 5 | ChatGPT 6 | 0.0.1-SNAPSHOT 7 | 8 | src 9 | 10 | 11 | maven-compiler-plugin 12 | 3.8.1 13 | 14 | 1.8 15 | 1.8 16 | 17 | 18 | 19 | 20 | 21 | 22 | com.theokanning.openai-gpt3-java 23 | service 24 | 0.12.0 25 | 26 | 27 | com.fasterxml.jackson.core 28 | jackson-databind 29 | 2.10.1 30 | 31 | 32 | com.google.code.gson 33 | gson 34 | 2.10.1 35 | 36 | 37 | org.commonmark 38 | commonmark 39 | 0.21.0 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /ChatGPT/src/AboutFrame.java: -------------------------------------------------------------------------------- 1 | import java.awt.Cursor; 2 | import java.awt.Desktop; 3 | import javax.swing.JFrame; 4 | import javax.swing.JPanel; 5 | import javax.swing.border.EmptyBorder; 6 | import javax.swing.JLabel; 7 | import javax.swing.ImageIcon; 8 | import java.awt.Font; 9 | import java.awt.Label; 10 | import java.awt.event.MouseAdapter; 11 | import java.awt.event.MouseEvent; 12 | import java.net.URI; 13 | 14 | public class AboutFrame extends JFrame { 15 | 16 | private JPanel contentPane; 17 | 18 | /** 19 | * Launch the application. 20 | */ 21 | 22 | /** 23 | * Create the frame. 24 | */ 25 | public AboutFrame() { 26 | setTitle("About"); 27 | setResizable(false); 28 | setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 29 | setBounds(100, 100, 401, 271); 30 | contentPane = new JPanel(); 31 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 32 | setContentPane(contentPane); 33 | contentPane.setLayout(null); 34 | 35 | JLabel lblNewLabel = new JLabel(""); 36 | lblNewLabel.setIcon(new ImageIcon(AboutFrame.class.getResource("logo.png"))); 37 | lblNewLabel.setBounds(160, 22, 84, 83); 38 | contentPane.add(lblNewLabel); 39 | 40 | JLabel lblNewLabel_1 = new JLabel("JavaGPT " + MainFrame.version); 41 | lblNewLabel_1.setFont(new Font("Tahoma", Font.BOLD, 23)); 42 | lblNewLabel_1.setBounds(118, 105, 169, 51); 43 | contentPane.add(lblNewLabel_1); 44 | 45 | JLabel lblNewLabel_2 = new JLabel("(May 12 2023)"); 46 | lblNewLabel_2.setFont(new Font("Tahoma", Font.PLAIN, 16)); 47 | lblNewLabel_2.setBounds(142, 145, 114, 28); 48 | contentPane.add(lblNewLabel_2); 49 | 50 | JLabel lblNewLabel_3 = new JLabel("Source code:"); 51 | lblNewLabel_3.setFont(new Font("Tahoma", Font.PLAIN, 14)); 52 | lblNewLabel_3.setBounds(24, 200, 84, 28); 53 | contentPane.add(lblNewLabel_3); 54 | JLabel lblHttpsgithubcomfrankcybjavagpt; 55 | if(MainFrame.seltheme != 1) { 56 | lblHttpsgithubcomfrankcybjavagpt = new JLabel("https://github.com/FrankCYB/JavaGPT"); 57 | }else { 58 | lblHttpsgithubcomfrankcybjavagpt = new JLabel("https://github.com/FrankCYB/JavaGPT"); 59 | } 60 | lblHttpsgithubcomfrankcybjavagpt.addMouseListener(new MouseAdapter() { 61 | @Override 62 | public void mouseClicked(MouseEvent e) { 63 | try { 64 | // Get the URI of the hyperlink 65 | URI uri = new URI("https://github.com/FrankCYB/JavaGPT"); 66 | 67 | // Open the URI in a browser 68 | Desktop.getDesktop().browse(uri); 69 | 70 | } catch (Exception ex) { 71 | ex.printStackTrace(); 72 | } 73 | 74 | } 75 | public void mouseEntered(MouseEvent e) { 76 | // Change the cursor to a hand when the mouse enters the label 77 | lblHttpsgithubcomfrankcybjavagpt.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 78 | } 79 | 80 | @Override 81 | public void mouseExited(MouseEvent e) { 82 | // Change the cursor back to the default when the mouse leaves the label 83 | lblHttpsgithubcomfrankcybjavagpt.setCursor(Cursor.getDefaultCursor()); 84 | } 85 | }); 86 | 87 | lblHttpsgithubcomfrankcybjavagpt.setFont(new Font("Tahoma", Font.PLAIN, 14)); 88 | lblHttpsgithubcomfrankcybjavagpt.setBounds(115, 200, 270, 28); 89 | contentPane.add(lblHttpsgithubcomfrankcybjavagpt); 90 | 91 | Label label = new Label(MainFrame.prop.getProperty("model")); 92 | label.setAlignment(Label.CENTER); 93 | label.setBounds(140, 172, 114, 22); 94 | contentPane.add(label); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | Image description 4 |
5 | 6 | # JavaGPT 7 | 8 | A Java GUI that interfaces ChatGPT API. 9 | 10 | 11 | ![](https://i.imgur.com/EbL1VRv.gif) 12 | 13 | 14 | ## Features 15 | 16 | - Chat Streaming 17 | - Just like the website, responses will generate in real time 18 | - You can terminate a response while it is in progress 19 | - Chat History 20 | - See and interact with previous chats 21 | - Saves chats as .json for easy external modification and viewing 22 | - Accessible through the "Load Chat" button 23 | 24 | ![Demo](https://i.imgur.com/q3s1frY.gif) 25 | 26 | - Chat Titles 27 | - Autogenerate titles like ChatGPT website 28 | - Manually name chats if preferred 29 | - Revert Chats 30 | - Be able to void previous prompts and responses from chat 31 | - You can revert multiple times 32 | - Proxy Support 33 | - Supports SOCKS and HTTP proxies 34 | - Easily configurable via config.properties 35 | - HTML Viewer 36 | - View your chat content in HTML 37 | - Supports Markdown Language syntax 38 | 39 | ![HTML View](https://i.imgur.com/W0pzIic.gif) 40 | 41 | 42 | - Import premade prompts 43 | - Save chats to file 44 | - Support for ChatGPT 4, and 3.5 models 45 | - Cross platform 46 | 47 | 48 | ## Setup 49 | 50 | To get started download the [latest release](https://github.com/FrankCYB/JavaGPT/releases/latest "Latest release page"). 51 | 52 | Afterwords, extract the archieve 53 | 54 | Then open the config.properties file in a text editor 55 | 56 | Add your [ChatGPT API-Key](https://platform.openai.com/account/api-keys "ChatGPT API-Key") on line 4 after "apikey=" 57 | 58 | Run JavaGPT.jar and enjoy! 😁 59 | 60 | 61 | 62 | ## Config Example 63 | ``` 64 | apikey=ENTER_CHAT_GPT_API_KEY_HERE 65 | model=gpt-3.5-turbo #Model used for ChatGPT Client (Supported Models: gpt-4, gpt-3.5-turbo, etc) > All supported models here "https://platform.openai.com/docs/models/gpt-3-5" 66 | maxTokens=1024 #Max ammount of tokens allowed per ChatGPT API request 67 | timeout=30 #Adjust allowed wait time for prompt response from ChatGPT API 68 | proxyip= #Proxy IP 69 | proxyport= #Proxy port number 70 | proxytype= #Options: SOCKS,HTTP,HTTPS 71 | autotitle=true #Adjusts wether new chats will automatically generate file name titles based on the context of the chat 72 | autoscroll=true #Adjusts wether chat will scroll as new text is added 73 | EnterToSubmit=true #Adjusts wether the Enter key should be used to submit or to create new lines 74 | chat_history= #Adjust wether chats will save automatically 75 | chat_location_override= #Overrides default "chat_history" folder path (Original path is set to the location of the jar file on runtime) 76 | WindowSize= #Adjusts JFrame (Window) size. Options: small,medium,large (Set to "medium" by default) 77 | FontSize= #Adjusts font size of chat content 78 | Theme=dark #Themes JFrame (Window) to set config. Options: dark,light 79 | ``` 80 | ## Requirements 81 | 82 | - Java 8 or higher 83 | 84 | ## Important note for legacy systems 85 | 86 | If your on a legacy Windows system such as 98, ME, 2000, and XP, there are additional steps involved to get Java 8 to run properly.
87 | I made a guide on how to get Java 8 to run on these various OSs in a reddit thread. Link can be found [here](https://www.reddit.com/r/windows/comments/12t9ax3/comment/jh1h1qm/?utm_source=share&utm_medium=web2x&context=3). 88 | 89 | ## Final notes 90 | 91 | If you enjoy JavaGPT and would like to support me in future updates and projects, please feel free to show your support by buying me a ☕ 92 | 93 | Buy Me A Coffee 94 | 95 | 96 | 97 | 98 | Also, shoutout to TheoKanning and his contributors for making [OpenAI-Java](https://github.com/TheoKanning/openai-java "Project page") : A ChatGPT API wrapper for Java 99 | 100 | Made my life much easier 😁👍 101 | 102 | 103 | -------------------------------------------------------------------------------- /ChatGPT/src/ChatLoader.java: -------------------------------------------------------------------------------- 1 | import javax.swing.JFrame; 2 | import javax.swing.JPanel; 3 | import javax.swing.JPopupMenu; 4 | import javax.swing.border.EmptyBorder; 5 | import javax.swing.text.BadLocationException; 6 | import javax.swing.JList; 7 | import javax.swing.JMenuItem; 8 | import javax.swing.JOptionPane; 9 | import javax.swing.JScrollPane; 10 | import java.awt.event.ActionListener; 11 | import java.awt.event.MouseAdapter; 12 | import java.awt.event.ActionEvent; 13 | import java.awt.event.MouseEvent; 14 | import java.io.File; 15 | import java.io.FileFilter; 16 | import java.util.Arrays; 17 | import java.util.Comparator; 18 | 19 | import javax.swing.DefaultListModel; 20 | import java.awt.BorderLayout; 21 | 22 | public class ChatLoader extends JFrame { 23 | 24 | private JPanel contentPane; 25 | private JList fileList; 26 | private DefaultListModel model; 27 | private JPopupMenu popupMenu; 28 | private JPopupMenu popupMenu2; 29 | private String path; 30 | private int selectedIndex; 31 | 32 | /** 33 | * Launch the application. 34 | */ 35 | /*public static void main(String[] args) { 36 | EventQueue.invokeLater(new Runnable() { 37 | public void run() { 38 | try { 39 | ChatLoader frame = new ChatLoader(); 40 | frame.setVisible(true); 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | } 44 | } 45 | }); 46 | }*/ 47 | 48 | class FileListItem { 49 | private String displayName; 50 | private String filePath; 51 | 52 | public FileListItem(String displayName, String filePath) { 53 | this.displayName = displayName; 54 | this.filePath = filePath; 55 | } 56 | 57 | public String getDisplayName() { 58 | return displayName; 59 | } 60 | 61 | public String getFilePath() { 62 | return filePath; 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return displayName; 68 | } 69 | } 70 | 71 | /** 72 | * Create the frame. 73 | */ 74 | public ChatLoader(String path) { 75 | this.path = path; 76 | setTitle("Chat History"); 77 | setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 78 | setBounds(100, 100, 450, 288); 79 | contentPane = new JPanel(); 80 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 81 | setContentPane(contentPane); 82 | 83 | model = new DefaultListModel<>(); 84 | fileList = new JList<>(model); 85 | 86 | 87 | popupMenu = new JPopupMenu(); 88 | popupMenu2 = new JPopupMenu(); 89 | 90 | // Add menu items to the PopupMenu 91 | JMenuItem deleteItem = new JMenuItem("Delete"); 92 | JMenuItem renameItem = new JMenuItem("Rename"); 93 | JMenuItem refreshItems = new JMenuItem("Refresh"); 94 | JMenuItem sortItems = new JMenuItem("Sort"); 95 | 96 | JMenuItem refreshItems2 = new JMenuItem("Refresh"); 97 | JMenuItem sortItems2 = new JMenuItem("Sort"); 98 | 99 | popupMenu.add(deleteItem); 100 | popupMenu.add(renameItem); 101 | popupMenu.add(refreshItems); 102 | popupMenu.add(sortItems); 103 | 104 | popupMenu2.add(refreshItems2); 105 | popupMenu2.add(sortItems2); 106 | 107 | 108 | //Deletes selected chat file from fileList if it exists 109 | deleteItem.addActionListener(new ActionListener() { 110 | public void actionPerformed(ActionEvent e) { 111 | 112 | File file = new File(fileList.getModel().getElementAt(selectedIndex).filePath); //replace path/to/file with the actual file path 113 | if(file.exists()) { //checks if the file exists 114 | file.delete(); //deletes the file 115 | model.removeElementAt(selectedIndex); 116 | } else { 117 | JOptionPane.showMessageDialog(null, "File not found", "Error", JOptionPane.ERROR_MESSAGE); 118 | } 119 | 120 | } 121 | }); 122 | 123 | 124 | //Renames selected chat file from fileList if it exists 125 | renameItem.addActionListener(new ActionListener() { 126 | public void actionPerformed(ActionEvent e) { 127 | String title = JOptionPane.showInputDialog(null, "Please enter a title:", "Rename", JOptionPane.PLAIN_MESSAGE); 128 | if (title != null) { 129 | File file = new File(fileList.getModel().getElementAt(selectedIndex).filePath); 130 | String path = file.getParent(); 131 | String name = file.getName(); 132 | String ext = name.substring(name.lastIndexOf('.')); 133 | File newFile = new File(path, title + ext); 134 | 135 | if (newFile.exists()) { 136 | JOptionPane.showMessageDialog(null, "File already exists", "Error", JOptionPane.ERROR_MESSAGE); 137 | } else { 138 | File txtFile = new File(path, title + ".json"); 139 | file.renameTo(newFile); 140 | new File(path, name.substring(0, name.length() - ext.length()) + ".json").renameTo(txtFile); 141 | refreshlist(); 142 | JOptionPane.showMessageDialog(null, "File renamed successfully", "Success", JOptionPane.INFORMATION_MESSAGE); 143 | } 144 | } 145 | 146 | } 147 | }); 148 | 149 | //Adds ActionListners to JPopMenu elements 150 | refreshItems.addActionListener(new ActionListener() { 151 | public void actionPerformed(ActionEvent e) { 152 | refreshlist(); 153 | } 154 | }); 155 | 156 | refreshItems2.addActionListener(new ActionListener() { 157 | public void actionPerformed(ActionEvent e) { 158 | refreshlist(); 159 | } 160 | }); 161 | 162 | sortItems.addActionListener(new ActionListener() { 163 | public void actionPerformed(ActionEvent e) { 164 | MainFrame.isAlpha = !MainFrame.isAlpha; 165 | refreshlist(); 166 | } 167 | }); 168 | 169 | sortItems2.addActionListener(new ActionListener() { 170 | public void actionPerformed(ActionEvent e) { 171 | MainFrame.isAlpha = !MainFrame.isAlpha; 172 | refreshlist(); 173 | } 174 | }); 175 | //------------------------------------------ 176 | 177 | //Sends selected chat file to MainFrame.loadchat() and loads it into the main JFrame 178 | fileList.addMouseListener(new MouseAdapter() { 179 | public void mousePressed(MouseEvent e) { 180 | if (e.isPopupTrigger()) showPopupMenu(e); 181 | } 182 | public void mouseReleased(MouseEvent e) { 183 | if (e.isPopupTrigger()) showPopupMenu(e); 184 | } 185 | public void mouseClicked(MouseEvent e) { 186 | if (e.getClickCount() == 2) { 187 | selectedIndex = fileList.getSelectedIndex(); 188 | try { 189 | MainFrame.loadchat(fileList.getModel().getElementAt(selectedIndex).filePath, fileList.getModel().getElementAt(selectedIndex).displayName); 190 | } catch (Exception e1) { 191 | // TODO Auto-generated catch block 192 | e1.printStackTrace(); 193 | } 194 | 195 | } 196 | } 197 | }); 198 | 199 | refreshlist(); 200 | contentPane.setLayout(new BorderLayout(0, 0)); 201 | JScrollPane scrollPane = new JScrollPane(fileList); 202 | scrollPane.setViewportView(fileList); 203 | contentPane.add(scrollPane); 204 | } 205 | 206 | //Refreshes a list with file names from a directory. 207 | //The directory is represented by the "path" variable. 208 | //The function applies a file filter that only accepts files with ".json" extension and sorts them by the date of last modification or alphabetically depending on the value of the "isAlpha" static boolean variable. 209 | //Finally, it creates a new list item for each file, which contains its name and complete path without an extension, and adds it to the list model. 210 | public void refreshlist() { 211 | File directory = new File(path); 212 | model.clear(); 213 | File[] files = directory.listFiles(new FileFilter() { 214 | public boolean accept(File file) { 215 | return file.isFile() && file.getName().endsWith(".json"); 216 | } 217 | }); 218 | 219 | if(MainFrame.isAlpha) { 220 | Arrays.sort(files, new Comparator() { 221 | public int compare(File f1, File f2) { 222 | long diff = f2.lastModified() - f1.lastModified(); 223 | return Long.signum(diff); 224 | } 225 | }); 226 | } 227 | 228 | for (File file : files) { 229 | String displayName = file.getName(); 230 | String filePath = file.getAbsolutePath(); 231 | FileListItem item = new FileListItem(displayName.replaceFirst("[.][^.]+$", ""), filePath); 232 | model.addElement(item); 233 | } 234 | } 235 | 236 | //Shows correct PopupMenu on right-click based on if a file from fileList is selected 237 | private void showPopupMenu(MouseEvent e) { 238 | selectedIndex = fileList.getSelectedIndex(); 239 | if (selectedIndex == -1) { 240 | popupMenu2.show(e.getComponent(), e.getX(), e.getY()); 241 | }else { 242 | 243 | popupMenu.show(e.getComponent(), e.getX(), e.getY()); 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /ChatGPT/src/MainFrame.java: -------------------------------------------------------------------------------- 1 | import java.awt.Color; 2 | import java.awt.Desktop; 3 | import java.awt.Dimension; 4 | import java.awt.EventQueue; 5 | 6 | import javax.swing.JFrame; 7 | import javax.swing.JOptionPane; 8 | import javax.swing.JPanel; 9 | import javax.swing.JPopupMenu; 10 | import javax.swing.border.EmptyBorder; 11 | import javax.swing.event.HyperlinkEvent; 12 | import javax.swing.event.HyperlinkListener; 13 | import javax.swing.text.BadLocationException; 14 | import javax.swing.text.DefaultCaret; 15 | import javax.swing.text.Style; 16 | import javax.swing.text.StyleConstants; 17 | import javax.swing.text.StyleContext; 18 | import javax.swing.text.StyledDocument; 19 | 20 | import com.google.gson.Gson; 21 | import com.jtattoo.plaf.hifi.HiFiLookAndFeel; 22 | 23 | 24 | import javax.swing.JTextArea; 25 | import javax.swing.KeyStroke; 26 | //import javax.swing.JTextPane; 27 | import javax.swing.UIManager; 28 | import javax.swing.JButton; 29 | import javax.swing.JFileChooser; 30 | 31 | import java.awt.event.ActionListener; 32 | import java.awt.event.KeyAdapter; 33 | import java.awt.event.KeyEvent; 34 | import java.io.BufferedReader; 35 | import java.io.File; 36 | import java.io.FileInputStream; 37 | import java.io.FileNotFoundException; 38 | import java.io.FileOutputStream; 39 | import java.io.FileReader; 40 | import java.io.FileWriter; 41 | import java.io.IOException; 42 | import java.io.InputStream; 43 | import java.io.PrintWriter; 44 | import java.lang.reflect.Field; 45 | import java.net.URISyntaxException; 46 | import java.nio.charset.Charset; 47 | import java.nio.file.Files; 48 | import java.nio.file.Paths; 49 | import java.time.Duration; 50 | import java.util.ArrayList; 51 | import java.util.HashMap; 52 | import java.util.Properties; 53 | import java.util.Random; 54 | import java.awt.event.ActionEvent; 55 | import javax.swing.JScrollPane; 56 | import java.awt.Font; 57 | import java.awt.Toolkit; 58 | 59 | import java.awt.datatransfer.Clipboard; 60 | import java.awt.datatransfer.DataFlavor; 61 | import java.awt.datatransfer.StringSelection; 62 | import java.awt.datatransfer.Transferable; 63 | import java.awt.datatransfer.UnsupportedFlavorException; 64 | 65 | import javax.swing.ImageIcon; 66 | import javax.swing.JMenuBar; 67 | import javax.swing.JMenu; 68 | import javax.swing.JMenuItem; 69 | import java.awt.event.MouseAdapter; 70 | import java.awt.event.MouseEvent; 71 | import javax.swing.JEditorPane; 72 | 73 | import com.theokanning.openai.completion.chat.ChatCompletionChoice; 74 | import com.theokanning.openai.completion.chat.ChatCompletionRequest; 75 | import com.theokanning.openai.completion.chat.ChatMessage; 76 | import com.theokanning.openai.completion.chat.ChatMessageRole; 77 | import com.theokanning.openai.service.OpenAiService; 78 | 79 | import org.commonmark.node.*; 80 | import org.commonmark.parser.Parser; 81 | import org.commonmark.renderer.html.HtmlRenderer; 82 | 83 | public class MainFrame extends JFrame { 84 | 85 | private static MainFrame frame; 86 | private JPanel contentPane; 87 | 88 | private OpenAiService service; 89 | private final static ArrayList messages = new ArrayList<>(); 90 | private static JTextArea ChatArea; 91 | private static JButton SubmitButton; 92 | private static JScrollPane scrollPane; 93 | private static JScrollPane scrollPane_1; 94 | private static JButton SaveButton; 95 | private static JButton ImportButton; 96 | private static JButton ResetButton; 97 | 98 | private static JEditorPane DisplayArea; 99 | private static JEditorPane HTMLArea; 100 | private static StyledDocument doc; 101 | private JMenuBar menuBar; 102 | private static String GPTConvo; 103 | 104 | private File FGPTConvo; 105 | 106 | public static Properties prop; 107 | public static String version = "1.3.2"; 108 | private Boolean first = true; 109 | private Boolean chathistory = true; 110 | private Boolean autotitle = true; 111 | private Boolean enter2submit = true; 112 | private Boolean cloaderopen = false; 113 | private Boolean aframeopen = false; 114 | private static Boolean isHTMLView = false; 115 | private static Parser parser; 116 | private static HtmlRenderer renderer; 117 | public static Boolean isAlpha = true; 118 | private Boolean isStreamRunning = false; 119 | private static int FormSize = 3; 120 | private static int FontSize = 12; 121 | public static int seltheme = 0; 122 | private ChatLoader cloader; 123 | private String chatDir; 124 | 125 | //Initializing Style objects for RTF text in DisplayArea 126 | private static Style YouStyle; 127 | private static Style InvisibleStyle; 128 | private static Style GPTStyle; 129 | private static Style ChatStyle; 130 | private static Style ErrorStyle; 131 | private static MainFrame INSTANCE = null; 132 | 133 | //This function is used to load a chat from a file specified by the full file path and filename. 134 | //It sets the title of the instance to include the filename and clears the display area. 135 | //It also resets the messages and reads them from the file. If the view is set to HTML, it resets the HTML area style and renders the document. 136 | //If there is an exception, it displays an error message and prints the stack trace. Finally, it sets the FGPTConvo file and sets the first flag to false. 137 | public static void loadchat(String fullfilepath, String filename) throws BadLocationException { 138 | 139 | INSTANCE.setTitle("JavaGPT - " + filename); 140 | try { 141 | 142 | DisplayArea.setText(""); 143 | 144 | messages.clear(); 145 | readMessagesFromFile(fullfilepath); 146 | if(isHTMLView) { 147 | resetHTMLAreaStyle(); 148 | Node document = parser.parse(DisplayArea.getDocument().getText(0, DisplayArea.getDocument().getLength())); 149 | //System.out.println(renderer.render(document)); 150 | HTMLArea.setText(renderer.render(document)); 151 | } 152 | 153 | 154 | } catch (Exception e) { 155 | JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 156 | e.printStackTrace(); 157 | } 158 | INSTANCE.FGPTConvo = new File(fullfilepath); 159 | 160 | INSTANCE.first = false; 161 | 162 | } 163 | 164 | //Writes chat contents to .json format 165 | public void writeMessagesToFile(String filename) throws IOException { 166 | try (PrintWriter writer = new PrintWriter(filename)) { 167 | Gson gson = new Gson(); 168 | for (ChatMessage message : messages) { 169 | String json = gson.toJson(message); 170 | writer.println(json); 171 | } 172 | } 173 | } 174 | 175 | //Reads chat contents from provided .json, stores it in the messages ArrayList and outputs contents in DisplayArea 176 | public static void readMessagesFromFile(String filename) throws IOException { 177 | try (BufferedReader reader = new BufferedReader(new FileReader(filename))) { 178 | String line; 179 | Gson gson = new Gson(); 180 | while ((line = reader.readLine()) != null) { 181 | ChatMessage message = gson.fromJson(line, ChatMessage.class); 182 | if(message.getRole().equals("user")) { 183 | try { 184 | doc.insertString(doc.getLength(), "You", YouStyle); 185 | doc.insertString(doc.getLength(), ":\n", InvisibleStyle); 186 | doc.insertString(doc.getLength(), message.getContent() + "\n\n", ChatStyle); 187 | } catch (BadLocationException e) { 188 | e.printStackTrace(); 189 | } 190 | }else{ 191 | try { 192 | doc.insertString(doc.getLength(), "ChatGPT", GPTStyle); 193 | doc.insertString(doc.getLength(), ":\n", InvisibleStyle); 194 | doc.insertString(doc.getLength(), message.getContent() + "\n\n", ChatStyle); 195 | } catch (BadLocationException e) { 196 | e.printStackTrace(); 197 | } 198 | } 199 | messages.add(message); 200 | } 201 | } 202 | } 203 | 204 | //Refreshes DisplayArea contents with current messages ArrayList items 205 | public void refreshMessages() { 206 | DisplayArea.setText(""); 207 | for (ChatMessage message : messages) { 208 | if(message.getRole().equals("user")) { 209 | try { 210 | doc.insertString(doc.getLength(), "You", YouStyle); 211 | doc.insertString(doc.getLength(), ":\n", InvisibleStyle); 212 | doc.insertString(doc.getLength(), message.getContent() + "\n\n", ChatStyle); 213 | } catch (BadLocationException e) { 214 | e.printStackTrace(); 215 | } 216 | }else{ 217 | try { 218 | doc.insertString(doc.getLength(), "ChatGPT", GPTStyle); 219 | doc.insertString(doc.getLength(), ":\n", InvisibleStyle); 220 | doc.insertString(doc.getLength(), message.getContent() + "\n\n", ChatStyle); 221 | } catch (BadLocationException e) { 222 | e.printStackTrace(); 223 | } 224 | } 225 | } 226 | } 227 | 228 | 229 | //Used in newFile() to create a new file name (Ex: Chat_x0y, Chat_09k, Chat_rc7) 230 | public static String getRandomString() { 231 | String letters = "abcdefghijklmnopqrstuvwxyz1234567890"; 232 | Random rand = new Random(); 233 | StringBuilder sb = new StringBuilder(); 234 | 235 | for (int i = 0; i < 3; i++) { 236 | int index = rand.nextInt(letters.length()); 237 | sb.append(letters.charAt(index)); 238 | } 239 | 240 | return sb.toString(); 241 | } 242 | 243 | 244 | //Creates a new chat file by setting FGPTConvo File object to a new file name 245 | public void newFile() { 246 | String randfilename = getRandomString(); 247 | FGPTConvo = new File(chatDir + "\\Chat_" + randfilename + ".json"); 248 | while(FGPTConvo.exists()) { 249 | randfilename = getRandomString(); 250 | FGPTConvo = new File(chatDir + "\\Chat_" + randfilename + ".json"); 251 | } 252 | setTitle("JavaGPT - Chat_" + randfilename); 253 | } 254 | 255 | //Resets all objects used for chat. Is invoked when "New Chat" is pressed or a chat file is loaded 256 | public void Reset() { 257 | isStreamRunning = false; 258 | messages.clear(); 259 | FGPTConvo = null; 260 | GPTConvo = ""; 261 | DisplayArea.setText(""); 262 | HTMLArea.setText(""); 263 | resetHTMLAreaStyle(); 264 | ChatArea.setText(""); 265 | setTitle("JavaGPT"); 266 | first = true; 267 | } 268 | 269 | /** 270 | * Launch the application. 271 | */ 272 | public static void main(String[] args) { 273 | EventQueue.invokeLater(new Runnable() { 274 | public void run() { 275 | 276 | //Sets project to support Unicode 277 | try { 278 | System.setProperty("file.encoding","UTF-8"); 279 | Field charset = Charset.class.getDeclaredField("defaultCharset"); 280 | charset.setAccessible(true); 281 | charset.set(null,null); 282 | }catch(Exception e) {} 283 | //------------------------------- 284 | //Loads properties------------------------ 285 | prop = new Properties(); 286 | InputStream input = null; 287 | 288 | try { 289 | input = new FileInputStream("config.properties"); 290 | prop.load(input); 291 | } catch (FileNotFoundException e1) { 292 | int choice = JOptionPane.showConfirmDialog(null, 293 | "No config file found. Would you like to create one?", 294 | "Create Config File", JOptionPane.YES_NO_OPTION); 295 | 296 | if(choice == JOptionPane.YES_OPTION) { 297 | String apikey = JOptionPane.showInputDialog( 298 | null, "Please enter your API key:"); 299 | 300 | prop.setProperty("apikey", apikey); 301 | prop.setProperty("model", "gpt-3.5-turbo"); 302 | prop.setProperty("maxTokens", "1024"); 303 | prop.setProperty("timeout", "30"); 304 | prop.setProperty("proxyip", ""); // WIP Support will be added back 305 | prop.setProperty("proxyport", ""); // WIP Support will be added back 306 | prop.setProperty("proxytype", ""); 307 | prop.setProperty("autotitle", "true"); 308 | prop.setProperty("autoscroll", "true"); 309 | prop.setProperty("EnterToSubmit", "true"); 310 | prop.setProperty("chat_history", "true"); 311 | prop.setProperty("chat_location_override", ""); 312 | prop.setProperty("WindowSize", "medium"); 313 | prop.setProperty("FontSize", "12"); 314 | prop.setProperty("Theme", "dark"); 315 | 316 | try { 317 | FileOutputStream out = new FileOutputStream("config.properties"); 318 | prop.store(out, "Generated config file"); 319 | out.close(); 320 | 321 | JOptionPane.showMessageDialog(null, "Config file created successfully!"); 322 | } catch (IOException ex) { 323 | ex.printStackTrace(); 324 | } 325 | } 326 | e1.printStackTrace(); 327 | } 328 | catch (IOException e) { 329 | // TODO Auto-generated catch block 330 | e.printStackTrace(); 331 | } finally { 332 | if (input != null) { 333 | try { 334 | input.close(); 335 | } catch (IOException e) { 336 | e.printStackTrace(); 337 | JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 338 | } 339 | } 340 | } 341 | //---------------------------------------- 342 | //Sets proxy settings 343 | if(prop.getProperty("proxyip") != null && !prop.getProperty("proxyip").isEmpty() && prop.getProperty("proxyport") != null && !prop.getProperty("proxyport").isEmpty()) { 344 | if(prop.getProperty("proxytype").toLowerCase().equals("http")) { 345 | System.setProperty("http.proxyHost", prop.getProperty("proxyip")); 346 | System.setProperty("http.proxyPort", prop.getProperty("proxyport")); 347 | }else if(prop.getProperty("proxytype").toLowerCase().equals("https")){ 348 | System.setProperty("https.proxyHost", prop.getProperty("proxyip")); 349 | System.setProperty("https.proxyPort", prop.getProperty("proxyport")); 350 | }else { 351 | System.getProperties().put( "proxySet", "true" ); 352 | System.getProperties().put( "socksProxyHost", prop.getProperty("proxyip") ); 353 | System.getProperties().put( "socksProxyPort", prop.getProperty("proxyport") ); 354 | } 355 | } 356 | //------------------- 357 | //Sets selected JTattoo theme------------- 358 | try { 359 | if(!prop.getProperty("Theme").isEmpty()) { 360 | if(prop.getProperty("Theme").equals("dark")) { 361 | Properties p = new Properties(); 362 | p.put("windowTitleFont", "Ebrima PLAIN 15"); 363 | p.put("backgroundPattern", "off"); 364 | p.put("logoString", ""); 365 | HiFiLookAndFeel.setCurrentTheme(p); 366 | UIManager.setLookAndFeel("com.jtattoo.plaf.hifi.HiFiLookAndFeel"); 367 | seltheme = 1; 368 | } 369 | } 370 | } catch (Exception e) { 371 | e.printStackTrace(); 372 | } 373 | //---------------------------------------- 374 | 375 | frame = new MainFrame(); //Loads main JFrame 376 | 377 | //Scales JFrame based on "WindowSize" prop 378 | switch(prop.getProperty("WindowSize")){ 379 | case "small": 380 | FormSize=1; 381 | break; 382 | case "large": 383 | FormSize=2; 384 | break; 385 | default: 386 | FormSize=3; 387 | break; 388 | } 389 | setFormSize(); 390 | //---------------------------------------- 391 | //Sets app icon to JavaGPT logo 392 | frame.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("logo.png"))); 393 | 394 | 395 | if(prop.getProperty("FontSize") != null && !prop.getProperty("FontSize").isEmpty()) { 396 | try { 397 | FontSize = Integer.parseInt(prop.getProperty("FontSize")); 398 | } catch (NumberFormatException e) { 399 | } 400 | } 401 | 402 | //Makes JFrame visible 403 | frame.setVisible(true); 404 | 405 | } 406 | }); 407 | } 408 | 409 | /** 410 | * Create the frame. 411 | * @param GPTStyle 412 | * @param ChatStyle 413 | */ 414 | public MainFrame() { 415 | setResizable(false); 416 | INSTANCE = this; 417 | 418 | setTitle("JavaGPT"); 419 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 420 | 421 | //Initializes OpenAI's ChatGPT API with provided API key 422 | service = new OpenAiService(prop.getProperty("apikey"),(prop.getProperty("timeout") == null && prop.getProperty("timeout").isEmpty()) ? Duration.ZERO : Duration.ofSeconds(Long.parseLong(prop.getProperty("timeout")))); 423 | 424 | menuBar = new JMenuBar(); 425 | setJMenuBar(menuBar); 426 | 427 | JMenu OptionMenu = new JMenu("Options"); 428 | menuBar.add(OptionMenu); 429 | 430 | //Renderer and Parser for HTMLView 431 | parser = Parser.builder().build(); 432 | renderer = HtmlRenderer.builder().build(); 433 | // 434 | //Code for HTML Viewer JMenu. If clicked, it will set isHTMLView to its counter value. 435 | //If true, it will switch scrollPane to show HTMLArea and display the plain text contents for DisplayArea in it 436 | //If false, it will switch scrollPane to show DisplayArea 437 | JMenuItem HTMLViewMenuItem = new JMenuItem("HTML View"); 438 | HTMLViewMenuItem.addActionListener(new ActionListener() { 439 | public void actionPerformed(ActionEvent e) { 440 | if(isHTMLView) { 441 | try { 442 | scrollPane.setViewportView(DisplayArea); 443 | HTMLViewMenuItem.setText("HTML View"); 444 | isHTMLView=false; 445 | } catch (Exception e1) { 446 | // TODO Auto-generated catch block 447 | e1.printStackTrace(); 448 | } 449 | }else { 450 | try { 451 | scrollPane.setViewportView(HTMLArea); 452 | resetHTMLAreaStyle(); 453 | Node document = parser.parse(DisplayArea.getDocument().getText(0, DisplayArea.getDocument().getLength())); 454 | HTMLArea.setText(renderer.render(document)); 455 | HTMLViewMenuItem.setText("Normal View"); 456 | isHTMLView=true; 457 | } catch (Exception e1) { 458 | // TODO Auto-generated catch block 459 | e1.printStackTrace(); 460 | } 461 | 462 | } 463 | 464 | } 465 | }); 466 | 467 | OptionMenu.add(HTMLViewMenuItem); 468 | 469 | 470 | //Will scale the JFrame based on preset dimensions for JMenu options Large, Medium, & Small 471 | JMenu FormSizeMenu = new JMenu("Form Size"); 472 | OptionMenu.add(FormSizeMenu); 473 | 474 | JMenuItem SmallMenuItem = new JMenuItem("Small"); 475 | FormSizeMenu.add(SmallMenuItem); 476 | SmallMenuItem.addActionListener(new ActionListener() { 477 | public void actionPerformed(ActionEvent e) { 478 | if(FormSize != 1) { 479 | FormSize = 1; 480 | setFormSize(); 481 | } 482 | } 483 | }); 484 | 485 | JMenuItem MediumMenuItem = new JMenuItem("Medium"); 486 | FormSizeMenu.add(MediumMenuItem); 487 | MediumMenuItem.addActionListener(new ActionListener() { 488 | public void actionPerformed(ActionEvent e) { 489 | if(FormSize != 3) { 490 | FormSize = 3; 491 | setFormSize(); 492 | } 493 | } 494 | }); 495 | 496 | JMenuItem LargeMenuItem = new JMenuItem("Large"); 497 | FormSizeMenu.add(LargeMenuItem); 498 | LargeMenuItem.addActionListener(new ActionListener() { 499 | public void actionPerformed(ActionEvent e) { 500 | if(FormSize != 2) { 501 | FormSize = 2; 502 | setFormSize(); 503 | } 504 | } 505 | }); 506 | 507 | JMenu FontSizeMenu = new JMenu("Font Size"); 508 | OptionMenu.add(FontSizeMenu); 509 | 510 | JMenuItem DefaultFSMenuItem = new JMenuItem("Default (12)"); 511 | FontSizeMenu.add(DefaultFSMenuItem); 512 | DefaultFSMenuItem.addActionListener(new ActionListener() { 513 | public void actionPerformed(ActionEvent e) { 514 | if(FontSize != 12) { 515 | FontSize = 12; 516 | setFontSize(); 517 | refreshMessages(); 518 | } 519 | } 520 | }); 521 | 522 | JMenuItem LargeFSMenuItem = new JMenuItem("Large (16)"); 523 | FontSizeMenu.add(LargeFSMenuItem); 524 | LargeFSMenuItem.addActionListener(new ActionListener() { 525 | public void actionPerformed(ActionEvent e) { 526 | if(FontSize != 16) { 527 | FontSize = 16; 528 | setFontSize(); 529 | refreshMessages(); 530 | } 531 | } 532 | }); 533 | 534 | JMenuItem ExtraLargeFSMenuItem = new JMenuItem("Ex-Large (20)"); 535 | FontSizeMenu.add(ExtraLargeFSMenuItem); 536 | ExtraLargeFSMenuItem.addActionListener(new ActionListener() { 537 | public void actionPerformed(ActionEvent e) { 538 | if(FontSize != 20) { 539 | FontSize = 20; 540 | setFontSize(); 541 | refreshMessages(); 542 | } 543 | } 544 | }); 545 | 546 | JMenuItem CustomFSMenuItem = new JMenuItem("Custom"); 547 | FontSizeMenu.add(CustomFSMenuItem); 548 | CustomFSMenuItem.addActionListener(new ActionListener() { 549 | public void actionPerformed(ActionEvent e) { 550 | String input = JOptionPane.showInputDialog(null, "Enter font size:", "Font Size", JOptionPane.PLAIN_MESSAGE); 551 | try { 552 | FontSize = Integer.parseInt(input); 553 | setFontSize(); 554 | refreshMessages(); 555 | } catch (NumberFormatException e1) { 556 | JOptionPane.showMessageDialog(null, "Invalid font size", "Error", JOptionPane.ERROR_MESSAGE); 557 | } 558 | 559 | } 560 | }); 561 | //---------------------------------------------------------------------------------- 562 | 563 | 564 | JMenu RenameMenu = new JMenu("Rename"); 565 | OptionMenu.add(RenameMenu); 566 | 567 | //Rename option which when clicked has ChatGPT generate a title based on current chat context 568 | JMenuItem AutoMenuItem = new JMenuItem("Auto"); 569 | RenameMenu.add(AutoMenuItem); 570 | AutoMenuItem.addActionListener(new ActionListener() { 571 | public void actionPerformed(ActionEvent e) { 572 | if(FGPTConvo != null) { 573 | AutoTitle(); 574 | }else { 575 | JOptionPane.showMessageDialog(null, "No chat file loaded", "Error", JOptionPane.ERROR_MESSAGE); 576 | } 577 | 578 | 579 | } 580 | }); 581 | 582 | //This code adds a manual menu item to a rename menu. 583 | //When the manual menu item is clicked, it prompts the user to enter a title for the file to be renamed. 584 | //If the file already exists with the inputted title, an error message is shown. 585 | //Otherwise, the file is renamed and a success message is shown along with the new title in the window title bar. 586 | //However, if no file is loaded, an error message is shown. 587 | JMenuItem ManualMenuItem = new JMenuItem("Manual"); 588 | RenameMenu.add(ManualMenuItem); 589 | ManualMenuItem.addActionListener(new ActionListener() { 590 | public void actionPerformed(ActionEvent e) { 591 | if(FGPTConvo != null) { 592 | String title = JOptionPane.showInputDialog(null, "Please enter a title:", "Rename", JOptionPane.PLAIN_MESSAGE); 593 | if(title != null) { 594 | File file = new File(FGPTConvo.getParentFile(), title + ".json"); 595 | if(file.exists()) { 596 | JOptionPane.showMessageDialog(null, "File already exists", "Error", JOptionPane.ERROR_MESSAGE); 597 | }else { 598 | FGPTConvo.renameTo(file); 599 | FGPTConvo = file; 600 | JOptionPane.showMessageDialog(null, "File renamed successfully", "Success", JOptionPane.INFORMATION_MESSAGE); 601 | INSTANCE.setTitle("JavaGPT - " + title); 602 | } 603 | } 604 | }else { 605 | JOptionPane.showMessageDialog(null, "No chat file loaded", "Error", JOptionPane.ERROR_MESSAGE); 606 | } 607 | 608 | } 609 | }); 610 | 611 | //Deletes chat file if it exists 612 | JMenuItem DeleteMenuItem = new JMenuItem("Delete"); 613 | DeleteMenuItem.addActionListener(new ActionListener() { 614 | public void actionPerformed(ActionEvent e) { 615 | if(FGPTConvo != null && FGPTConvo.exists()) { //checks if the file exists 616 | FGPTConvo.delete(); //deletes the file 617 | Reset(); 618 | } else { 619 | JOptionPane.showMessageDialog(null, "File not found", "Error", JOptionPane.ERROR_MESSAGE); 620 | } 621 | 622 | } 623 | }); 624 | 625 | //Reverts chat contents to previous state by removing the last prompt & response from messages ArrayList and reloads the DisplayArea 626 | JMenuItem RevertMenuItem = new JMenuItem("Revert"); 627 | RevertMenuItem.addActionListener(new ActionListener() { 628 | public void actionPerformed(ActionEvent e) { 629 | if(messages.size() >= 4) { //checks if the file exists 630 | messages.remove(messages.size() - 1); 631 | messages.remove(messages.size() - 1); 632 | refreshMessages(); 633 | } else { 634 | if(messages.isEmpty()) { 635 | JOptionPane.showMessageDialog(null, "No chat loaded", "Error", JOptionPane.ERROR_MESSAGE); 636 | }else { 637 | JOptionPane.showMessageDialog(null, "Can't revert first prompt", "Error", JOptionPane.ERROR_MESSAGE); 638 | } 639 | } 640 | 641 | } 642 | }); 643 | OptionMenu.add(RevertMenuItem); 644 | OptionMenu.add(DeleteMenuItem); 645 | 646 | //Opens "About" JFrame 647 | JMenuItem AboutMenuItem = new JMenuItem("About"); 648 | AboutMenuItem.addActionListener(new ActionListener() { 649 | public void actionPerformed(ActionEvent e) { 650 | if(aframeopen != true) { 651 | AboutFrame aframe = new AboutFrame(); 652 | aframe.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("logo.png"))); 653 | aframe.setVisible(true); 654 | aframeopen = true; 655 | aframe.addWindowListener(new java.awt.event.WindowAdapter() { 656 | @Override 657 | public void windowClosing(java.awt.event.WindowEvent windowEvent) { 658 | aframeopen = false; 659 | } 660 | }); 661 | } 662 | 663 | } 664 | }); 665 | OptionMenu.add(AboutMenuItem); 666 | 667 | //Opens "ChatLoader" (Chat History) JFrame 668 | JMenu LoadChatButton = new JMenu("Load Chat"); 669 | LoadChatButton.addMouseListener(new MouseAdapter() { 670 | @Override 671 | public void mouseClicked(MouseEvent e) { 672 | if(cloaderopen != true) { 673 | cloader = new ChatLoader(chatDir); 674 | cloader.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("logo.png"))); 675 | cloader.setVisible(true); 676 | cloaderopen = true; 677 | cloader.addWindowListener(new java.awt.event.WindowAdapter() { 678 | @Override 679 | public void windowClosing(java.awt.event.WindowEvent windowEvent) { 680 | cloaderopen = false; 681 | } 682 | }); 683 | } 684 | } 685 | }); 686 | 687 | menuBar.add(LoadChatButton); 688 | 689 | contentPane = new JPanel(); 690 | 691 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 692 | setContentPane(contentPane); 693 | contentPane.setLayout(null); 694 | 695 | scrollPane = new JScrollPane(); 696 | 697 | contentPane.add(scrollPane); 698 | 699 | 700 | DisplayArea = new JEditorPane(); 701 | scrollPane.setViewportView(DisplayArea); 702 | DisplayArea.setEditable(false); 703 | DisplayArea.setContentType("text/rtf"); 704 | 705 | HTMLArea = new JEditorPane(); 706 | HTMLArea.setEditable(false); 707 | HTMLArea.setBackground(Color.white); 708 | HTMLArea.setContentType("text/html"); 709 | 710 | //Sets properties for Style objects 711 | StyleContext sc = StyleContext.getDefaultStyleContext(); 712 | 713 | YouStyle = sc.addStyle("bold", null); 714 | StyleConstants.setFontFamily(YouStyle, "Tahoma"); 715 | StyleConstants.setFontSize(YouStyle, FontSize); 716 | StyleConstants.setBold(YouStyle, true); 717 | 718 | GPTStyle = sc.addStyle("bold", null); 719 | StyleConstants.setFontFamily(GPTStyle, "Tahoma"); 720 | StyleConstants.setFontSize(GPTStyle, FontSize); 721 | StyleConstants.setBold(GPTStyle, true); 722 | StyleConstants.setForeground(GPTStyle, Color.RED); //getHSBColor(0, 0.8f, 0.8f) 723 | 724 | InvisibleStyle = sc.addStyle("bold", null); 725 | StyleConstants.setForeground(InvisibleStyle, DisplayArea.getBackground()); 726 | 727 | ChatStyle = sc.addStyle("black", null); 728 | StyleConstants.setFontFamily(ChatStyle, "Tahoma"); 729 | StyleConstants.setFontSize(ChatStyle, FontSize); 730 | 731 | ErrorStyle = sc.addStyle("ErrorStyle", null); 732 | StyleConstants.setItalic(ErrorStyle, true); 733 | StyleConstants.setFontFamily(ErrorStyle, "Tahoma"); 734 | StyleConstants.setFontSize(ErrorStyle, FontSize); 735 | 736 | if(seltheme == 1) { 737 | StyleConstants.setForeground(YouStyle, Color.ORANGE); //getHSBColor(30f/360, 0.8f, 1f) 738 | StyleConstants.setForeground(ChatStyle, Color.WHITE); //Color.getHSBColor(0f, 0f, 0.8f) 739 | StyleConstants.setForeground(ErrorStyle, Color.WHITE); //Color.getHSBColor(0f, 0f, 0.8f) 740 | }else { 741 | StyleConstants.setForeground(YouStyle, Color.BLUE); 742 | StyleConstants.setForeground(ChatStyle, Color.BLACK); 743 | StyleConstants.setForeground(ErrorStyle, Color.BLACK); 744 | } 745 | //------------------------------------ 746 | 747 | doc = (StyledDocument) DisplayArea.getDocument(); 748 | 749 | 750 | //"Submit" button 751 | SubmitButton = new JButton("Submit"); 752 | SubmitButton.addActionListener(new ActionListener() { 753 | public void actionPerformed(ActionEvent e) { 754 | submit(); 755 | } 756 | }); 757 | contentPane.add(SubmitButton); 758 | 759 | //"New Chat" button 760 | ResetButton = new JButton("New Chat"); 761 | ResetButton.addActionListener(new ActionListener() { 762 | public void actionPerformed(ActionEvent e) { 763 | Reset(); 764 | } 765 | }); 766 | contentPane.add(ResetButton); 767 | 768 | scrollPane_1 = new JScrollPane(); 769 | 770 | contentPane.add(scrollPane_1); 771 | 772 | ChatArea = new JTextArea(); 773 | ChatArea.setWrapStyleWord(true); 774 | scrollPane_1.setViewportView(ChatArea); 775 | ChatArea.setLineWrap(true); 776 | 777 | //Makes hotkeys for ChatArea 778 | ChatArea.addKeyListener(new KeyAdapter() { 779 | public void keyPressed(KeyEvent e) { 780 | if(enter2submit) { 781 | if (e.getKeyCode() == KeyEvent.VK_ENTER && e.isShiftDown()) { 782 | int caret = ChatArea.getCaretPosition(); 783 | ChatArea.insert("\n", caret); 784 | ChatArea.setCaretPosition(caret + 1); 785 | }else if(e.getKeyCode() == KeyEvent.VK_ENTER) { 786 | submit(); 787 | } 788 | }else { 789 | if (e.getKeyCode() == KeyEvent.VK_ENTER && e.isControlDown()) { 790 | submit(); 791 | } 792 | } 793 | } 794 | }); 795 | 796 | //Save Button code: takes contents of DisplayArea and saves it in plain text in user selected location with user provided filename 797 | SaveButton = new JButton(""); 798 | try { 799 | SaveButton.setIcon(new ImageIcon(MainFrame.class.getResource("FloppyDrive.gif"))); 800 | }catch(Exception e4) { 801 | JOptionPane.showMessageDialog(null, e4.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 802 | } 803 | SaveButton.setFont(new Font("Arial Black", Font.BOLD, 6)); 804 | SaveButton.addActionListener(new ActionListener() { 805 | public void actionPerformed(ActionEvent e) { 806 | 807 | File defaultDir = new File("."); 808 | JFileChooser fileChooser = new JFileChooser(defaultDir); 809 | fileChooser.setDialogTitle("Save chat"); 810 | 811 | int result = fileChooser.showSaveDialog(null); 812 | 813 | if (result == JFileChooser.APPROVE_OPTION) { 814 | 815 | File selectedFile = fileChooser.getSelectedFile(); 816 | 817 | try { 818 | 819 | FileWriter writer = new FileWriter(selectedFile); 820 | String plaintext = DisplayArea.getDocument().getText(0, DisplayArea.getDocument().getLength()); 821 | writer.write(plaintext); 822 | writer.close(); 823 | JOptionPane.showMessageDialog(null, "File saved successfully."); 824 | 825 | } catch (IOException e1) { 826 | e1.printStackTrace(); 827 | JOptionPane.showMessageDialog(null, e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 828 | } catch (BadLocationException e1) { 829 | // TODO Auto-generated catch block 830 | e1.printStackTrace(); 831 | } 832 | } 833 | } 834 | }); 835 | 836 | contentPane.add(SaveButton); 837 | 838 | //Imports user selected file and sets contents to ChatArea 839 | ImportButton = new JButton(""); 840 | ImportButton.addActionListener(new ActionListener() { 841 | public void actionPerformed(ActionEvent e) { 842 | JFileChooser fileChooser = new JFileChooser(); 843 | fileChooser.setDialogTitle("Import prompt"); 844 | int returnVal = fileChooser.showOpenDialog(null); 845 | if (returnVal == JFileChooser.APPROVE_OPTION) { 846 | String filename = fileChooser.getSelectedFile().getAbsolutePath(); 847 | try { 848 | ChatArea.setText(new String(Files.readAllBytes(Paths.get(filename)))); 849 | } catch (IOException e1) { 850 | // TODO Auto-generated catch block 851 | JOptionPane.showMessageDialog(null, e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 852 | } 853 | } 854 | } 855 | }); 856 | ImportButton.setIcon(new ImageIcon(MainFrame.class.getResource("upFolder.gif"))); 857 | contentPane.add(ImportButton); 858 | 859 | //Right-click menu MouseListners for various chat elements 860 | DisplayArea.addMouseListener(new MouseAdapter() { 861 | @Override 862 | public void mousePressed(MouseEvent e) { 863 | if (e.isPopupTrigger()) { 864 | showDisplayMenu(e.getX(), e.getY()); 865 | } 866 | } 867 | 868 | @Override 869 | public void mouseReleased(MouseEvent e) { 870 | if (e.isPopupTrigger()) { 871 | showDisplayMenu(e.getX(), e.getY()); 872 | } 873 | } 874 | }); 875 | 876 | HTMLArea.addMouseListener(new MouseAdapter() { 877 | @Override 878 | public void mousePressed(MouseEvent e) { 879 | if (e.isPopupTrigger()) { 880 | showHTMLMenu(e.getX(), e.getY()); 881 | } 882 | } 883 | 884 | @Override 885 | public void mouseReleased(MouseEvent e) { 886 | if (e.isPopupTrigger()) { 887 | showHTMLMenu(e.getX(), e.getY()); 888 | } 889 | } 890 | }); 891 | 892 | ChatArea.addMouseListener(new MouseAdapter() { 893 | @Override 894 | public void mousePressed(MouseEvent e) { 895 | if (e.isPopupTrigger()) { 896 | showChatMenu(e.getX(), e.getY()); 897 | } 898 | } 899 | 900 | @Override 901 | public void mouseReleased(MouseEvent e) { 902 | if (e.isPopupTrigger()) { 903 | showChatMenu(e.getX(), e.getY()); 904 | } 905 | } 906 | }); 907 | //-------------------------------------------------------------------- 908 | 909 | //Allows for HTMLArea to have HyperLinks 910 | HTMLArea.addHyperlinkListener(new HyperlinkListener() { 911 | public void hyperlinkUpdate(HyperlinkEvent e) { 912 | if(e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { 913 | try { 914 | Desktop.getDesktop().browse(e.getURL().toURI()); 915 | } catch (IOException | URISyntaxException e1) { 916 | // TODO Auto-generated catch block 917 | e1.printStackTrace(); 918 | } 919 | } 920 | } 921 | }); 922 | //Default 923 | /*setBounds(100, 100, 702, 707); //Uncomment this when editing design 924 | SubmitButton.setBounds(10, 554, 89, 23); 925 | ResetButton.setBounds(10, 616, 89, 23); 926 | scrollPane.setBounds(10, 11, 667, 532); 927 | scrollPane_1.setBounds(109, 554, 568, 85); 928 | SaveButton.setBounds(10, 585, 43, 23); 929 | ImportButton.setBounds(56, 585, 43, 23);*/ 930 | 931 | 932 | //Bulk property setting------------------- 933 | try { 934 | if(prop.getProperty("autoscroll") != null && !prop.getProperty("autoscroll").isEmpty()) { 935 | if(prop.getProperty("autoscroll").equals("true")) { 936 | DefaultCaret caret = (DefaultCaret)DisplayArea.getCaret(); 937 | caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); 938 | } 939 | } 940 | 941 | if(prop.getProperty("chat_history") != null && !prop.getProperty("chat_history").isEmpty()) { 942 | if(prop.getProperty("chat_history").equals("true")){ 943 | chathistory = true; 944 | }else{ 945 | chathistory = false; 946 | } 947 | } 948 | 949 | if(prop.getProperty("autotitle") != null && !prop.getProperty("autotitle").isEmpty()) { 950 | if(prop.getProperty("autotitle").equals("true")){ 951 | autotitle = true; 952 | }else{ 953 | autotitle = false; 954 | } 955 | } 956 | 957 | if(prop.getProperty("EnterToSubmit") != null && !prop.getProperty("EnterToSubmit").isEmpty()) { 958 | if(prop.getProperty("EnterToSubmit").equals("true")){ 959 | ChatArea.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "none"); 960 | }else{ 961 | enter2submit = false; 962 | } 963 | } 964 | 965 | 966 | if(prop.getProperty("chat_location_override") != null && !prop.getProperty("chat_location_override").isEmpty()){ 967 | chatDir = prop.getProperty("chat_location_override"); 968 | }else { 969 | try { 970 | chatDir = new File(getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath()).getParent(); 971 | chatDir = chatDir + "\\chat_history"; 972 | File directory = new File(chatDir); 973 | if (!directory.exists()) { 974 | directory.mkdirs(); 975 | } 976 | } catch (URISyntaxException e1) { 977 | JOptionPane.showMessageDialog(null, e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 978 | } 979 | } 980 | //---------------------------------------- 981 | } catch (Exception ex) { 982 | ex.printStackTrace(); 983 | JOptionPane.showMessageDialog(null, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 984 | 985 | } 986 | } 987 | 988 | //Processes ChatArea contents submitted by user to ChatGPT API and displays response 989 | private void submit() { 990 | if(isStreamRunning) { 991 | isStreamRunning = false; 992 | SubmitButton.setText("Submit"); 993 | return; 994 | } 995 | Thread myThread = new Thread(new Runnable() { 996 | public void run() { 997 | 998 | SubmitButton.setText("Cancel Req"); 999 | //Boolean success = false; 1000 | 1001 | try { 1002 | doc.insertString(doc.getLength(), "You", YouStyle); 1003 | doc.insertString(doc.getLength(), ":\n", InvisibleStyle); 1004 | doc.insertString(doc.getLength(), ChatArea.getText() + "\n\n", ChatStyle); 1005 | doc.insertString(doc.getLength(), "ChatGPT", GPTStyle); 1006 | doc.insertString(doc.getLength(), ":\n", InvisibleStyle); 1007 | } catch (BadLocationException e2) { 1008 | e2.printStackTrace(); 1009 | } 1010 | 1011 | 1012 | try { 1013 | 1014 | StringBuilder GPTConvoBuilder = new StringBuilder(); 1015 | 1016 | final ChatMessage userMessage = new ChatMessage(ChatMessageRole.USER.value(), ChatArea.getText()); 1017 | messages.add(userMessage); 1018 | 1019 | ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest 1020 | .builder() 1021 | .model(prop.getProperty("model")) 1022 | .messages(messages) 1023 | .n(1) 1024 | .maxTokens(Integer.parseInt(prop.getProperty("maxTokens"))) 1025 | .logitBias(new HashMap<>()) 1026 | .build(); 1027 | 1028 | isStreamRunning = true; 1029 | service.streamChatCompletion(chatCompletionRequest) 1030 | .doOnError(Throwable::printStackTrace) 1031 | .takeWhile(resultsBatch -> isStreamRunning) 1032 | .blockingForEach(chunk -> { 1033 | for (ChatCompletionChoice choice : chunk.getChoices()) { 1034 | if(choice.getMessage().getContent() != null) { 1035 | GPTConvoBuilder.append(choice.getMessage().getContent()); 1036 | } 1037 | try { 1038 | //String messageContent = new String(choice.getMessage().getContent().getBytes("UTF-8"), "UTF-8"); 1039 | //doc.putProperty("console.encoding", "UTF-8"); 1040 | 1041 | doc.insertString(doc.getLength(), choice.getMessage().getContent(), ChatStyle); 1042 | 1043 | } catch (BadLocationException e2) { 1044 | e2.printStackTrace(); 1045 | } 1046 | } 1047 | }); 1048 | 1049 | //service.shutdownExecutor(); 1050 | 1051 | if(isStreamRunning) { 1052 | 1053 | try { 1054 | doc.insertString(doc.getLength(), "\n\n", ChatStyle); 1055 | if(isHTMLView) { 1056 | resetHTMLAreaStyle(); 1057 | Node document = parser.parse(DisplayArea.getDocument().getText(0, DisplayArea.getDocument().getLength())); 1058 | HTMLArea.setText(renderer.render(document)); 1059 | } 1060 | 1061 | } catch (BadLocationException e2) { 1062 | e2.printStackTrace(); 1063 | } 1064 | 1065 | GPTConvo = GPTConvoBuilder.toString(); 1066 | final ChatMessage systemMessage = new ChatMessage(ChatMessageRole.SYSTEM.value(), GPTConvo); 1067 | messages.add(systemMessage); 1068 | 1069 | if(chathistory) { 1070 | 1071 | if(first) { 1072 | 1073 | newFile(); 1074 | } 1075 | 1076 | try { 1077 | writeMessagesToFile(FGPTConvo.getPath()); 1078 | } catch (IOException e) { 1079 | // TODO Auto-generated catch block 1080 | e.printStackTrace(); 1081 | } 1082 | if(first && autotitle){ 1083 | AutoTitle(); 1084 | first = false; 1085 | } 1086 | } 1087 | 1088 | ChatArea.setText(""); 1089 | 1090 | }else { 1091 | if(messages.size() != 0) { 1092 | messages.remove(messages.size() - 1); 1093 | doc.insertString(doc.getLength(), "\n\n" + "Note: The previous prompt and response did not save as it was canceled" + "\n\n", ErrorStyle); 1094 | } 1095 | } 1096 | 1097 | }catch(Exception e) { 1098 | //JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 1099 | try { 1100 | doc.insertString(doc.getLength(), "Error: " + e.getMessage() + "\n\n", ErrorStyle); 1101 | } catch (BadLocationException e2) { 1102 | e2.printStackTrace(); 1103 | } 1104 | } 1105 | 1106 | isStreamRunning = false; 1107 | 1108 | SubmitButton.setText("Submit"); 1109 | } 1110 | }); 1111 | myThread.start(); // Start the thread 1112 | } 1113 | 1114 | //Right-click functions for various JFrame objects 1115 | private void showDisplayMenu(int x, int y) { 1116 | JPopupMenu popupMenu = new JPopupMenu(); 1117 | JMenuItem copyMenuItem = new JMenuItem("Copy"); 1118 | copyMenuItem.addActionListener(new ActionListener() { 1119 | @Override 1120 | public void actionPerformed(ActionEvent e) { 1121 | String selectedText = DisplayArea.getSelectedText(); 1122 | if (selectedText != null) { 1123 | StringSelection selection = new StringSelection(selectedText); 1124 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 1125 | clipboard.setContents(selection, null); 1126 | } 1127 | } 1128 | }); 1129 | popupMenu.add(copyMenuItem); 1130 | popupMenu.show(DisplayArea, x, y); 1131 | } 1132 | 1133 | private void showHTMLMenu(int x, int y) { 1134 | JPopupMenu popupMenu = new JPopupMenu(); 1135 | JMenuItem copyMenuItem = new JMenuItem("Copy"); 1136 | copyMenuItem.addActionListener(new ActionListener() { 1137 | @Override 1138 | public void actionPerformed(ActionEvent e) { 1139 | String selectedText = HTMLArea.getSelectedText(); 1140 | if (selectedText != null) { 1141 | StringSelection selection = new StringSelection(selectedText); 1142 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 1143 | clipboard.setContents(selection, null); 1144 | } 1145 | } 1146 | }); 1147 | popupMenu.add(copyMenuItem); 1148 | popupMenu.show(HTMLArea, x, y); 1149 | } 1150 | 1151 | private void showChatMenu(int x, int y) { 1152 | JPopupMenu popupMenu = new JPopupMenu(); 1153 | 1154 | JMenuItem copyMenuItem = new JMenuItem("Copy"); 1155 | copyMenuItem.addActionListener(new ActionListener() { 1156 | @Override 1157 | public void actionPerformed(ActionEvent e) { 1158 | String selectedText = ChatArea.getSelectedText(); 1159 | if (selectedText != null) { 1160 | StringSelection selection = new StringSelection(selectedText); 1161 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 1162 | clipboard.setContents(selection, null); 1163 | } 1164 | } 1165 | }); 1166 | 1167 | 1168 | popupMenu.add(copyMenuItem); 1169 | 1170 | JMenuItem pasteMenuItem = new JMenuItem("Paste"); 1171 | pasteMenuItem.addActionListener(new ActionListener() { 1172 | @Override 1173 | public void actionPerformed(ActionEvent e) { 1174 | String selectedText = ChatArea.getSelectedText(); 1175 | if (selectedText != null && !selectedText.isEmpty()) { 1176 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 1177 | Transferable contents = clipboard.getContents(null); 1178 | if (contents != null && contents.isDataFlavorSupported(DataFlavor.stringFlavor)) { 1179 | try { 1180 | String clipboardText = (String) contents.getTransferData(DataFlavor.stringFlavor); 1181 | ChatArea.replaceSelection(clipboardText); 1182 | } catch (UnsupportedFlavorException | IOException ex) { 1183 | ex.printStackTrace(); 1184 | } 1185 | } 1186 | } else { 1187 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 1188 | Transferable contents = clipboard.getContents(null); 1189 | if (contents != null && contents.isDataFlavorSupported(DataFlavor.stringFlavor)) { 1190 | try { 1191 | String clipboardText = (String) contents.getTransferData(DataFlavor.stringFlavor); 1192 | int caretPos = ChatArea.getCaretPosition(); 1193 | ChatArea.insert(clipboardText, caretPos); 1194 | } catch (UnsupportedFlavorException | IOException ex) { 1195 | ex.printStackTrace(); 1196 | } 1197 | } 1198 | } 1199 | } 1200 | }); 1201 | popupMenu.add(pasteMenuItem); 1202 | 1203 | JMenuItem clearMenuItem = new JMenuItem("Clear"); 1204 | clearMenuItem.addActionListener(new ActionListener() { 1205 | @Override 1206 | public void actionPerformed(ActionEvent e) { 1207 | ChatArea.setText(""); 1208 | } 1209 | }); 1210 | popupMenu.add(clearMenuItem); 1211 | 1212 | popupMenu.show(ChatArea, x, y); 1213 | } 1214 | //-------------------------------------------------- 1215 | 1216 | //Function that auto generates title for current chat based off its context 1217 | public void AutoTitle() { 1218 | Thread myThread = new Thread(new Runnable() { 1219 | public void run() { 1220 | setTitle("JavaGPT *** ChatGPT is generating a title. Please wait..."); 1221 | SubmitButton.setText("Loading..."); 1222 | StringBuilder TitleBuilder = new StringBuilder(); 1223 | try { 1224 | final ChatMessage systemMessage = new ChatMessage(ChatMessageRole.SYSTEM.value(), "Create a short title that summarizes this conversation. Provide title only."); 1225 | messages.add(systemMessage); 1226 | 1227 | ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest 1228 | .builder() 1229 | .model(prop.getProperty("model")) 1230 | .messages(messages) 1231 | .n(1) 1232 | .maxTokens(25) 1233 | .logitBias(new HashMap<>()) 1234 | .build(); 1235 | service.streamChatCompletion(chatCompletionRequest) 1236 | .doOnError(Throwable::printStackTrace) 1237 | .blockingForEach(chunk -> { 1238 | for (ChatCompletionChoice choice : chunk.getChoices()) { 1239 | if(choice.getMessage().getContent() != null) { 1240 | TitleBuilder.append(choice.getMessage().getContent()); 1241 | } 1242 | } 1243 | }); 1244 | messages.remove(messages.size() - 1); 1245 | 1246 | String title = TitleBuilder.toString(); 1247 | 1248 | title = title.replaceAll("[\\\\/:*?\"<>|]", ""); 1249 | if(title.substring(title.length() - 1).equals(".")) { 1250 | title = title.substring(0, title.length() - 1); 1251 | } 1252 | SubmitButton.setText("Submit"); 1253 | if(title != null) { 1254 | File file = new File(FGPTConvo.getParentFile(), title + ".json"); 1255 | if(file.exists()) { 1256 | JOptionPane.showMessageDialog(null, "File already exists", "Error", JOptionPane.ERROR_MESSAGE); 1257 | setTitle("JavaGPT - " + FGPTConvo.getName().substring(0, FGPTConvo.getName().length()-5)); 1258 | }else { 1259 | FGPTConvo.renameTo(file); 1260 | FGPTConvo = file; 1261 | INSTANCE.setTitle("JavaGPT - " + title); 1262 | } 1263 | } 1264 | 1265 | }catch(Exception e) { 1266 | JOptionPane.showMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); 1267 | SubmitButton.setText("Submit"); 1268 | setTitle("JavaGPT - " + FGPTConvo.getName().substring(0, FGPTConvo.getName().length()-5)); 1269 | } 1270 | 1271 | } 1272 | }); 1273 | myThread.start(); 1274 | } 1275 | 1276 | 1277 | //Resets HTMLArea to properly display new HTML content 1278 | public static void resetHTMLAreaStyle() { 1279 | HTMLArea.setContentType("text/plain"); 1280 | HTMLArea.setContentType("text/html"); 1281 | } 1282 | 1283 | //sets FormSize to presets defined 1284 | public static void setFormSize(){ 1285 | switch(FormSize){ 1286 | case 1: 1287 | frame.getContentPane().setPreferredSize(new Dimension(475, 532)); 1288 | frame.pack(); 1289 | scrollPane_1.setBounds(103, 454, 363, 69); 1290 | scrollPane.setBounds(10, 11, 456, 432); 1291 | SubmitButton.setBounds(10, 454, 89, 23); 1292 | SaveButton.setBounds(10, 477, 43, 23); 1293 | ImportButton.setBounds(56, 477, 43, 23); 1294 | ResetButton.setBounds(10, 500, 89, 23); 1295 | break; 1296 | case 2: 1297 | frame.getContentPane().setPreferredSize(new Dimension(1370, 960)); 1298 | frame.pack(); 1299 | SubmitButton.setBounds(13, 831, 148, 36); 1300 | ResetButton.setBounds(13, 914, 148, 36); 1301 | scrollPane.setBounds(13, 15, 1344, 802); 1302 | scrollPane_1.setBounds(171, 831, 1186, 118); 1303 | SaveButton.setBounds(13, 873, 73, 36); 1304 | ImportButton.setBounds(88, 873, 73, 36); 1305 | break; 1306 | default: 1307 | frame.getContentPane().setPreferredSize(new Dimension(686, 647)); 1308 | frame.pack(); 1309 | SubmitButton.setBounds(10, 554, 89, 23); 1310 | ResetButton.setBounds(10, 616, 89, 23); 1311 | scrollPane.setBounds(10, 11, 667, 532); 1312 | scrollPane_1.setBounds(109, 554, 568, 85); 1313 | SaveButton.setBounds(10, 585, 43, 23); 1314 | ImportButton.setBounds(56, 585, 43, 23); 1315 | break; 1316 | } 1317 | } 1318 | 1319 | public void setFontSize() { 1320 | StyleConstants.setFontSize(YouStyle, FontSize); 1321 | StyleConstants.setFontSize(GPTStyle, FontSize); 1322 | StyleConstants.setFontSize(ChatStyle, FontSize); 1323 | StyleConstants.setFontSize(ErrorStyle, FontSize); 1324 | } 1325 | } 1326 | --------------------------------------------------------------------------------