From 74946f6bce9ebf508a890effbc2adaa1b692ab57 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Wed, 2 Apr 2025 00:11:16 +0200 Subject: [PATCH] chatbot enhancements --- .../presenter/ComponentProcessorTest.java | 9 + diylc/diylc-swing/pom.xml | 6 + .../swing/plugins/chatbot/ChatbotPane.java | 155 +++++++++++++++--- .../java/org/diylc/ComponentTypeDumpTest.java | 115 +++++++++++++ 4 files changed, 259 insertions(+), 26 deletions(-) create mode 100644 diylc/diylc-swing/src/test/java/org/diylc/ComponentTypeDumpTest.java diff --git a/diylc/diylc-core/src/test/java/org/diylc/presenter/ComponentProcessorTest.java b/diylc/diylc-core/src/test/java/org/diylc/presenter/ComponentProcessorTest.java index 27c2b2c4..ec90e856 100644 --- a/diylc/diylc-core/src/test/java/org/diylc/presenter/ComponentProcessorTest.java +++ b/diylc/diylc-core/src/test/java/org/diylc/presenter/ComponentProcessorTest.java @@ -5,9 +5,18 @@ import static org.mockito.Mockito.*; import java.awt.Point; import java.awt.geom.Point2D; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.thoughtworks.xstream.XStream; import org.diylc.common.ComponentType; import org.diylc.common.IComponentTransformer; import org.diylc.common.PropertyWrapper; diff --git a/diylc/diylc-swing/pom.xml b/diylc/diylc-swing/pom.xml index b0795ecc..89abe035 100644 --- a/diylc/diylc-swing/pom.xml +++ b/diylc/diylc-swing/pom.xml @@ -112,6 +112,12 @@ slf4j-simple 1.7.36 + + + com.fasterxml.jackson.core + jackson-databind + 2.16.1 + com.oracle appbundler 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 index 0245cbf7..de7c3f4f 100644 --- 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 @@ -3,18 +3,13 @@ package org.diylc.swing.plugins.chatbot; import org.apache.log4j.Logger; import org.diylc.common.IPlugInPort; import org.diylc.common.ITask; -import org.diylc.core.IView; import org.diylc.plugins.chatbot.presenter.ChatbotPresenter; -import org.diylc.plugins.cloud.presenter.CloudPresenter; -import org.diylc.plugins.cloud.presenter.NotLoggedInException; import org.diylc.swing.ISwingUI; -import org.diylc.swing.plugins.config.ConfigPlugin; import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.File; +import java.util.Random; public class ChatbotPane extends JPanel { @@ -22,6 +17,93 @@ public class ChatbotPane extends JPanel { private static final Logger LOG = Logger.getLogger(ChatbotPane.class); + public static final String ASSISTANT = "assistant"; + public static final String SYSTEM = "system"; + public static final String USER = "user"; + public static final String TEMPORARY = "temporary"; + + String[] electronicsQuestions = { + "What is Ohm's Law, and how do voltage, current, and resistance relate to each other?", + "Why are resistors important in circuits, and what happens if the resistor value is incorrect?", + "What is a capacitor, and how does it behave differently from a resistor?", + "How does an inductor work, and where might you find one in common DIY projects?", + "What's the difference between AC (Alternating Current) and DC (Direct Current), and where is each used?", + "How do diodes work, and why are they essential in electronics?", + "What's the difference between series and parallel circuits, and why does it matter?", + "How does a transistor function, and what role does it typically play in a circuit?", + "Why is grounding important in electronic circuits, and what exactly does it do?", + "What is impedance, and why is it important when connecting speakers or audio equipment?", + "What exactly is an operational amplifier (op amp), and what are its common uses in audio circuits?", + "How does a relay work, and when would you choose a relay over a transistor or MOSFET?", + "What's the purpose of using a fuse or circuit breaker in a DIY electronics project?", + "Why do certain electronic components need specific voltage ratings?", + "What is electromagnetic interference (EMI), and how can you minimize it in your projects?", + "What does polarity mean for components like electrolytic capacitors and diodes, and why is it important?", + "How does filtering work to remove unwanted noise from audio signals or power supplies?", + "What is signal clipping in audio circuits, and how can you prevent it?", + "Why do some circuits require a heatsink, and how do heatsinks help protect components?", + "What is frequency response, and why is it crucial for audio circuits like guitar pedals and amplifiers?", + "How do you calculate the correct resistor value for limiting current to an LED?", + "What is the role of a voltage regulator, and how does it keep voltage stable?", + "How can you measure and verify voltage and current safely using a multimeter?", + "What does it mean when a circuit is described as having a 'high input impedance' or 'low output impedance,' and why does it matter?", + "How can you determine if components in your circuit are properly rated for the voltage and current they're experiencing?" + }; + + String[] diylcQuestions = { + "How do I add a component to my layout in DIYLC?", + "How can I quickly find a specific component in DIYLC?s component toolbox?", + "How do I move or reposition components on the canvas in DIYLC?", + "How do I edit or change a component?s properties (e.g. value or color) in DIYLC?", + "Is there a shortcut to duplicate or repeat the last component I placed in DIYLC?", + "How can I group multiple components so I can move or edit them together in DIYLC?", + "What are ?building blocks? in DIYLC and how do I create or use them?", + "How do I save my DIYLC project and reopen it later on?", + "How do I export my DIYLC layout as an image or PDF file?", + "Can DIYLC generate a bill of materials (BOM) for my project, and how would I do that?", + "How can I change or hide different layers in a DIYLC project to manage overlapping components?", + "Is it possible to view the underside of a board in DIYLC, and how can I enable that?", + "What does the ?Highlight Connected Areas? feature do in DIYLC and how do I use it for debugging?", + "How can I analyze a guitar wiring diagram using DIYLC?s built-in tools?", + "How do I assign a custom keyboard shortcut to a component in DIYLC for quick access?", + "What are component variants in DIYLC and how can I create and apply them?", + "What is the continuous creation mode in DIYLC and how does it help when adding many components?", + "How can I create a custom component to add to DIYLC?s component library?", + "Does DIYLC support plug-ins, and how can I add new functionality to the app with them?", + "What is the DIYLC cloud feature and how can I share or download projects using it?" + }; + + String[] circuitQuestions = { + "What's the purpose of this specific resistor in my circuit?", + "How do I correctly determine capacitor values for my power supply?", + "How should I route wiring to reduce interference and noise?", + "Which component should I choose for this switching application, a MOSFET or BJT?", + "How do I verify if my ground connections are optimal?", + "Why might this transistor get hot during operation?", + "What's the best approach to simplify this section of my circuit?", + "How can I ensure my circuit is safe against short circuits?", + "What could be causing voltage instability in this circuit?", + "How do I properly size resistors to protect LEDs?", + "What are the steps to debug unexpected oscillations in my circuit?", + "Should I add a bypass capacitor here, and what value should I use?", + "How can I check if my components are operating within their rated specifications?", + "Is this the right way to connect multiple power rails in my design?", + "How can I test my circuit before physically assembling it?", + "What are good layout practices to minimize electromagnetic interference?", + "How do I identify if my circuit has any redundant or unnecessary components?", + "Why might the voltage regulator not output the expected voltage?", + "What's the recommended method for isolating digital and analog grounds?", + "How do I safely integrate high-voltage components into my DIY circuit?", + "Why isn't my LED lighting up when powered?", + "How do I troubleshoot a circuit that's drawing more current than expected?", + "Why does my amplifier circuit produce a humming or buzzing sound?", + "How can I detect and fix intermittent connections in my circuit?", + "What should I check if my circuit isn't responding to input signals as expected?", + "How do I determine if my soldering joints are causing reliability issues?", + "What steps can I take if my microcontroller circuit isn't functioning as intended?", + "How do I troubleshoot a noisy or unstable sensor reading?" + }; + 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); @@ -129,6 +211,19 @@ public class ChatbotPane extends JPanel { promptArea.setForeground(TERMINAL_FG); promptArea.setCaretColor(TERMINAL_FG); + // Add Enter key handling + promptArea.addKeyListener(new java.awt.event.KeyAdapter() { + @Override + public void keyPressed(java.awt.event.KeyEvent e) { + if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER && !e.isShiftDown()) { + e.consume(); // Prevent the newline from being added + if (getAskButton().isEnabled()) { + getAskButton().doClick(); + } + } + } + }); + // Calculate height based on font metrics int lineHeight = promptArea.getFontMetrics(promptArea.getFont()).getHeight(); int desiredHeight = lineHeight * 3 + 10; // 3 lines + padding @@ -207,33 +302,36 @@ public class ChatbotPane extends JPanel { ".user { color: #98C379; }" + // Softer green that's easier on the eyes ".assistant { color: #61AFEF; }" + // Lighter, more vibrant blue ".system { color: #E5C07B; }" + // Warmer, muted yellow + ".temporary { color: #555555; }" + "", 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
"; - + + String sampleChat = getSampleChat(); + chatEditorPane.setContentType("text/html"); chatEditorPane.setText(htmlStyle + "
" + sampleChat + "
"); } return chatEditorPane; } + private String getSampleChat() { + Random r = new Random(); + + return + "
Welcome to the DIYLC AI Assistant!


\n" + + "
I'm here to help you design, build, and troubleshoot your electronics projects using DIY Layout Creator (DIYLC). You can ask me questions about:

\n" + + "
\n" + + "
Feel free to ask your own question or use one of these examples to get started!

"; + } + public JButton getAskButton() { if (askButton == null) { askButton = new JButton("Ask"); @@ -243,7 +341,8 @@ public class ChatbotPane extends JPanel { askButton.setBorderPainted(true); askButton.addActionListener(e -> { final String prompt = getPromptArea().getText(); - appendSection("user", prompt); + appendSection(USER, prompt); + appendSection(TEMPORARY, "Waiting for the response..."); getPromptArea().setText(null); String currentFile = plugInPort.getCurrentFileName(); String fileName = extractFileName(currentFile); @@ -259,14 +358,14 @@ public class ChatbotPane extends JPanel { @Override public void failed(Exception e) { - appendSection("system", "Failed to retrieve the response from the server. Error: " + e.getMessage()); + appendSection(SYSTEM, "Failed to retrieve the response from the server. Error: " + e.getMessage()); LOG.error("Failed to retrieve the response from the server", e); getClearButton().setEnabled(true); } @Override public void complete(String result) { - appendSection("assistant", result); + appendSection(ASSISTANT, result); getClearButton().setEnabled(true); } }, true); @@ -277,9 +376,13 @@ public class ChatbotPane extends JPanel { private void appendSection(String style, String insertText) { String text = getChatEditorPane().getText(); + // remove any temporary divs + String regex = "(?s)]*>.*?\\s*
\\s*"; + text = text.replaceAll(regex, ""); + // add a new div int bodyCloseIndex = text.indexOf(""); text = text.substring(0, bodyCloseIndex) + - "\n
" + insertText + "
\n" + + "\n
" + insertText + "

" + text.substring(bodyCloseIndex); getChatEditorPane().setText(text); } diff --git a/diylc/diylc-swing/src/test/java/org/diylc/ComponentTypeDumpTest.java b/diylc/diylc-swing/src/test/java/org/diylc/ComponentTypeDumpTest.java new file mode 100644 index 00000000..c560bb22 --- /dev/null +++ b/diylc/diylc-swing/src/test/java/org/diylc/ComponentTypeDumpTest.java @@ -0,0 +1,115 @@ +package org.diylc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.diylc.common.ComponentType; +import org.diylc.presenter.ComponentProcessor; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class ComponentTypeDumpTest { + +// @Test + public void dumpComponentTypesToKnowledgeBaseSQLFile() { + Map> componentTypes = + ComponentProcessor.getInstance().getComponentTypes(); + + List components = componentTypes.values().stream() + .flatMap(x -> x.stream()) + .map(x -> new DIYLCComponentType(x.getName(), x.getDescription(), x.getCategory(), + ComponentProcessor.getInstance().extractProperties(x.getInstanceClass()).stream() + .map(y -> new DIYLCProperty(y.getName(), formatType(y.getType()))) + .toList())) + .toList(); + + try (PrintWriter writer = new PrintWriter(new File("/users/branislavstojkovic/downloads/test.sql"))) { + ObjectMapper mapper = new ObjectMapper(); + mapper.enable(SerializationFeature.INDENT_OUTPUT); + + for (DIYLCComponentType component : components) { + String section = "Component Type: " + component.getName(); + String escapedSection = section.replace("'", "''"); + + // First delete any existing record for this component type + String deleteSql = String.format( + "DELETE FROM diylc_knowledge_base WHERE section = '%s';\n", + escapedSection + ); + writer.write(deleteSql); + + // Then insert the new record + String content = mapper.writeValueAsString(component); + content = content.replace("'", "''"); + + String insertSql = String.format( + "INSERT INTO diylc_knowledge_base (section, content) VALUES ('%s', '%s');\n", + escapedSection, + content + ); + + writer.write(insertSql); + } + } catch (IOException e) { + throw new RuntimeException("Failed to write SQL file", e); + } + } + + private String formatType(Class type) { + if (type.isEnum()) { + String enumValues = Arrays.stream(type.getEnumConstants()) + .map(Object::toString) + .collect(Collectors.joining(",")); + return type.getName() + "(" + enumValues + ")"; + } + return type.getName(); + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + static class DIYLCProperty { + private String name; + private String type; + + public DIYLCProperty(String name, String type) { + this.name = name; + this.type = type; + } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getType() { return type; } + public void setType(String type) { this.type = type; } + } + + @JsonInclude(JsonInclude.Include.NON_NULL) + private static class DIYLCComponentType { + private String name; + private String description; + private String category; + private List properties; + + public DIYLCComponentType(String name, String description, String category, + List properties) { + this.name = name; + this.description = description; + this.category = category; + this.properties = properties; + } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + public String getDescription() { return description; } + public void setDescription(String description) { this.description = description; } + public String getCategory() { return category; } + public void setCategory(String category) { this.category = category; } + public List getProperties() { return properties; } + public void setProperties(List properties) { this.properties = properties; } + } +}