From 757ddf3e6d9e736c214e5ae0291bc38968c00645 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Mon, 31 Mar 2025 23:04:35 +0200 Subject: [PATCH] chatbot shell --- .../plugins/chatbot/model/IChatbotAPI.java | 42 +++ .../chatbot/presenter/ChatbotException.java | 36 +++ .../chatbot/presenter/ChatbotPresenter.java | 86 ++++++ .../java/org/diylc/swing/gui/MainFrame.java | 4 +- .../swing/plugins/chatbot/ChatbotPane.java | 246 ++++++++++++++++++ .../swing/plugins/chatbot/ChatbotPlugin.java | 93 +++++++ .../swing/plugins/cloud/CloudPlugIn.java | 3 +- .../swing/plugins/config/ConfigPlugin.java | 9 +- 8 files changed, 515 insertions(+), 4 deletions(-) create mode 100644 diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/model/IChatbotAPI.java create mode 100644 diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/presenter/ChatbotException.java create mode 100644 diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/presenter/ChatbotPresenter.java create mode 100644 diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/chatbot/ChatbotPane.java create mode 100644 diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/chatbot/ChatbotPlugin.java diff --git a/diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/model/IChatbotAPI.java b/diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/model/IChatbotAPI.java new file mode 100644 index 00000000..9be3d2c3 --- /dev/null +++ b/diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/model/IChatbotAPI.java @@ -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 . + +*/ +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); +} diff --git a/diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/presenter/ChatbotException.java b/diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/presenter/ChatbotException.java new file mode 100644 index 00000000..199d99cd --- /dev/null +++ b/diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/presenter/ChatbotException.java @@ -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 . + +*/ +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); + } + +} diff --git a/diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/presenter/ChatbotPresenter.java b/diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/presenter/ChatbotPresenter.java new file mode 100644 index 00000000..67063b3a --- /dev/null +++ b/diylc/diylc-core/src/main/java/org/diylc/plugins/chatbot/presenter/ChatbotPresenter.java @@ -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 . + +*/ +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; + } +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/MainFrame.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/MainFrame.java index ea855ec2..d69c7302 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/MainFrame.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/gui/MainFrame.java @@ -60,6 +60,7 @@ import org.diylc.appframework.miscutils.ConfigurationManager; import org.diylc.appframework.miscutils.IConfigurationManager; import org.diylc.swing.IDynamicSubmenuHandler; import org.diylc.swing.plugins.analyze.AnalyzeMenuPlugin; +import org.diylc.swing.plugins.chatbot.ChatbotPlugin; import org.diylc.swingframework.ButtonDialog; import org.diylc.common.BadPositionException; @@ -111,7 +112,7 @@ public class MainFrame extends JFrame implements ISwingUI { private CanvasPlugin canvasPlugin; public MainFrame() { - super("DIYLC 4"); + super("DIYLC 5"); setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); setPreferredSize(new Dimension(1024, 700)); createBasePanels(); @@ -131,6 +132,7 @@ public class MainFrame extends JFrame implements ISwingUI { presenter.installPlugin(() -> new EditMenuPlugin(this)); presenter.installPlugin(() -> new ConfigPlugin(this)); presenter.installPlugin(() -> new LayersMenuPlugin(this)); + presenter.installPlugin(() -> new ChatbotPlugin(this)); presenter.installPlugin(() -> new CloudPlugIn(this)); if ("true".equalsIgnoreCase(System.getProperty("org.diylc.enableTests"))) presenter.installPlugin(() -> new TestMenuPlugin(this)); diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/chatbot/ChatbotPane.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/chatbot/ChatbotPane.java new file mode 100644 index 00000000..f1335d74 --- /dev/null +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/chatbot/ChatbotPane.java @@ -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", ""); + 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( + "", + 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 = + "
Welcome to DIYLC AI Assistant! How can I help you today?
\n\n" + + "
How do I create a simple circuit?
\n\n" + + "
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
\n\n" + + "
What's the best way to organize components?
\n\n" + + "
For best organization:\n" + + "- Group related components together\n" + + "- Use consistent spacing\n" + + "- Label important connections\n" + + "- Consider signal flow direction
"; + + chatEditorPane.setContentType("text/html"); + chatEditorPane.setText(htmlStyle + "
" + sampleChat + "
"); + } + 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; + } +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/chatbot/ChatbotPlugin.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/chatbot/ChatbotPlugin.java new file mode 100644 index 00000000..85661764 --- /dev/null +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/chatbot/ChatbotPlugin.java @@ -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 . + +*/ +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 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>) 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; + } +} diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/cloud/CloudPlugIn.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/cloud/CloudPlugIn.java index e26fe97c..bd0d98a1 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/cloud/CloudPlugIn.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/cloud/CloudPlugIn.java @@ -55,7 +55,7 @@ import org.diylc.swing.plugins.file.FileFilterEnum; 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); @@ -99,6 +99,7 @@ public class CloudPlugIn implements IPlugIn { // default state getUploadAction().setEnabled(false); getManageProjectsAction().setEnabled(false); + getManageAccountAction().setEnabled(false); getLogOutAction().setEnabled(false); } diff --git a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/config/ConfigPlugin.java b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/config/ConfigPlugin.java index e678b86a..e487f84d 100644 --- a/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/config/ConfigPlugin.java +++ b/diylc/diylc-swing/src/main/java/org/diylc/swing/plugins/config/ConfigPlugin.java @@ -61,6 +61,7 @@ public class ConfigPlugin implements IPlugIn { private static final String COMPONENT_BROWSER_MENU = "Toolbox"; public static final String COMPONENT_BROWSER = "componentBrowser"; 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 TABBED_TOOLBAR = "Tabbed Toolbar"; private static final String LANGUAGE_MENU = LangUtil.translate("Language"); @@ -143,11 +144,15 @@ public class ConfigPlugin implements IPlugIn { RULER_MENU); swingUI.injectMenuAction( 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( ActionFactory.getInstance().createConfigAction(plugInPort, "Show Project Explorer", - ConfigPlugin.PROJECT_EXPLORER, true), CONFIG_MENU); + ConfigPlugin.PROJECT_EXPLORER, false), CONFIG_MENU); swingUI.injectMenuAction( ActionFactory.getInstance().createConfigAction(plugInPort, "Show Rulers", IPlugInPort.SHOW_RULERS_KEY, true),