chatbot shell

This commit is contained in:
Bane Stojkovic
2025-03-31 23:04:35 +02:00
parent 58e28a95fd
commit 757ddf3e6d
8 changed files with 515 additions and 4 deletions
@@ -0,0 +1,42 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.plugins.chatbot.model;
import com.diyfever.httpproxy.ParamName;
public interface IChatbotAPI {
/**
* Prompts the chatbot with a question
*
* @param username
* @param token
* @param machineId
* @param project
* @param netlist
* @param prompt
* @return
*/
String promptChatbot(@ParamName("username") String username, @ParamName("token") String token,
@ParamName("machineId") String machineId, @ParamName("project") String project,
@ParamName("netlist") String netlist, @ParamName("prompt") String prompt);
}
@@ -0,0 +1,36 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.plugins.chatbot.presenter;
public class ChatbotException extends Exception {
private static final long serialVersionUID = 1L;
public ChatbotException(Exception e) {
super(e);
}
public ChatbotException(String string) {
super(string);
}
}
@@ -0,0 +1,86 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.plugins.chatbot.presenter;
import com.diyfever.httpproxy.PhpFlatProxy;
import com.diyfever.httpproxy.ProxyFactory;
import org.apache.log4j.Logger;
import org.checkerframework.checker.units.qual.N;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.common.PropertyWrapper;
import org.diylc.plugins.chatbot.model.IChatbotAPI;
import org.diylc.plugins.cloud.model.CommentEntity;
import org.diylc.plugins.cloud.model.IServiceAPI;
import org.diylc.plugins.cloud.model.ProjectEntity;
import org.diylc.plugins.cloud.model.UserEntity;
import org.diylc.plugins.cloud.presenter.CloudException;
import org.diylc.plugins.cloud.presenter.CloudPresenter;
import org.diylc.plugins.cloud.presenter.NotLoggedInException;
import org.diylc.presenter.ComparatorFactory;
import org.diylc.presenter.ComponentProcessor;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.Collections;
import java.util.List;
/**
* Contains all the back-end logic for using the cloud and manipulating projects on the cloud.
*
* @author Branislav Stojkovic
*/
public class ChatbotPresenter {
public static final ChatbotPresenter Instance = new ChatbotPresenter();
private static String ERROR = "Error";
private final static Logger LOG = Logger.getLogger(ChatbotPresenter.class);
private static final Object SUCCESS = "Success";
private IChatbotAPI service;
private String serviceUrl;
private ChatbotPresenter() {}
private IChatbotAPI getService() {
if (service == null) {
serviceUrl =
ConfigurationManager.getInstance().readString(IServiceAPI.URL_KEY, "http://www.diy-fever.com/diylc/api/v1/ai");
ProxyFactory factory = new ProxyFactory(new PhpFlatProxy());
service = factory.createProxy(IChatbotAPI.class, serviceUrl);
}
return service;
}
String promptChatbot() throws NotLoggedInException {
if (!CloudPresenter.Instance.isLoggedIn())
throw new NotLoggedInException();
return null;
}
}
@@ -60,6 +60,7 @@ import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.appframework.miscutils.IConfigurationManager; import org.diylc.appframework.miscutils.IConfigurationManager;
import org.diylc.swing.IDynamicSubmenuHandler; import org.diylc.swing.IDynamicSubmenuHandler;
import org.diylc.swing.plugins.analyze.AnalyzeMenuPlugin; import org.diylc.swing.plugins.analyze.AnalyzeMenuPlugin;
import org.diylc.swing.plugins.chatbot.ChatbotPlugin;
import org.diylc.swingframework.ButtonDialog; import org.diylc.swingframework.ButtonDialog;
import org.diylc.common.BadPositionException; import org.diylc.common.BadPositionException;
@@ -111,7 +112,7 @@ public class MainFrame extends JFrame implements ISwingUI {
private CanvasPlugin canvasPlugin; private CanvasPlugin canvasPlugin;
public MainFrame() { public MainFrame() {
super("DIYLC 4"); super("DIYLC 5");
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
setPreferredSize(new Dimension(1024, 700)); setPreferredSize(new Dimension(1024, 700));
createBasePanels(); createBasePanels();
@@ -131,6 +132,7 @@ public class MainFrame extends JFrame implements ISwingUI {
presenter.installPlugin(() -> new EditMenuPlugin(this)); presenter.installPlugin(() -> new EditMenuPlugin(this));
presenter.installPlugin(() -> new ConfigPlugin(this)); presenter.installPlugin(() -> new ConfigPlugin(this));
presenter.installPlugin(() -> new LayersMenuPlugin(this)); presenter.installPlugin(() -> new LayersMenuPlugin(this));
presenter.installPlugin(() -> new ChatbotPlugin(this));
presenter.installPlugin(() -> new CloudPlugIn(this)); presenter.installPlugin(() -> new CloudPlugIn(this));
if ("true".equalsIgnoreCase(System.getProperty("org.diylc.enableTests"))) if ("true".equalsIgnoreCase(System.getProperty("org.diylc.enableTests")))
presenter.installPlugin(() -> new TestMenuPlugin(this)); presenter.installPlugin(() -> new TestMenuPlugin(this));
@@ -0,0 +1,246 @@
package org.diylc.swing.plugins.chatbot;
import org.diylc.common.IPlugInPort;
import org.diylc.swing.ISwingUI;
import javax.swing.*;
import java.awt.*;
public class ChatbotPane extends JPanel {
private static final long serialVersionUID = 1L;
public static Font DEFAULT_FONT = new Font("Square721 BT", Font.PLAIN, 12);
private static final Font MONOSPACED_FONT = new Font("Monospaced", Font.PLAIN, 12);
private static final Color TERMINAL_BG = new Color(40, 40, 40);
private static final Color TERMINAL_FG = new Color(200, 200, 200);
private static final Color PROMPT_BG = new Color(60, 60, 60); // Slightly brighter than TERMINAL_BG
private ISwingUI swingUI;
private IPlugInPort plugInPort;
private JLabel titleLabel;
private JTextArea promptArea;
private JScrollPane chatScrollPane;
private JEditorPane chatEditorPane;
private JButton askButton;
private JButton clearButton;
public ChatbotPane(ISwingUI swingUI, IPlugInPort plugInPort) {
super();
this.swingUI = swingUI;
this.plugInPort = plugInPort;
setName("AI Assistant");
// Set panel background color
setBackground(TERMINAL_BG);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.insets = new Insets(5, 5, 5, 5);
// Chat scroll pane takes most space
JScrollPane chatScrollPane = new JScrollPane(getChatEditorPane(),
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
chatScrollPane.setBackground(TERMINAL_BG);
chatScrollPane.getViewport().setBackground(TERMINAL_BG);
chatScrollPane.setBorder(BorderFactory.createLineBorder(TERMINAL_BG));
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.gridwidth = 2; // Span both columns
add(chatScrollPane, gbc);
// Prompt label
JLabel promptLabel = new JLabel("Prompt:");
promptLabel.setForeground(TERMINAL_FG);
promptLabel.setFont(DEFAULT_FONT);
gbc.gridx = 0;
gbc.gridy = 2;
gbc.weightx = 0.0;
gbc.weighty = 0.0;
gbc.gridwidth = 1;
// add(promptLabel, gbc);
// Prompt scroll pane
JScrollPane promptScrollPane = new JScrollPane(getPromptArea(),
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
promptScrollPane.setBackground(TERMINAL_BG);
promptScrollPane.getViewport().setBackground(TERMINAL_BG);
promptScrollPane.setBorder(BorderFactory.createLineBorder(TERMINAL_BG));
// Calculate height based on font size and lines
int lineHeight = getPromptArea().getFontMetrics(getPromptArea().getFont()).getHeight();
int desiredHeight = lineHeight * 3 + 10; // 3 lines + padding
// Set minimum and preferred size for both text area and scroll pane
Dimension promptSize = new Dimension(150, desiredHeight);
getPromptArea().setMinimumSize(promptSize);
getPromptArea().setPreferredSize(promptSize);
promptScrollPane.setMinimumSize(new Dimension(150, desiredHeight + 10));
promptScrollPane.setPreferredSize(new Dimension(150, desiredHeight + 10));
gbc.gridx = 0;
gbc.gridy = 3;
gbc.weightx = 1.0;
gbc.weighty = 0.0;
gbc.gridwidth = 2; // Span both columns
add(promptScrollPane, gbc);
// Button panel for Clear and Ask buttons
JPanel buttonPanel = new JPanel(new BorderLayout(5, 0));
buttonPanel.setBackground(TERMINAL_BG);
buttonPanel.add(getClearButton(), BorderLayout.WEST);
buttonPanel.add(getAskButton(), BorderLayout.EAST);
gbc.gridx = 0; // Start from first column
gbc.gridy = 4;
gbc.weightx = 1.0; // Allow horizontal stretching
gbc.weighty = 0.0;
gbc.gridwidth = 2; // Span both columns
gbc.fill = GridBagConstraints.HORIZONTAL; // Fill horizontally
add(buttonPanel, gbc);
setPreferredSize(new Dimension(300, 400));
}
public JTextArea getPromptArea() {
if (promptArea == null) {
promptArea = new JTextArea();
promptArea.setLineWrap(true);
promptArea.setWrapStyleWord(true);
promptArea.setRows(3); // Set to 3 lines height
promptArea.setFont(DEFAULT_FONT);
promptArea.setBackground(PROMPT_BG); // Use brighter background
promptArea.setForeground(TERMINAL_FG);
promptArea.setCaretColor(TERMINAL_FG);
// Calculate height based on font metrics
int lineHeight = promptArea.getFontMetrics(promptArea.getFont()).getHeight();
int desiredHeight = lineHeight * 3 + 10; // 3 lines + padding
promptArea.setMinimumSize(new Dimension(150, desiredHeight));
promptArea.setPreferredSize(new Dimension(150, desiredHeight));
// Add placeholder text behavior
final String placeholder = "Type your question here...";
promptArea.setText(placeholder);
promptArea.setForeground(new Color(128, 128, 128)); // Dimmer text for placeholder
// Initially disable Ask button since we start with placeholder
getAskButton().setEnabled(false);
promptArea.addFocusListener(new java.awt.event.FocusListener() {
@Override
public void focusGained(java.awt.event.FocusEvent e) {
if (promptArea.getText().equals(placeholder)) {
promptArea.setText("");
promptArea.setForeground(TERMINAL_FG);
getAskButton().setEnabled(false); // Disable when clearing placeholder
}
}
@Override
public void focusLost(java.awt.event.FocusEvent e) {
if (promptArea.getText().isEmpty()) {
promptArea.setText(placeholder);
promptArea.setForeground(new Color(128, 128, 128));
getAskButton().setEnabled(false); // Disable when empty/placeholder
}
}
});
// Add document listener to monitor text changes
promptArea.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
private void updateAskButton() {
String text = promptArea.getText();
boolean hasContent = !text.isEmpty() && !text.equals(placeholder);
getAskButton().setEnabled(hasContent);
}
@Override
public void insertUpdate(javax.swing.event.DocumentEvent e) {
updateAskButton();
}
@Override
public void removeUpdate(javax.swing.event.DocumentEvent e) {
updateAskButton();
}
@Override
public void changedUpdate(javax.swing.event.DocumentEvent e) {
updateAskButton();
}
});
}
return promptArea;
}
public JEditorPane getChatEditorPane() {
if (chatEditorPane == null) {
chatEditorPane = new JEditorPane("text/html", "<html></html>");
chatEditorPane.setEditable(false); // Make it read-only
chatEditorPane.setFont(MONOSPACED_FONT);
chatEditorPane.setBackground(TERMINAL_BG);
chatEditorPane.setForeground(TERMINAL_FG);
chatEditorPane.setCaretColor(TERMINAL_FG); // Make the cursor visible on dark background
// Set up HTML styling
String htmlStyle = String.format(
"<style>" +
"body { font-family: %s; font-size: %dpt; color: rgb(%d,%d,%d); background-color: rgb(%d,%d,%d); padding: 0px; margin: 0; }" +
"pre { white-space: pre-wrap; margin: 0; }" +
".user { color: #4CAF50; }" +
".assistant { color: #2196F3; }" +
".system { color: #FF9800; }" +
"</style>",
MONOSPACED_FONT.getFamily(),
MONOSPACED_FONT.getSize(),
TERMINAL_FG.getRed(), TERMINAL_FG.getGreen(), TERMINAL_FG.getBlue(),
TERMINAL_BG.getRed(), TERMINAL_BG.getGreen(), TERMINAL_BG.getBlue());
// Sample chat history
String sampleChat =
"<div class='system'>Welcome to DIYLC AI Assistant! How can I help you today?</div>\n\n" +
"<div class='user'>How do I create a simple circuit?</div>\n\n" +
"<div class='assistant'>To create a simple circuit:\n" +
"1. Drag components from the component panel\n" +
"2. Connect them by dragging from one terminal to another\n" +
"3. Use the grid to align components neatly</div>\n\n" +
"<div class='user'>What's the best way to organize components?</div>\n\n" +
"<div class='assistant'>For best organization:\n" +
"- Group related components together\n" +
"- Use consistent spacing\n" +
"- Label important connections\n" +
"- Consider signal flow direction</div>";
chatEditorPane.setContentType("text/html");
chatEditorPane.setText(htmlStyle + "<body><pre>" + sampleChat + "</pre></body>");
}
return chatEditorPane;
}
public JButton getAskButton() {
if (askButton == null) {
askButton = new JButton("Ask");
askButton.setBackground(TERMINAL_BG);
askButton.setForeground(TERMINAL_FG);
askButton.setFocusPainted(false);
askButton.setBorderPainted(true);
}
return askButton;
}
public JButton getClearButton() {
if (clearButton == null) {
clearButton = new JButton("Clear");
clearButton.setBackground(TERMINAL_BG);
clearButton.setForeground(TERMINAL_FG);
clearButton.setFocusPainted(false);
clearButton.setBorderPainted(true);
}
return clearButton;
}
}
@@ -0,0 +1,93 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2018 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.swing.plugins.chatbot;
import org.apache.log4j.Logger;
import org.diylc.common.BadPositionException;
import org.diylc.common.EventType;
import org.diylc.common.IPlugIn;
import org.diylc.common.IPlugInPort;
import org.diylc.core.IDIYComponent;
import org.diylc.core.Project;
import org.diylc.swing.ISwingUI;
import org.diylc.swing.plugins.config.ConfigPlugin;
import javax.swing.*;
import java.util.EnumSet;
import java.util.Set;
public class ChatbotPlugin implements IPlugIn {
private static final Logger LOG = Logger.getLogger(ChatbotPlugin.class);
private IPlugInPort plugInPort;
private ISwingUI swingUI;
private ChatbotPane chatbotPane;
public ChatbotPlugin(ISwingUI swingUI) {
super();
this.swingUI = swingUI;
}
@Override
public EnumSet<EventType> getSubscribedEventTypes() {
return EnumSet.of(EventType.FILE_STATUS_CHANGED, EventType.PROJECT_LOADED,
EventType.PROJECT_MODIFIED, EventType.SELECTION_CHANGED);
}
@SuppressWarnings("unchecked")
@Override
public void processMessage(EventType eventType, Object... params) {
// switch (eventType) {
// case PROJECT_MODIFIED:
// getChatbotPane().setComponents(((Project) params[1]).getComponents(),
// plugInPort.getSelectedComponents());
// break;
// case PROJECT_LOADED:
// getChatbotPane().setComponents(((Project) params[0]).getComponents(),
// plugInPort.getSelectedComponents());
// break;
// case SELECTION_CHANGED:
// getChatbotPane().setSelection((Set<IDIYComponent<?>>) params[0]);
// default:
// break;
// }
}
@Override
public void connect(IPlugInPort plugInPort) {
this.plugInPort = plugInPort;
try {
this.swingUI.injectGUIComponent(getChatbotPane(), SwingConstants.RIGHT, true,
ConfigPlugin.CHATBOT);
} catch (BadPositionException e) {
LOG.error("Could not install project explorer", e);
}
}
public ChatbotPane getChatbotPane() {
if (chatbotPane == null) {
chatbotPane = new ChatbotPane(swingUI, plugInPort);
}
return chatbotPane;
}
}
@@ -55,7 +55,7 @@ import org.diylc.swing.plugins.file.FileFilterEnum;
public class CloudPlugIn implements IPlugIn { public class CloudPlugIn implements IPlugIn {
private static final String ONLINE_TITLE = "Project Cloud"; private static final String ONLINE_TITLE = "Cloud";
private final static Logger LOG = Logger.getLogger(CloudPlugIn.class); private final static Logger LOG = Logger.getLogger(CloudPlugIn.class);
@@ -99,6 +99,7 @@ public class CloudPlugIn implements IPlugIn {
// default state // default state
getUploadAction().setEnabled(false); getUploadAction().setEnabled(false);
getManageProjectsAction().setEnabled(false); getManageProjectsAction().setEnabled(false);
getManageAccountAction().setEnabled(false);
getLogOutAction().setEnabled(false); getLogOutAction().setEnabled(false);
} }
@@ -61,6 +61,7 @@ public class ConfigPlugin implements IPlugIn {
private static final String COMPONENT_BROWSER_MENU = "Toolbox"; private static final String COMPONENT_BROWSER_MENU = "Toolbox";
public static final String COMPONENT_BROWSER = "componentBrowser"; public static final String COMPONENT_BROWSER = "componentBrowser";
public static final String PROJECT_EXPLORER = "projectExplorer"; public static final String PROJECT_EXPLORER = "projectExplorer";
public static final String CHATBOT = "chatbot";
public static final String SEARCHABLE_TREE = "Searchable Tree"; public static final String SEARCHABLE_TREE = "Searchable Tree";
public static final String TABBED_TOOLBAR = "Tabbed Toolbar"; public static final String TABBED_TOOLBAR = "Tabbed Toolbar";
private static final String LANGUAGE_MENU = LangUtil.translate("Language"); private static final String LANGUAGE_MENU = LangUtil.translate("Language");
@@ -143,11 +144,15 @@ public class ConfigPlugin implements IPlugIn {
RULER_MENU); RULER_MENU);
swingUI.injectMenuAction( swingUI.injectMenuAction(
ActionFactory.getInstance().createToggleAction(IPlugInPort.RULER_IN_SUBDIVISION_2, IPlugInPort.RULER_IN_SUBDIVISION_KEY, RULER_MENU, IPlugInPort.RULER_IN_SUBDIVISION_DEFAULT), ActionFactory.getInstance().createToggleAction(IPlugInPort.RULER_IN_SUBDIVISION_2, IPlugInPort.RULER_IN_SUBDIVISION_KEY, RULER_MENU, IPlugInPort.RULER_IN_SUBDIVISION_DEFAULT),
RULER_MENU); RULER_MENU);
swingUI.injectMenuAction(
ActionFactory.getInstance().createConfigAction(plugInPort, "Show AI Assistant",
ConfigPlugin.CHATBOT, true), CONFIG_MENU);
swingUI.injectMenuAction( swingUI.injectMenuAction(
ActionFactory.getInstance().createConfigAction(plugInPort, "Show Project Explorer", ActionFactory.getInstance().createConfigAction(plugInPort, "Show Project Explorer",
ConfigPlugin.PROJECT_EXPLORER, true), CONFIG_MENU); ConfigPlugin.PROJECT_EXPLORER, false), CONFIG_MENU);
swingUI.injectMenuAction( swingUI.injectMenuAction(
ActionFactory.getInstance().createConfigAction(plugInPort, "Show Rulers", IPlugInPort.SHOW_RULERS_KEY, true), ActionFactory.getInstance().createConfigAction(plugInPort, "Show Rulers", IPlugInPort.SHOW_RULERS_KEY, true),