diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..dfe0770
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8587dc1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 safaritrader
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..22c70c5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,97 @@
+# Custom Command-Line Interface (CLI) Project
+
+Welcome to the Custom CLI project! This project provides an interactive command-line interface implemented in JavaScript, along with a GUI-based Command Creator to build custom commands and sub-commands.
+
+> [!IMPORTANT]
+> All of codes in this repository Made with the ChatGPT :white_flag:
+> [![ License](https://img.shields.io/badge/Chat_GPT_Version-o1_preview-blue&?color=red)](https://chatgpt.com)
+
+##[Demo](https://safaritrader.github.io/js-cli/index.html)
+
+## Features
+
+- **Interactive CLI**: Navigate through commands and frameworks, execute actions, and receive feedback in real-time.
+- **Dynamic Command Hierarchy**: Supports infinite nesting of commands and sub-commands.
+- **Command History Navigation**: Use the up and down arrow keys to navigate through previous commands.
+- **Tree-Structured Help Display**: Type `help` to see a hierarchical list of available commands.
+- **Dark Dracula Theme**: A visually appealing dark theme inspired by PyCharm's Dracula theme.
+- **GUI-based Command Creator**: Build custom commands with specific handlers using a user-friendly interface.
+
+Getting Started
+
+
+### Launch the CLI Interface
+
+- Open [`cli.html`](https://safaritrader.github.io/js-cli/cli.html) in your web browser.
+- Type commands into the input field and press **Enter** to execute them.
+- Use `help` to see available commands.
+
+### Use the Command Creator
+
+- Open [`command_creator.html`](https://safaritrader.github.io/js-cli/command_creator.html) in your web browser.
+- Use the GUI to create custom commands and sub-commands.
+- Generate the JavaScript code and integrate it into your CLI.
+
+## How to Add Custom Commands
+
+1. **Open the Command Creator**: Use the Command Creator page to build your commands.
+2. **Define Commands**: Enter command names, types, descriptions, and handler code.
+3. **Generate Code**: Click **Generate Code** to create the JavaScript code for your commands.
+4. **Copy and Integrate**: Copy the generated code and paste it into `js/cli.js` after the CLI initialization code.
+5. **Test Your Commands**: Reload the CLI page and test your new commands.
+
+## Project Structure
+
+JS-CLI/ ├── index.html ├── cli.html ├── command_creator.html ├── css/ │ └── style.css ├── js/ │ ├── cli.js │ └── command_creator.js └── README.md
+
+
+## Customization
+
+Feel free to modify and extend the project to suit your needs. You can:
+
+- Add new commands and frameworks.
+- Customize the theme and styles.
+- Integrate additional features such as syntax highlighting or command auto-completion.
+
+## Acknowledgments
+
+This project was developed to provide a flexible platform for creating interactive command-line interfaces in web environments. It combines practical functionality with an intuitive user experience.
+
+---
+
+## **Deploying to GitHub Pages**
+
+To host your site on GitHub Pages:
+
+1. **Push Your Code to GitHub**: Commit and push all your files to your GitHub repository.
+2. **Enable GitHub Pages**:
+ - Go to your repository's settings.
+ - Scroll down to **GitHub Pages**.
+ - Under **Source**, select the branch you want to use (usually `main` or `master`).
+ - Click **Save**.
+3. **Access Your Site**: After a few minutes, your site will be available at:
+ - `https://
Welcome to the Custom CLI project! This project provides a versatile command-line interface implemented in JavaScript, along with a GUI-based Command Creator to build custom commands and sub-commands.
+ +Choose an option below to explore the project:
+ + +For detailed instructions on how to use the CLI and Command Creator, refer to the README file.
+ +This project was developed to provide a flexible and extensible command-line interface that can be easily customized to suit various needs. The Command Creator allows users to build their own commands with specific handlers and integrate them into the CLI seamlessly.
+ +I hope you find this tool useful and enjoy exploring its capabilities!
+ + + + diff --git a/js-cli.jpg b/js-cli.jpg new file mode 100644 index 0000000..8211782 Binary files /dev/null and b/js-cli.jpg differ diff --git a/js/cli.js b/js/cli.js new file mode 100644 index 0000000..132fae1 --- /dev/null +++ b/js/cli.js @@ -0,0 +1,182 @@ +class Command { + constructor(name, type = 'self', description = '', handler = null) { + this.name = name; + this.type = type; // 'framework' or 'self' + this.description = description; + this.handler = handler; // Function to execute + this.subCommands = {}; + } + + addSubCommand(command) { + this.subCommands[command.name] = command; + } + + getSubCommand(name) { + return this.subCommands[name]; + } + + hasSubCommand(name) { + return this.subCommands.hasOwnProperty(name); + } +} + +class CLI { + constructor(outputElement) { + this.outputElement = outputElement; + this.root = new Command('root', 'framework', 'Root framework'); + this.currentFramework = this.root; + this.lastAnswer = null; + this.isAwaitingInput = false; + this.awaitingCommand = null; + this.commandHistory = []; + this.historyIndex = -1; + } + + registerCommand(command, parent = this.root) { + parent.addSubCommand(command); + } + + parseInput(input) { + if (input.trim() === '') return; + + // Add command to history + this.commandHistory.push(input); + this.historyIndex = this.commandHistory.length; + + if (input.trim() === 'exit()') { + this.currentFramework = this.root; + this.print('Exited to root framework.'); + return; + } + + if (input.trim() === 'clear') { + this.clearOutput(); + return; + } + + if (input.trim() === 'help') { + this.displayHelp(this.currentFramework); + return; + } + + if (this.isAwaitingInput && this.awaitingCommand) { + this.processAwaitingInput(input); + return; + } + + const args = input.split(' '); + const commandSequence = args.filter(arg => !arg.startsWith('-')); + const options = args.filter(arg => arg.startsWith('-')); + + let command = this.currentFramework; + for (let cmdName of commandSequence) { + if (command.hasSubCommand(cmdName)) { + command = command.getSubCommand(cmdName); + } else { + this.print(`Command "${cmdName}" not found.`); + return; + } + } + + if (options.includes('-help')) { + this.displayHelp(command); + return; + } + + this.executeCommand(command); + } + + executeCommand(command) { + if (command.type === 'framework') { + this.currentFramework = command; + this.print(`Switched to framework: ${command.name} Use exit()`); + if (command.description) { + this.print(command.description); + } + } else { + if (command.handler) { + const result = command.handler(this.lastAnswer); + if (result && result.requiresInput) { + this.isAwaitingInput = true; + this.awaitingCommand = command; + this.print(result.message); + } else if (result && result.response) { + this.print(result.response); + } + } + } + } + + processAwaitingInput(input) { + const result = this.awaitingCommand.handler(this.lastAnswer, input); + this.lastAnswer = input; + if (result && result.response) { + this.print(result.response); + } + this.isAwaitingInput = false; + this.awaitingCommand = null; + } + + displayHelp(command) { + this.print(`Commands for framework "${command.name}":`); + this.displaySubCommands(command, 0); + } + + displaySubCommands(command, indentLevel) { + const indent = ' '.repeat(indentLevel); + for (let subCmdName in command.subCommands) { + const subCmd = command.subCommands[subCmdName]; + this.print(`${indent}- ${subCmd.name}: ${subCmd.description}`); + if (Object.keys(subCmd.subCommands).length > 0) { + this.displaySubCommands(subCmd, indentLevel + 1); + } + } + } + + print(message) { + this.outputElement.innerHTML += message + '\n'; + this.outputElement.scrollTop = this.outputElement.scrollHeight; + } + + clearOutput() { + this.outputElement.innerHTML = ''; + } +} + +// Create CLI instance +const outputElement = document.getElementById('output'); +const cli = new CLI(outputElement); + +function clearHandler() { + cli.clearOutput(); +} + +// Input handling +const inputElement = document.getElementById('input'); +inputElement.addEventListener('keydown', function(event) { + if (event.key === 'Enter') { + const input = inputElement.value; + cli.print(`> ${input}`); // Echo the command + cli.parseInput(input); + inputElement.value = ''; + } else if (event.key === 'ArrowUp') { + if (cli.historyIndex > 0) { + cli.historyIndex--; + inputElement.value = cli.commandHistory[cli.historyIndex]; + } + event.preventDefault(); + } else if (event.key === 'ArrowDown') { + if (cli.historyIndex < cli.commandHistory.length - 1) { + cli.historyIndex++; + inputElement.value = cli.commandHistory[cli.historyIndex]; + } else { + cli.historyIndex = cli.commandHistory.length; + inputElement.value = ''; + } + event.preventDefault(); + } +}); + +// Welcome message +cli.print('Welcome to the CLI. Type "type" to switch to the type framework.'); +cli.print('Type "help" to see available commands.'); diff --git a/js/command_creator.js b/js/command_creator.js new file mode 100644 index 0000000..c756e1d --- /dev/null +++ b/js/command_creator.js @@ -0,0 +1,129 @@ +let commandCount = 0; + +function createCommandElement(parent) { + commandCount++; + const commandDiv = document.createElement('div'); + commandDiv.className = 'command'; + commandDiv.innerHTML = ` + + + + + + + + `; + + // Add event listeners for the buttons + const addSubCommandButton = commandDiv.querySelector('.addSubCommandButton'); + const removeCommandButton = commandDiv.querySelector('.removeCommandButton'); + const subcommandsDiv = commandDiv.querySelector('.subcommands'); + + addSubCommandButton.addEventListener('click', () => { + const subCommand = createCommandElement(commandDiv); + subcommandsDiv.appendChild(subCommand); + }); + + removeCommandButton.addEventListener('click', () => { + commandDiv.remove(); + }); + + return commandDiv; +} + +const commandsContainer = document.getElementById('commandsContainer'); +const addCommandButton = document.getElementById('addCommandButton'); +const generateButton = document.getElementById('generateButton'); +const generatedCodeTextarea = document.getElementById('generatedCode'); +const copyButton = document.getElementById('copyButton'); + +addCommandButton.addEventListener('click', () => { + const commandElement = createCommandElement(); + commandsContainer.appendChild(commandElement); +}); + +function generateCode() { + let code = ''; + + function processCommand(commandElement, parentName) { + const commandName = commandElement.querySelector('input[name="commandName"]').value.trim(); + const commandType = commandElement.querySelector('select[name="commandType"]').value; + const commandDescription = commandElement.querySelector('input[name="commandDescription"]').value.trim(); + const handlerCode = commandElement.querySelector('textarea[name="handlerCode"]').value.trim(); + const subcommands = commandElement.querySelector('.subcommands').children; + + if (!commandName) { + alert('Command name is required.'); + throw new Error('Command name is missing.'); + } + + // Generate handler function name + const handlerFunctionName = `${commandName}Handler`; + + // Add handler function code + if (handlerCode) { + code += ` +function ${handlerFunctionName}(lastAnswer, input) { + ${handlerCode} +} +`; + } else { + code += ` +function ${handlerFunctionName}(lastAnswer, input) { + // Handler code for ${commandName} +} +`; + } + + // Create command instance + code += ` +const ${commandName}Command = new Command('${commandName}', '${commandType}', '${commandDescription}', ${handlerFunctionName}); +`; + + // Register command + if (parentName) { + code += `cli.registerCommand(${commandName}Command, ${parentName}Command);\n`; + } else { + code += `cli.registerCommand(${commandName}Command);\n`; + } + + // Process sub-commands + for (let subcommandElement of subcommands) { + processCommand(subcommandElement, commandName); + } + } + + // Process top-level commands + const topCommands = commandsContainer.children; + for (let topCommand of topCommands) { + processCommand(topCommand, null); + } + + generatedCodeTextarea.value = code; +} + +generateButton.addEventListener('click', () => { + try { + generatedCodeTextarea.value = ''; + generateCode(); + } catch (error) { + console.error(error); + } +}); + +copyButton.addEventListener('click', () => { + generatedCodeTextarea.select(); + document.execCommand('copy'); + alert('Code copied to clipboard.'); +}); \ No newline at end of file