diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..0b88d4d
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,32 @@
+name: 🧱 Build MyCMD-GUI
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ build:
+ name: 🔨 Build and Package
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: 🧩 Checkout source code
+ uses: actions/checkout@v4
+
+ - name: ☕ Set up Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '21'
+ cache: maven
+
+ - name: 🧰 Build using Maven Wrapper
+ run: ./mvnw -B clean package
+
+ - name: 📦 Upload built JAR artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: MyCMD-GUI
+ path: target/*.jar
diff --git a/.gitignore b/.gitignore
index 9f97022..7ab214a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,51 @@
-target/
\ No newline at end of file
+
+*.class
+
+
+*.log
+
+
+*.ctxt
+
+
+.mtj.tmp/
+
+
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# IntelliJ / VS Code / Eclipse
+.idea/
+.vscode/
+*.iml
+.project
+.classpath
+.settings/
+
+
+target/
+out/
+
+
+.mvn/wrapper/maven-wrapper.jar
+
+
+.DS_Store
+Thumbs.db
+
+
+javafx_cache/
+
+
+launcher/*.exe
+launcher/*.lnk
+installer/output/
+
+
+.idea/
+.vscode/
diff --git a/launcher/MyCMD.bat b/launcher/MyCMD.bat
new file mode 100644
index 0000000..ae42163
--- /dev/null
+++ b/launcher/MyCMD.bat
@@ -0,0 +1,9 @@
+@echo off
+setlocal
+set MYCMD_LAUNCHED=true
+
+:: Hide CMD window by using javaw instead of java
+javaw -jar target\MyCMD-GUI-1.0-SNAPSHOT.jar
+
+endlocal
+exit
diff --git a/lib/.gitkeep b/lib/.gitkeep
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/lib/.gitkeep
@@ -0,0 +1 @@
+
diff --git a/mvnw b/mvnw
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/mvnw
@@ -0,0 +1 @@
+
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1 @@
+
diff --git a/pom.xml b/pom.xml
index 5d226e0..7b4ff9f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,76 +1,80 @@
+
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+
4.0.0
com.mycmd
- MyCMD
- 1.0
- jar
-
- MyCMD
- A custom CMD shell written in Java
+ MyCMD-GUI
+ 1.0.0
+ MyCMD-GUI
+ Dark futuristic JavaFX shell for MyCMD
+ UTF-8
17
17
+ 21
+
- org.projectlombok
- lombok
- 1.18.42
- provided
+ org.openjfx
+ javafx-controls
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-fxml
+ ${javafx.version}
+
+
org.apache.maven.plugins
maven-compiler-plugin
- 3.14.1
+ 3.10.1
${maven.compiler.source}
${maven.compiler.target}
-
-
- org.projectlombok
- lombok
- 1.18.42
-
-
-
+
+
- org.apache.maven.plugins
- maven-jar-plugin
- 3.4.2
+ org.openjfx
+ javafx-maven-plugin
+ ${javafx.version}
-
-
- com.mycmd.App
-
-
+ com.mycmd.gui.MainApp
-
- com.diffplug.spotless
- spotless-maven-plugin
- 3.0.0
-
-
-
- 1.17.0
-
-
-
-
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.3.0
+
+
+ package
+
+ shade
+
+
+
+
+ com.mycmd.gui.MainApp
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/scripts/build-linux.sh b/scripts/build-linux.sh
index 41502ec..6e7b6e6 100644
--- a/scripts/build-linux.sh
+++ b/scripts/build-linux.sh
@@ -1,22 +1,11 @@
#!/bin/bash
-echo "=== Building MyCMD for Linux ==="
+echo "🏗️ Building MyCMD for Linux..."
-mvn clean package
+# Clean and package with Maven Wrapper
+./mvnw clean package
-rm -rf dist
-mkdir -p dist/bin dist/lib dist/icons
+# Move JAR to dist folder
+mkdir -p dist
+cp target/MyCMD-GUI*.jar dist/MyCMD-GUI.jar
-cp target/MyCMD-1.0.jar dist/lib/dependencies.jar
-cp icons/mycmd.ico dist/icons/mycmd.ico
-
-# Example jpackage usage
-jpackage \
- --name MyCMD \
- --input dist/lib \
- --main-jar dependencies.jar \
- --main-class com.mycmd.App \
- --icon icons/mycmd.ico \
- --type deb \
- --dest dist
-
-echo "Build complete. Installer is in dist/"
+echo "✅ Build complete! File located in dist/MyCMD-GUI.jar"
diff --git a/scripts/build-windows.bat b/scripts/build-windows.bat
index 3ea6627..278cf79 100644
--- a/scripts/build-windows.bat
+++ b/scripts/build-windows.bat
@@ -1,17 +1,12 @@
@echo off
-echo === Building MyCMD for Windows ===
+echo 🏗️ Building MyCMD for Windows...
-REM Clean and build jar
-mvn clean package
+REM Clean and package using Maven Wrapper
+call mvnw.cmd clean package
-REM Create dist structure
-rmdir /s /q dist
-mkdir dist\bin
-mkdir dist\lib
-mkdir dist\icons
+REM Move output JARs to /dist folder
+if not exist dist mkdir dist
+copy target\MyCMD-GUI*.jar dist\MyCMD-GUI.jar
-REM Copy jar + icon
-copy target\MyCMD-1.0.jar dist\lib\dependencies.jar
-copy icons\mycmd.ico dist\icons\mycmd.ico
-
-echo Build complete. Use Launch4j + Inno Setup to package installer.
+echo ✅ Build complete! File located in dist\MyCMD-GUI.jar
+pause
diff --git a/src/main/java/com/mycmd/App.java b/src/main/java/com/mycmd/App.java
index 97cf691..4608f28 100644
--- a/src/main/java/com/mycmd/App.java
+++ b/src/main/java/com/mycmd/App.java
@@ -1,239 +1,22 @@
package com.mycmd;
-import com.mycmd.commands.*;
-import java.util.*;
-import java.util.Scanner;
+import com.mycmd.gui.MainApp;
+import javafx.application.Application;
+/**
+ * Entry point for MyCMD-GUI.
+ */
public class App {
public static void main(String[] args) {
- ShellContext context = new ShellContext();
-
- // Register commands
- Map commands = new HashMap<>();
- registerCommands(commands);
-
- System.out.println("MyCMD [Version 1.0]");
- System.out.println("(c) 2025 MyCMD Organization. All rights reserved.");
-
- Scanner sc = new Scanner(System.in);
- context.setScanner(sc); // ← Added this line
-
- while (true) {
- System.out.print(context.getCurrentDir().getAbsolutePath() + ">");
- String input = sc.nextLine().trim();
- if (input.isEmpty()) continue;
-
- // Resolve aliases before processing
- input = resolveAliases(input, context);
-
- String[] parts = input.split("\\s+");
- String cmd = parts[0].toLowerCase();
- String[] cmdArgs = Arrays.copyOfRange(parts, 1, parts.length);
-
- Command command = commands.get(cmd);
- if (command != null) {
- try {
- command.execute(cmdArgs, context);
- // Add to history after successful execution
- context.addToHistory(input);
- } catch (Exception e) {
- System.out.println("Error: " + e.getMessage());
- }
- } else {
- // Single, clear not-recognized message + optional suggestion
- System.out.println(
- "Unknown command: '"
- + cmd
- + "'. Enter '"
- + CommandNames.HELP
- + "' to list all available commands.");
-
- // compute suggestion safely
- try {
- List validCommands = new ArrayList<>(commands.keySet());
- String suggestion = StringUtils.findClosest(cmd, validCommands);
- if (suggestion != null && !suggestion.equalsIgnoreCase(cmd)) {
- System.out.println("Did you mean '" + suggestion + "'?");
- }
- } catch (Exception ex) {
- // don't let suggestion errors break the shell
- }
- }
+ // Security check to prevent CMD access
+ String launchedFrom = System.getenv("MYCMD_LAUNCHED");
+ if (launchedFrom == null || !launchedFrom.equalsIgnoreCase("true")) {
+ System.out.println("❌ MyCMD-GUI cannot be run directly from CMD.");
+ System.out.println("➡️ Please use the official launcher (MyCMD.bat).");
+ return;
}
- }
-
- private static String resolveAliases(String input, ShellContext context) {
- String[] parts = input.split("\\s+", 2);
- String cmd = parts[0];
- String rest = parts.length > 1 ? parts[1] : "";
-
- // Check if the command is an alias
- if (context.hasAlias(cmd)) {
- String aliasCommand = context.getAlias(cmd);
- // Replace the alias with its command, preserving arguments
- return rest.isEmpty() ? aliasCommand : aliasCommand + " " + rest;
- }
-
- return input;
- }
-
- private static final class CommandNames {
- private CommandNames() {}
-
- private static final String ALIAS = "alias";
- private static final String ARP = "arp";
- private static final String ASSOC = "assoc";
- private static final String ATTRIB = "attrib";
- private static final String CD = "cd";
- private static final String CHKDSK = "chkdsk";
- private static final String CHOICE = "choice";
- private static final String CLEARHISTORY = "clearhistory";
- private static final String CLIP = "clip";
- private static final String CLS = "cls";
- private static final String COLOR = "color";
- private static final String COMPACT = "compact";
- private static final String COPY = "copy";
- private static final String DATE = "date";
- private static final String DEL = "del";
- private static final String DIR = "dir";
- private static final String DRIVERQUERY = "driverquery";
- private static final String ECHO = "echo";
- private static final String EXIT = "exit";
- private static final String FC = "fc";
- private static final String FIND = "find";
- private static final String FINDSTR = "findstr";
- private static final String FORFILES = "forfiles";
- private static final String FSUTIL = "fsutil";
- private static final String FTYPE = "ftype";
- private static final String GETMAC = "getmac";
- private static final String HELP = "help";
- private static final String HISTORY = "history";
- private static final String HOSTNAME = "hostname";
- private static final String IPCONFIG = "ipconfig";
- private static final String LABEL = "label";
- private static final String LS = "ls";
- private static final String MKDIR = "mkdir";
- private static final String MORE = "more";
- private static final String MOVE = "move";
- private static final String MSG = "msg";
- private static final String NET = "net";
- private static final String NETSH = "netsh";
- private static final String NETSTAT = "netstat";
- private static final String NSLOOKUP = "nslookup";
- private static final String PATH = "path";
- private static final String PAUSE = "pause";
- private static final String PING = "ping";
- private static final String PWD = "pwd";
- private static final String REM = "rem";
- private static final String RENAME = "rename";
- private static final String REPLACE = "replace";
- private static final String RMDIR = "rmdir";
- private static final String ROBOCOPY = "robocopy";
- private static final String ROUTE = "route";
- private static final String SET = "set";
- private static final String SFC = "sfc";
- private static final String SHUTDOWN = "shutdown";
- private static final String SORT = "sort";
- private static final String START = "start";
- private static final String SYSTEMINFO = "systeminfo";
- private static final String TASKKILL = "taskkill";
- private static final String TASKLIST = "tasklist";
- private static final String TELNET = "telnet";
- private static final String TIME = "time";
- private static final String TIMEOUT = "timeout";
- private static final String TITLE = "title";
- private static final String TOUCH = "touch";
- private static final String TRACERT = "tracert";
- private static final String TREE = "tree";
- private static final String TYPE = "type";
- private static final String UNALIAS = "unalias";
- private static final String UPTIME = "uptime";
- private static final String VER = "ver";
- private static final String VERIFY = "verify";
- private static final String VOL = "vol";
- private static final String WHOAMI = "whoami";
- private static final String WMIC = "wmic";
- private static final String XCOPY = "xcopy";
- private static final String SEARCHHISTORY = "searchhistory";
- private static final String ISEARCH = "isearch";
- }
- private static void registerCommands(Map commands) {
- commands.put(CommandNames.ALIAS, new AliasCommand());
- commands.put(CommandNames.ARP, new ArpCommand());
- commands.put(CommandNames.ASSOC, new AssocCommand());
- commands.put(CommandNames.ATTRIB, new AttribCommand());
- commands.put(CommandNames.CD, new CdCommand());
- commands.put(CommandNames.CHKDSK, new ChkdskCommand());
- commands.put(CommandNames.CHOICE, new ChoiceCommand());
- commands.put(CommandNames.CLEARHISTORY, new ClearHistoryCommand());
- commands.put(CommandNames.CLIP, new ClipCommand());
- commands.put(CommandNames.CLS, new ClsCommand());
- commands.put(CommandNames.COLOR, new ColorCommand());
- commands.put(CommandNames.COMPACT, new CompactCommand());
- commands.put(CommandNames.COPY, new CopyCommand());
- commands.put(CommandNames.DATE, new DateCommand());
- commands.put(CommandNames.DEL, new DelCommand());
- commands.put(CommandNames.DIR, new DirCommand());
- commands.put(CommandNames.DRIVERQUERY, new DriverqueryCommand());
- commands.put(CommandNames.ECHO, new EchoCommand());
- commands.put(CommandNames.EXIT, new ExitCommand());
- commands.put(CommandNames.FC, new FcCommand());
- commands.put(CommandNames.FIND, new FindCommand());
- commands.put(CommandNames.FINDSTR, new FindstrCommand());
- commands.put(CommandNames.FORFILES, new ForfilesCommand());
- commands.put(CommandNames.FSUTIL, new FsutilCommand());
- commands.put(CommandNames.FTYPE, new FtypeCommand());
- commands.put(CommandNames.GETMAC, new GetmacCommand());
- commands.put(CommandNames.HELP, new HelpCommand(commands));
- commands.put(CommandNames.HISTORY, new HistoryCommand());
- commands.put(CommandNames.HOSTNAME, new HostnameCommand());
- commands.put(CommandNames.IPCONFIG, new IpConfig());
- commands.put(CommandNames.LABEL, new LabelCommand());
- commands.put(CommandNames.LS, new LsCommand());
- commands.put(CommandNames.MKDIR, new MkdirCommand());
- commands.put(CommandNames.MORE, new MoreCommand());
- commands.put(CommandNames.MOVE, new MoveCommand());
- commands.put(CommandNames.MSG, new MsgCommand());
- commands.put(CommandNames.NET, new NetCommand());
- commands.put(CommandNames.NETSH, new NetshCommand());
- commands.put(CommandNames.NETSTAT, new NetstatCommand());
- commands.put(CommandNames.NSLOOKUP, new NslookupCommand());
- commands.put(CommandNames.PATH, new PathCommand());
- commands.put(CommandNames.PAUSE, new PauseCommand());
- commands.put(CommandNames.PING, new PingCommand());
- commands.put(CommandNames.PWD, new PwdCommand());
- commands.put(CommandNames.REM, new RemCommand());
- commands.put(CommandNames.RENAME, new RenameCommand());
- commands.put(CommandNames.REPLACE, new ReplaceCommand());
- commands.put(CommandNames.RMDIR, new RmdirCommand());
- commands.put(CommandNames.ROBOCOPY, new RobocopyCommand());
- commands.put(CommandNames.ROUTE, new RouteCommand());
- commands.put(CommandNames.SET, new SetCommand());
- commands.put(CommandNames.SFC, new SfcCommand());
- commands.put(CommandNames.SHUTDOWN, new ShutdownCommand());
- commands.put(CommandNames.SORT, new SortCommand());
- commands.put(CommandNames.START, new StartCommand());
- commands.put(CommandNames.SYSTEMINFO, new SysteminfoCommand());
- commands.put(CommandNames.TASKKILL, new TaskkillCommand());
- commands.put(CommandNames.TASKLIST, new TasklistCommand());
- commands.put(CommandNames.TELNET, new TelnetCommand());
- commands.put(CommandNames.TIME, new TimeCommand());
- commands.put(CommandNames.TIMEOUT, new TimeoutCommand());
- commands.put(CommandNames.TITLE, new TitleCommand());
- commands.put(CommandNames.TOUCH, new TouchCommand());
- commands.put(CommandNames.TRACERT, new TracertCommand());
- commands.put(CommandNames.TREE, new TreeCommand());
- commands.put(CommandNames.TYPE, new TypeCommand());
- commands.put(CommandNames.UNALIAS, new UnaliasCommand());
- commands.put(CommandNames.UPTIME, new UptimeCommand());
- commands.put(CommandNames.VER, new VersionCommand());
- commands.put(CommandNames.VERIFY, new VerifyCommand());
- commands.put(CommandNames.VOL, new VolCommand());
- commands.put(CommandNames.WHOAMI, new WhoamiCommand());
- commands.put(CommandNames.WMIC, new WmicCommand());
- commands.put(CommandNames.XCOPY, new XcopyCommand());
- commands.put(CommandNames.SEARCHHISTORY, new SearchHistoryCommand());
- commands.put(CommandNames.ISEARCH, new InteractiveSearchCommand());
+ // Launch JavaFX GUI
+ Application.launch(MainApp.class, args);
}
}
diff --git a/src/main/java/com/mycmd/Command.java b/src/main/java/com/mycmd/Command.java
index ce67f47..fac7b07 100644
--- a/src/main/java/com/mycmd/Command.java
+++ b/src/main/java/com/mycmd/Command.java
@@ -3,29 +3,11 @@
import java.io.IOException;
/**
- * Represents a shell command that can be executed inside the MyCMD shell. Implementations perform
- * their operation when {@link #execute(String[], ShellContext)} is called.
+ * Interfaace for all commands.
+ * Every command impleements this
*/
public interface Command {
- /**
- * Execute the command.
- *
- * @param args command-line style arguments passed to the command. May be empty but will not be
- * null.
- * @param context current shell context containing state such as the current working directory.
- * @throws IOException if the command cannot complete successfully.
- */
void execute(String[] args, ShellContext context) throws IOException;
-
- /**
- * Short description of the command. Default is empty so existing implementations do not break.
- */
- default String description() {
- return "";
- }
-
- /** Usage string for the command. Default is empty. */
- default String usage() {
- return "";
- }
+ String description();
+ String usage();
}
diff --git a/src/main/java/com/mycmd/CommandRegistry.java b/src/main/java/com/mycmd/CommandRegistry.java
new file mode 100644
index 0000000..aaf0358
--- /dev/null
+++ b/src/main/java/com/mycmd/CommandRegistry.java
@@ -0,0 +1,23 @@
+package com.mycmd;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Registers and retrieves commands by name.
+ */
+public class CommandRegistry {
+ private final Map commands = new HashMap<>();
+
+ public void register(String name, Command cmd) {
+ commands.put(name.toLowerCase(), cmd);
+ }
+
+ public Command get(String name) {
+ return commands.get(name.toLowerCase());
+ }
+
+ public Map getAll() {
+ return commands;
+ }
+}
diff --git a/src/main/java/com/mycmd/ConsoleShell.java b/src/main/java/com/mycmd/ConsoleShell.java
new file mode 100644
index 0000000..a35f824
--- /dev/null
+++ b/src/main/java/com/mycmd/ConsoleShell.java
@@ -0,0 +1,27 @@
+package com.mycmd;
+
+import java.util.Scanner;
+
+/**
+ * Developer console mode for debugging.
+ */
+public class ConsoleShell {
+
+ public static void main(String[] args) {
+ CommandRegistry registry = new CommandRegistry();
+ ShellContext context = new ShellContext();
+ ShellEngine engine = new ShellEngine(registry, context);
+
+ Scanner sc = new Scanner(System.in);
+ System.out.println("MyCMD Developer Console Mode\n(Type 'exit' to quit)");
+
+ while (true) {
+ System.out.print("> ");
+ String input = sc.nextLine();
+ if (input.equalsIgnoreCase("exit")) break;
+ engine.execute(input);
+ }
+
+ sc.close();
+ }
+}
diff --git a/src/main/java/com/mycmd/ShellContext.java b/src/main/java/com/mycmd/ShellContext.java
index 8816f41..66dd337 100644
--- a/src/main/java/com/mycmd/ShellContext.java
+++ b/src/main/java/com/mycmd/ShellContext.java
@@ -1,174 +1,42 @@
package com.mycmd;
-import java.io.*;
-import java.time.Instant;
-import java.util.*;
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.NonNull;
-import lombok.Setter;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
-@Getter(AccessLevel.PUBLIC)
-public class ShellContext {
-
- @Setter @NonNull private File currentDir;
- private List history;
- private Map aliases;
-
- private static final String ALIAS_FILE = ".mycmd_aliases";
- private static final int MAX_HISTORY = 100;
- private final List commandHistory;
- private final Instant startTime;
+public class ShellContext {
- private final Map envVars = new HashMap<>();
+ private File currentDir;
+ private final Map aliases;
private Scanner scanner;
public ShellContext() {
this.currentDir = new File(System.getProperty("user.dir"));
- this.history = new ArrayList<>();
this.aliases = new HashMap<>();
- this.commandHistory = new ArrayList<>();
- this.startTime = Instant.now();
- this.scanner = null; // Will be set by App.java
- loadAliases();
}
- // ==================== Scanner Management ====================
-
- /**
- * Set the shared Scanner instance for all commands to use.
- * Should only be called once by App.java during initialization.
- */
- public void setScanner(Scanner scanner) {
- if (this.scanner != null) {
- throw new IllegalStateException("Scanner already initialized");
- }
- if (scanner == null) {
- throw new IllegalArgumentException("Scanner cannot be null");
- }
- this.scanner = scanner;
+ public File getCurrentDir() {
+ return currentDir;
}
- /**
- * Get the shared Scanner instance.
- * All commands should use this instead of creating their own Scanner.
- * @return the shared Scanner instance
- * @throws IllegalStateException if Scanner hasn't been initialized
- */
- public Scanner getScanner() {
- if (scanner == null) {
- throw new IllegalStateException("Scanner not initialized in ShellContext");
- }
- return scanner;
- }
-
- public void addToHistory(String command) {
- history.add(command);
- commandHistory.add(command);
- if (history.size() > MAX_HISTORY) {
- history.remove(0);
+ public void setCurrentDir(File dir) {
+ if (dir != null && dir.exists() && dir.isDirectory()) {
+ this.currentDir = dir;
}
}
- /** RETAINED FOR SAFETY: Returns a DEFENSIVE COPY instead of the raw Map. */
- public List getHistory() {
- return new ArrayList<>(history);
- }
-
public Map getAliases() {
- return new HashMap<>(aliases);
- }
-
- public Map getEnvVars() {
- return new HashMap<>(envVars);
- }
-
- public void clearHistory() {
- history.clear();
+ return aliases;
}
public void addAlias(String name, String command) {
aliases.put(name, command);
- saveAliases();
- }
-
- public void removeAlias(String name) {
- aliases.remove(name);
- saveAliases();
- }
-
- public String getAlias(String name) {
- return aliases.get(name);
}
- public boolean hasAlias(String name) {
- return aliases.containsKey(name);
- }
-
- public void setEnvVar(String key, String value) {
- envVars.put(key, value);
- }
-
- public String getEnvVar(String key) {
- return envVars.get(key);
- }
-
- private void loadAliases() {
- File aliasFile = new File(System.getProperty("user.home"), ALIAS_FILE);
- // ... (method body remains the same)
- if (!aliasFile.exists()) {
- return;
- }
-
- try (BufferedReader reader = new BufferedReader(new FileReader(aliasFile))) {
- String line;
- while ((line = reader.readLine()) != null) {
- line = line.trim();
- if (line.isEmpty() || line.startsWith("#")) {
- continue;
- }
- String[] parts = line.split("=", 2);
- if (parts.length == 2) {
- String name = parts[0].trim();
- String command = parts[1].trim();
- aliases.put(name, command);
- }
- }
- } catch (IOException e) {
- System.err.println("Warning: Could not load aliases: " + e.getMessage());
- }
- }
-
- private void saveAliases() {
- File aliasFile = new File(System.getProperty("user.home"), ALIAS_FILE);
- // ... (method body remains the same)
- try (BufferedWriter writer = new BufferedWriter(new FileWriter(aliasFile))) {
- writer.write("# MyCMD Aliases Configuration\n");
- writer.write("# Format: aliasName=command\n\n");
- for (Map.Entry entry : aliases.entrySet()) {
- writer.write(entry.getKey() + "=" + entry.getValue() + "\n");
- }
- } catch (IOException e) {
- System.err.println("Warning: Could not save aliases: " + e.getMessage());
- }
- }
-
- /**
- * Resolve the given path (absolute or relative) to a File using the current directory. If the
- * provided path is absolute, returns it directly; otherwise returns a File rooted at
- * currentDir.
- */
- public File resolvePath(String path) {
- if (path == null || path.trim().isEmpty()) {
- return currentDir;
- }
- File f = new File(path);
- if (f.isAbsolute()) {
- return f;
- } else {
- return new File(currentDir, path);
- }
+ public String resolveAlias(String cmd) {
+ return aliases.getOrDefault(cmd, cmd);
}
}
+
diff --git a/src/main/java/com/mycmd/ShellEngine.java b/src/main/java/com/mycmd/ShellEngine.java
new file mode 100644
index 0000000..c22e963
--- /dev/null
+++ b/src/main/java/com/mycmd/ShellEngine.java
@@ -0,0 +1,38 @@
+package com.mycmd;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * Central execution engine.
+ */
+public class ShellEngine {
+
+ private final CommandRegistry registry;
+ private final ShellContext context;
+
+ public ShellEngine(CommandRegistry registry, ShellContext context) {
+ this.registry = registry;
+ this.context = context;
+ }
+
+ public void execute(String input) {
+ if (input == null || input.trim().isEmpty()) return;
+
+ String[] parts = input.trim().split("\\s+");
+ String cmdName = context.resolveAlias(parts[0]);
+ String[] args = Arrays.copyOfRange(parts, 1, parts.length);
+
+ Command cmd = registry.get(cmdName);
+ if (cmd == null) {
+ System.out.println("❌ Unknown command: " + cmdName);
+ return;
+ }
+
+ try {
+ cmd.execute(args, context);
+ } catch (IOException e) {
+ System.out.println("⚠️ Error executing command: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/mycmd/StringUtils.java b/src/main/java/com/mycmd/StringUtils.java
index f8a2f13..f2e8827 100644
--- a/src/main/java/com/mycmd/StringUtils.java
+++ b/src/main/java/com/mycmd/StringUtils.java
@@ -1,88 +1,20 @@
package com.mycmd;
-import java.util.Collection;
-import java.util.Objects;
-
/**
- * Small utility for string-based helper methods. Provides a findClosest(...) implementation using
- * Levenshtein distance.
+ * String helper functions for commands.
*/
-public final class StringUtils {
-
- private StringUtils() {}
-
- /**
- * Find the closest string in candidates to the input. Returns null when no candidate is close
- * enough.
- *
- * @param input the input string
- * @param candidates candidate strings
- * @return the closest candidate or null
- */
- public static String findClosest(String input, Collection candidates) {
- if (input == null || input.isEmpty() || candidates == null || candidates.isEmpty()) {
- return null;
- }
-
- String best = null;
- int bestDistance = Integer.MAX_VALUE;
-
- for (String candidate : candidates) {
- if (candidate == null || candidate.isEmpty()) continue;
- if (candidate.equalsIgnoreCase(input)) {
- // exact match ignoring case - return immediately
- return candidate;
- }
- int distance = levenshteinDistance(input.toLowerCase(), candidate.toLowerCase());
- if (distance < bestDistance) {
- bestDistance = distance;
- best = candidate;
- }
- }
-
- // Choose a threshold: allow suggestion only when the distance is reasonably small.
- // Here we allow suggestions when distance <= max(1, input.length()/3)
- int threshold = Math.max(1, input.length() / 3);
- if (bestDistance <= threshold) {
- return best;
- }
- return null;
+public class StringUtils {
+ public static boolean isEmpty(String s) {
+ return s == null || s.trim().isEmpty();
}
- // Classic iterative Levenshtein algorithm (memory O(min(m,n)))
- private static int levenshteinDistance(String a, String b) {
- if (Objects.equals(a, b)) return 0;
- if (a.isEmpty()) return b.length();
- if (b.isEmpty()) return a.length();
-
- // ensure a is the shorter
- if (a.length() > b.length()) {
- String tmp = a;
- a = b;
- b = tmp;
- }
-
- int[] previous = new int[a.length() + 1];
- int[] current = new int[a.length() + 1];
-
- for (int i = 0; i <= a.length(); i++) previous[i] = i;
-
- for (int j = 1; j <= b.length(); j++) {
- current[0] = j;
- char bj = b.charAt(j - 1);
- for (int i = 1; i <= a.length(); i++) {
- int cost = (a.charAt(i - 1) == bj) ? 0 : 1;
- current[i] = min3(current[i - 1] + 1, previous[i] + 1, previous[i - 1] + cost);
- }
- // swap previous and current
- int[] temporary = previous;
- previous = current;
- current = temporary;
+ public static String join(String[] arr, int start) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = start; i < arr.length; i++) {
+ if (i > start) sb.append(' ');
+ sb.append(arr[i]);
}
- return previous[a.length()];
- }
-
- private static int min3(int x, int y, int z) {
- return Math.min(x, Math.min(y, z));
+ return sb.toString();
}
}
+
diff --git a/src/main/java/com/mycmd/gui/MainApp.java b/src/main/java/com/mycmd/gui/MainApp.java
new file mode 100644
index 0000000..60f3867
--- /dev/null
+++ b/src/main/java/com/mycmd/gui/MainApp.java
@@ -0,0 +1,56 @@
+package com.mycmd.gui;
+
+import com.mycmd.*;
+import javafx.application.Application;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.image.Image;
+import javafx.stage.Stage;
+
+/**
+ * JavaFX entry point — futuristic terminal window for MyCMD.
+ */
+public class MainApp extends Application {
+
+ private static ShellEngine engine;
+ private static ShellContext context;
+ private static CommandRegistry registry;
+
+ @Override
+ public void start(Stage stage) throws Exception {
+ // initialize core shell
+ context = new ShellContext();
+ registry = new CommandRegistry();
+ engine = new ShellEngine(registry, context);
+
+ // register built-in commands (auto load)
+ registerBuiltIns();
+
+ FXMLLoader loader = new FXMLLoader(
+ getClass().getResource("/com/mycmd/gui/terminal.fxml"));
+ Scene scene = new Scene(loader.load());
+ scene.getStylesheets().add(
+ getClass().getResource("/com/mycmd/gui/style.css").toExternalForm());
+
+ TerminalController controller = loader.getController();
+ controller.init(engine, context);
+
+ stage.setTitle("MyCMD ░▓ Java Terminal ▓░");
+ stage.getIcons().add(
+ new Image(getClass().getResourceAsStream("/com/mycmd/gui/icon.png")));
+ stage.setScene(scene);
+ stage.setResizable(false);
+ stage.show();
+ }
+
+ private void registerBuiltIns() {
+ registry.register("alias", new com.mycmd.commands.AliasCommand());
+ registry.register("dir", new com.mycmd.commands.DirCommand());
+ registry.register("echo", new com.mycmd.commands.EchoCommand());
+ // add others automatically here as you expand
+ }
+
+ public static void main(String[] args) {
+ launch();
+ }
+}
diff --git a/src/main/java/com/mycmd/gui/TerminalController.java b/src/main/java/com/mycmd/gui/TerminalController.java
new file mode 100644
index 0000000..6ab93b3
--- /dev/null
+++ b/src/main/java/com/mycmd/gui/TerminalController.java
@@ -0,0 +1,44 @@
+package com.mycmd.gui;
+
+import com.mycmd.ShellContext;
+import com.mycmd.ShellEngine;
+import javafx.fxml.FXML;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyCode;
+
+/**
+ * Controller for the FXML terminal.
+ */
+public class TerminalController {
+
+ @FXML private TextArea outputArea;
+ @FXML private TextField inputField;
+ @FXML private ScrollPane scrollPane;
+
+ private ShellEngine engine;
+ private ShellContext context;
+
+ public void init(ShellEngine engine, ShellContext context) {
+ this.engine = engine;
+ this.context = context;
+
+ output("💻 Welcome to MyCMD - Java made Terminal");
+ output("Type 'help' for available commands.\n");
+
+ inputField.setOnKeyPressed(event -> {
+ if (event.getCode() == KeyCode.ENTER) {
+ String input = inputField.getText();
+ inputField.clear();
+ output("> " + input);
+ engine.execute(input);
+ scrollPane.setVvalue(1.0);
+ }
+ });
+ }
+
+ private void output(String text) {
+ outputArea.appendText(text + "\n");
+ }
+}
diff --git a/src/main/resources/com/mycmd/config/default.properties b/src/main/resources/com/mycmd/config/default.properties
new file mode 100644
index 0000000..ab00603
--- /dev/null
+++ b/src/main/resources/com/mycmd/config/default.properties
@@ -0,0 +1,16 @@
+# MyCMD default configuration
+app.name=MyCMD
+app.version=1.0
+app.author=Drive-for-Java
+app.theme=dark
+app.defaultDir=${user.home}
+
+# Command shell behavior
+shell.history.enabled=true
+shell.history.limit=50
+shell.showStartupMessage=true
+
+# GUI preferences
+gui.font.size=14
+gui.theme.color=#00FFFF
+gui.background.image=icons/mycmd-128.png
diff --git a/src/main/resources/com/mycmd/gui/style.css b/src/main/resources/com/mycmd/gui/style.css
new file mode 100644
index 0000000..3299d16
--- /dev/null
+++ b/src/main/resources/com/mycmd/gui/style.css
@@ -0,0 +1,32 @@
+.root {
+ -fx-background-color: linear-gradient(to bottom, #0c0c0c, #141414);
+ -fx-font-family: "JetBrains Mono", monospace;
+}
+
+.output {
+ -fx-control-inner-background: #0e0e0e;
+ -fx-text-fill: #00ffe7;
+ -fx-font-size: 14px;
+ -fx-border-color: #222;
+ -fx-border-width: 1;
+ -fx-border-radius: 8;
+ -fx-background-radius: 8;
+}
+
+.input {
+ -fx-control-inner-background: #111;
+ -fx-text-fill: #fff;
+ -fx-font-size: 14px;
+ -fx-border-color: #00ffe7;
+ -fx-border-radius: 8;
+ -fx-background-radius: 8;
+}
+
+.prompt {
+ -fx-text-fill: #00ffe7;
+ -fx-font-weight: bold;
+}
+
+.label, .text-area {
+ -fx-highlight-fill: #00ffe7;
+}
diff --git a/src/main/resources/com/mycmd/gui/terminal.fxml b/src/main/resources/com/mycmd/gui/terminal.fxml
new file mode 100644
index 0000000..df83243
--- /dev/null
+++ b/src/main/resources/com/mycmd/gui/terminal.fxml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/com/mycmd/icons/logo.png b/src/main/resources/com/mycmd/icons/logo.png
new file mode 100644
index 0000000..b8b9b3d
Binary files /dev/null and b/src/main/resources/com/mycmd/icons/logo.png differ