diff --git a/.gitignore b/.gitignore
index 8483905c6..50a4dc8df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -238,3 +238,4 @@
/installers/nuts-installer/dist/portable-jar/nuts-*.jar
/installers/nuts-builder-tool/target
/tutorials/naf/nuts-spring-boot-integration-hello-world/nuts-spring-boot-integration-hello-world.iml
+/libraries/nlib-swing/target
diff --git a/core/nuts-runtime/src/main/java/net/thevpc/nuts/runtime/standalone/io/printstream/NPrintStreamRaw.java b/core/nuts-runtime/src/main/java/net/thevpc/nuts/runtime/standalone/io/printstream/NPrintStreamRaw.java
index 766d24022..58abf47a8 100644
--- a/core/nuts-runtime/src/main/java/net/thevpc/nuts/runtime/standalone/io/printstream/NPrintStreamRaw.java
+++ b/core/nuts-runtime/src/main/java/net/thevpc/nuts/runtime/standalone/io/printstream/NPrintStreamRaw.java
@@ -141,6 +141,12 @@ protected NPrintStream convertImpl(NTerminalMode other) {
case FILTERED: {
return new NPrintStreamFiltered(this, getSession(), bindings);
}
+ case ANSI: {
+ if(this.getTerminalMode()==NTerminalMode.INHERITED){
+ return this;
+ }
+ break;
+ }
}
throw new NIllegalArgumentException(getSession(), NMsg.ofC("unsupported %s -> %s", getTerminalMode(), other));
}
diff --git a/libraries/nlib-swing/pom.xml b/libraries/nlib-swing/pom.xml
new file mode 100644
index 000000000..d0025b8bb
--- /dev/null
+++ b/libraries/nlib-swing/pom.xml
@@ -0,0 +1,219 @@
+
+
+ 4.0.0
+ net.thevpc.nuts.lib
+ nlib-swing
+ 0.8.4.0
+ jar
+ Nuts Community Library for Swing Support
+ https://github.com/thevpc/nuts
+ Swing Lib
+
+ UTF-8
+ 1.8
+ 1.8
+ github
+
+
+ scm:git:git://github.com/thevpc/nuts.git
+ scm:git:ssh://github.com:thevpc/nuts.git
+ https://github.com/thevpc/nuts/tree/master
+
+
+
+ vpc open source initiative
+ https://thevpc.net
+
+
+
+
+ vpc
+ Taha Ben Salah
+ taha.bensalah@gmail.com
+ https://tahabensalah.net
+ thevpc open source initiative
+ https://thevpc.net
+
+ architect
+ developer
+
+ Africa/Tunis
+
+ https://gravatar.com/avatar/977025550163b4a91397007f6ea9ee17
+
+
+
+
+
+
+ Apache License, Version 2.0
+ https://www.apache.org/licenses/LICENSE-2.0
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.8.2
+ test
+
+
+
+
+ ossrh
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ compile
+ compile
+
+ compile
+
+
+
+ testCompile
+ test-compile
+
+ testCompile
+
+
+
+
+ true
+ true
+
+ ${maven.compiler.target}
+
+
+
+
+
+
+ deploy
+
+ false
+
+
+
+
+ maven-dependency-plugin
+ 3.0.2
+
+
+ process-sources
+
+ copy-dependencies
+
+
+ ${targetdirectory}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.0.1
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.4.0
+
+
+
+ app.category
+ a
+ Category:
+
+
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.6
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.3.0
+
+
+
+ net.thevpc.nuts.lib.talkagent
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.8
+
+
+
+ prepare-agent
+
+
+
+ report
+ prepare-package
+
+ report
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.13
+ true
+
+ ossrh
+ https://oss.sonatype.org/
+ true
+
+
+
+
+
+
+
diff --git a/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/AnsiTermPane.java b/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/AnsiTermPane.java
new file mode 100644
index 000000000..e7422dfe9
--- /dev/null
+++ b/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/AnsiTermPane.java
@@ -0,0 +1,368 @@
+package net.thevpc.nuts.lib.nswing;
+
+import javax.swing.*;
+import javax.swing.text.*;
+import java.awt.*;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * thanks to https://stackoverflow.com/questions/6913983/jtextpane-removing-first-line
+ */
+public class AnsiTermPane extends JTextPane {
+ public static Color colorForeground = Color.BLACK;//cReset;
+ public static Color colorBackground = null;//cReset;
+ public Color D_Black = Color.getHSBColor(0.000f, 0.000f, 0.000f);
+ public Color D_Red = Color.getHSBColor(0.000f, 1.000f, 0.502f);
+ public Color D_Blue = Color.getHSBColor(0.667f, 1.000f, 0.502f);
+ public Color D_Magenta = Color.getHSBColor(0.833f, 1.000f, 0.502f);
+ public Color D_Green = Color.getHSBColor(0.333f, 1.000f, 0.502f);
+ public Color D_Yellow = Color.getHSBColor(0.167f, 1.000f, 0.502f);
+ public Color D_Cyan = Color.getHSBColor(0.500f, 1.000f, 0.502f);
+ public Color D_White = Color.getHSBColor(0.000f, 0.000f, 0.753f);
+ public Color B_Black = Color.getHSBColor(0.000f, 0.000f, 0.502f);
+ public Color B_Red = Color.getHSBColor(0.000f, 1.000f, 1.000f);
+ public Color B_Blue = Color.getHSBColor(0.667f, 1.000f, 1.000f);
+ public Color B_Magenta = Color.getHSBColor(0.833f, 1.000f, 1.000f);
+ public Color B_Green = Color.getHSBColor(0.333f, 1.000f, 1.000f);
+ public Color B_Yellow = Color.getHSBColor(0.167f, 1.000f, 1.000f).darker();
+ public Color B_Cyan = Color.getHSBColor(0.500f, 1.000f, 1.000f);
+ public Color B_White = Color.getHSBColor(0.000f, 0.000f, 1.000f);
+ public Color cReset = Color.BLACK;//Color.getHSBColor(0.000f, 0.000f, 1.000f);
+ public Color[] COLS = new Color[]{
+ D_Black, D_Red, D_Green, D_Yellow, D_Blue, D_Magenta, D_Cyan, D_White,
+ B_Black, B_Red, B_Green, B_Yellow, B_Blue, B_Magenta, B_Cyan, B_White,
+ };
+ // public static Color colorCurrent = Color.WHITE;//cReset;
+ String remaining = "";
+ PrintStream ps;
+
+ public AnsiTermPane(boolean darkMode) {
+ setDarkMode(darkMode);
+ }
+
+ public String colorName(Color c) {
+ if (c.equals(cReset)) {
+ return "reset";
+ }
+ if (c.equals(D_Black)) {
+ return "D_Black";
+ }
+ if (c.equals(D_Red)) {
+ return "D_Red";
+ }
+ if (c.equals(D_Green)) {
+ return "D_Green";
+ }
+ if (c.equals(D_Yellow)) {
+ return "D_Yellow";
+ }
+ if (c.equals(D_Blue)) {
+ return "D_Blue";
+ }
+ if (c.equals(D_Magenta)) {
+ return "D_Magenta";
+ }
+ if (c.equals(D_Cyan)) {
+ return "D_Cyan";
+ }
+ if (c.equals(D_White)) {
+ return "D_White";
+ }
+ if (c.equals(B_Black)) {
+ return "B_Black";
+ }
+ if (c.equals(B_Red)) {
+ return "B_Red";
+ }
+ if (c.equals(B_Green)) {
+ return "B_Green";
+ }
+ if (c.equals(B_Yellow)) {
+ return "B_Yellow";
+ }
+ if (c.equals(B_Blue)) {
+ return "B_Blue";
+ }
+ if (c.equals(B_Magenta)) {
+ return "B_Magenta";
+ }
+ if (c.equals(B_Cyan)) {
+ return "B_Cyan";
+ }
+ if (c.equals(B_White)) {
+ return "B_White";
+ }
+ return "?";
+ }
+
+ public void setDarkMode(boolean darkMode) {
+ cReset = darkMode ? Color.WHITE : Color.BLACK;
+// setForeground(Color.WHITE);
+ setFont(new Font("Courier New", Font.PLAIN, 14));
+ setForeground(cReset);
+ colorForeground = cReset;
+ if (darkMode) {
+ D_Blue = new Color(124, 124, 220);
+ B_Blue = new Color(162, 162, 225);
+ B_White = new Color(255, 255, 255);
+ D_Red = new Color(200, 0, 0);
+ D_Magenta = new Color(142, 57, 137);
+ setBackground(new Color(22, 22, 22));
+ } else {
+ D_Blue = Color.getHSBColor(0.667f, 1.000f, 0.502f);
+ B_Blue = Color.getHSBColor(0.667f, 1.000f, 1.000f);
+ B_White = new Color(0, 0, 0);
+ D_Red = Color.getHSBColor(0.000f, 1.000f, 0.502f);
+ D_Magenta = Color.getHSBColor(0.833f, 1.000f, 0.502f);
+ setBackground(new Color(250, 250, 250));
+ }
+ COLS = new Color[]{
+ D_Black, D_Red, D_Green, D_Yellow, D_Blue, D_Magenta, D_Cyan, D_White,
+ B_Black, B_Red, B_Green, B_Yellow, B_Blue, B_Magenta, B_Cyan, B_White,
+ };
+ }
+
+ public void clearLastLine() {
+ Element root = getDocument().getDefaultRootElement();
+ if (root.getElementCount() > 0) {
+ Element first = root.getElement(root.getElementCount() - 1);
+ try {
+ getDocument().remove(first.getStartOffset(), first.getEndOffset());
+ } catch (BadLocationException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public Color color256(int c) {
+ if (c < 0) {
+ c = 0;
+ }
+ if (c < 16) {
+ c = Math.abs(c) % COLS.length;
+ if (c == 0) {
+ return cReset;
+ } else {
+ return COLS[c];
+ }
+ }
+ if (c <= 231) {
+ c = c - 16;
+ int r = (c / 36);
+ int g = ((c % 36) / 6);
+ int b = (c % 6);
+ // 36 * r + 6 * g + b (0 ≤ r, g, b ≤ 5)
+ r = r * 255 / 5;
+ g = g * 255 / 5;
+ b = b * 255 / 5;
+ return new Color(r, g, b);
+ } else {
+ int i = c - 232;
+ int r = 255 * i / 24;
+ return new Color(r, r, r);
+ }
+ }
+
+
+ public void append(int c, String s) {
+ append(color256(c), s);
+ }
+
+ public void append(Color c, String s) {
+// System.out.println(">>"+colorName(c)+" : "+s);
+ StyleContext sc = StyleContext.getDefaultStyleContext();
+ AttributeSet aset = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, c);
+ int len = getDocument().getLength(); // same value as getText().length();
+ boolean editable = isEditable();
+ setEditable(true);
+ setCaretPosition(len); // place caret at the end (with no selection)
+ setCharacterAttributes(aset, false);
+ replaceSelection(s); // there is no selection, so inserts at caret
+ setEditable(editable);
+ }
+
+ public PrintStream asPrintStream() {
+ if (ps == null) {
+ ps = new PrintStream(
+ new OutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ UI.withinGUI(()->{
+ appendANSI(String.valueOf((char) b));
+ });
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ UI.withinGUI(()->{
+ appendANSI(new String(b, off, len));
+ });
+ }
+ }
+ );
+ }
+ return ps;
+ }
+
+ public void printlnAnsi(String s) {
+ appendANSI(s);
+ appendANSI("\n");
+ }
+
+ public int endOfEscape(String s, int pos) {
+ for (int i = pos; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '\u001B' || (c >= '0' && c <= '9') || c == ';' || c == '[') {
+ continue;
+ } else {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public void appendANSI(String s) { // convert ANSI color codes first
+ int aPos = 0; // current char position in addString
+ int aIndex = 0; // index of next Escape sequence
+ int mIndex = 0; // index of "m" terminating Escape sequence
+ String tmpString = "";
+ boolean stillSearching = true; // true until no more Escape sequences
+ String addString = remaining + s;
+ remaining = "";
+
+ if (addString.length() > 0) {
+ aIndex = addString.indexOf("\u001B"); // find first escape
+ if (aIndex == -1) { // no escape/color change in this string, so just send it with current color
+ append(colorForeground, addString);
+ return;
+ }
+// otherwise There is an escape character in the string, so we must process it
+
+ if (aIndex > 0) { // Escape is not first char, so send text up to first escape
+ tmpString = addString.substring(0, aIndex);
+ append(colorForeground, tmpString);
+ aPos = aIndex;
+ }
+// aPos is now at the beginning of the first escape sequence
+
+ stillSearching = true;
+ while (stillSearching) {
+ mIndex = endOfEscape(addString, aPos); // find the end of the escape sequence
+ if (mIndex < 0) { // the buffer ends halfway through the ansi string!
+ remaining = addString.substring(aPos);
+ stillSearching = false;
+ continue;
+ } else {
+ tmpString = addString.substring(aPos, mIndex + 1);
+ getANSIColor(tmpString);
+ }
+ aPos = mIndex + 1;
+// now we have the color, send text that is in that color (up to next escape)
+
+ aIndex = addString.indexOf("\u001B", aPos);
+
+ if (aIndex == -1) { // if that was the last sequence of the input, send remaining text
+ tmpString = addString.substring(aPos);
+ append(colorForeground, tmpString);
+ stillSearching = false;
+ continue; // jump out of loop early, as the whole string has been sent now
+ }
+
+ // there is another escape sequence, so send part of the string and prepare for the next
+ tmpString = addString.substring(aPos, aIndex);
+ aPos = aIndex;
+ append(colorForeground, tmpString);
+
+ } // while there's text in the input buffer
+ }
+ }
+
+ public void getANSIColor(String ANSIColor) {
+ Pattern p = Pattern.compile("\u001B\\[(?\\d+)(;(?\\d+)(;(?\\d+)(;(?\\d+)(;(?\\d+))?)?)?)?m");
+ Matcher m = p.matcher(ANSIColor);
+ if (m.find()) {
+ int a = Integer.parseInt(m.group("a"));
+ int b = m.group("b") == null ? -1 : Integer.parseInt(m.group("b"));
+ int c = m.group("c") == null ? -1 : Integer.parseInt(m.group("c"));
+ int d = m.group("d") == null ? -1 : Integer.parseInt(m.group("d"));
+ int e = m.group("e") == null ? -1 : Integer.parseInt(m.group("e"));
+ switch (a) {
+ case 0: {
+ colorForeground = color256(0);
+ break;
+ }
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ case 36:
+ case 37: {
+ colorForeground = color256(a - 30);
+ break;
+ }
+ case 38: {
+ switch (b) {
+ case 5: {
+ colorForeground = color256(c);
+ break;
+ }
+ case 2: {
+ int rr = valid255(c);
+ int gg = valid255(d);
+ int bb = valid255(e);
+ colorForeground = new Color(rr, gg, bb);
+ break;
+ }
+ }
+ break;
+ }
+ case 48: {
+ switch (b) {
+ case 5: {
+ if (c == 0) {
+ colorBackground = null;
+ } else {
+ colorBackground = color256(c);
+ }
+ break;
+ }
+ case 2: {
+ int rr = valid255(c);
+ int gg = valid255(d);
+ int bb = valid255(e);
+ colorBackground = new Color(rr, gg, bb);
+ break;
+ }
+ }
+ }
+ }
+ return;
+ }
+ switch (ANSIColor) {
+ default: {
+ //colorCurrent = cReset;
+ break;
+ }
+ }
+ }
+
+ private int valid255(int c) {
+ if (c < 0) {
+ return 0;
+ }
+ if (c > 255) {
+ return 255;
+ }
+ return c;
+ }
+
+ public void clearScreen() {
+ setText("");
+ }
+}
diff --git a/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/GBC.java b/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/GBC.java
new file mode 100644
index 000000000..38214f400
--- /dev/null
+++ b/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/GBC.java
@@ -0,0 +1,155 @@
+package net.thevpc.nuts.lib.nswing;
+
+import java.awt.*;
+
+public class GBC {
+ GridBagConstraints b = new GridBagConstraints();
+
+ public static GBC of(int x, int y) {
+ return of().at(x, y);
+ }
+
+ public static GBC ofAt(int x, int y) {
+ return of().at(x, y);
+ }
+
+ public GBC ipad(int x, int y) {
+ b.ipadx = x;
+ b.ipady = y;
+ return this;
+ }
+
+ public GBC weightx(int x) {
+ b.weightx = x;
+ return this;
+ }
+
+ public GBC weighty(int y) {
+ b.weighty = y;
+ return this;
+ }
+
+ public GBC weight(int x) {
+ return weight(x,x);
+ }
+
+ public GBC weight(int x, int y) {
+ b.weightx = x;
+ b.weighty = y;
+ return this;
+ }
+
+ public GBC at(int x, int y) {
+ b.gridx = x;
+ b.gridy = y;
+ return this;
+ }
+
+ public static GBC of() {
+ return new GBC();
+ }
+
+ public GBC fillNone() {
+ b.fill = GridBagConstraints.NONE;
+ return this;
+ }
+
+ public GBC fillVertical() {
+ b.fill = GridBagConstraints.VERTICAL;
+ return this;
+ }
+
+ public GBC fillHorizontal() {
+ b.fill = GridBagConstraints.HORIZONTAL;
+ return this;
+ }
+
+ public GBC fillBoth() {
+ b.fill = GridBagConstraints.BOTH;
+ return this;
+ }
+
+ public GridBagConstraints build() {
+ return b;
+ }
+
+ public GBC anchorWest() {
+ return anchor(GridBagConstraints.WEST);
+ }
+
+ public GBC anchorEast() {
+ return anchor(GridBagConstraints.EAST);
+ }
+
+ public GBC anchorSouth() {
+ return anchor(GridBagConstraints.SOUTH);
+ }
+
+ public GBC anchorCenter() {
+ return anchor(GridBagConstraints.CENTER);
+ }
+
+ public GBC anchorNorthWest() {
+ return anchor(GridBagConstraints.NORTHWEST);
+ }
+
+ public GBC anchorNorthEast() {
+ return anchor(GridBagConstraints.NORTHEAST);
+ }
+ public GBC anchorNorth() {
+ return anchor(GridBagConstraints.NORTH);
+ }
+
+ public GBC anchor(int a) {
+ this.b.anchor = a;
+ return this;
+ }
+
+ public GBC colspanReminder() {
+ b.gridwidth = GridBagConstraints.REMAINDER;
+ return this;
+ }
+
+ public GBC colspanRelative() {
+ b.gridwidth = GridBagConstraints.RELATIVE;
+ return this;
+ }
+
+ public GBC colspan(int c) {
+ b.gridwidth = c;
+ return this;
+ }
+
+ public GBC rowspan(int c) {
+ b.gridheight = c;
+ return this;
+ }
+
+ public GBC rowspanReminder() {
+ b.gridheight = GridBagConstraints.REMAINDER;
+ return this;
+ }
+
+ public GBC rowspanRelative() {
+ b.gridheight = GridBagConstraints.RELATIVE;
+ return this;
+ }
+
+ public GBC insets(int v, int h) {
+ return insets(new Insets(v, h, v, h));
+ }
+
+ public GBC insets(int top,int left,int bottom,int right) {
+ return insets(new Insets(top, left, bottom, right));
+ }
+
+ public GBC insets(int i) {
+ return insets(new Insets(i, i, i, i));
+ }
+
+ public GBC insets(Insets i) {
+ b.insets = i;
+ return this;
+ }
+
+}
diff --git a/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/PlaceholderTextField.java b/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/PlaceholderTextField.java
new file mode 100644
index 000000000..427648ce8
--- /dev/null
+++ b/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/PlaceholderTextField.java
@@ -0,0 +1,60 @@
+package net.thevpc.nuts.lib.nswing;
+
+import javax.swing.*;
+import javax.swing.text.Document;
+import java.awt.*;
+
+public class PlaceholderTextField extends JTextField {
+
+
+ private String placeholder;
+
+ public PlaceholderTextField() {
+ }
+
+ public PlaceholderTextField(
+ final Document pDoc,
+ final String pText,
+ final int pColumns) {
+ super(pDoc, pText, pColumns);
+ }
+
+ public PlaceholderTextField(final int pColumns) {
+ super(pColumns);
+ }
+
+ public PlaceholderTextField(final String pText) {
+ super(pText);
+ }
+
+ public PlaceholderTextField(final String pText, final int pColumns) {
+ super(pText, pColumns);
+ }
+
+ public String getPlaceholder() {
+ return placeholder;
+ }
+
+ @Override
+ protected void paintComponent(final Graphics pG) {
+ super.paintComponent(pG);
+
+ if (placeholder == null || placeholder.length() == 0 || getText().length() > 0) {
+ return;
+ }
+
+ final Graphics2D g = (Graphics2D) pG;
+ g.setRenderingHint(
+ RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setColor(getDisabledTextColor());
+ g.drawString(placeholder, getInsets().left, pG.getFontMetrics()
+ .getMaxAscent() + getInsets().top);
+ }
+
+ public PlaceholderTextField setPlaceholder(final String s) {
+ placeholder = s;
+ return this;
+ }
+
+}
diff --git a/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/UI.java b/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/UI.java
new file mode 100644
index 000000000..623296dc9
--- /dev/null
+++ b/libraries/nlib-swing/src/main/java/net/thevpc/nuts/lib/nswing/UI.java
@@ -0,0 +1,24 @@
+package net.thevpc.nuts.lib.nswing;
+
+import javax.swing.*;
+import java.lang.reflect.InvocationTargetException;
+
+public class UI {
+ public static void async(Runnable r){
+ new Thread(r).start();
+ }
+
+ public static void withinGUI(Runnable r){
+ if(SwingUtilities.isEventDispatchThread()){
+ r.run();
+ }else{
+ try {
+ SwingUtilities.invokeAndWait(r);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/libraries/pom.xml b/libraries/pom.xml
index 3e4b22547..069d09afb 100755
--- a/libraries/pom.xml
+++ b/libraries/pom.xml
@@ -15,6 +15,7 @@
nlib-template
nlib-tomcat-classloader
nlib-spring-boot
+ nlib-swing