added quick module

This commit is contained in:
Dominik Krenn 2025-09-15 15:34:01 +02:00
parent fc1aca80d2
commit 6d2b939bc6
20 changed files with 699 additions and 0 deletions

6
src/quick/Action.java Normal file
View File

@ -0,0 +1,6 @@
package quick;
public abstract class Action {
protected Global global = Global.getInstance();
public abstract Boolean execute(Interface base);
}

22
src/quick/Color.java Normal file
View File

@ -0,0 +1,22 @@
package quick;
public class Color {
public static final String ANSI_RESET = "\u001B[0m";
public static final String ANSI_BLACK = "\u001B[30m";
public static final String ANSI_RED = "\u001B[31m";
public static final String ANSI_GREEN = "\u001B[32m";
public static final String ANSI_YELLOW = "\u001B[33m";
public static final String ANSI_BLUE = "\u001B[34m";
public static final String ANSI_PURPLE = "\u001B[35m";
public static final String ANSI_CYAN = "\u001B[36m";
public static final String ANSI_WHITE = "\u001B[37m";
public static final String ANSI_BLACK_BACKGROUND = "\u001B[40m";
public static final String ANSI_RED_BACKGROUND = "\u001B[41m";
public static final String ANSI_GREEN_BACKGROUND = "\u001B[42m";
public static final String ANSI_YELLOW_BACKGROUND = "\u001B[43m";
public static final String ANSI_BLUE_BACKGROUND = "\u001B[44m";
public static final String ANSI_PURPLE_BACKGROUND = "\u001B[45m";
public static final String ANSI_CYAN_BACKGROUND = "\u001B[46m";
public static final String ANSI_WHITE_BACKGROUND = "\u001B[47m";
}

18
src/quick/Global.java Normal file
View File

@ -0,0 +1,18 @@
package quick;
import java.util.ArrayList;
import java.util.List;
public class Global {
private static Global instance;
public List<Class<? extends View>> registeredViews = new ArrayList<>();
private Global() {}
public static synchronized Global getInstance() {
if (instance == null) {
instance = new Global();
}
return instance;
}
}

7
src/quick/Helper.java Normal file
View File

@ -0,0 +1,7 @@
package quick;
public class Helper {
public static void clearScreen() {
System.out.print("");
}
}

99
src/quick/Interface.java Normal file
View File

@ -0,0 +1,99 @@
package quick;
import java.util.Scanner;
import quick.exceptions.InvalidViewActionReturn;
import quick.guard.PrintDog;
import quick.intern.v_help;
import quick.intern.v_select;
public class Interface {
public final Scanner scanner = new Scanner(System.in);
private Global global = Global.getInstance();
private Boolean active = true;
private Class<? extends View> defaultView = v_select.class;
private View selectedView;
public int[] __terminalSize;
public int __renderCycleLines;
public Interface() {}
public Interface(Class<? extends View> defaultView) {
this.defaultView = defaultView;
}
public void registerView(Class<? extends View> viewClass) {
global.registeredViews.add(viewClass);
}
public Boolean selectView(View viewInstance) {
selectedView = viewInstance;
return viewInstance.init();
}
public Boolean selectView(Class<? extends View> viewClass) {
View new_view = View.instantiate(viewClass);
selectedView = new_view;
return new_view.init();
}
private Boolean cycle() {
selectedView.initBase(this);
// Always start with a clean break
System.out.print("\r\n");
System.out.flush();
this.__terminalSize = TerminalSize.getTerminalSize();
this.__renderCycleLines = 0;
Helper.clearScreen();
// Draw the view; it should update __renderCycleLines
selectedView.draw();
System.out.flush();
// Always leave at least 2 lines: one for prompt, one for safety
int overheadLines = 2;
int linesToFill = Math.max(0, this.__terminalSize[0] - __renderCycleLines - overheadLines);
for (int i = 0; i < linesToFill; i++) {
System.out.println();
}
String view_prompt = (selectedView.viewPrompt == null ? "" : selectedView.viewPrompt);
System.err.print(view_prompt);
System.err.flush();
String userInputRaw = scanner.nextLine();
System.out.println();
Action viewResponse = selectedView.onCommand(userInputRaw);
if (viewResponse == null) {
return true;
}
if (viewResponse instanceof Action) {
return viewResponse.execute(this);
}
throw new InvalidViewActionReturn();
}
private void mainLoop() {
selectView(defaultView);
while (active) {
active = cycle();
}
}
public void start() {
PrintDog.start(true);
View.scanner = scanner;
registerView(v_help.class);
registerView(v_select.class);
mainLoop();
}
}

View File

@ -0,0 +1,25 @@
package quick;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class TerminalSize {
public static int[] getTerminalSize() {
try {
Process process = Runtime.getRuntime().exec(new String[]{"sh", "-c", "stty size < /dev/tty"});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = reader.readLine();
if (line != null) {
String[] parts = line.trim().split(" ");
int rows = Integer.parseInt(parts[0]);
int cols = Integer.parseInt(parts[1]);
return new int[]{rows, cols};
}
} catch (Exception e) {
System.err.println("[ERROR] Failed to get terminal size: " + e.getMessage());
}
return new int[]{69, 69}; // Soggy UwU fallback
}
}

53
src/quick/View.java Normal file
View File

@ -0,0 +1,53 @@
package quick;
import java.util.Scanner;
import quick.exceptions.DuplicateViewMatchException;
import java.util.ArrayList;
public abstract class View {
protected static Global global = Global.getInstance();
protected static ViewActionFactories actions = new ViewActionFactories();
protected ViewTerminalAcces terminal;
public static Scanner scanner;
public static Interface __base;
public String viewPrompt;
public String viewSignature;
public String helperText;
public abstract Boolean init();
public abstract void draw();
public abstract Action onCommand(String command);
public abstract Boolean onSelection(String userInputRaw);
public void initBase(Interface base) {
View.__base = base;
this.terminal = new ViewTerminalAcces(base);
}
public static View instantiate(Class<? extends View> viewClass) {
try {
return viewClass.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new RuntimeException("Could not instantiate view: " + viewClass.getName(), e);
}
}
public static View signatureParse(String userInputRaw) {
ArrayList<View> matched = new ArrayList<View>();
for (Class<? extends View> ViewClass : global.registeredViews) {
View viewInstance = View.instantiate(ViewClass);
if (viewInstance.onSelection(userInputRaw)) {
matched.add(viewInstance);
}
}
if (matched.size() > 1) {
throw new DuplicateViewMatchException(userInputRaw, matched.size());
}
return matched.isEmpty() ? null : matched.get(0);
}
}

View File

@ -0,0 +1,28 @@
package quick;
import quick.action.a_dd;
import quick.action.a_nop;
import quick.action.a_redirect;
import quick.action.a_stop;
public class ViewActionFactories {
public Action redirect(Class<? extends View> target) {
return new a_redirect(target);
}
public Action redirect(View target) {
return new a_redirect(target);
}
public Action stop() {
return new a_stop();
}
public Action nop() {
return new a_nop();
}
public Action dd(Object... data) {
return new a_dd(data);
}
}

View File

@ -0,0 +1,32 @@
package quick;
public class ViewTerminalAcces {
private Interface base;
public ViewTerminalAcces(Interface base) {
this.base = base;
}
public void print(String text) {
base.__renderCycleLines += 1;
System.out.print(text);
}
public int getWidth() {
return base.__terminalSize[1];
}
public int getHeight() {
return base.__terminalSize[0];
}
public int getCurrentRclCount() {
return base.__renderCycleLines;
}
public void fill(int size) {
for (int i = 0; i < size; i++) {
print("\n");
}
}
}

109
src/quick/action/a_dd.java Normal file
View File

@ -0,0 +1,109 @@
package quick.action;
import java.util.List;
import java.util.Map;
import java.util.Set;
import quick.Action;
import quick.Color;
import quick.Interface;
public class a_dd extends Action {
private final Object[] data;
public a_dd(Object... data) {
this.data = data;
}
@Override
public Boolean execute(Interface base) {
System.out.println(Color.ANSI_BLUE + "========== DUMP & DIE ==========" + Color.ANSI_RESET);
for (int i = 0; i < data.length; i++) {
Object item = data[i];
String type = (item == null) ? "null" : item.getClass().getSimpleName();
String typeColor = getTypeColor(type);
System.out.println(
Color.ANSI_WHITE + "[" + i + "] => " +
typeColor + "(" + type + ") " + formatValue(item) + Color.ANSI_RESET
);
}
System.out.println(Color.ANSI_BLUE + "============ END ============" + Color.ANSI_RESET);
return false;
}
private String formatValue(Object obj) {
if (obj == null) return Color.ANSI_RED + "null" + Color.ANSI_RESET;
if (obj instanceof Map) {
StringBuilder sb = new StringBuilder(Color.ANSI_PURPLE + "Map {\n");
((Map<?, ?>) obj).forEach((k, v) -> {
String type = (v == null) ? "null" : v.getClass().getSimpleName();
String color = getTypeColor(type);
sb.append(Color.ANSI_WHITE + " " + k + " => " + color + "(" + type + ") " + formatValue(v) + "\n");
});
sb.append(Color.ANSI_PURPLE + "}");
return sb.toString();
}
if (obj instanceof List) {
StringBuilder sb = new StringBuilder(Color.ANSI_BLUE + "List [\n");
int i = 0;
for (Object val : (List<?>) obj) {
String type = (val == null) ? "null" : val.getClass().getSimpleName();
String color = getTypeColor(type);
sb.append(Color.ANSI_WHITE + " " + i++ + " => " + color + "(" + type + ") " + formatValue(val) + "\n");
}
sb.append(Color.ANSI_BLUE + "]");
return sb.toString();
}
if (obj instanceof Set) {
StringBuilder sb = new StringBuilder(Color.ANSI_RED + "Set {\n");
for (Object val : (Set<?>) obj) {
String type = (val == null) ? "null" : val.getClass().getSimpleName();
String color = getTypeColor(type);
sb.append(Color.ANSI_WHITE + " => " + color + "(" + type + ") " + formatValue(val) + "\n");
}
sb.append(Color.ANSI_RED + "}");
return sb.toString();
}
if (obj.getClass().isArray()) {
StringBuilder sb = new StringBuilder(Color.ANSI_CYAN + "Array [\n");
int len = java.lang.reflect.Array.getLength(obj);
for (int i = 0; i < len; i++) {
Object val = java.lang.reflect.Array.get(obj, i);
String type = (val == null) ? "null" : val.getClass().getSimpleName();
String color = getTypeColor(type);
sb.append(Color.ANSI_WHITE + " " + i + " => " + color + "(" + type + ") " + formatValue(val) + "\n");
}
sb.append(Color.ANSI_CYAN + "]");
return sb.toString();
}
return getTypeColor(obj.getClass().getSimpleName()) + obj.toString();
}
private String getTypeColor(String type) {
if (type == null) return Color.ANSI_RED;
switch (type) {
case "String": return Color.ANSI_GREEN;
case "Integer":
case "Long":
case "Double":
case "Float":
case "Short":
case "Byte": return Color.ANSI_YELLOW;
case "Boolean": return Color.ANSI_CYAN;
case "Map": return Color.ANSI_PURPLE;
case "List": return Color.ANSI_BLUE;
case "Set": return Color.ANSI_RED;
case "Object[]": return Color.ANSI_WHITE;
default: return Color.ANSI_WHITE;
}
}
}

View File

@ -0,0 +1,10 @@
package quick.action;
import quick.Action;
import quick.Interface;
public class a_nop extends Action {
public Boolean execute(Interface base) {
return true;
};
}

View File

@ -0,0 +1,49 @@
package quick.action;
import quick.Action;
import quick.Interface;
import quick.View;
import quick.exceptions.InvalidRedirect;
public class a_redirect extends Action{
public Class<? extends View> targetClass;
public View targetInstance;
private Interface base;
public a_redirect(Class<? extends View> target) {
this.targetClass = target;
}
public a_redirect(View target) {
this.targetInstance = target;
}
public Boolean execute(Interface base) {
this.base = base;
if (targetClass != null) {
return redirectByClass();
}
if (targetInstance != null) {
return redirectByInstance();
}
throw new InvalidRedirect(null);
}
private Boolean redirectByClass() {
if (!global.registeredViews.contains(targetClass)) {
throw new InvalidRedirect(targetClass);
}
return base.selectView(targetClass);
}
private Boolean redirectByInstance() {
if (!global.registeredViews.contains(targetInstance.getClass())) {
throw new InvalidRedirect(targetInstance.getClass());
}
return base.selectView(targetInstance);
}
}

View File

@ -0,0 +1,10 @@
package quick.action;
import quick.Action;
import quick.Interface;
public class a_stop extends Action {
public Boolean execute(Interface base) {
return false;
};
}

View File

@ -0,0 +1,7 @@
package quick.exceptions;
public class DuplicateViewMatchException extends RuntimeException {
public DuplicateViewMatchException(String input, int count) {
super("Input \"" + input + "\" matched " + count + " views. View signatures must be unique for this input.");
}
}

View File

@ -0,0 +1,7 @@
package quick.exceptions;
public class IllegalTerminalPrintException extends RuntimeException {
public IllegalTerminalPrintException(String message) {
super(message);
}
}

View File

@ -0,0 +1,11 @@
package quick.exceptions;
import quick.View;
public class InvalidRedirect extends RuntimeException {
public InvalidRedirect(Class<? extends View> target) {
super(target == null
? "invalid use of null in redirect"
: "View" + target.getName() + " is not registered in base");
}
}

View File

@ -0,0 +1,7 @@
package quick.exceptions;
public class InvalidViewActionReturn extends RuntimeException {
public InvalidViewActionReturn() {
super("Please only return a Child of Action or null from a View");
}
}

View File

@ -0,0 +1,101 @@
package quick.guard;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.ByteArrayOutputStream;
import java.util.Set;
import quick.Color;
import java.util.HashSet;
public class PrintDog {
private static final Set<String> trustedPackages = new HashSet<>();
private static boolean strict = true;
private static PrintStream originalOut;
private static PrintStream mirroredOut;
public static void start(boolean strictMode) {
strict = strictMode;
originalOut = System.out;
// Register your actual source packages only
trustedPackages.add("quick.");
trustedPackages.add("quick.action.");
trustedPackages.add("quick.exceptions.");
trustedPackages.add("quick.guard.");
trustedPackages.add("quick.intern.");
// Setup global uncaught exception handler
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
originalOut.println(Color.ANSI_RED + "🔥 UNCAUGHT EXCEPTION IN THREAD: " + thread.getName() + Color.ANSI_RESET);
throwable.printStackTrace(originalOut);
// Optional kill:
// System.exit(1);
});
// Setup mirror output stream
mirroredOut = new PrintStream(new MirrorOutputStream(originalOut), true);
System.setOut(mirroredOut);
}
private static class MirrorOutputStream extends OutputStream {
private final OutputStream original;
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
public MirrorOutputStream(OutputStream original) {
this.original = original;
}
@Override
public void write(int b) {
try {
original.write(b);
buffer.write(b);
if (b == '\n') {
flushBuffer();
}
} catch (Exception e) {
e.printStackTrace(originalOut);
System.exit(69);
}
}
private void flushBuffer() {
String content = buffer.toString();
buffer.reset();
Throwable t = new Throwable();
StackTraceElement[] stack = t.getStackTrace();
if (strict && !isFromTrustedCode(stack)) {
originalOut.println(
Color.ANSI_WHITE + Color.ANSI_RED_BACKGROUND +
"🐶 BARK! UNAUTHORIZED TERMINAL PRINT DETECTED!" +
Color.ANSI_RESET + "\n" +
Color.ANSI_YELLOW + "→ Use terminal.print() instead.\n" +
"→ Offending content:\n" +
Color.ANSI_WHITE + content.trim() + Color.ANSI_RESET
);
// 🚨 HARD CRASH
System.err.println(Color.ANSI_RED + "☠️ FATAL: System.out used in untrusted context!" + Color.ANSI_RESET);
System.exit(123);
}
}
private boolean isFromTrustedCode(StackTraceElement[] stack) {
for (StackTraceElement frame : stack) {
String cls = frame.getClassName();
for (String prefix : trustedPackages) {
if (cls.startsWith(prefix)) {
return true;
}
}
}
return false;
}
}
}

View File

@ -0,0 +1,30 @@
package quick.intern;
import quick.Action;
import quick.View;
public class v_help extends View {
@Override
public Boolean init() {
viewPrompt = "HELP";
viewSignature = "help";
helperText = "help view most likely not implemented 😂 u noob";
return true;
}
@Override
public void draw() {
return;
}
@Override
public Action onCommand(String command) {
return null;
}
@Override
public Boolean onSelection(String userInputRaw) {
return false;
}
}

View File

@ -0,0 +1,68 @@
package quick.intern;
import quick.Action;
import quick.Color;
import quick.View;
import quick.action.a_nop;
import quick.action.a_redirect;
public class v_select extends View {
private String viewList;
private String lastSelectionWrong;
@Override
public Boolean init() {
StringBuilder sb = new StringBuilder();
for (Class<? extends View> viewClass : global.registeredViews) {
if (viewClass == v_select.class) continue;
View viewInstance = View.instantiate(viewClass);
viewInstance.init();
if (viewInstance.viewSignature != null && !viewInstance.viewSignature.isEmpty()) {
sb.append(": " + viewInstance.viewSignature + " - " + viewInstance.helperText).append("\n");
} else {
sb.append(": " + viewInstance.helperText).append("\n");
}
}
if (sb.length() > 0 && sb.charAt(sb.length() - 1) == '\n') {
sb.deleteCharAt(sb.length() - 1);
}
viewList = sb.toString();
return true;
}
@Override
public void draw() {
terminal.print("Select a action:\n");
for (String line : viewList.split("\n")) terminal.print(line+"\n");
terminal.print("\n");
if (lastSelectionWrong != null) {
terminal.print(Color.ANSI_RED + "Something went wrong with: " + lastSelectionWrong + "\"" + Color.ANSI_RESET + "\n");
terminal.print("\n");
lastSelectionWrong = null;
} else {
terminal.print("\n");
}
}
@Override
public Action onCommand(String command) {
if (command.isEmpty()) {
return new a_nop();
}
View selectedView = View.signatureParse(command);
if (selectedView == null) {
lastSelectionWrong = command;
return new a_nop();
}
return new a_redirect(selectedView);
}
@Override
public Boolean onSelection(String userInputRaw) {
return false;
}
}