LANCommander/LANCommander.UI/Components/CodeInput/FileDropHandler.ts

245 lines
7.1 KiB
TypeScript
Raw Normal View History

declare const monaco: any;
export function SetupFileDropHandler(): void {
if (typeof monaco === "undefined")
return;
const editors = monaco.editor.getEditors();
const editor = editors[editors.length - 1];
if (!editor)
return;
const domNode = editor.getDomNode();
if (!domNode)
return;
domNode.addEventListener("dragover", (e: DragEvent) => {
if (e.dataTransfer?.types?.includes("Files")) {
e.preventDefault();
e.stopPropagation();
}
});
domNode.addEventListener("drop", (e: DragEvent) => {
const files = e.dataTransfer?.files;
if (files && files.length > 0) {
e.preventDefault();
e.stopPropagation();
handleFile(editor, files[0]);
}
});
document.addEventListener("paste", (e: ClipboardEvent) => {
if (!editor.hasWidgetFocus())
return;
if (!e.clipboardData?.files.length)
return;
e.preventDefault();
e.stopPropagation();
handleFile(editor, e.clipboardData.files[0]);
}, true);
}
const MAX_FILE_SIZE = 1024 * 1024; // 1 MB
function IsTextFile(file: File): boolean {
if (file.type.startsWith("text/")) return true;
const textExtensions = [
".reg", ".ps1", ".txt", ".cfg", ".ini", ".conf",
".json", ".xml", ".yaml", ".yml", ".csv", ".log",
".bat", ".cmd", ".sh", ".py", ".lua",
];
return textExtensions.includes(getFileExtension(file.name));
}
function handleFile(editor: any, file: File): void {
if (file.size > MAX_FILE_SIZE)
return;
if (!IsTextFile(file))
return;
const reader = new FileReader();
reader.onload = () => {
const content = reader.result as string;
const ext = getFileExtension(file.name);
if (ext === ".reg") {
const psCommands = ParseRegToPowerShell(content);
insertTextAtCursor(editor, psCommands);
} else if (ext === ".ps1") {
editor.setValue(content);
} else {
const hereString = `@'\n${content}\n'@`;
insertTextAtCursor(editor, hereString);
}
};
reader.readAsText(file);
}
function getFileExtension(filename: string): string {
const idx = filename.lastIndexOf(".");
return idx >= 0 ? filename.substring(idx).toLowerCase() : "";
}
function insertTextAtCursor(editor: any, text: string): void {
const selection = editor.getSelection();
editor.executeEdits("fileImport", [{
range: selection,
text: text,
forceMoveMarkers: true,
}]);
}
interface RegEntry {
type: "key" | "sz" | "dword" | "binary";
path: string;
property?: string;
value?: any;
}
function ParseRegToPowerShell(regContent: string): string {
const entries = ParseRegFile(regContent);
const lines: string[] = [];
for (const entry of entries) {
switch (entry.type) {
case "key":
if (lines.length > 0)
lines.push("");
lines.push(`New-Item -Path "registry::\\${entry.path}"`);
break;
case "sz": {
const escaped = (entry.value as string).replace(/"/g, '""""');
lines.push(`New-ItemProperty -Path "registry::\\${entry.path}" -Name "${entry.property}" -Value "${escaped}" -Force`);
break;
}
case "dword":
lines.push(`New-ItemProperty -Path "registry::\\${entry.path}" -Name "${entry.property}" -Value ${entry.value} -Force`);
break;
case "binary": {
const bytes = entry.value as number[];
const chunks: string[] = [];
for (let i = 0; i < bytes.length; i += 32) {
const chunk = bytes.slice(i, i + 32);
chunks.push(chunk.map(b => "0x" + b.toString(16).toUpperCase().padStart(2, "0")).join(", "));
}
const formatted = chunks.join("\n\t");
lines.push(`New-ItemProperty -Path "registry::\\${entry.path}" -Name "${entry.property}" -PropertyType Binary -Value ([byte[]]@(\n\t${formatted}\n)) -Force`);
break;
}
}
}
return lines.join("\n");
}
function ParseRegFile(content: string): RegEntry[] {
const entries: RegEntry[] = [];
const normalized = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
const joined = normalized.replace(/\\\n\s*/g, "");
const rawLines = joined.split("\n");
let currentPath = "";
for (const line of rawLines) {
const trimmed = line.trim();
// Skip empty lines, comments, and headers
if (!trimmed || trimmed.startsWith(";") ||
trimmed === "Windows Registry Editor Version 5.00" ||
trimmed === "REGEDIT4") {
continue;
}
// Key section: [HKEY_...]
const keyMatch = trimmed.match(/^\[(.+)\]$/);
if (keyMatch) {
const path = keyMatch[1];
if (path.startsWith("-"))
continue;
currentPath = path;
entries.push({ type: "key", path: currentPath });
continue;
}
if (!currentPath)
continue;
// Parse value lines
let name: string;
let valueStr: string;
if (trimmed.startsWith("@=")) {
name = "@";
valueStr = trimmed.substring(2);
} else {
const valueMatch = trimmed.match(/^"(.+?)"=(.*)$/);
if (!valueMatch)
continue;
name = valueMatch[1].replace(/\\\\/g, "\\").replace(/\\"/g, '"');
valueStr = valueMatch[2];
}
// Skip delete values
if (valueStr === "-")
continue;
// String value: "..."
if (valueStr.startsWith('"') && valueStr.endsWith('"')) {
const str = valueStr.slice(1, -1).replace(/\\\\/g, "\\").replace(/\\"/g, '"');
entries.push({ type: "sz", path: currentPath, property: name, value: str });
continue;
}
// DWORD: dword:XXXXXXXX
const dwordMatch = valueStr.match(/^dword:([0-9a-fA-F]{1,8})$/);
if (dwordMatch) {
const val = parseInt(dwordMatch[1], 16);
entries.push({ type: "dword", path: currentPath, property: name, value: val });
continue;
}
// Binary/hex types: hex:XX,XX,... or hex(N):XX,XX,...
const hexMatch = valueStr.match(/^hex(?:\([0-9a-fA-F]+\))?:(.*)$/);
if (hexMatch) {
const hexStr = hexMatch[1].replace(/\s/g, "");
const bytes = hexStr.split(",")
.filter(s => s.length > 0)
.map(s => parseInt(s, 16));
entries.push({ type: "binary", path: currentPath, property: name, value: bytes });
}
}
return entries;
}