feat(core): 添加高级文件选择器和暗黑主题聊天界面

- 新增 AdvancedJFileChooser 类,实现记住上次访问目录的功能
- 添加 AIaToolbox_dark.html 文件,实现暗黑主题的聊天界面- 优化代码结构,提高可读性和可维护性
This commit is contained in:
tzdwindows 7
2025-05-02 19:11:25 +08:00
parent 32e4274d61
commit c4a10214db
31 changed files with 4726 additions and 78 deletions

View File

@@ -319,6 +319,10 @@ public class AxisInnovatorsBox {
windowsJDialog.repaint();
}
//public void execute(Runnable runnable){
// thread.
//}
/**
* 重新加载窗口
*/

View File

@@ -1,16 +1,13 @@
package com.axis.innovators.box;
import com.axis.innovators.box.browser.MainApplication;
import com.axis.innovators.box.decompilation.gui.ModernJarViewer;
import com.axis.innovators.box.events.GlobalEventBus;
import com.axis.innovators.box.events.OpenFileEvents;
import com.axis.innovators.box.gui.LoginWindow;
import com.axis.innovators.box.tools.ArgsParser;
import com.axis.innovators.box.tools.FolderCleaner;
import com.axis.innovators.box.tools.FolderCreator;
import com.axis.innovators.box.register.LanguageManager;
import javax.swing.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -42,6 +39,11 @@ public class Main {
});
return;
}
if (".html".equals(extension)){
MainApplication.popupHTMLWindow(path);
return;
}
}
AxisInnovatorsBox.run(args);

View File

@@ -56,6 +56,7 @@ public class CefAppManager {
private static void disposeCefApp() {
if (cefApp != null) {
cefApp.clearSchemeHandlerFactories();
cefApp.dispose();
cefApp = null;
isInitialized = false;

View File

@@ -1,5 +1,8 @@
package com.axis.innovators.box.browser;
import com.axis.innovators.box.tools.FolderCreator;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.browser.CefMessageRouter;
@@ -8,9 +11,13 @@ import org.cef.handler.CefMessageRouterHandlerAdapter;
import org.tzd.lm.LM;
import javax.swing.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
@@ -27,46 +34,7 @@ public class MainApplication {
private static long ctxHandle;
private static boolean isSystem = true;
public static void main(String[] args) {
LM.loadLibrary(LM.CUDA);
modelHandle = LM.llamaLoadModelFromFile(LM.DEEP_SEEK);
ctxHandle = LM.createContext(modelHandle);
AtomicReference<BrowserWindowJDialog> window = new AtomicReference<>();
SwingUtilities.invokeLater(() -> {
WindowRegistry.getInstance().createNewChildWindow("main", builder ->
window.set(builder.title("Axis Innovators Box AI 工具箱")
.icon(new ImageIcon(Objects.requireNonNull(MainApplication.class.getClassLoader().getResource("icons/logo.png"))).getImage())
.size(1280, 720)
.htmlPath(Objects.requireNonNull(MainApplication.class.getClassLoader().getResource("javascript/AIaToolbox.html")).getFile())
.operationHandler(createOperationHandler())
.build())
);
CefMessageRouter msgRouter = window.get().getMsgRouter();
if (msgRouter != null) {
msgRouter.addHandler(new CefMessageRouterHandlerAdapter() {
@Override
public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId,
String request, boolean persistent, CefQueryCallback callback) {
// 处理浏览器请求
handleBrowserQuery(browser, request, callback);
return true;
}
@Override
public void onQueryCanceled(CefBrowser browser, CefFrame frame, long queryId) {
// 处理请求取消
}
}, true);
}
});
// 关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LM.llamaFreeContext(ctxHandle);
LM.llamaFreeModel(modelHandle);
executor.shutdown();
}));
//popupHTMLWindow();
}
@@ -87,7 +55,7 @@ public class MainApplication {
.parentFrame(parent)
.icon(new ImageIcon(Objects.requireNonNull(MainApplication.class.getClassLoader().getResource("icons/logo.png"))).getImage())
.size(1280, 720)
.htmlPath(Objects.requireNonNull(MainApplication.class.getClassLoader().getResource("javascript/AIaToolbox_dark.html")).getFile())
.htmlPath(FolderCreator.getJavaScriptFolder() + "\\" + "AIaToolbox_dark.html")
.operationHandler(createOperationHandler())
.build())
);
@@ -110,13 +78,60 @@ public class MainApplication {
}, true);
}
});
}
// 关闭钩子
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
LM.llamaFreeContext(ctxHandle);
LM.llamaFreeModel(modelHandle);
executor.shutdown();
}));
/**
* 弹出html预览窗口
*/
public static void popupHTMLWindow(String path) {
AtomicReference<BrowserWindow> window = new AtomicReference<>();
SwingUtilities.invokeLater(() -> {
WindowRegistry.getInstance().createNewWindow("main", builder ->
window.set(builder.title("Axis Innovators Box HTML查看器")
.icon(new ImageIcon(Objects.requireNonNull(MainApplication.class.getClassLoader().getResource("icons/logo.png"))).getImage())
.size(1487, 836)
.htmlPath(FolderCreator.getJavaScriptFolder() + "\\" + "HtmlViewer.html")
.operationHandler(createOperationHandler())
.build())
);
CefMessageRouter msgRouter = window.get().getMsgRouter();
if (msgRouter != null) {
msgRouter.addHandler(new CefMessageRouterHandlerAdapter() {
@Override
public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId,
String request, boolean persistent, CefQueryCallback callback) {
try {
// 解析JSON请求
JsonObject requestJson = JsonParser.parseString(request).getAsJsonObject();
if (requestJson.has("type") && "loadInitialContent".equals(requestJson.get("type").getAsString())) {
Path filePath = Paths.get(path);
// 验证文件存在性
if (!Files.exists(filePath)) {
callback.failure(404, "{\"code\":404,\"message\":\"文件未找到\"}");
return true;
}
// 读取文件内容
String content = Files.readString(filePath, StandardCharsets.UTF_8);
callback.success(content);
return true;
}
} catch (Exception e) {
callback.failure(500, "{\"code\":500,\"message\":\"" + e.getMessage() + "\"}");
}
return false;
}
@Override
public void onQueryCanceled(CefBrowser browser, CefFrame frame, long queryId) {
// 处理请求取消
}
}, true);
}
});
}
private static void handleBrowserQuery(CefBrowser browser, String request, CefQueryCallback callback) {

View File

@@ -0,0 +1,339 @@
package com.axis.innovators.box.decompilation.gui;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.analysis.*;
import java.lang.reflect.Field;
import java.util.*;
public class JavaPseudocodeGenerator extends MethodVisitor {
private final StringBuilder output = new StringBuilder();
private int indentLevel = 0;
private final Deque<ControlStructure> controlStack = new ArrayDeque<>();
private final Map<Label, Integer> labelPositions = new HashMap<>();
private int currentPosition = 0;
// 类型推断相关数据结构
private final Map<Integer, Type> localVarTypes = new HashMap<>();
private final Analyzer<BasicValue> analyzer;
private Frame<BasicValue>[] frames;
private final Deque<TypeValue> operandStack = new ArrayDeque<>();
private final MethodNode methodNode;
private static class TypeValue {
final Type type;
final String expression;
TypeValue(Type type, String expression) {
this.type = type;
this.expression = expression;
}
@Override
public String toString() {
return expression;
}
}
private static class ControlStructure {
enum Type { LOOP, CONDITIONAL, TRY_CATCH }
private final String header;
private final Type type;
private Label endLabel;
ControlStructure(String header, Type type) {
this.header = header;
this.type = type;
}
ControlStructure setEndLabel(Label endLabel) {
this.endLabel = endLabel;
return this;
}
String getHeader() { return header; }
Label getEndLabel() { return endLabel; }
}
public JavaPseudocodeGenerator(MethodNode methodNode) throws AnalyzerException {
super(Opcodes.ASM9);
this.methodNode = methodNode;
this.analyzer = new Analyzer<>(new BasicInterpreter());
this.frames = analyzer.analyze(methodNode.name, methodNode);
initLabelPositions(methodNode);
analyzeLocalVariables(methodNode);
}
private void initLabelPositions(MethodNode methodNode) {
labelPositions.clear();
int pos = 0;
for (AbstractInsnNode insn : methodNode.instructions) {
if (insn instanceof LabelNode) {
Label label = ((LabelNode) insn).getLabel();
labelPositions.put(label, pos);
}
pos++;
}
}
private void analyzeLocalVariables(MethodNode methodNode) {
localVarTypes.clear();
if (methodNode.localVariables != null) {
for (LocalVariableNode local : methodNode.localVariables) {
Type type = Type.getType(local.desc);
localVarTypes.put(local.index, type);
}
}
}
public String getOutput() {
return output.toString();
}
@Override
public void visitCode() {
// 生成方法签名
String access = getAccessModifier(methodNode.access);
Type returnType = Type.getReturnType(methodNode.desc);
Type[] argTypes = Type.getArgumentTypes(methodNode.desc);
StringBuilder signature = new StringBuilder();
signature.append(access)
.append(" ")
.append(returnType.getClassName())
.append(" ")
.append(methodNode.name)
.append("(");
List<String> params = new ArrayList<>();
for (int i = 0; i < argTypes.length; i++) {
params.add(argTypes[i].getClassName() + " arg" + i);
}
signature.append(String.join(", ", params))
.append(") {");
addLine(signature.toString());
indentLevel++;
}
private String getAccessModifier(int access) {
List<String> modifiers = new ArrayList<>();
if ((access & Opcodes.ACC_PUBLIC) != 0) modifiers.add("public");
if ((access & Opcodes.ACC_PRIVATE) != 0) modifiers.add("private");
if ((access & Opcodes.ACC_PROTECTED) != 0) modifiers.add("protected");
if ((access & Opcodes.ACC_STATIC) != 0) modifiers.add("static");
return String.join(" ", modifiers);
}
private void pushValue(Type type, String expr) {
operandStack.push(new TypeValue(type, expr));
}
private TypeValue popValue(Type expectedType) {
if (operandStack.isEmpty()) {
return new TypeValue(expectedType, "/* missing value */");
}
TypeValue value = operandStack.pop();
if (!value.type.equals(expectedType)) {
return new TypeValue(expectedType, "/* type error: " + value.expression + " */");
}
return value;
}
@Override
public void visitInsn(int opcode) {
switch (opcode) {
case Opcodes.RETURN:
addLine("return;");
break;
case Opcodes.IRETURN:
addLine("return " + popValue(Type.INT_TYPE) + ";");
break;
case Opcodes.ARETURN:
addLine("return " + popValue(Type.getType(Object.class)) + ";");
break;
case Opcodes.ATHROW:
addLine("throw " + popValue(Type.getType(Throwable.class)) + ";");
break;
case Opcodes.IADD: {
TypeValue val2 = popValue(Type.INT_TYPE);
TypeValue val1 = popValue(Type.INT_TYPE);
pushValue(Type.INT_TYPE, val1.expression + " + " + val2.expression);
break;
}
default:
addLine("// asm: " + OpcodesUtil.getOpcodeName(opcode));
}
currentPosition++;
}
@Override
public void visitVarInsn(int opcode, int var) {
Type type = localVarTypes.getOrDefault(var, Type.VOID_TYPE);
switch (opcode) {
case Opcodes.ILOAD:
pushValue(Type.INT_TYPE, "var" + var);
break;
case Opcodes.ALOAD:
pushValue(type, "var" + var);
break;
case Opcodes.ISTORE:
addLine("var" + var + " = " + popValue(Type.INT_TYPE) + ";");
break;
case Opcodes.ASTORE:
addLine("var" + var + " = " + popValue(type) + ";");
break;
default:
addLine("// asm: " + OpcodesUtil.getOpcodeName(opcode));
}
currentPosition++;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name,
String descriptor, boolean isInterface) {
try {
Type returnType = Type.getReturnType(descriptor);
Type[] argTypes = Type.getArgumentTypes(descriptor);
List<String> args = new ArrayList<>();
for (int i = argTypes.length - 1; i >= 0; i--) {
args.add(0, popValue(argTypes[i]).toString());
}
String methodCall;
if (opcode == Opcodes.INVOKESTATIC) {
methodCall = owner.replace('/', '.') + "." + name;
} else {
TypeValue target = popValue(Type.getType("L" + owner + ";"));
methodCall = target + "." + name;
}
String callExpr = methodCall + "(" + String.join(", ", args) + ")";
if (returnType != Type.VOID_TYPE) {
pushValue(returnType, callExpr);
} else {
addLine(callExpr + ";");
}
} catch (Exception e) {
addLine("// asm: " + OpcodesUtil.getOpcodeName(opcode));
}
currentPosition++;
}
@Override
public void visitJumpInsn(int opcode, Label label) {
try {
TypeValue condition = popValue(Type.BOOLEAN_TYPE);
String operator = getConditionOperator(opcode);
if (isLoopStart(label)) {
addLine("while (" + condition.expression + operator + ") {");
indentLevel++;
controlStack.push(new ControlStructure("loop", ControlStructure.Type.LOOP).setEndLabel(label));
} else {
addLine("if (" + condition.expression + operator + ") {");
indentLevel++;
controlStack.push(new ControlStructure("if", ControlStructure.Type.CONDITIONAL).setEndLabel(label));
}
} catch (Exception e) {
addLine("// asm: " + OpcodesUtil.getOpcodeName(opcode));
}
currentPosition++;
}
private String getConditionOperator(int opcode) {
switch (opcode) {
case Opcodes.IFEQ: return " == 0";
case Opcodes.IFNE: return " != 0";
case Opcodes.IFLT: return " < 0";
case Opcodes.IFGE: return " >= 0";
default: return "";
}
}
private boolean isLoopStart(Label label) {
int targetPos = getLabelPosition(label);
return targetPos != -1 && targetPos < currentPosition;
}
private int getLabelPosition(Label label) {
return labelPositions.getOrDefault(label, -1);
}
@Override
public void visitLabel(Label label) {
if (!controlStack.isEmpty()) {
ControlStructure structure = controlStack.peek();
if (structure.getEndLabel() == null) {
structure.setEndLabel(label);
} else if (structure.getEndLabel().equals(label)) {
controlStack.pop();
indentLevel--;
addLine("}");
}
}
currentPosition++;
}
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
addLine("try {");
indentLevel++;
controlStack.push(new ControlStructure("try", ControlStructure.Type.TRY_CATCH).setEndLabel(end));
currentPosition++;
}
@Override
public void visitEnd() {
while (!controlStack.isEmpty()) {
indentLevel--;
addLine("}");
controlStack.pop();
}
indentLevel--;
addLine("}");
}
private void addLine(String line) {
output.append(" ".repeat(indentLevel)).append(line).append("\n");
}
private static class OpcodesUtil {
private static final Map<Integer, String> OPCODE_NAMES = new HashMap<>();
static {
try {
for (Field field : Opcodes.class.getDeclaredFields()) {
if (field.getType() == int.class) {
OPCODE_NAMES.put(field.getInt(null), field.getName());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
static String getOpcodeName(int opcode) {
return OPCODE_NAMES.getOrDefault(opcode, "UNKNOWN_OPCODE_" + opcode);
}
}
public static void main(String[] args) throws Exception {
MethodNode methodNode = new MethodNode(Opcodes.ASM9,
Opcodes.ACC_PUBLIC, "add",
"(II)I",
null,
null);
methodNode.instructions.add(new VarInsnNode(Opcodes.ILOAD, 0));
methodNode.instructions.add(new VarInsnNode(Opcodes.ILOAD, 1));
methodNode.instructions.add(new InsnNode(Opcodes.IADD));
methodNode.instructions.add(new InsnNode(Opcodes.IRETURN));
JavaPseudocodeGenerator generator = new JavaPseudocodeGenerator(methodNode);
methodNode.accept(generator);
System.out.println(generator.getOutput());
}
}

View File

@@ -1,6 +1,7 @@
package com.axis.innovators.box.decompilation.gui;
import com.axis.innovators.box.gui.LoadIcon;
import com.axis.innovators.box.util.AdvancedJFileChooser;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ParseResult;
import com.github.javaparser.Position;
@@ -794,7 +795,7 @@ public class ModernJarViewer extends JFrame {
/****/
private void openJarFile() {
JFileChooser chooser = new JFileChooser();
AdvancedJFileChooser chooser = new AdvancedJFileChooser();
chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
public boolean accept(File f) {
return f.getName().endsWith(".jar") || f.isDirectory();
@@ -829,6 +830,7 @@ public class ModernJarViewer extends JFrame {
String name = entry.getName();
// 忽略已被源码覆盖的class文件
if (name.endsWith(".class") && sourceFiles.contains(name)) continue;
addEntryToTree(root, name);
}
} catch (Exception ex) {

View File

@@ -0,0 +1,7 @@
package com.axis.innovators.box.decompilation.gui;
public class TypeMismatchException extends Exception{
public TypeMismatchException(String s) {
super(s);
}
}

View File

@@ -1,6 +1,7 @@
package com.axis.innovators.box.gui;
import com.axis.innovators.box.gui.renderer.DarculaCompletionCellRenderer;
import com.axis.innovators.box.util.AdvancedJFileChooser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.fife.rsta.ac.LanguageSupport;
@@ -479,7 +480,7 @@ public class FridaWindow extends WindowsJDialog {
private void openFile() {
JFileChooser fileChooser = new JFileChooser();
AdvancedJFileChooser fileChooser = new AdvancedJFileChooser();
fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("Frida脚本配置 (*.frida)", "frida"));
int result = fileChooser.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
@@ -503,7 +504,7 @@ public class FridaWindow extends WindowsJDialog {
}
private void saveFile() {
JFileChooser fileChooser = new JFileChooser();
AdvancedJFileChooser fileChooser = new AdvancedJFileChooser();
fileChooser.setFileFilter(new javax.swing.filechooser.FileNameExtensionFilter("Frida脚本配置 (*.frida)", "frida"));
int result = fileChooser.showSaveDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,7 @@ public class MainWindow extends JFrame {
private final boolean isBackground = true;
private final boolean isBlur = true;
private SystemTray systemTray;
private TrayIcon trayIcon;
//private TrayIcon trayIcon;
public MainWindow() {
setIconImage(LoadIcon.loadIcon("logo.png", 32).getImage());
@@ -123,17 +123,17 @@ public class MainWindow extends JFrame {
popup.add(exitItem);
// 5. 创建托盘图标
trayIcon = new TrayIcon(roundedImage, "轴创工具箱", popup);
trayIcon.setImageAutoSize(true);
//trayIcon = new TrayIcon(roundedImage, "轴创工具箱", popup);
//trayIcon.setImageAutoSize(true);
// 6. 添加事件监听
addTrayEventListeners();
//addTrayEventListeners();
try {
systemTray.add(trayIcon);
} catch (AWTException ex) {
logger.error("添加系统托盘图标失败", ex);
}
//try {
// systemTray.add(trayIcon);
//} catch (AWTException ex) {
// logger.error("添加系统托盘图标失败", ex);
//}
}
// 基础菜单项创建方法(解决方法不存在问题)
@@ -161,20 +161,20 @@ public class MainWindow extends JFrame {
}
// 添加事件监听
private void addTrayEventListeners() {
// 双击恢复窗口
trayIcon.addActionListener(e -> setVisible(true));
// 右键菜单触发兼容处理
trayIcon.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
trayIcon.getPopupMenu().show(e.getComponent(), e.getX(), e.getY());
}
}
});
}
//private void addTrayEventListeners() {
// // 双击恢复窗口
// trayIcon.addActionListener(e -> setVisible(true));
//
// // 右键菜单触发兼容处理
// trayIcon.addMouseListener(new MouseAdapter() {
// @Override
// public void mouseReleased(MouseEvent e) {
// if (e.isPopupTrigger()) {
// trayIcon.getPopupMenu().show(e.getComponent(), e.getX(), e.getY());
// }
// }
// });
//}
// 初始化中文字体在main方法或构造函数中调用
private void initChineseFont() {

View File

@@ -3,6 +3,7 @@ package com.axis.innovators.box.register;
import com.axis.innovators.box.AxisInnovatorsBox;
import com.axis.innovators.box.browser.MainApplication;
import com.axis.innovators.box.gui.FridaWindow;
import com.axis.innovators.box.gui.JarApiProfilingWindow;
import com.axis.innovators.box.gui.LocalWindow;
import com.axis.innovators.box.gui.MainWindow;
import com.axis.innovators.box.plugins.PluginDescriptor;
@@ -47,6 +48,20 @@ public class RegistrationTool {
}
}));
MainWindow.ToolCategory programmingToolsCategory = new MainWindow.ToolCategory("编程工具",
"programming/programming.png",
"编程工具");
programmingToolsCategory.addTool(new MainWindow.ToolItem("JarApi查看器", "programming/JarApiViewer/JarApi_Viewer.png",
"查看Jar内的方法以及其注解" +
"\n作者tzdwindows 7", ++id, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
Window owner = SwingUtilities.windowForComponent((Component) e.getSource());
JarApiProfilingWindow jarApiProfilingWindow = new JarApiProfilingWindow(owner);
main.popupWindow(jarApiProfilingWindow);
}
}));
MainWindow.ToolCategory aICategory = new MainWindow.ToolCategory("AI工具",
"ai/ai.png",
"人工智能/大语言模型");
@@ -75,6 +90,7 @@ public class RegistrationTool {
addToolCategory(debugCategory, "system:debugTools");
addToolCategory(aICategory,"system:fridaTools");
addToolCategory(programmingToolsCategory, "system:programmingTools");
}
/**

View File

@@ -17,6 +17,7 @@ public class FolderCreator {
public static final String LOGS_PATH = "logs";
public static final String LANGUAGE_PATH = "language";
public static final String CONFIGURATION_PATH = "state";
public static final String JAVA_SCRIPT_PATH = "javascript";
public static String getConfigurationFolder() {
String folder = createFolder(CONFIGURATION_PATH);
if (folder == null) {
@@ -25,6 +26,15 @@ public class FolderCreator {
}
return folder;
}
public static String getJavaScriptFolder() {
String folder = createFolder(JAVA_SCRIPT_PATH);
if (folder == null) {
logger.error("Plugin folder creation failed, please use administrator privileges to execute this procedure");
return null;
}
return folder;
}
public static String getLanguageFolder() {
String folder = createFolder(LANGUAGE_PATH);
if (folder == null) {

View File

@@ -0,0 +1,233 @@
package com.axis.innovators.box.tools;
import java.util.ArrayList;
import java.util.List;
/**
* Windows系统托盘管理器
* <p>提供系统托盘图标的管理功能,支持菜单操作和事件回调</p>
*
* @author tzdwindows
* @version 1.0
*/
public class RegisterTray {
static {
LibraryLoad.loadLibrary("RegisterTray");
}
/**
* 托盘菜单项构建器(流畅接口)
*/
public static class MenuBuilder {
private final List<Item> items = new ArrayList<>();
/**
* 添加菜单项
* @param id 菜单项唯一标识符需大于0
* @param label 菜单显示文本
* @param onClick 点击事件处理器
* @return 当前构建器实例
*/
public MenuBuilder addItem(int id, String label, MenuItemClickListener onClick) {
items.add(new Item(
id,
label,
"", "", "",
(Event) combinedId -> {
int itemId = (int)(combinedId >> 32);
onClick.onClick(itemId);
}
));
return this;
}
/**
* 构建菜单项列表
* @return 可用于注册的菜单项集合
*/
public List<Item> build() {
return new ArrayList<>(items);
}
}
/**
* 托盘配置器(流畅接口)
*/
public static class TrayConfig {
private String title = "Application";
private String iconPath = "";
private String tooltip = "";
private List<Item> menuItems = new ArrayList<>();
private TrayClickListener clickListener = id -> {};
/**
* 设置托盘标题(必选)
* @param title 显示在托盘区域的标题
* @return 当前配置实例
*/
public TrayConfig title(String title) {
this.title = title;
return this;
}
/**
* 设置托盘图标(可选)
* @param iconPath ICO图标的绝对路径
* @return 当前配置实例
*/
public TrayConfig icon(String iconPath) {
this.iconPath = iconPath;
return this;
}
/**
* 设置悬停提示信息(可选)
* @param tooltip 鼠标悬停时显示的文本
* @return 当前配置实例
*/
public TrayConfig tooltip(String tooltip) {
this.tooltip = tooltip;
return this;
}
/**
* 设置右键菜单(可选)
* @param menuItems 通过MenuBuilder构建的菜单项列表
* @return 当前配置实例
*/
public TrayConfig menu(List<Item> menuItems) {
this.menuItems = new ArrayList<>(menuItems);
return this;
}
/**
* 设置托盘点击事件(可选)
* @param listener 点击事件监听器
* @return 当前配置实例
*/
public TrayConfig onClick(TrayClickListener listener) {
this.clickListener = listener;
return this;
}
/**
* 完成配置并注册托盘
* @return 注册成功的托盘ID
* @throws TrayException 注册失败时抛出
*/
public long register() throws TrayException {
try {
return RegisterTray.register(
title,
menuItems,
iconPath,
tooltip,
clickListener::onClick
);
} catch (Exception e) {
throw new TrayException("托盘注册失败", e);
}
}
}
/**
* 托盘点击事件监听器
*/
@FunctionalInterface
public interface TrayClickListener {
/**
* 当托盘图标被点击时触发
* @param trayId 托盘实例的唯一标识符
*/
void onClick(long trayId);
}
/**
* 菜单项点击事件监听器
*/
@FunctionalInterface
public interface MenuItemClickListener {
/**
* 当菜单项被点击时触发
* @param itemId 菜单项的唯一标识符
*/
void onClick(int itemId);
}
/**
* 托盘操作异常
*/
public static class TrayException extends Exception {
public TrayException(String message, Throwable cause) {
super(message, cause);
}
}
public static native long register(String name, List<Item> value,
String icon, String description, Event event);
public static native void unregister(long id);
public interface Event {
void onClick(long id);
}
public static class Item {
private final long id;
private final String name;
private final String value;
private final String icon;
private final String description;
private final Event event;
/**
* @param id 菜单项唯一ID
* @param name 显示名称
* @param value 保留字段
* @param icon 保留字段
* @param description 保留字段
* @param event 点击事件处理器
*/
public Item(int id, String name, String value,
String icon, String description, Event event) {
this.id = id;
this.name = name;
this.value = value;
this.icon = icon;
this.description = description;
this.event = event;
}
}
public static void main(String[] args) {
try {
// 构建菜单
List<Item> menuItems = new MenuBuilder()
.addItem(1, "显示日志", itemId ->
System.out.println("打开日志菜单项ID: " + itemId))
.addItem(2, "退出系统", itemId -> System.exit(0))
.build();
// 配置并注册托盘
long trayId = new TrayConfig()
.title("数据监控系统")
.icon("C:/icons/app.ico")
.tooltip("双击查看实时数据")
.menu(menuItems)
.onClick(id ->
System.out.println("托盘被点击ID: " + id))
.register();
// 程序主循环
while (true) {
Thread.sleep(1000);
}
} catch (RegisterTray.TrayException | InterruptedException e) {
e.printStackTrace();
}
}
//static {
// System.loadLibrary("RegisterTray");
//}
}

View File

@@ -0,0 +1,67 @@
package com.axis.innovators.box.util;
import com.axis.innovators.box.gui.JarApiProfilingWindow;
import com.formdev.flatlaf.FlatLaf;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.util.prefs.Preferences;
/**
* @author tzdwindows 7
*/
public class AdvancedJFileChooser extends JFileChooser {
private static final String PREF_KEY = "LAST_DIRECTORY";
private static final File FALLBACK_DIR = new File(System.getProperty("user.home"));
public AdvancedJFileChooser() {
super(loadValidDirectory());
setFileSelectionMode(FILES_AND_DIRECTORIES);
}
@Override
public int showDialog(Component parent, String approveButtonText) {
int result = super.showDialog(parent, approveButtonText);
if (result == APPROVE_OPTION) {
persistDirectory(getSelectedPath());
}
return result;
}
private String getSelectedPath() {
File selected = getSelectedFile();
return (selected != null && selected.isDirectory()) ?
selected.getAbsolutePath() :
getCurrentDirectory().getAbsolutePath();
}
private static File loadValidDirectory() {
try {
Preferences prefs = Preferences.userNodeForPackage(JarApiProfilingWindow.class);
String path = prefs.get(PREF_KEY, FALLBACK_DIR.getAbsolutePath());
File dir = new File(path).getCanonicalFile();
return dir.isDirectory() && Files.isReadable(dir.toPath()) ?
dir :
FALLBACK_DIR;
} catch (IOException | InvalidPathException | SecurityException e) {
return FALLBACK_DIR;
}
}
private void persistDirectory(String path) {
try {
File dir = new File(path).getCanonicalFile();
if (dir.isDirectory() && Files.isWritable(dir.toPath())) {
Preferences prefs = Preferences.userNodeForPackage(JarApiProfilingWindow.class);
prefs.put(PREF_KEY, dir.getAbsolutePath());
}
} catch (IOException | InvalidPathException | SecurityException e) {
System.err.println("无法保存目录: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,7 @@
package org.editing;
import javax.swing.*;
public class CodeEditingComponent extends JTextPane {
}

View File

@@ -0,0 +1,9 @@
package org.editing;
import java.io.File;
public class Main {
public static void main(String[] args) {
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -0,0 +1,462 @@
<!DOCTYPE html>
<html lang="zh-CN" data-theme="dark">
<head>
<meta charset="UTF-8">
<title>Axis Innovators Pro</title>
<!-- CodeMirror 资源 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.0/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.0/theme/material-darker.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.0/theme/nord.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.0/addon/hint/show-hint.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.0/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.0/mode/xml/xml.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.0/addon/edit/closetag.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.0/addon/hint/show-hint.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.0/addon/hint/xml-hint.min.js"></script>
<style>
:root {
--bg-color: #1a1a1a;
--text-color: #e0e0e0;
--border-color: #404040;
--toolbar-bg: #2d2d2d;
--primary-color: #4dabf7;
--hover-color: #339af0;
--success: #69db7c;
--warning: #ffd43b;
--error: #ff6b6b;
--radius: 6px;
}
[data-theme="light"] {
--bg-color: #f8f9fa;
--text-color: #343a40;
--border-color: #dee2e6;
--toolbar-bg: #e9ecef;
--primary-color: #4dabf7;
--hover-color: #339af0;
}
* {
box-sizing: border-box;
font-family: 'Fira Code', 'JetBrains Mono', monospace;
}
body {
margin: 0;
height: 100vh;
display: flex;
flex-direction: column;
background-color: var(--bg-color);
color: var(--text-color);
}
.toolbar {
padding: 12px 24px;
background-color: var(--toolbar-bg);
display: flex;
gap: 18px;
align-items: center;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.container {
flex: 1;
display: flex;
height: calc(100vh - 56px);
}
.editor-pane {
width: 50%;
position: relative;
border-right: 1px solid var(--border-color);
}
.preview-container {
width: 50%;
display: flex;
flex-direction: column;
background: var(--bg-color);
}
#preview {
flex: 1;
border: none;
background: white;
}
.output-pane {
height: 200px;
border-top: 1px solid var(--border-color);
background: var(--toolbar-bg);
overflow-y: auto;
padding: 12px;
}
.log-item {
padding: 8px 12px;
margin: 4px 0;
border-radius: var(--radius);
display: flex;
align-items: center;
gap: 10px;
font-size: 13px;
background: rgba(0,0,0,0.1);
}
.log-item.error {
color: var(--error);
border-left: 3px solid var(--error);
}
.log-item.warning {
color: var(--warning);
border-left: 3px solid var(--warning);
}
.CodeMirror {
height: 100% !important;
font-size: 14px;
line-height: 1.5;
padding: 16px 0;
}
.cm-editor {
border-radius: 0;
}
button {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 16px;
border-radius: var(--radius);
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
button:hover {
background: var(--hover-color);
transform: translateY(-1px);
}
select {
padding: 8px 32px 8px 12px;
border-radius: var(--radius);
border: 1px solid var(--border-color);
background: var(--bg-color) url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23ffffff'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e") no-repeat right 8px center/12px;
color: var(--text-color);
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23999'%3e%3cpath d='M7 10l5 5 5-5z'/%3e%3c/svg%3e");
}
.file-info {
margin-left: auto;
display: flex;
align-items: center;
gap: 12px;
font-size: 13px;
}
.resizer {
width: 8px;
background: var(--primary-color);
cursor: col-resize;
position: absolute;
right: -4px;
top: 0;
bottom: 0;
z-index: 10;
opacity: 0;
transition: opacity 0.2s;
}
.editor-pane:hover .resizer {
opacity: 1;
}
</style>
</head>
<body>
<div class="toolbar">
<select id="theme-selector" onchange="toggleTheme(this.value)">
<option value="dark">🌙 深色主题</option>
<option value="light">☀️ 浅色主题</option>
</select>
</div>
<div class="container">
<div class="editor-pane">
<textarea id="code-editor"></textarea>
<div class="resizer" onmousedown="startResize(event)"></div>
</div>
<div class="preview-container">
<iframe id="preview"></iframe>
<div class="output-pane" id="output"></div>
</div>
</div>
<input type="file" id="file-input" accept=".html" onchange="loadFile(this.files[0])">
<script>
// 初始化代码编辑器
const editor = CodeMirror.fromTextArea(document.getElementById('code-editor'), {
mode: "xml",
theme: "material-darker",
lineNumbers: true,
lineWrapping: true,
autoCloseTags: true,
matchTags: {bothTags: true},
extraKeys: {
"'<'": function(cm) {
cm.showHint({completeSingle: false});
},
"Ctrl-Space": "autocomplete",
"Tab": "emmetExpandAbbreviation"
},
hintOptions: {
schemaInfo: {}
},
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
});
// 实时预览
let updateTimeout;
editor.on("change", () => {
clearTimeout(updateTimeout);
updateTimeout = setTimeout(updatePreview, 300);
});
function updatePreview() {
const content = editor.getValue();
const fullHTML = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body {
margin: 20px;
font-family: system-ui;
}
</style>
</head>
<body>${content}</body>
</html>
`;
const blob = new Blob([fullHTML], {type: 'text/html;charset=utf-8'});
const url = URL.createObjectURL(blob);
const iframe = document.getElementById('preview');
// 添加错误处理
iframe.onload = function() {
try {
const doc = iframe.contentDocument || iframe.contentWindow.document;
const errors = [];
// 检测解析错误
const parser = new DOMParser();
const parsedDoc = parser.parseFromString(content, "text/html");
const parserErrors = parsedDoc.getElementsByTagName("parsererror");
if (parserErrors.length > 0) {
errors.push({
type: 'error',
message: 'HTML语法错误: ' + parserErrors[0].textContent
});
}
// 检测控制台错误
const consoleErrors = [];
const originalConsoleError = console.error;
console.error = function(message) {
consoleErrors.push(message);
originalConsoleError.apply(console, arguments);
};
// 触发重新解析
doc.open();
doc.write(fullHTML);
doc.close();
consoleErrors.forEach(msg => {
errors.push({type: 'error', message: msg});
});
updateOutput(errors);
URL.revokeObjectURL(url); // 释放内存
} catch(e) {
addLog('error', '预览加载失败: ' + e.message);
}
};
// 设置src最后执行
iframe.src = url;
}
// 输出控制台
function addLog(type, message) {
const output = document.getElementById('output');
const item = document.createElement('div');
item.className = `log-item ${type}`;
item.innerHTML = `
<i class="fas ${type === 'error' ? 'fa-times-circle' : 'fa-exclamation-triangle'}"></i>
${new Date().toLocaleTimeString()}: ${message}
`;
output.appendChild(item);
output.scrollTop = output.scrollHeight;
}
function updateOutput(errors) {
const output = document.getElementById('output');
output.innerHTML = '';
errors.forEach(e => addLog(e.type, e.message));
}
// 主题切换
function toggleTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
editor.setOption("theme", theme === 'dark' ? 'material-darker' : 'nord');
}
// 文件操作
function loadFile(file) {
const reader = new FileReader();
reader.onload = (e) => {
editor.setValue(e.target.result);
document.getElementById('file-name').textContent = file.name;
};
reader.readAsText(file);
}
// 窗口大小调整
let isResizing = false;
function startResize(e) {
isResizing = true;
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', stopResize);
}
function onMouseMove(e) {
if (!isResizing) return;
const container = document.querySelector('.container');
const editorPane = document.querySelector('.editor-pane');
const previewPane = document.querySelector('.preview-container');
const containerWidth = container.offsetWidth;
const newWidth = (e.clientX / containerWidth) * 100;
editorPane.style.width = Math.min(Math.max(newWidth, 20), 80) + '%';
previewPane.style.width = (100 - newWidth) + '%';
}
function stopResize() {
isResizing = false;
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', stopResize);
}
window.addEventListener('error', function(e) {
addLog('error', `运行时错误: ${e.message} (${e.lineno}:${e.colno})`);
});
// 初始化
window.addEventListener('load', () => {
// 初始化后立即更新预览
updatePreview();
// 添加编辑器变更的防抖处理
editor.on("change", () => {
clearTimeout(updateTimeout);
updateTimeout = setTimeout(() => {
updatePreview();
}, 500); // 调整为500ms防抖
});
});
(function() {
// 存储回调的映射
const callbacks = new Map();
let requestId = 0;
// 实现cefQuery规范
window.cefQuery = function(config) {
const currentId = ++requestId;
callbacks.set(currentId, {
onSuccess: config.onSuccess,
onFailure: config.onFailure
});
// 发送到Java端
window.javaMessageRouterQuery({
request: JSON.stringify({
id: currentId,
data: config.request
}),
onSuccess: (response) => {
const cb = callbacks.get(currentId);
cb?.onSuccess(response);
callbacks.delete(currentId);
},
onFailure: (error, code) => {
const cb = callbacks.get(currentId);
cb?.onFailure?.(error, code);
callbacks.delete(currentId);
}
});
};
// 初始化逻辑
document.addEventListener('DOMContentLoaded', () => {
window.cefQuery({
request: JSON.stringify({ type: "loadInitialContent" }),
onSuccess: (response) => {
try {
if (window.javaMessageHandler?.loadContent) {
javaMessageHandler.loadContent(response, '默认文档');
}
} catch(e) {
console.error('内容处理失败:', e);
}
},
onFailure: (error, code) => {
console.error(`加载失败 (CODE ${code}):`, error);
showErrorDialog(`加载失败: ${error}`);
}
});
});
// 错误显示函数
function showErrorDialog(msg) {
const dialog = document.createElement('div');
dialog.style = `/* 样式代码 */`;
dialog.innerHTML = `
<div class="error-dialog">
<i class="fa fa-exclamation-triangle"></i>
<p>${msg}</p>
<button onclick="this.parentElement.remove()">确定</button>
</div>
`;
document.body.appendChild(dialog);
}
})();
window.javaMessageHandler = {
loadContent: (content, fileName) => {
editor.setValue(content);
document.getElementById('file-name').textContent = fileName || '';
updatePreview();
},
setTheme: (theme) => {
toggleTheme(theme);
document.getElementById('theme-selector').value = theme;
}
};
</script>
</body>
</html>