From 64b1b87117a3095d52823e4fd5e5f9c605825a90 Mon Sep 17 00:00:00 2001 From: Elia Lazzari Date: Fri, 23 Dec 2022 19:36:18 +0100 Subject: [PATCH] Added Typescript and CommonJS examples --- README.md | 13 + examples/test-cgt-cjs/index.cjs | 337 ++++++++++++++++ examples/test-cgt-cjs/package-lock.json | 50 +++ examples/test-cgt-cjs/package.json | 15 + examples/test-cgt-ts/.vscode/launch.json | 18 + examples/test-cgt-ts/index.ts | 483 +++++++++++++++++++++++ examples/test-cgt-ts/package-lock.json | 83 ++++ examples/test-cgt-ts/package.json | 19 + examples/test-cgt-ts/tsconfig.json | 103 +++++ package.json | 2 +- 10 files changed, 1122 insertions(+), 1 deletion(-) create mode 100644 examples/test-cgt-cjs/index.cjs create mode 100644 examples/test-cgt-cjs/package-lock.json create mode 100644 examples/test-cgt-cjs/package.json create mode 100644 examples/test-cgt-ts/.vscode/launch.json create mode 100644 examples/test-cgt-ts/index.ts create mode 100644 examples/test-cgt-ts/package-lock.json create mode 100644 examples/test-cgt-ts/package.json create mode 100644 examples/test-cgt-ts/tsconfig.json diff --git a/README.md b/README.md index f79a968..8f52b18 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,19 @@ Install with: npm i console-gui-tools ``` +## Importing the library +The library is transpiled to ESM and CJS, so you can import it with both syntax. + +### ESM syntax +```js +import { ConsoleManager } from 'console-gui-tools' +``` + +### CommonJS syntax +```js +const { ConsoleManager } = require('console-gui-tools') +``` +
OPTIONS diff --git a/examples/test-cgt-cjs/index.cjs b/examples/test-cgt-cjs/index.cjs new file mode 100644 index 0000000..c0ed9ab --- /dev/null +++ b/examples/test-cgt-cjs/index.cjs @@ -0,0 +1,337 @@ +const net = require("net") +const EventEmitter = require("events") + +const PORT = 9090 +const HOST = "127.0.0.1" +let period = 100 + +let mode = "random" + +const periodList = [10, 100, 250, 500, 1000, 2000, 5000, 10000, 20000, 30000, 60000, 120000, 300000, 600000, 900000, 1800000, 3600000, 7200000, 14400000, 28800000, 43200000, 86400000] +const modeList = ["random", "linear"] + +const clientManager = new EventEmitter() + +const { ConsoleManager, OptionPopup, InputPopup, PageBuilder, ButtonPopup, ConfirmPopup, CustomPopup, FileSelectorPopup, Control, InPageWidgetBuilder, Button, Progress } = require("console-gui-tools") +const GUI = new ConsoleManager({ + title: "TCP Simulator", // Title of the console + logPageSize: 20, // Number of lines to show in logs page + logLocation: 1, // Location of the logs page + enableMouse: true, // Enable mouse support + layoutOptions: { + boxed: true, // Set to true to enable boxed layout + showTitle: true, // Set to false to hide title + changeFocusKey: "ctrl+l", // Change layout with ctrl+l to switch to the logs page + type: "double", // Set to 'double' to enable double layout + direction: "vertical", // Set to 'horizontal' to enable horizontal layout + boxColor: "yellow", + boxStyle: "bold", + }, +}) + +let connectedClients = 0 + +// The number of TCP message sent since start +let tcpCounter = 0 + +// Make A TCP Server that listens on port 9090 +const server = net.createServer(socket => { + connectedClients++ + //drawGui() + clientManager.on("send", (data) => { + socket.write(data + "\n") + tcpCounter++ + }) + socket.on("error", function (err) { + lastErr = `Error: ${err.stack}` + }) + socket.on("end", function () { + lastErr = "Error: Client disconnected!" + connectedClients-- + }) +}).listen(PORT, HOST) + +let lastErr = "" + +server.on("error", err => { + lastErr = `Error: ${err.message}` + GUI.error(lastErr) +}) + +let min = 9 +let max = 12 + +let values = [0, 0, 0, 0, 0, 0] + +const frame = async () => { + switch (mode) { + case "random": + await updateWithRandomValues() + break + case "linear": + await updateWithLinearValues() + break + default: + break + } + await sendValuesAsCsv() +} + +let valueEmitter = null //setInterval(frame, period) +let direction = [1, 0, 1, 1, 0, 0] // 1 = Up +let step = 0.01 + +const updateWithRandomValues = async () => { + values = values.map(() => Math.random() * (max - min) + min) +} + +const updateWithLinearValues = async () => { + // Generate linear values using direction and max/min values + values = values.map((value, index) => { + if (value >= max) { + direction[index] = 0 + } else if (value <= min) { + direction[index] = 1 + } + return direction[index] === 1 ? value + step : value - step + }) +} + +const sendValuesAsCsv = async () => { + const csv = values.map(v => v.toFixed(4)).join(",") + clientManager.emit("send", csv) + drawGui() +} + +const defineCustomWidget = () => { + const widget1 = new InPageWidgetBuilder() + widget1.addRow({ text: "┌────────┐", color: "yellow", style: "bold" }) + widget1.addRow({ text: "│ START! │", color: "yellow", style: "bold" }) + widget1.addRow({ text: "└────────┘", color: "yellow", style: "bold" }) + + //id: string, visible = false, attributes: PhisicalValues, children: PageBuilder + const button1 = new Control("btn1", true, { x: 32, y: 18, width: 10, height: 3 }, widget1) + button1.on("relativeMouse", (event) => { + // The relative mouse event is triggered with the mouse position relative to the widget + //console.log(`Mouse event: x: ${event.data.x}, y: ${event.data.y}`) + if (event.name === "MOUSE_LEFT_BUTTON_RELEASED") { + GUI.log("Button 1 clicked!") + if (valueEmitter) { + clearInterval(valueEmitter) + valueEmitter = null + } else { + valueEmitter = setInterval(frame, period) + } + } + }) +} + +const defineButton = () => { + new Button("btnRun", "Run me!", 10, 3, 21, 18, + { + color: "magentaBright", + bold: true, + italic: true, + borderColor: "green" + }, + () => { + GUI.log("Button clicked!") + }) + + const pStyle = { + boxed: true, + showTitle: true, + showValue: true, + showPercentage: true, + showMinMax: false, + } + const p = new Progress("prog1", 20, 1, 3, 23, pStyle, "htop", "horizontal") + p.setText("Mem") + const incr = setInterval(() => { + const value = p.getValue() + 0.25 + p.setValue(value) + if (value >= p.getMax()) { + clearInterval(incr) + } + }, 100) + + const p1Style = { + background: "bgBlack", + borderColor: "yellow", + color: "green", + boxed: true, + showTitle: true, + showValue: true, + showPercentage: true, + showMinMax: true, + + } + const p1 = new Progress("prog2", 25, 2, 3, 25, p1Style, "precision", "horizontal") + p1.setText("Precision") + const incr1 = setInterval(() => { + const value = p1.getValue() + 0.25 + p1.setValue(value) + if (value >= p1.getMax()) { + clearInterval(incr1) + } + }, 100) + + const p2Style = { + background: "bgBlack", + borderColor: "yellow", + color: "magenta", + boxed: true, + showTitle: true, + showValue: true, + showPercentage: true, + showMinMax: true, + + } + const p2 = new Progress("prog3", 25, 2, 3, 31, p2Style, "precision", "horizontal", true) + p2.setText("Interactive") + p2.on("valueChanged", (value) => { + console.log(`Value changed: ${value}`) + }) +} + +/** + * @description Updates the console screen + * + */ +const updateConsole = async () => { + const p = new PageBuilder() + p.addRow({ text: "TCP server simulator app! Welcome...", color: "yellow" }) + p.addRow({ text: `TCP Server listening on ${HOST}:${PORT}`, color: "green" }) + p.addRow({ text: "Connected clients:", color: "green" }, { text: ` ${connectedClients}`, color: "white" }) + p.addRow({ text: "TCP messages sent:", color: "green", bg: "bgRed", bold: true, italic: true, underline: true }, { text: ` ${tcpCounter}`, color: "white" }) + + // Print if simulator is running or not + if (!valueEmitter) { + p.addRow({ text: "Simulator is not running! ", color: "red" }, { text: "press 'space' to start", color: "white" }) + } else { + p.addRow({ text: "Simulator is running! ", color: "green" }, { text: "press 'space' to stop", color: "white" }) + } + + // Print mode: + p.addRow({ text: "Mode: ", color: "cyan" }, { text: `${mode}`, color: "white" }) + + // Print message frequency: + p.addRow({ text: "Message period: ", color: "cyan" }, { text: `${period} ms`, color: "white" }) + + // Print Min and Max + p.addRow({ text: "Min: ", color: "cyan" }, { text: `${min}`, color: "white" }) + p.addRow({ text: "Max: ", color: "cyan" }, { text: `${max}`, color: "white" }) + + // Print current values: + p.addRow({ text: "Values: ", color: "cyan" }, { text: ` ${values.map(v => v.toFixed(4)).join(" ")}`, color: "white" }) + + // Spacer + p.addSpacer() + + if (lastErr.length > 0) { + p.addRow({ text: lastErr, color: "red" }) + p.addSpacer(2) + } + + p.addRow({ text: "Commands:", color: "white", bg: "black" }) + p.addRow({ text: " 'space'", color: "gray", bold: true }, { text: " - Start/stop simulator", color: "white", italic: true }) + p.addRow({ text: " 'm'", color: "gray", bold: true }, { text: " - Select simulation mode", color: "white", italic: true }) + p.addRow({ text: " 's'", color: "gray", bold: true }, { text: " - Select message period", color: "white", italic: true }) + p.addRow({ text: " 'h'", color: "gray", bold: true }, { text: " - Set max value", color: "white", italic: true }) + p.addRow({ text: " 'l'", color: "gray", bold: true }, { text: " - Set min value", color: "white", italic: true }) + p.addRow({ text: " 'q'", color: "gray", bold: true }, { text: " - Quit", color: "white", italic: true }) + + GUI.setPage(p, 0) +} + +GUI.on("exit", () => { + closeApp() +}) + +GUI.on("keypressed", (key) => { + switch (key.name) { + case "space": + if (valueEmitter) { + clearInterval(valueEmitter) + valueEmitter = null + } else { + valueEmitter = setInterval(frame, period) + } + break + case "m": + new OptionPopup("popupSelectMode", "Select simulation mode", modeList, mode).show().on("confirm", (_mode) => { + mode = _mode + GUI.warn(`NEW MODE: ${mode}`) + drawGui() + }) + break + case "s": + new OptionPopup("popupSelectPeriod", "Select simulation period", periodList, period).show().on("confirm", (_period) => { + const msgMultiLine = `Changing period from ${period} to ${_period} ms.\nThis will restart the simulator.\nDo you want to continue?` + new ButtonPopup("popupConfirmPeriod", "Confirm period", msgMultiLine, ["Yes", "No", "?"]).show().on("confirm", (answer) => { + if (answer === "Yes") { + period = _period + GUI.warn(`NEW PERIOD: ${period}`) + } else if (answer === "?") { + GUI.info("Choose ok to confirm period") + } + drawGui() + }) + }) + break + case "h": + new InputPopup("popupTypeMax", "Type max value", max, true).show().on("confirm", (_max) => { + max = _max + GUI.warn(`NEW MAX VALUE: ${max}`) + drawGui() + }) + break + case "l": + new InputPopup("popupTypeMin", "Type min value", min, true).show().on("confirm", (_min) => { + min = _min + GUI.warn(`NEW MIN VALUE: ${min}`) + drawGui() + }) + break + case "1": + { + const p = new PageBuilder(5) // Add a scroll limit so it will be scrollable with up and down + p.addRow({ text: "Example of a custom popup content!", color: "yellow" }) + p.addRow({ text: "This is a custom popup!", color: "green" }) + p.addRow({ text: "It can be used to show a message,", color: "green" }) + p.addRow({ text: "or to show variables.", color: "green" }) + p.addRow({ text: "TCP Message sent: ", color: "green" }, { text: `${tcpCounter}`, color: "white" }) + p.addRow({ text: "Connected clients: ", color: "green" }, { text: `${connectedClients}`, color: "white" }) + p.addRow({ text: "Mode: ", color: "green" }, { text: `${mode}`, color: "white" }) + p.addRow({ text: "Message period: ", color: "green" }, { text: `${period} ms`, color: "white" }) + new CustomPopup("popupCustom1", "See that values", p, 32).show() + } + break + case "f": + new FileSelectorPopup("popupFileManager", "File Manager", "./").show() + break + case "q": + new ConfirmPopup("popupQuit", "Are you sure you want to quit?").show().on("confirm", () => closeApp()) + break + default: + break + } +}) + +const drawGui = () => { + updateConsole() +} + +const closeApp = () => { + console.clear() + clearInterval(valueEmitter) + server.close() + process.exit() +} + +drawGui() +defineCustomWidget() +defineButton() + +// If GUI.options.overrideConsole is true, the console.log|warn|error|info will be overriden to show the messages in the GUI console +console.log("Press 'q' to quit") \ No newline at end of file diff --git a/examples/test-cgt-cjs/package-lock.json b/examples/test-cgt-cjs/package-lock.json new file mode 100644 index 0000000..d2d5ae2 --- /dev/null +++ b/examples/test-cgt-cjs/package-lock.json @@ -0,0 +1,50 @@ +{ + "name": "test-cgt", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "test-cgt", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "console-gui-tools": "^2.1.1" + } + }, + "node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/console-gui-tools": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/console-gui-tools/-/console-gui-tools-2.1.1.tgz", + "integrity": "sha512-RL3NQTV2mzQc9Hu/T5qmNIPDZEdo9LVI8V09mNoeO3BZxM85SVLyO/2Y7zBVpFJLFUSTKc0mYDxUS0i+OZgNJg==", + "dependencies": { + "chalk": "^5.1.2" + } + } + }, + "dependencies": { + "chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==" + }, + "console-gui-tools": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/console-gui-tools/-/console-gui-tools-2.1.1.tgz", + "integrity": "sha512-RL3NQTV2mzQc9Hu/T5qmNIPDZEdo9LVI8V09mNoeO3BZxM85SVLyO/2Y7zBVpFJLFUSTKc0mYDxUS0i+OZgNJg==", + "requires": { + "chalk": "^5.1.2" + } + } + } +} diff --git a/examples/test-cgt-cjs/package.json b/examples/test-cgt-cjs/package.json new file mode 100644 index 0000000..88ac45a --- /dev/null +++ b/examples/test-cgt-cjs/package.json @@ -0,0 +1,15 @@ +{ + "name": "test-cgt", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "commonjs", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Elia Lazzari", + "license": "ISC", + "dependencies": { + "console-gui-tools": "^2.1.1" + } +} diff --git a/examples/test-cgt-ts/.vscode/launch.json b/examples/test-cgt-ts/.vscode/launch.json new file mode 100644 index 0000000..40fa1f6 --- /dev/null +++ b/examples/test-cgt-ts/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + // Usare IntelliSense per informazioni sui possibili attributi. + // Al passaggio del mouse vengono visualizzate le descrizioni degli attributi esistenti. + // Per altre informazioni, visitare: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}/helloworld.ts", + "preLaunchTask": "tsc: build - tsconfig.json", + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ] + } + ] +} \ No newline at end of file diff --git a/examples/test-cgt-ts/index.ts b/examples/test-cgt-ts/index.ts new file mode 100644 index 0000000..28cda85 --- /dev/null +++ b/examples/test-cgt-ts/index.ts @@ -0,0 +1,483 @@ +import net from "net"; +import EventEmitter from "events"; + +const PORT = 9090; +const HOST = "127.0.0.1"; +let period = 100; + +let mode = "random"; + +const periodList = [ + 10, 100, 250, 500, 1000, 2000, 5000, 10000, 20000, 30000, 60000, 120000, + 300000, 600000, 900000, 1800000, 3600000, 7200000, 14400000, 28800000, + 43200000, 86400000, +]; +const modeList = ["random", "linear"]; + +const clientManager = new EventEmitter(); + +import { + ConsoleManager, + OptionPopup, + InputPopup, + PageBuilder, + ButtonPopup, + ConfirmPopup, + CustomPopup, + FileSelectorPopup, + Control, + InPageWidgetBuilder, + Button, + Progress, + SimplifiedStyledElement, +} from "console-gui-tools"; +import { ProgressStyle } from "console-gui-tools/dist/types/components/widgets/ProgressBar"; +const GUI = new ConsoleManager({ + title: "TCP Simulator", // Title of the console + logPageSize: 20, // Number of lines to show in logs page + logLocation: 1, // Location of the logs page + enableMouse: true, // Enable mouse support + layoutOptions: { + boxed: true, // Set to true to enable boxed layout + showTitle: true, // Set to false to hide title + changeFocusKey: "ctrl+l", // Change layout with ctrl+l to switch to the logs page + type: "double", // Set to 'double' to enable double layout + direction: "vertical", // Set to 'horizontal' to enable horizontal layout + boxColor: "yellow", + boxStyle: "bold", + }, + overrideConsole: true, // Set to false to disable overriding console.log +}); + +let connectedClients = 0; + +// The number of TCP message sent since start +let tcpCounter = 0; + +// Make A TCP Server that listens on port 9090 +const server = net + .createServer((socket) => { + connectedClients++; + //drawGui() + clientManager.on("send", (data) => { + socket.write(data + "\n"); + tcpCounter++; + }); + socket.on("error", function (err) { + lastErr = `Error: ${err.stack}`; + }); + socket.on("end", function () { + lastErr = "Error: Client disconnected!"; + connectedClients--; + }); + }) + .listen(PORT, HOST); + +let lastErr = ""; + +server.on("error", (err) => { + lastErr = `Error: ${err.message}`; + GUI.error(lastErr); +}); + +let min = 9; +let max = 12; + +let values = [0, 0, 0, 0, 0, 0]; + +const frame = async () => { + switch (mode) { + case "random": + await updateWithRandomValues(); + break; + case "linear": + await updateWithLinearValues(); + break; + default: + break; + } + await sendValuesAsCsv(); +}; + +let valueEmitter: any = null; //setInterval(frame, period) +let direction = [1, 0, 1, 1, 0, 0]; // 1 = Up +let step = 0.01; + +const updateWithRandomValues = async () => { + values = values.map(() => Math.random() * (max - min) + min); +}; + +const updateWithLinearValues = async () => { + // Generate linear values using direction and max/min values + values = values.map((value, index) => { + if (value >= max) { + direction[index] = 0; + } else if (value <= min) { + direction[index] = 1; + } + return direction[index] === 1 ? value + step : value - step; + }); +}; + +const sendValuesAsCsv = async () => { + const csv = values.map((v) => v.toFixed(4)).join(","); + clientManager.emit("send", csv); + drawGui(); +}; + +const defineCustomWidget = () => { + const widget1 = new InPageWidgetBuilder(); + widget1.addRow({ text: "┌────────┐", color: "yellow", style: "bold" } as SimplifiedStyledElement); + widget1.addRow({ text: "│ START! │", color: "yellow", style: "bold" } as SimplifiedStyledElement); + widget1.addRow({ text: "└────────┘", color: "yellow", style: "bold" } as SimplifiedStyledElement); + + //id: string, visible = false, attributes: PhisicalValues, children: PageBuilder + const button1 = new Control( + "btn1", + true, + { x: 32, y: 18, width: 10, height: 3 }, + widget1 + ); + button1.on("relativeMouse", (event) => { + // The relative mouse event is triggered with the mouse position relative to the widget + //console.log(`Mouse event: x: ${event.data.x}, y: ${event.data.y}`) + if (event.name === "MOUSE_LEFT_BUTTON_RELEASED") { + GUI.log("Button 1 clicked!"); + if (valueEmitter) { + clearInterval(valueEmitter); + valueEmitter = null; + } else { + valueEmitter = setInterval(frame, period); + } + } + }); +}; + +const defineButton = () => { + new Button( + "btnRun", + "Run me!", + 10, + 3, + 21, + 18, + { + color: "magentaBright", + bold: true, + italic: true, + borderColor: "green", + background: "bgBlack", + }, + () => { + GUI.log("Button clicked!"); + }, () => {} + ); + + const pStyle = { + boxed: true, + showTitle: true, + showValue: true, + showPercentage: true, + showMinMax: false, + } as ProgressStyle; + const p = new Progress("prog1", 20, 1, 3, 23, pStyle, "htop", "horizontal"); + p.setText("Mem"); + const incr = setInterval(() => { + const value = p.getValue() + 0.25; + p.setValue(value); + if (value >= p.getMax()) { + clearInterval(incr); + } + }, 100); + + const p1Style = { + background: "bgBlack", + borderColor: "yellow", + color: "green", + boxed: true, + showTitle: true, + showValue: true, + showPercentage: true, + showMinMax: true, + } as ProgressStyle; + const p1 = new Progress( + "prog2", + 25, + 2, + 3, + 25, + p1Style, + "precision", + "horizontal" + ); + p1.setText("Precision"); + const incr1 = setInterval(() => { + const value = p1.getValue() + 0.25; + p1.setValue(value); + if (value >= p1.getMax()) { + clearInterval(incr1); + } + }, 100); + + const p2Style = { + background: "bgBlack", + borderColor: "yellow", + color: "magenta", + boxed: true, + showTitle: true, + showValue: true, + showPercentage: true, + showMinMax: true, + } as ProgressStyle; + const p2 = new Progress( + "prog3", + 25, + 2, + 3, + 31, + p2Style, + "precision", + "horizontal", + true + ); + p2.setText("Interactive"); + p2.on("valueChanged", (value) => { + console.log(`Value changed: ${value}`); + }); +}; + +/** + * @description Updates the console screen + * + */ +const updateConsole = async () => { + const p = new PageBuilder(); + p.addRow({ text: "TCP server simulator app! Welcome...", color: "yellow" }); + p.addRow({ text: `TCP Server listening on ${HOST}:${PORT}`, color: "green" }); + p.addRow( + { text: "Connected clients:", color: "green" }, + { text: ` ${connectedClients}`, color: "white" } + ); + p.addRow( + { + text: "TCP messages sent:", + color: "green", + bg: "bgRed", + bold: true, + italic: true, + underline: true, + }, + { text: ` ${tcpCounter}`, color: "white" } + ); + + // Print if simulator is running or not + if (!valueEmitter) { + p.addRow( + { text: "Simulator is not running! ", color: "red" }, + { text: "press 'space' to start", color: "white" } + ); + } else { + p.addRow( + { text: "Simulator is running! ", color: "green" }, + { text: "press 'space' to stop", color: "white" } + ); + } + + // Print mode: + p.addRow( + { text: "Mode: ", color: "cyan" }, + { text: `${mode}`, color: "white" } + ); + + // Print message frequency: + p.addRow( + { text: "Message period: ", color: "cyan" }, + { text: `${period} ms`, color: "white" } + ); + + // Print Min and Max + p.addRow( + { text: "Min: ", color: "cyan" }, + { text: `${min}`, color: "white" } + ); + p.addRow( + { text: "Max: ", color: "cyan" }, + { text: `${max}`, color: "white" } + ); + + // Print current values: + p.addRow( + { text: "Values: ", color: "cyan" }, + { text: ` ${values.map((v) => v.toFixed(4)).join(" ")}`, color: "white" } + ); + + // Spacer + p.addSpacer(); + + if (lastErr.length > 0) { + p.addRow({ text: lastErr, color: "red" }); + p.addSpacer(2); + } + + p.addRow({ text: "Commands:", color: "white", bg: "bgBlack" }); + p.addRow( + { text: " 'space'", color: "gray", bold: true }, + { text: " - Start/stop simulator", color: "white", italic: true } + ); + p.addRow( + { text: " 'm'", color: "gray", bold: true }, + { text: " - Select simulation mode", color: "white", italic: true } + ); + p.addRow( + { text: " 's'", color: "gray", bold: true }, + { text: " - Select message period", color: "white", italic: true } + ); + p.addRow( + { text: " 'h'", color: "gray", bold: true }, + { text: " - Set max value", color: "white", italic: true } + ); + p.addRow( + { text: " 'l'", color: "gray", bold: true }, + { text: " - Set min value", color: "white", italic: true } + ); + p.addRow( + { text: " 'q'", color: "gray", bold: true }, + { text: " - Quit", color: "white", italic: true } + ); + + GUI.setPage(p, 0); +}; + +GUI.on("exit", () => { + closeApp(); +}); + +GUI.on("keypressed", (key) => { + switch (key.name) { + case "space": + if (valueEmitter) { + clearInterval(valueEmitter); + valueEmitter = null; + } else { + valueEmitter = setInterval(frame, period); + } + break; + case "m": + new OptionPopup( + "popupSelectMode", + "Select simulation mode", + modeList, + mode + ) + .show() + .on("confirm", (_mode) => { + mode = _mode; + GUI.warn(`NEW MODE: ${mode}`); + drawGui(); + }); + break; + case "s": + new OptionPopup( + "popupSelectPeriod", + "Select simulation period", + periodList, + period + ) + .show() + .on("confirm", (_period) => { + const msgMultiLine = `Changing period from ${period} to ${_period} ms.\nThis will restart the simulator.\nDo you want to continue?`; + new ButtonPopup( + "popupConfirmPeriod", + "Confirm period", + msgMultiLine, + ["Yes", "No", "?"] + ) + .show() + .on("confirm", (answer) => { + if (answer === "Yes") { + period = _period; + GUI.warn(`NEW PERIOD: ${period}`); + } else if (answer === "?") { + GUI.info("Choose ok to confirm period"); + } + drawGui(); + }); + }); + break; + case "h": + new InputPopup("popupTypeMax", "Type max value", max, true) + .show() + .on("confirm", (_max) => { + max = _max; + GUI.warn(`NEW MAX VALUE: ${max}`); + drawGui(); + }); + break; + case "l": + new InputPopup("popupTypeMin", "Type min value", min, true) + .show() + .on("confirm", (_min) => { + min = _min; + GUI.warn(`NEW MIN VALUE: ${min}`); + drawGui(); + }); + break; + case "1": + { + const p = new PageBuilder(5); // Add a scroll limit so it will be scrollable with up and down + p.addRow({ + text: "Example of a custom popup content!", + color: "yellow", + }); + p.addRow({ text: "This is a custom popup!", color: "green" }); + p.addRow({ text: "It can be used to show a message,", color: "green" }); + p.addRow({ text: "or to show variables.", color: "green" }); + p.addRow( + { text: "TCP Message sent: ", color: "green" }, + { text: `${tcpCounter}`, color: "white" } + ); + p.addRow( + { text: "Connected clients: ", color: "green" }, + { text: `${connectedClients}`, color: "white" } + ); + p.addRow( + { text: "Mode: ", color: "green" }, + { text: `${mode}`, color: "white" } + ); + p.addRow( + { text: "Message period: ", color: "green" }, + { text: `${period} ms`, color: "white" } + ); + new CustomPopup("popupCustom1", "See that values", p, 32).show(); + } + break; + case "f": + new FileSelectorPopup("popupFileManager", "File Manager", "./").show(); + break; + case "q": + new ConfirmPopup("popupQuit", "Are you sure you want to quit?", "Yes") + .show() + .on("confirm", () => closeApp()); + break; + default: + break; + } +}); + +const drawGui = () => { + updateConsole(); +}; + +const closeApp = () => { + console.clear(); + clearInterval(valueEmitter); + server.close(); + process.exit(); +}; + +drawGui(); +defineCustomWidget(); +defineButton(); + +// If GUI.options.overrideConsole is true, the console.log|warn|error|info will be overriden to show the messages in the GUI console +console.log("Press 'q' to quit"); diff --git a/examples/test-cgt-ts/package-lock.json b/examples/test-cgt-ts/package-lock.json new file mode 100644 index 0000000..76408e4 --- /dev/null +++ b/examples/test-cgt-ts/package-lock.json @@ -0,0 +1,83 @@ +{ + "name": "test-cgt", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "test-cgt", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "console-gui-tools": "^2.1.1", + "typescript": "^4.9.4" + }, + "devDependencies": { + "@types/node": "^18.11.17" + } + }, + "node_modules/@types/node": { + "version": "18.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", + "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", + "dev": true + }, + "node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/console-gui-tools": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/console-gui-tools/-/console-gui-tools-2.1.1.tgz", + "integrity": "sha512-RL3NQTV2mzQc9Hu/T5qmNIPDZEdo9LVI8V09mNoeO3BZxM85SVLyO/2Y7zBVpFJLFUSTKc0mYDxUS0i+OZgNJg==", + "dependencies": { + "chalk": "^5.1.2" + } + }, + "node_modules/typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + }, + "dependencies": { + "@types/node": { + "version": "18.11.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz", + "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==", + "dev": true + }, + "chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==" + }, + "console-gui-tools": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/console-gui-tools/-/console-gui-tools-2.1.1.tgz", + "integrity": "sha512-RL3NQTV2mzQc9Hu/T5qmNIPDZEdo9LVI8V09mNoeO3BZxM85SVLyO/2Y7zBVpFJLFUSTKc0mYDxUS0i+OZgNJg==", + "requires": { + "chalk": "^5.1.2" + } + }, + "typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" + } + } +} diff --git a/examples/test-cgt-ts/package.json b/examples/test-cgt-ts/package.json new file mode 100644 index 0000000..847c8b2 --- /dev/null +++ b/examples/test-cgt-ts/package.json @@ -0,0 +1,19 @@ +{ + "name": "test-cgt", + "version": "1.0.0", + "description": "", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Elia Lazzari", + "license": "ISC", + "dependencies": { + "console-gui-tools": "^2.1.1", + "typescript": "^4.9.4" + }, + "devDependencies": { + "@types/node": "^18.11.17" + } +} diff --git a/examples/test-cgt-ts/tsconfig.json b/examples/test-cgt-ts/tsconfig.json new file mode 100644 index 0000000..e2164b6 --- /dev/null +++ b/examples/test-cgt-ts/tsconfig.json @@ -0,0 +1,103 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "ES2022", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./out", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/package.json b/package.json index 12fed09..406ec43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "console-gui-tools", - "version": "2.1.1", + "version": "2.1.2", "description": "A simple library to draw option menu, text popup or other widgets and layout on a Node.js console.", "main": "dist/esm/ConsoleGui.mjs", "types": "dist/types/ConsoleGui.d.ts",