Najeebsk

ALL-SCRIPTS-ZIP-PASSWORD.pyw

Apr 13th, 2026
323
0
Never
8
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 72.96 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Najeeb ULTRA PRO MAX V3.8 - FINAL COMPLETE EDITION [FIXED]
  5. ✅ Python 3.7.9+ | Windows 7/10/11 32/64-bit | Pure StdLib | Zero Dependencies
  6. ✅ PERFECT: Line Numbers + Code Scroll Together 100%
  7. ✅ 175+ Extensions: Code + Audio + Video + Images + EXE
  8. ✅ NEW: 🔒 Password ZIP Extract (Thread-Safe) | 💻 CMD Command Field + Run Button
  9. ✅ Fixed: Tuple Unpacking | Syntax Error | Truncated Settings | Tkinter Thread Crash
  10. """
  11.  
  12. import os
  13. import sys
  14. import json
  15. import shutil
  16. import tempfile
  17. import threading
  18. import subprocess
  19. import tarfile
  20. import zipfile
  21. import queue
  22. import re
  23. import time  # ✅ Added for thread-safe dialog
  24. from pathlib import Path
  25. from datetime import datetime
  26. from tkinter import *
  27. from tkinter import ttk, filedialog, messagebox, simpledialog
  28. from tkinter import TclError
  29.  
  30. # ------------------------------- SPLASH -----------------------
  31. def show_splash(image_path):
  32.     """Display splash screen for 3 seconds"""
  33.     try:
  34.         splash = Tk()
  35.         splash.overrideredirect(True)
  36.         if os.path.exists(image_path):
  37.             from PIL import Image, ImageTk
  38.             image = Image.open(image_path)
  39.             img = ImageTk.PhotoImage(image)
  40.             img_width, img_height = image.size
  41.             screen_width = splash.winfo_screenwidth()
  42.             screen_height = splash.winfo_screenheight()
  43.             x = (screen_width - img_width) // 2
  44.             y = (screen_height - img_height) // 2
  45.             splash.geometry(f"{img_width}x{img_height}+{x}+{y}")
  46.             splash.config(bg="white")
  47.             splash.attributes("-transparentcolor", "white")
  48.             label = Label(splash, image=img, bg="white")
  49.             label.image = img
  50.             label.pack()
  51.             splash.after(3000, splash.destroy)
  52.             splash.mainloop()
  53.         else:
  54.             splash.destroy()
  55.     except Exception:
  56.         pass
  57.  
  58. # Get paths
  59. base_path = os.path.dirname(os.path.abspath(__file__))
  60. splash_path = os.path.join(base_path, "THEME", "splash-1.png")
  61. if os.path.exists(splash_path):
  62.     show_splash(splash_path)
  63.  
  64. # ---------------- CONFIG ----------------
  65. APP_NAME = "Najeeb ULTRA PRO MAX V3.8"
  66. TEMP_DIR = os.path.join(tempfile.gettempdir(), "UltraProMax_V38")
  67. SETTINGS_FILE = Path.home() / ".ultrapro_settings_v38.json"
  68. APP_ICON = "SOFT.ico" if os.name == "nt" else "icon.xbm"
  69. RECENT_MAX = 10
  70. HIGHLIGHT_DELAY = 200
  71. CREATE_NO_WINDOW = 0x08000000 if os.name == "nt" else 0
  72.  
  73. # ✅ EXT_LANG: Complete extension support (175+ Extensions)
  74. EXT_LANG = {
  75.     ".py": "Python", ".pyw": "Python", ".pyi": "Python",
  76.     ".js": "JavaScript", ".jsx": "JavaScript", ".ts": "TypeScript", ".tsx": "TypeScript",
  77.     ".html": "HTML", ".htm": "HTML", ".css": "CSS", ".scss": "CSS", ".sass": "CSS", ".less": "CSS",
  78.     ".vue": "Vue", ".svelte": "Svelte", ".hta": "HTML",
  79.     ".json": "JSON", ".json5": "JSON", ".xml": "XML", ".yaml": "YAML", ".yml": "YAML",
  80.     ".toml": "TOML", ".ini": "Config", ".cfg": "Config", ".conf": "Config", ".env": "Env",
  81.     ".md": "Markdown", ".markdown": "Markdown", ".rst": "reST", ".txt": "Text", ".ls": "Log",
  82.     ".sh": "Shell", ".bash": "Shell", ".zsh": "Shell", ".fish": "Shell",
  83.     ".bat": "Batch", ".cmd": "Batch", ".ps1": "PowerShell", ".psm1": "PowerShell",
  84.     ".c": "C", ".h": "C", ".cpp": "C++", ".hpp": "C++", ".cc": "C++", ".cxx": "C++",
  85.     ".java": "Java", ".kt": "Kotlin", ".kts": "Kotlin", ".scala": "Scala",
  86.     ".cs": "C#", ".vb": "VBScript", ".fs": "F#",
  87.     ".rb": "Ruby", ".rake": "Ruby", ".php": "PHP", ".phtml": "PHP",
  88.     ".sql": "SQL", ".pls": "SQL", ".r": "R", ".R": "R",
  89.     ".rs": "Rust", ".go": "Go", ".dart": "Dart", ".swift": "Swift",
  90.     ".lua": "Lua", ".pl": "Perl", ".pm": "Perl", ".tcl": "TCL",
  91.     ".graphql": "GraphQL", ".gql": "GraphQL",
  92.     ".log": "Log", ".csv": "CSV", ".tsv": "CSV", ".diff": "Diff", ".patch": "Diff",
  93.     ".ahk": "AutoHotKey", ".au3": "AutoIt",
  94.     ".m3u": "M3U", ".m3u8": "M3U",
  95.     ".mp3": "Audio", ".wav": "Audio", ".flac": "Audio", ".aac": "Audio",
  96.     ".ogg": "Audio", ".wma": "Audio", ".m4a": "Audio", ".alac": "Audio",
  97.     ".opus": "Audio", ".aiff": "Audio", ".aif": "Audio", ".ape": "Audio",
  98.     ".ac3": "Audio", ".amr": "Audio", ".au": "Audio", ".mid": "Audio",
  99.     ".midi": "Audio", ".mka": "Audio", ".mp2": "Audio", ".ra": "Audio",
  100.     ".ram": "Audio", ".snd": "Audio", ".wv": "Audio", ".dsd": "Audio",
  101.     ".mp4": "Video", ".avi": "Video", ".mkv": "Video", ".mov": "Video",
  102.     ".wmv": "Video", ".flv": "Video", ".webm": "Video", ".mpeg": "Video",
  103.     ".mpg": "Video", ".m4v": "Video", ".3gp": "Video", ".3g2": "Video",
  104.     ".asf": "Video", ".f4v": "Video", ".f4p": "Video", ".f4a": "Video",
  105.     ".f4b": "Video", ".m2ts": "Video", ".m2v": "Video", ".m4p": "Video",
  106.     ".mpe": "Video", ".mpv": "Video", ".mts": "Video", ".mxf": "Video",
  107.     ".nsv": "Video", ".ogv": "Video", ".qt": "Video", ".rm": "Video",
  108.     ".rmvb": "Video", ".roq": "Video", ".svi": "Video", ".vob": "Video",
  109.     ".yuv": "Video", ".divx": "Video", ".xvid": "Video", ".avchd": "Video",
  110.     ".gif": "Video", ".gifv": "Video", ".h264": "Video", ".hevc": "Video",
  111.     ".jpg": "Image", ".jpeg": "Image", ".png": "Image", ".gif": "Image",
  112.     ".bmp": "Image", ".svg": "Image", ".webp": "Image", ".ico": "Image",
  113.     ".tiff": "Image", ".tif": "Image", ".psd": "Image", ".raw": "Image",
  114.     ".cr2": "Image", ".nef": "Image", ".arw": "Image", ".dng": "Image",
  115.     ".heic": "Image", ".heif": "Image", ".avif": "Image", ".jxr": "Image",
  116.     ".pgm": "Image", ".ppm": "Image", ".pbm": "Image", ".pnm": "Image",
  117.     ".hdr": "Image", ".exr": "Image", ".j2k": "Image", ".jp2": "Image",
  118.     ".jpf": "Image", ".jpm": "Image", ".mj2": "Image", ".sgi": "Image",
  119.     ".tga": "Image", ".icns": "Image", ".eps": "Image", ".ai": "Image",
  120.     ".exe": "Executable", ".msi": "Installer", ".msu": "Update",
  121.     ".com": "Command", ".scr": "ScreenSaver",
  122. }
  123.  
  124. # ✅ Helper: Find interpreter with auto-detection
  125. def find_interpreter(names, common_paths):
  126.     for name in names:
  127.         try:
  128.             if shutil.which(name):
  129.                 return name
  130.         except Exception:
  131.             pass
  132.     if os.name == "nt":
  133.         for path in common_paths:
  134.             path = os.path.expandvars(path)
  135.             if os.path.isfile(path):
  136.                 return path
  137.             if not path.lower().endswith(".exe") and os.path.isfile(path + ".exe"):
  138.                 return path + ".exe"
  139.     return names[0] if names else None
  140.  
  141. # ✅ Build default interpreters dict with auto-detection
  142. def get_default_interpreters():
  143.     interpreters = {
  144.         "python": "python", "pythonw": "pythonw", "node": "node", "ts-node": "ts-node",
  145.         "bash": "bash", "zsh": "zsh", "fish": "fish", "ruby": "ruby", "php": "php", "perl": "perl",
  146.         "lua": "lua", "rscript": "Rscript", "tclsh": "tclsh", "cargo": "cargo", "go": "go",
  147.         "dart": "dart", "swift": "swift", "java": "java", "kotlin": "kotlin", "scala": "scala",
  148.         "dotnet": "dotnet", "sqlite3": "sqlite3", "sqlplus": "sqlplus",
  149.     }
  150.     if os.name == "nt":
  151.         ahk_paths = [r"C:\Program Files\AutoHotkey\AutoHotkey.exe", r"C:\Program Files (x86)\AutoHotkey\AutoHotkey.exe", r"%ProgramFiles%\AutoHotkey\AutoHotkey.exe", r"%ProgramFiles(x86)%\AutoHotkey\AutoHotkey.exe"]
  152.         interpreters["autohotkey"] = find_interpreter(["autohotkey", "AutoHotkey.exe"], ahk_paths)
  153.         au3_paths = [r"C:\Program Files\AutoIt3\AutoIt3.exe", r"C:\Program Files (x86)\AutoIt3\AutoIt3.exe", r"%ProgramFiles%\AutoIt3\AutoIt3.exe", r"%ProgramFiles(x86)%\AutoIt3\AutoIt3.exe"]
  154.         interpreters["autoit3"] = find_interpreter(["autoit3", "AutoIt3.exe"], au3_paths)
  155.         interpreters["powershell"] = "powershell"
  156.         interpreters["cmd"] = "cmd"
  157.         interpreters["mshta"] = "mshta"
  158.     return interpreters
  159.  
  160. # ✅ RUNNERS: Complete language executors - FIXED VERSION
  161. def build_runners():
  162.     runners = {}
  163.     runners.update({
  164.         ".py": ["python"], ".pyw": ["pythonw"], ".pyi": ["python"],
  165.         ".js": ["node"], ".jsx": ["node"], ".ts": ["ts-node"], ".tsx": ["ts-node"],
  166.         ".sh": ["bash"], ".bash": ["bash"], ".zsh": ["zsh"], ".fish": ["fish"],
  167.         ".bat": ["cmd", "/c"], ".cmd": ["cmd", "/c"],
  168.         ".ps1": ["powershell", "-ExecutionPolicy", "Bypass", "-File"],
  169.         ".psm1": ["powershell", "-ExecutionPolicy", "Bypass", "-File"],
  170.         ".hta": ["mshta"] if os.name == "nt" else ["echo", "HTA requires Windows"],
  171.         ".rb": ["ruby"], ".rake": ["ruby"], ".php": ["php"], ".phtml": ["php"],
  172.         ".pl": ["perl"], ".pm": ["perl"], ".lua": ["lua"],
  173.         ".r": ["Rscript"], ".R": ["Rscript"], ".tcl": ["tclsh"],
  174.         ".rs": ["cargo", "run"], ".go": ["go", "run"], ".dart": ["dart"], ".swift": ["swift"],
  175.         ".java": ["java"], ".kt": ["kotlin"], ".kts": ["kotlin", "-script"], ".scala": ["scala"],
  176.         ".cs": ["dotnet", "run"], ".vb": ["dotnet", "run"], ".fs": ["dotnet", "run"],
  177.         ".sql": ["sqlite3"], ".pls": ["sqlplus"],
  178.         ".ahk": ["autohotkey"], ".au3": ["autoit3"],
  179.     })
  180.     if os.name == "nt":
  181.         interpreters = get_default_interpreters()
  182.         if interpreters.get("autohotkey"):
  183.             runners[".ahk"] = [interpreters["autohotkey"]]
  184.         if interpreters.get("autoit3"):
  185.             runners[".au3"] = [interpreters["autoit3"]]
  186.         runners[".exe"] = ["start"]
  187.         runners[".msi"] = ["msiexec", "/i"]
  188.         runners[".msu"] = ["wusa"]
  189.         runners[".com"] = ["start"]
  190.         runners[".scr"] = ["start"]
  191.     else:
  192.         runners[".exe"] = ["wine"] if shutil.which("wine") else ["echo", "Requires Wine"]
  193.         runners[".msi"] = ["echo", "MSI requires Windows"]
  194.         runners[".msu"] = ["echo", "MSU requires Windows"]
  195.         runners[".com"] = ["echo", "COM requires Windows"]
  196.         runners[".scr"] = ["echo", "SCR requires Windows"]
  197.    
  198.     open_cmd = ["start"] if os.name == "nt" else (["open"] if sys.platform == "darwin" else ["xdg-open"])
  199.     for ext in [".html", ".htm", ".vue", ".svelte", ".json", ".json5", ".xml", ".yaml", ".yml", ".toml",
  200.                 ".ini", ".cfg", ".conf", ".env", ".md", ".markdown", ".rst", ".txt", ".log",
  201.                 ".csv", ".tsv", ".diff", ".patch", ".graphql", ".gql", ".ls"]:
  202.         runners[ext] = open_cmd
  203.     for ext in [".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a", ".alac", ".opus", ".aiff", ".aif", ".ape",
  204.                 ".ac3", ".amr", ".au", ".mid", ".midi", ".mka", ".mp2", ".ra", ".ram", ".snd", ".wv", ".dsd",
  205.                 ".mp4", ".avi", ".mkv", ".mov", ".wmv", ".flv", ".webm", ".mpeg", ".mpg", ".m4v", ".3gp", ".3g2",
  206.                 ".asf", ".f4v", ".f4p", ".f4a", ".f4b", ".m2ts", ".m2v", ".m4p", ".mpe", ".mpv", ".mts", ".mxf",
  207.                 ".nsv", ".ogv", ".qt", ".rm", ".rmvb", ".roq", ".svi", ".vob", ".yuv", ".divx", ".xvid", ".avchd",
  208.                 ".gif", ".gifv", ".h264", ".hevc", ".jpg", ".jpeg", ".png", ".bmp", ".svg", ".webp", ".ico",
  209.                 ".tiff", ".tif", ".psd", ".raw", ".cr2", ".nef", ".arw", ".dng", ".heic", ".heif", ".avif", ".jxr",
  210.                 ".pgm", ".ppm", ".pbm", ".pnm", ".hdr", ".exr", ".j2k", ".jp2", ".jpf", ".jpm", ".mj2", ".sgi",
  211.                 ".tga", ".icns", ".eps", ".ai", ".m3u", ".m3u8"]:
  212.         runners[ext] = open_cmd
  213.     for ext in [".css", ".scss", ".sass", ".less"]:
  214.         runners[ext] = ["echo", f"{ext.upper()} files need to be linked/compiled first."]
  215.     runners[".h"] = ["echo", "Header file - not directly executable"]
  216.     for ext in [".c", ".cpp", ".cc", ".cxx"]:
  217.         runners[ext] = ["echo", "C/C++ files need compilation first (gcc/clang)"]
  218.     for ext in [".java", ".kt", ".scala"]:
  219.         runners[ext] = ["echo", "Java/Kotlin/Scala need compilation first"]
  220.     return runners
  221.  
  222. RUNNERS = build_runners()
  223.  
  224. # ✅ SYNTAX_PATTERNS (Code highlighting for main languages)
  225. SYNTAX_PATTERNS = {
  226.     "Python": [
  227.         (r'\b(def|class|import|from|if|else|elif|for|while|try|except|finally|return|yield|with|as|and|or|not|in|is|lambda|pass|break|continue|raise|assert|del|global|nonlocal|True|False|None|async|await|match|case|type)\b', "keyword"),
  228.         (r'(#.*)$', "comment"),
  229.         (r'("""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')', "string"),
  230.         (r'\b(\d+\.?\d*(?:[eE][+-]?\d+)?)\b', "number"),
  231.         (r'(@\w+)', "decorator"), (r'\b(self|cls)\b', "special"),
  232.     ],
  233.     "JavaScript": [
  234.         (r'\b(const|let|var|function|return|if|else|for|while|do|switch|case|break|continue|class|extends|new|this|super|import|export|default|async|await|try|catch|finally|throw|typeof|instanceof|in|of|delete|void|yield|true|false|null|undefined)\b', "keyword"),
  235.         (r'(//.*$|/\*[\s\S]*?\*/)', "comment"),
  236.         (r'(`[\s\S]*?`|"(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')', "string"),
  237.         (r'\b(\d+\.?\d*(?:[eE][+-]?\d+)?)\b', "number"),
  238.     ],
  239.     "HTML": [
  240.         (r'(</?[a-zA-Z][a-zA-Z0-9\-]*\s*[^>]*?>)', "tag"),
  241.         (r'\b([a-zA-Z\-:]+)\s*=', "attribute"),
  242.         (r'("(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')', "string"),
  243.         (r'(<!--[\s\S]*?-->)', "comment"),
  244.     ],
  245.     "CSS": [(r'([a-zA-Z\-]+)\s*:', "property"), (r'#[0-9a-fA-F]{3,8}\b', "color"), (r'("(?:[^"\\]|\\.)*"|\'(?:[^\'\\]|\\.)*\')', "string"), (r'(/\*[\s\S]*?\*/)', "comment")],
  246.     "JSON": [(r'("(?:[^"\\]|\\.)*")\s*:', "key"), (r'("(?:[^"\\]|\\.)*")', "string"), (r'\b(true|false|null)\b', "keyword"), (r'\b(-?\d+\.?\d*(?:[eE][+-]?\d+)?)\b', "number")],
  247.     "Shell": [(r'\b(if|then|else|elif|fi|case|esac|for|while|until|do|done|in|function|break|continue|return|exit|export)\b', "keyword"), (r'(#.*$)', "comment"), (r'("(?:[^"\\]|\\.)*"|\'[^\']*\'|`[^`]*`)', "string"), (r'(\$[a-zA-Z_][a-zA-Z0-9]*|\$\{[^}]*\})', "variable")],
  248.     "Batch": [(r'\b(@echo|echo|call|goto|if|else|for|in|do|set|setlocal|endlocal|exit|rem|::|pause)\b', "keyword"), (r'(rem\s.*$|::.*$)', "comment"), (r'(%[^%]+%|![^!]+!)', "variable")],
  249.     "PowerShell": [(r'\b(function|if|else|elseif|for|foreach|while|switch|break|continue|return|exit|throw|try|catch|finally)\b', "keyword"), (r'(#.*$|<#[\s\S]*?#>)', "comment"), (r'(\$(?:[a-zA-Z_][a-zA-Z0-9]*|\{[^}]*\}))', "variable")],
  250. }
  251.  
  252. class SafeSettings:
  253.     def __init__(self):
  254.         self.data = self._load()
  255.     def _load(self):
  256.         defaults = {"theme": "dark", "font_size": 11, "font_family": "Consolas", "auto_save": True, "auto_save_interval": 30, "word_wrap": False, "recent": [], "interpreters": get_default_interpreters()}
  257.         if SETTINGS_FILE.exists():
  258.             try:
  259.                 with open(SETTINGS_FILE, "r", encoding="utf-8") as f:
  260.                     loaded = json.load(f)
  261.                     if "interpreters" in loaded:
  262.                         defaults["interpreters"].update(loaded["interpreters"])
  263.                         del loaded["interpreters"]
  264.                     return {**defaults, **loaded}
  265.             except Exception:
  266.                 pass
  267.         return defaults
  268.     def save(self):
  269.         try:
  270.             SETTINGS_FILE.parent.mkdir(parents=True, exist_ok=True)
  271.             with open(SETTINGS_FILE, "w", encoding="utf-8") as f:
  272.                 json.dump(self.data, f, indent=2, ensure_ascii=False)
  273.         except Exception:
  274.             pass
  275.     def get(self, k, d=None):
  276.         return self.data.get(k, d)
  277.     def set(self, k, v):
  278.         self.data[k] = v
  279.         self.save()
  280.  
  281. class LineNumberedText:
  282.     def __init__(self, master, font_name, font_size, theme="dark"):
  283.         self.master = master
  284.         self.font_name = font_name
  285.         self.font_size = font_size
  286.         self.theme = theme
  287.         if theme == "dark":
  288.             self.bg, self.fg, self.line_bg, self.line_fg, self.cursor, self.highlight_bg = "#1e1e1e", "#d4d4d4", "#2d2d2d", "#888888", "#ffffff", "#333344"
  289.         else:
  290.             self.bg, self.fg, self.line_bg, self.line_fg, self.cursor, self.highlight_bg = "#ffffff", "#000000", "#f0f0f0", "#666666", "#000000", "#e0e0e0"
  291.         self.frame = Frame(master, bg=self.bg)
  292.         self.line_canvas = Canvas(self.frame, bg=self.line_bg, width=50, highlightthickness=0)
  293.         self.line_canvas.pack(side=LEFT, fill=Y)
  294.         self.code = Text(self.frame, bg=self.bg, fg=self.fg, insertbackground=self.cursor, font=(font_name, font_size), wrap="none", undo=True, autoseparators=True, maxundo=-1, borderwidth=0, highlightthickness=0, padx=10, pady=5, yscrollcommand=self._on_yscroll)
  295.         self.code.pack(side=LEFT, fill=BOTH, expand=True)
  296.         self.vscroll = ttk.Scrollbar(master, orient=VERTICAL)
  297.         self.vscroll.pack(side=RIGHT, fill=Y)
  298.         self.vscroll.config(command=self._shared_scroll)
  299.         self.code.config(yscrollcommand=self.vscroll.set)
  300.         for ev, act in [("<MouseWheel>", 1), ("<Button-4>", -1), ("<Button-5>", 1), ("<KeyRelease>", 0), ("<ButtonRelease-1>", 0)]:
  301.             self.code.bind(ev, self._on_change if act == 0 else self._on_mousewheel, add="+")
  302.         self.code.bind("<<Modified>>", self._on_modified, add="+")
  303.         self.code.tag_config("current_line", background=self.highlight_bg)
  304.         self.line_canvas.bind("<Button-1>", lambda e: "break")
  305.         self._draw_line_numbers()
  306.         self._highlight_current_line()
  307.     def _shared_scroll(self, *args):
  308.         self.code.yview(*args)
  309.         self.line_canvas.yview(*args)
  310.         self._draw_line_numbers()
  311.     def _on_yscroll(self, first, last):
  312.         self.vscroll.set(first, last)
  313.     def _on_mousewheel(self, event):
  314.         delta = -1 if (event.num == 5 or event.delta == -120) else 1
  315.         self._shared_scroll("scroll", -delta, "units")
  316.         return "break"
  317.     def _on_change(self, event=None):
  318.         self._draw_line_numbers()
  319.         self._highlight_current_line()
  320.     def _on_modified(self, event=None):
  321.         if self.code.edit_modified():
  322.             self._draw_line_numbers()
  323.             self.code.edit_modified(False)
  324.     def _draw_line_numbers(self):
  325.         try:
  326.             self.line_canvas.delete("all")
  327.             first_visible = int(float(self.code.index("@0,0").split(".")[0]))
  328.             last_visible = int(float(self.code.index(f"@0,{self.code.winfo_height()}").split(".")[0])) + 1
  329.             total_lines = int(self.code.index("end-1c").split(".")[0])
  330.             width_needed = max(len(str(total_lines)) * 8 + 20, 50)
  331.             if width_needed != self.line_canvas.cget("width"):
  332.                 self.line_canvas.config(width=min(width_needed, 80))
  333.             self.code.update_idletasks()
  334.             line_info = self.code.dlineinfo("1.0")
  335.             line_height = line_info[3] if line_info else self.font_size + 4
  336.             y_offset = 5
  337.             for ln in range(first_visible, min(last_visible, total_lines + 1)):
  338.                 y_pos = ((ln - 1) * line_height) - (self.code.yview()[0] * (total_lines * line_height)) + y_offset
  339.                 cur_line = int(self.code.index(INSERT).split(".")[0])
  340.                 color, style = ("#00ff00", "bold") if ln == cur_line else (self.line_fg, "normal")
  341.                 self.line_canvas.create_text(40, y_pos, text=str(ln), fill=color, font=(self.font_name, self.font_size, style), anchor="e")
  342.             self.line_canvas.config(scrollregion=(0, 0, 50, total_lines * line_height + 10))
  343.         except Exception:
  344.             pass
  345.     def _highlight_current_line(self, event=None):
  346.         try:
  347.             self.code.tag_remove("current_line", "1.0", END)
  348.             ln = self.code.index(INSERT).split(".")[0]
  349.             self.code.tag_add("current_line", f"{ln}.0", f"{ln}.end")
  350.             self._draw_line_numbers()
  351.         except Exception:
  352.             pass
  353.     def see(self, index):
  354.         self.code.see(index)
  355.         self.line_canvas.yview("moveto", self.code.yview()[0])
  356.         self._draw_line_numbers()
  357.     def get_frame(self):
  358.         return self.frame
  359.     def get_text(self):
  360.         return self.code
  361.     def insert(self, i, t):
  362.         return self.code.insert(i, t)
  363.     def get(self, s, e=None):
  364.         return self.code.get(s, e)
  365.     def index(self, i):
  366.         return self.code.index(i)
  367.     def tag_config(self, t, **k):
  368.         return self.code.tag_config(t, **k)
  369.     def tag_add(self, t, s, e=None):
  370.         return self.code.tag_add(t, s, e)
  371.     def tag_remove(self, t, s, e=None):
  372.         return self.code.tag_remove(t, s, e)
  373.     def tag_names(self, i=None):
  374.         return self.code.tag_names(i)
  375.     def tag_ranges(self, t):
  376.         return self.code.tag_ranges(t)
  377.     def search(self, p, s, **k):
  378.         return self.code.search(p, s, **k)
  379.     def compare(self, i1, o, i2):
  380.         return self.code.compare(i1, o, i2)
  381.     def delete(self, s, e=None):
  382.         return self.code.delete(s, e)
  383.     def mark_set(self, m, i):
  384.         return self.code.mark_set(m, i)
  385.     def mark_get(self, m):
  386.         return self.code.mark_get(m)
  387.     def focus_set(self):
  388.         return self.code.focus_set()
  389.     def bind(self, s, f, a=None):
  390.         return self.code.bind(s, f, a)
  391.     def config(self, **k):
  392.         return self.code.config(**k)
  393.  
  394. class UltraProMaxV3:
  395.     def __init__(self, root):
  396.         self.root = root
  397.         self.root.title(APP_NAME)
  398.         self.root.geometry("1250x750")
  399.         self.root.minsize(800, 500)
  400.         if os.name == "nt" and os.path.exists(APP_ICON):
  401.             try:
  402.                 self.root.iconbitmap(APP_ICON)
  403.             except Exception:
  404.                 pass
  405.         if os.name == "nt":
  406.             try:
  407.                 from ctypes import windll
  408.                 windll.shcore.SetProcessDpiAwareness(1)
  409.             except Exception:
  410.                 pass
  411.         self.settings = SafeSettings()
  412.         self.tabs = {}
  413.         self.tab_order = []
  414.         self.current_file = None
  415.         self.run_proc = None
  416.         self.auto_timer = None
  417.         self.hl_timer = None
  418.         self.log_q = queue.Queue()
  419.         self.recent = self.settings.get("recent", [])
  420.         self.file_list = []
  421.         self.search_var = None
  422.         Path(TEMP_DIR).mkdir(parents=True, exist_ok=True)
  423.         self._setup_ui()
  424.         self._setup_bindings()
  425.         self._start_auto_save()
  426.         self.root.after(50, self._poll_console)
  427.         self.root.protocol("WM_DELETE_WINDOW", self.on_close)
  428.  
  429.     def _setup_ui(self):
  430.         menubar = Menu(self.root, tearoff=0)
  431.         self.root.config(menu=menubar)
  432.         file_menu = Menu(menubar, tearoff=0)
  433.         for txt, cmd, acc in [("Open File", self.open_file, "Ctrl+O"), ("Open Archive", self.open_archive, "Ctrl+Shift+O"), ("New Script", self.new_script, "Ctrl+N"), ("Save", self.save, "Ctrl+S"), ("Save As...", self.save_as, "Ctrl+Shift+S"), ("Export to ZIP", self.export_to_zip, "Ctrl+E"), ("Exit", self.on_close, "Alt+F4")]:
  434.             file_menu.add_command(label=txt, command=cmd, accelerator=acc)
  435.         menubar.add_cascade(label="File", menu=file_menu)
  436.         edit_menu = Menu(menubar, tearoff=0)
  437.         for txt, cmd, acc in [("Find & Replace", self.find_replace, "Ctrl+F"), ("Global Search", self.global_search, "Ctrl+Shift+F"), ("Toggle Word Wrap", self.toggle_wrap, "Ctrl+Alt+W")]:
  438.             edit_menu.add_command(label=txt, command=cmd, accelerator=acc)
  439.         edit_menu.add_separator()
  440.         for txt, key in [("✂️ Cut", "<<Cut>>"), ("Copy", "<<Copy>>"), ("Paste", "<<Paste>>")]:
  441.             edit_menu.add_command(label=txt, command=lambda k=key: self.root.focus_get().event_generate(k), accelerator=f"Ctrl+{key[-2]}")
  442.         menubar.add_cascade(label="Edit", menu=edit_menu)
  443.         tab_menu = Menu(menubar, tearoff=0)
  444.         for txt, cmd, acc in [("✏️ Rename Tab", self.rename_tab, "Ctrl+Shift+R"), ("❌ Close Tab", self.close_tab, "Ctrl+W"), ("Close Others", self.close_other_tabs, "Ctrl+Shift+W"), ("Close All", self.close_all_tabs, "Ctrl+Shift+Q")]:
  445.             tab_menu.add_command(label=txt, command=cmd, accelerator=acc)
  446.         menubar.add_cascade(label="Tab", menu=tab_menu)
  447.         run_menu = Menu(menubar, tearoff=0)
  448.         for txt, cmd, acc in [("▶ Run/Open", self.run_script, "Ctrl+R"), ("⏹ Stop Process", self.stop_run, "Ctrl+Shift+R"), ("❌ Clear Console", self.clear_console, "")]:
  449.             run_menu.add_command(label=txt, command=cmd, accelerator=acc)
  450.         menubar.add_cascade(label="Run", menu=run_menu)
  451.         help_menu = Menu(menubar, tearoff=0)
  452.         help_menu.add_command(label="⚙ Settings", command=self.show_settings)
  453.         help_menu.add_command(label="ℹ About", command=self.about)
  454.         menubar.add_cascade(label="Help", menu=help_menu)
  455.        
  456.         toolbar = Frame(self.root, bg="#2d2d2d", height=38)
  457.         toolbar.pack(fill=X, side=TOP, padx=4, pady=2)
  458.         for t, c in [("Open", self.open_file), ("New", self.new_script), ("Save", self.save), ("OpenZip", self.open_archive), ("ExpZip", self.export_to_zip), ("Rename", self.rename_tab), ("Close", self.close_tab), ("▶ Run", self.run_script), ("Stop", self.stop_run), ("Find", self.find_replace), ("⚙ Set", self.show_settings)]:
  459.             ttk.Button(toolbar, text=t, command=c, width=8).pack(side=LEFT, padx=2)
  460.            
  461.         self.status = ttk.Label(self.root, text="Ready | UTF-8 | Ln 1, Col 1 | Wrap: OFF", background="#007acc", foreground="white", anchor=W, font=("Segoe UI", 9))
  462.         self.status.pack(fill=X, side=BOTTOM)
  463.        
  464.         paned = ttk.PanedWindow(self.root, orient=HORIZONTAL)
  465.         paned.pack(fill=BOTH, expand=True, padx=2, pady=2)
  466.         left_panel = ttk.Frame(paned, width=220)
  467.         paned.add(left_panel, weight=1)
  468.         search_frame = ttk.Frame(left_panel)
  469.         search_frame.pack(fill=X, padx=4, pady=(4, 2))
  470.         ttk.Label(search_frame, text="🔍").pack(side=LEFT, padx=(0, 2))
  471.         self.search_var = StringVar()
  472.         self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=20)
  473.         self.search_entry.pack(side=LEFT, fill=X, expand=True)
  474.         self.search_entry.bind("<KeyRelease>", self._filter_file_list)
  475.         ttk.Button(search_frame, text="✕", width=3, command=self._clear_search).pack(side=LEFT, padx=(2, 0))
  476.         tree_frame = ttk.Frame(left_panel)
  477.         tree_frame.pack(fill=BOTH, expand=True, padx=4, pady=(0, 4))
  478.         tree_scroll = ttk.Scrollbar(tree_frame)
  479.         tree_scroll.pack(side=RIGHT, fill=Y)
  480.         self.tree = ttk.Treeview(tree_frame, show="tree", selectmode="browse", yscrollcommand=tree_scroll.set)
  481.         self.tree.pack(fill=BOTH, expand=True)
  482.         tree_scroll.config(command=self.tree.yview)
  483.         self.tree.bind("<<TreeviewSelect>>", self._on_tree_select, add="+")
  484.        
  485.         right_panel = ttk.Frame(paned)
  486.         paned.add(right_panel, weight=3)
  487.         self.notebook = ttk.Notebook(right_panel)
  488.         self.notebook.pack(fill=BOTH, expand=True)
  489.         self.notebook.bind("<<NotebookTabChanged>>", self._on_tab_changed)
  490.         self.tab_menu_popup = Menu(self.root, tearoff=0)
  491.         self.tab_menu_popup.add_command(label="✏️ Rename", command=self.rename_tab)
  492.         self.tab_menu_popup.add_command(label="❌ Close", command=self.close_tab)
  493.         self.tab_menu_popup.add_command(label="🗑️ Close Others", command=self.close_other_tabs)
  494.         self.tab_menu_popup.add_command(label="🧹 Close All", command=self.close_all_tabs)
  495.         self.notebook.bind("<Button-3>", self._show_tab_context_menu)
  496.        
  497.         console_frame = ttk.Frame(right_panel)
  498.         console_frame.pack(fill=X, padx=4, pady=(4, 0))
  499.         console_scroll = ttk.Scrollbar(console_frame)
  500.         console_scroll.pack(side=RIGHT, fill=Y)
  501.         self.console = Text(console_frame, height=10, bg="#0c0c0c", fg="#00ff00", font=(self.settings.get("font_family", "Consolas"), self.settings.get("font_size", 11)), state=DISABLED, wrap="word", yscrollcommand=console_scroll.set)
  502.         self.console.pack(fill=BOTH, expand=True)
  503.         console_scroll.config(command=self.console.yview)
  504.  
  505.         # ✅ NEW: CMD Command Input Field + Run Button
  506.         cmd_frame = Frame(right_panel, bg="#2d2d2d", height=32)
  507.         cmd_frame.pack(fill=X, padx=4, pady=(2, 2))
  508.         self.cmd_var = StringVar()
  509.         self.cmd_entry = ttk.Entry(cmd_frame, textvariable=self.cmd_var, width=60, font=("Consolas", 10))
  510.         self.cmd_entry.pack(side=LEFT, fill=X, expand=True, padx=(0, 5))
  511.         self.cmd_entry.bind("<Return>", lambda e: self.run_custom_command())
  512.         ttk.Button(cmd_frame, text="▶ Run Command", command=self.run_custom_command, width=14).pack(side=LEFT)
  513.         ttk.Label(cmd_frame, text="💡 Type: dir / ls / python -V / ping 8.8.8.8 -n 2 | Press Enter", background="#2d2d2d", foreground="#888", font=("Segoe UI", 8)).pack(side=LEFT, padx=(8, 0))
  514.  
  515.     # ✅ NEW: Thread-Safe Password Prompt Helper
  516.     def _ask_password_threadsafe(self, prompt_title, prompt_msg):
  517.         """Ask for password on main thread - thread-safe wrapper"""
  518.         result = {"password": None, "cancelled": False}
  519.        
  520.         def _show_dialog():
  521.             if not self.root.winfo_exists():
  522.                 result["cancelled"] = True
  523.                 return
  524.             pwd = simpledialog.askstring(prompt_title, prompt_msg, parent=self.root, show='*')
  525.             if pwd is None:
  526.                 result["cancelled"] = True
  527.             else:
  528.                 result["password"] = pwd.encode('utf-8') if pwd else b""
  529.        
  530.         # Schedule on main thread and wait
  531.         self.root.after(0, _show_dialog)
  532.         # Simple busy-wait (safe for short dialogs)
  533.         while result["password"] is None and not result["cancelled"]:
  534.             try:
  535.                 self.root.update_idletasks()
  536.                 time.sleep(0.05)
  537.             except Exception:
  538.                 break
  539.         return result
  540.  
  541.     def _show_tab_context_menu(self, event):
  542.         try:
  543.             tab_id = self.notebook.index(f"@{event.x},{event.y}")
  544.             if tab_id is not None:
  545.                 self.notebook.select(tab_id)
  546.                 self._on_tab_changed(None)
  547.                 self.tab_menu_popup.post(event.x_root, event.y_root)
  548.         except Exception:
  549.             pass
  550.     def _clear_search(self):
  551.         self.search_var.set("")
  552.         self._populate_file_tree()
  553.     def _filter_file_list(self, event=None):
  554.         term = self.search_var.get().lower()
  555.         self.tree.delete(*self.tree.get_children())
  556.         for fp in self.file_list:
  557.             if not term or term in os.path.basename(fp).lower():
  558.                 self.tree.insert("", "end", iid=fp, text=f"{self._get_file_icon(Path(fp).suffix.lower())} {os.path.basename(fp)}")
  559.     def _get_file_icon(self, ext):
  560.         if ext in [".mp3", ".wav", ".flac", ".aac", ".ogg", ".wma", ".m4a", ".alac", ".opus"]:
  561.             return "🎵"
  562.         if ext in [".mp4", ".avi", ".mkv", ".mov", ".wmv", ".webm", ".mpeg", ".flv"]:
  563.             return "🎬"
  564.         if ext in [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".svg", ".webp", ".ico", ".tiff"]:
  565.             return "🖼️"
  566.         if ext in [".exe", ".msi", ".msu", ".com", ".scr"]:
  567.             return "🚀"
  568.         if ext in [".py", ".js", ".html", ".css", ".java", ".cpp", ".c", ".php", ".rb"]:
  569.             return "📄"
  570.         return "📁"
  571.     def _on_tree_select(self, event):
  572.         try:
  573.             sel = self.tree.selection()
  574.             if sel and os.path.isfile(sel[0]):
  575.                 self._open_file_safe(sel[0])
  576.         except Exception:
  577.             pass
  578.     def _on_tab_changed(self, event):
  579.         self._update_current_file()
  580.         self._update_status()
  581.     def _setup_bindings(self):
  582.         for key, cb in [("<Control-o>", self.open_file), ("<Control-Shift-O>", self.open_archive), ("<Control-n>", self.new_script), ("<Control-s>", self.save), ("<Control-Shift-S>", self.save_as), ("<Control-e>", self.export_to_zip), ("<Control-f>", self.find_replace), ("<Control-Shift-F>", self.global_search), ("<Control-r>", self.run_script), ("<Control-Shift-R>", self.stop_run), ("<Control-Alt-W>", self.toggle_wrap), ("<Control-q>", self.on_close), ("<Control-Shift-r>", self.rename_tab), ("<Control-w>", self.close_tab), ("<Control-Shift-w>", self.close_other_tabs), ("<Control-Shift-q>", self.close_all_tabs)]:
  583.             self.root.bind(key, lambda e, callback=cb: callback(), add="+")
  584.     def _start_auto_save(self):
  585.         if self.settings.get("auto_save", True):
  586.             self.auto_timer = self.root.after(self.settings.get("auto_save_interval", 30) * 1000, self._auto_save)
  587.     def _auto_save(self):
  588.         try:
  589.             for fp, td in list(self.tabs.items()):
  590.                 if td.get("modified") and os.path.exists(fp):
  591.                     try:
  592.                         with open(fp, "w", encoding="utf-8") as f:
  593.                             f.write(td["text"].get("1.0", END))
  594.                         td["modified"] = False
  595.                     except IOError:
  596.                         self.log(f"⚠ Auto-save failed: {os.path.basename(fp)}")
  597.         except RuntimeError:
  598.             pass
  599.         finally:
  600.             if self.settings.get("auto_save", True):
  601.                 self.auto_timer = self.root.after(self.settings.get("auto_save_interval", 30) * 1000, self._auto_save)
  602.     def _open_file_safe(self, fp):
  603.         if not os.path.exists(fp):
  604.             return
  605.         if fp in self.tabs:
  606.             self.notebook.select(self.tabs[fp]["frame"])
  607.         else:
  608.             self.open_tab(fp)
  609.     def open_tab(self, file_path):
  610.         try:
  611.             ext = Path(file_path).suffix.lower()
  612.             lang = EXT_LANG.get(ext, "Text")
  613.             theme = self.settings.get("theme", "dark")
  614.             font_fam, font_sz = self.settings.get("font_family", "Consolas"), self.settings.get("font_size", 11)
  615.             if ext in [".mp3", ".wav", ".flac", ".mp4", ".avi", ".mkv", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".exe", ".msi", ".msu", ".com", ".scr"]:
  616.                 self._open_with_default_app(file_path)
  617.                 return
  618.             ln_text = LineNumberedText(self.notebook, font_fam, font_sz, theme)
  619.             frame, text_widget = ln_text.get_frame(), ln_text.get_text()
  620.             with open(file_path, "r", encoding="utf-8", errors="replace") as f:
  621.                 content = f.read()
  622.             if len(content) > 500000:
  623.                 self.log(f"⚠️ Large file ({len(content)} chars). Opening with default app.")
  624.                 self._open_with_default_app(file_path)
  625.                 return
  626.             text_widget.insert("1.0", content)
  627.             tab_name = f" {Path(file_path).name} "
  628.             tab_idx = self.notebook.add(frame, text=tab_name)
  629.             self.notebook.select(frame)
  630.             if file_path not in self.tab_order:
  631.                 self.tab_order.append(file_path)
  632.             self.tabs[file_path] = {"frame": frame, "text": text_widget, "lang": lang, "modified": False, "encoding": "utf-8", "original_name": Path(file_path).name, "tab_index": tab_idx}
  633.             self.current_file = file_path
  634.             text_widget.bind("<KeyRelease>", lambda e, p=file_path: self._on_text_change(p), add="+")
  635.             text_widget.bind("<ButtonRelease-1>", lambda e: self._update_status(), add="+")
  636.             self._schedule_highlight(file_path)
  637.             self._update_status()
  638.             self._add_recent(file_path)
  639.         except Exception as e:
  640.             self.log(f"❌ Error opening {file_path}: {e}")
  641.     def _open_with_default_app(self, file_path):
  642.         try:
  643.             if os.name == "nt":
  644.                 os.startfile(file_path)
  645.             elif sys.platform == "darwin":
  646.                 subprocess.run(["open", file_path])
  647.             else:
  648.                 subprocess.run(["xdg-open", file_path])
  649.             self.log(f"▶ Opened: {Path(file_path).name}")
  650.         except Exception as e:
  651.             self.log(f"❌ Open failed: {e}")
  652.             messagebox.showerror("Open Error", str(e))
  653.     def _on_text_change(self, fp):
  654.         if fp in self.tabs:
  655.             self.tabs[fp]["modified"] = True
  656.             name = Path(fp).name
  657.             for i in range(self.notebook.index("end")):
  658.                 if self.notebook.tab(i, "text").strip() == name:
  659.                     self.notebook.tab(i, text=f" *{name} ")
  660.                     break
  661.             self._schedule_highlight(fp)
  662.     def _schedule_highlight(self, fp):
  663.         if fp not in self.tabs:
  664.             return
  665.         if self.hl_timer:
  666.             try:
  667.                 self.root.after_cancel(self.hl_timer)
  668.             except Exception:
  669.                 pass
  670.         self.hl_timer = self.root.after(HIGHLIGHT_DELAY, lambda p=fp: self._apply_syntax_highlighting(p))
  671.     def _apply_syntax_highlighting(self, fp):
  672.         if fp not in self.tabs:
  673.             return
  674.         td, tw, lang = self.tabs[fp], self.tabs[fp]["text"], self.tabs[fp]["lang"]
  675.         if lang not in SYNTAX_PATTERNS:
  676.             return
  677.         try:
  678.             for tag in tw.tag_names():
  679.                 if tag not in ("current_line", "sel", "find_highlight"):
  680.                     tw.tag_delete(tag)
  681.             styles = {"keyword": {"foreground": "#569cd6"}, "string": {"foreground": "#ce9178"}, "comment": {"foreground": "#6a9955"}, "number": {"foreground": "#b5cea8"}, "decorator": {"foreground": "#dcdcaa"}, "special": {"foreground": "#9cdcfe"}, "class": {"foreground": "#4ec9b0"}, "tag": {"foreground": "#569cd6"}, "attribute": {"foreground": "#9cdcfe"}, "property": {"foreground": "#9cdcfe"}, "key": {"foreground": "#9cdcfe"}, "constant": {"foreground": "#4fc1ff"}, "variable": {"foreground": "#9cdcfe"}, "header": {"foreground": "#569cd6", "font": ("", "", "bold")}}
  682.             for tag, style in styles.items():
  683.                 try:
  684.                     tw.tag_config(tag, **style)
  685.                 except Exception:
  686.                     pass
  687.             content = tw.get("1.0", END)
  688.             for pat_data in SYNTAX_PATTERNS[lang]:
  689.                 pattern, tag_name = pat_data[:2]
  690.                 flags = pat_data[2] if len(pat_data) == 3 else 0
  691.                 try:
  692.                     for m in re.finditer(pattern, content, flags):
  693.                         tw.tag_add(tag_name, f"1.0+{m.start()}c", f"1.0+{m.end()}c")
  694.                 except Exception:
  695.                     continue
  696.         except Exception:
  697.             pass
  698.     def _update_current_file(self):
  699.         try:
  700.             cur = self.notebook.select()
  701.             if cur:
  702.                 for p, d in self.tabs.items():
  703.                     if str(d["frame"]) == cur:
  704.                         self.current_file = p
  705.                         return
  706.             self.current_file = None
  707.         except Exception:
  708.             self.current_file = None
  709.     def _update_status(self):
  710.         try:
  711.             if not self.current_file or self.current_file not in self.tabs:
  712.                 self.status.config(text="Ready | No file open")
  713.                 return
  714.             td, tw = self.tabs[self.current_file], self.tabs[self.current_file]["text"]
  715.             line, col = map(int, tw.index(INSERT).split("."))
  716.             col += 1
  717.             name = Path(self.current_file).name
  718.             lang = td.get("lang", "Text")
  719.             mod = "*" if td.get("modified") else ""
  720.             wrap = "ON" if self.settings.get("word_wrap") else "OFF"
  721.             theme = self.settings.get("theme", "dark").upper()
  722.             self.status.config(text=f"{mod}{name} | {lang} | UTF-8 | Ln {line}, Col {col} | Wrap: {wrap} | Theme: {theme}")
  723.         except Exception:
  724.             self.status.config(text="Ready")
  725.     def _add_recent(self, fp):
  726.         if fp in self.recent:
  727.             self.recent.remove(fp)
  728.         self.recent.insert(0, fp)
  729.         self.recent = self.recent[:RECENT_MAX]
  730.         self.settings.set("recent", self.recent)
  731.     def open_file(self):
  732.         fp = filedialog.askopenfilename(title="Open File", filetypes=[("All Files", "*.*"), ("Python", "*.py *.pyw"), ("Web", "*.js *.jsx *.ts *.tsx *.html *.htm *.css *.vue"), ("Data", "*.json *.xml *.yaml *.yml *.csv"), ("C/C++", "*.c *.h *.cpp *.hpp"), ("Shell", "*.sh *.bash *.bat *.ps1"), ("Other Code", "*.rb *.php *.sql *.rs *.go *.java"), ("Text", "*.txt *.md *.log"), ("🎵 Audio", "*.mp3 *.wav *.flac *.aac *.ogg *.wma *.m4a"), ("🎬 Video", "*.mp4 *.avi *.mkv *.mov *.wmv *.webm *.mpeg"), ("🖼️ Images", "*.jpg *.jpeg *.png *.gif *.bmp *.svg *.webp *.ico"), ("🚀 Executables", "*.exe *.msi *.msu *.com *.scr")])
  733.         if fp:
  734.             self.open_tab(fp)
  735.     def save_as(self):
  736.         if not self.current_file or self.current_file not in self.tabs:
  737.             return
  738.         ext = Path(self.current_file).suffix.lower()
  739.         ft = [("All Files", "*.*")]
  740.         if ext in EXT_LANG:
  741.             ft.insert(0, (EXT_LANG[ext], f"*{ext}"))
  742.         fp = filedialog.asksaveasfilename(title="Save As", initialfile=Path(self.current_file).name, defaultextension=ext if ext else ".txt", filetypes=ft)
  743.         if fp:
  744.             old = self.current_file
  745.             self.current_file = fp
  746.             self.save()
  747.             if old in self.tabs:
  748.                 self.tabs[fp] = self.tabs.pop(old)
  749.             if old in self.tab_order:
  750.                 self.tab_order[self.tab_order.index(old)] = fp
  751.             self._add_recent(fp)
  752.     def save(self):
  753.         if not self.current_file or self.current_file not in self.tabs:
  754.             return
  755.         try:
  756.             td = self.tabs[self.current_file]
  757.             with open(self.current_file, "w", encoding="utf-8") as f:
  758.                 f.write(td["text"].get("1.0", END))
  759.             td["modified"] = False
  760.             name = Path(self.current_file).name
  761.             for i in range(self.notebook.index("end")):
  762.                 if self.notebook.tab(i, "text").strip() == name:
  763.                     self.notebook.tab(i, text=f" {name} ")
  764.                     break
  765.             self.log(f"💾 Saved: {name}")
  766.             self._update_status()
  767.         except IOError as e:
  768.             self.log(f"❌ Save failed: {e}")
  769.             messagebox.showerror("Save Error", f"Could not save:\n{e}")
  770.     def rename_tab(self):
  771.         """✅ FIXED: Complete if statement & proper tab data handling"""
  772.         if not self.current_file or self.current_file not in self.tabs:
  773.             messagebox.showinfo("Rename", "No file open")
  774.             return
  775.         td = self.tabs[self.current_file]
  776.         cur_name = Path(self.current_file).name
  777.         new_name = simpledialog.askstring("Rename Tab", f"New name for:\n{cur_name}", initialvalue=cur_name, parent=self.root)
  778.         if not new_name or new_name == cur_name:
  779.             return
  780.         if Path(new_name).suffix.lower() not in EXT_LANG:
  781.             messagebox.showwarning("Invalid", f"Extension '{Path(new_name).suffix}' not supported")
  782.             return
  783.         try:
  784.             if os.path.exists(self.current_file):
  785.                 new_path = os.path.join(os.path.dirname(self.current_file), new_name)
  786.                 if os.path.exists(new_path) and new_path != self.current_file:
  787.                     if not messagebox.askyesno("Exists", f"'{new_name}' exists. Overwrite?"):
  788.                         return
  789.                 os.rename(self.current_file, new_path)
  790.                 self.current_file = new_path
  791.             if self.current_file in self.tabs:
  792.                 old_data = self.tabs.pop(self.current_file, None)
  793.                 if old_data is not None:
  794.                     self.tabs[self.current_file] = old_data
  795.             self.notebook.tab(td["frame"], text=f" {new_name} ")
  796.             td["modified"] = False
  797.             td["original_name"] = new_name
  798.             self.log(f"✏️ Renamed: {cur_name} → {new_name}")
  799.             self._update_status()
  800.         except Exception as e:
  801.             self.log(f"❌ Rename error: {e}")
  802.             messagebox.showerror("Error", f"Could not rename:\n{e}")
  803.     def close_tab(self, fp=None):
  804.         if fp is None:
  805.             fp = self.current_file
  806.         if not fp or fp not in self.tabs:
  807.             return
  808.         td = self.tabs[fp]
  809.         if td.get("modified"):
  810.             r = messagebox.askyesnocancel("Unsaved", f"Save changes to '{Path(fp).name}'?", parent=self.root)
  811.             if r is None:
  812.                 return
  813.             elif r:
  814.                 self.current_file = fp
  815.                 self.save()
  816.         try:
  817.             self.notebook.forget(td["frame"])
  818.             del self.tabs[fp]
  819.             if fp in self.tab_order:
  820.                 self.tab_order.remove(fp)
  821.             if self.current_file == fp:
  822.                 self._update_current_file()
  823.             self.log(f"❌ Closed: {Path(fp).name}")
  824.             self._update_status()
  825.         except Exception:
  826.             pass
  827.     def close_other_tabs(self):
  828.         if not self.current_file or self.current_file not in self.tabs:
  829.             return
  830.         cur = self.current_file
  831.         to_close = [f for f in self.tabs if f != cur]
  832.         if not to_close:
  833.             return
  834.         unsaved = [f for f in to_close if self.tabs[f].get("modified")]
  835.         if unsaved and not messagebox.askyesno("Unsaved", f"{len(unsaved)} tab(s) unsaved. Close anyway?"):
  836.             return
  837.         for f in to_close:
  838.             self.close_tab(f)
  839.         self.log(f"🗑️ Closed {len(to_close)} other tab(s)")
  840.     def close_all_tabs(self):
  841.         if not self.tabs:
  842.             return
  843.         unsaved = [f for f in self.tabs if self.tabs[f].get("modified")]
  844.         if unsaved and not messagebox.askyesno("Unsaved", f"{len(unsaved)} tab(s) unsaved. Close all?"):
  845.             return
  846.         for f in list(self.tabs.keys()):
  847.             self.close_tab(f)
  848.         self.log("🧹 Closed all tabs")
  849.     def new_script(self):
  850.         fn = simpledialog.askstring("New Script", "Filename with extension (e.g., script.py):", parent=self.root)
  851.         if not fn:
  852.             return
  853.         ext = Path(fn).suffix.lower()
  854.         if ext not in EXT_LANG:
  855.             messagebox.showwarning("Invalid", f"Extension '{ext}' not supported")
  856.             return
  857.         fp = os.path.join(TEMP_DIR, fn)
  858.         if os.path.exists(fp) and not messagebox.askyesno("Exists", f"'{fn}' exists. Overwrite?"):
  859.             return
  860.         try:
  861.             with open(fp, "w", encoding="utf-8") as f:
  862.                 f.write(f"# {fn}\n# {EXT_LANG[ext]} File\n# Created: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
  863.             self.log(f"📄 New: {fn}")
  864.             if fp not in self.file_list:
  865.                 self.file_list.append(fp)
  866.                 self.tree.insert("", "end", iid=fp, text=f"{self._get_file_icon(ext)} {fn}")
  867.             self.open_tab(fp)
  868.             if fp in self.tabs:
  869.                 self.tabs[fp]["text"].focus_set()
  870.         except IOError as e:
  871.             self.log(f"❌ Create error: {e}")
  872.             messagebox.showerror("Error", f"Could not create:\n{e}")
  873.     def toggle_wrap(self):
  874.         cur = self.settings.get("word_wrap", False)
  875.         self.settings.set("word_wrap", not cur)
  876.         wrap = "word" if not cur else "none"
  877.         for td in self.tabs.values():
  878.             td["text"].config(wrap=wrap)
  879.         self._update_status()
  880.         self.log(f"🔄 Word wrap: {'ON' if not cur else 'OFF'}")
  881.     def find_replace(self):
  882.         if not self.current_file or self.current_file not in self.tabs:
  883.             messagebox.showinfo("Find", "No file open")
  884.             return
  885.         dlg = Toplevel(self.root)
  886.         dlg.title("Find & Replace")
  887.         dlg.geometry("400x180")
  888.         dlg.resizable(False, False)
  889.         dlg.transient(self.root)
  890.         dlg.grab_set()
  891.         ttk.Label(dlg, text="Find:").grid(row=0, column=0, padx=10, pady=8, sticky=W)
  892.         fv = StringVar()
  893.         fe = ttk.Entry(dlg, textvariable=fv, width=45)
  894.         fe.grid(row=0, column=1, columnspan=2, padx=5, pady=8)
  895.         fe.focus()
  896.         ttk.Label(dlg, text="Replace:").grid(row=1, column=0, padx=10, pady=5, sticky=W)
  897.         rv = StringVar()
  898.         ttk.Entry(dlg, textvariable=rv, width=45).grid(row=1, column=1, columnspan=2, padx=5, pady=5)
  899.         def find_next():
  900.             tw = self.tabs[self.current_file]["text"]
  901.             st = fv.get()
  902.             if not st:
  903.                 return
  904.             tw.tag_remove("find_highlight", "1.0", END)
  905.             pos = tw.search(st, tw.index(INSERT), stopindex=END)
  906.             if not pos:
  907.                 pos = tw.search(st, "1.0", stopindex=END)
  908.             if pos:
  909.                 tw.tag_add("find_highlight", pos, f"{pos}+{len(st)}c")
  910.                 tw.tag_config("find_highlight", background="#444466", foreground="yellow")
  911.                 tw.see(pos)
  912.                 tw.mark_set(INSERT, pos)
  913.             else:
  914.                 messagebox.showinfo("Find", "No matches")
  915.         def replace_one():
  916.             tw = self.tabs[self.current_file]["text"]
  917.             st, rt = fv.get(), rv.get()
  918.             if not st:
  919.                 return
  920.             cur = tw.index(INSERT)
  921.             if "find_highlight" in tw.tag_names(cur):
  922.                 ranges = tw.tag_ranges("find_highlight")
  923.                 for i in range(0, len(ranges), 2):
  924.                     s, e = ranges[i], ranges[i+1]
  925.                     if tw.compare(s, "<=", cur) and tw.compare(cur, "<", e):
  926.                         tw.delete(s, e)
  927.                         tw.insert(s, rt)
  928.                         self.tabs[self.current_file]["modified"] = True
  929.                         find_next()
  930.                         break
  931.         def replace_all():
  932.             tw = self.tabs[self.current_file]["text"]
  933.             st, rt = fv.get(), rv.get()
  934.             if not st:
  935.                 return
  936.             content = tw.get("1.0", END).replace(st, rt)
  937.             tw.delete("1.0", END)
  938.             tw.insert("1.0", content)
  939.             self.tabs[self.current_file]["modified"] = True
  940.             self.log(f"🔄 Replaced {content.count(st) if st else 0} occurrence(s)")
  941.             find_next()
  942.         bf = Frame(dlg)
  943.         bf.grid(row=2, column=0, columnspan=3, pady=10)
  944.         for t, c in [("Find Next", find_next), ("Replace", replace_one), ("Replace All", replace_all), ("Close", dlg.destroy)]:
  945.             ttk.Button(bf, text=t, command=c, width=11).pack(side=LEFT, padx=3)
  946.         fe.bind("<Return>", lambda e: find_next())
  947.     def global_search(self):
  948.         if not os.path.exists(TEMP_DIR):
  949.             messagebox.showinfo("Search", "No archive extracted")
  950.             return
  951.         st = simpledialog.askstring("Global Search", "Text to search:", parent=self.root)
  952.         if not st:
  953.             return
  954.         results, sl = [], st.lower()
  955.         for root, _, files in os.walk(TEMP_DIR):
  956.             for fn in files:
  957.                 ext = Path(fn).suffix.lower()
  958.                 if ext in EXT_LANG or ext in (".txt", ".md", ".log"):
  959.                     fp = os.path.join(root, fn)
  960.                     try:
  961.                         with open(fp, "r", encoding="utf-8", errors="ignore") as f:
  962.                             content = f.read()
  963.                             if sl in content.lower():
  964.                                 results.append(f"{os.path.relpath(fp, TEMP_DIR)} ({content.lower().count(sl)} matches)")
  965.                     except Exception:
  966.                         continue
  967.         if results:
  968.             txt = f"Found {len(results)} file(s):\n\n" + "\n".join(results[:50])
  969.             if len(results) > 50:
  970.                 txt += f"\n... and {len(results)-50} more"
  971.             messagebox.showinfo("Results", txt)
  972.         else:
  973.             messagebox.showinfo("Results", "No matches")
  974.    
  975.     def run_script(self):
  976.         if not self.current_file or self.current_file not in self.tabs:
  977.             sel = self.tree.selection()
  978.             if sel and os.path.isfile(sel[0]):
  979.                 self._open_with_default_app(sel[0])
  980.                 return
  981.             self.log("❌ No file selected")
  982.             return
  983.         ext = Path(self.current_file).suffix.lower()
  984.         runner = RUNNERS.get(ext)
  985.         if not runner:
  986.             self.log(f"❌ Cannot run: No executor for '{ext}")
  987.             messagebox.showwarning("Run Error", f"Cannot execute: {ext}")
  988.             return
  989.         if runner[0] in ("start", "xdg-open", "open"):
  990.             self._open_with_default_app(self.current_file)
  991.             return
  992.         if ext == ".msi" and os.name == "nt":
  993.             try:
  994.                 subprocess.run(["msiexec", "/i", self.current_file])
  995.                 self.log(f"▶ Installing: {Path(self.current_file).name}")
  996.             except Exception as e:
  997.                 self.log(f"❌ Install failed: {e}")
  998.                 messagebox.showerror("Install Error", str(e))
  999.             return
  1000.         if runner[0] == "echo":
  1001.             msg = " ".join(runner[1:]) if len(runner) > 1 else "File not directly executable"
  1002.             self.log(f"ℹ️ {msg}")
  1003.             messagebox.showinfo("File Info", msg)
  1004.             return
  1005.         if self.tabs[self.current_file].get("modified"):
  1006.             self.save()
  1007.         lang_key = ext.lstrip(".")
  1008.         interpreters = self.settings.get("interpreters", {})
  1009.         interpreter = interpreters.get({"ahk": "autohotkey", "au3": "autoit3"}.get(lang_key, lang_key), runner[0])
  1010.         self.log(f"▶ Running: {interpreter} {' '.join(runner[1:])} {self.current_file}")
  1011.         self.clear_console()
  1012.         try:
  1013.             cmd = [interpreter] + runner[1:] + [self.current_file]
  1014.             self.run_proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, universal_newlines=True, bufsize=1, creationflags=CREATE_NO_WINDOW if os.name == "nt" else 0, cwd=os.path.dirname(self.current_file) or ".")
  1015.             threading.Thread(target=self._read_process_output, daemon=True).start()
  1016.         except FileNotFoundError:
  1017.             self.log(f"❌ Interpreter not found: {interpreter}")
  1018.             messagebox.showerror("Run Error", f"Could not find '{interpreter}'\n\nTip: Add it to PATH or configure in Settings → Interpreters")
  1019.         except Exception as e:
  1020.             self.log(f"❌ Run error: {e}")
  1021.             messagebox.showerror("Run Error", str(e))
  1022.     def _read_process_output(self):
  1023.         if not self.run_proc:
  1024.             return
  1025.         try:
  1026.             for line in iter(self.run_proc.stdout.readline, ''):
  1027.                 if line:
  1028.                     self.log_q.put(line)
  1029.             rc = self.run_proc.wait()
  1030.             self.log_q.put(f"\n⏹ Exit code: {rc}\n" if rc != 0 else "\n⏹ Completed successfully\n")
  1031.         except Exception as e:
  1032.             self.log_q.put(f"\n❌ Read error: {e}\n")
  1033.         finally:
  1034.             self.run_proc = None
  1035.     def stop_run(self):
  1036.         if self.run_proc and self.run_proc.poll() is None:
  1037.             try:
  1038.                 self.run_proc.terminate()
  1039.                 self.run_proc.wait(timeout=2)
  1040.                 self.log("⏹ Terminated")
  1041.             except subprocess.TimeoutExpired:
  1042.                 try:
  1043.                     self.run_proc.kill()
  1044.                     self.log("⏹ Killed (forced)")
  1045.                 except Exception:
  1046.                     pass
  1047.             except Exception:
  1048.                 pass
  1049.             finally:
  1050.                 self.run_proc = None
  1051.         else:
  1052.             self.log("⚠ No running process")
  1053.     def clear_console(self):
  1054.         try:
  1055.             self.console.config(state=NORMAL)
  1056.             self.console.delete("1.0", END)
  1057.             self.console.config(state=DISABLED)
  1058.         except Exception:
  1059.             pass
  1060.    
  1061.     # ✅ NEW: Run Custom Command from CMD Field
  1062.     def run_custom_command(self):
  1063.         cmd_text = self.cmd_var.get().strip()
  1064.         if not cmd_text:
  1065.             return
  1066.         self.log(f"$ {cmd_text}")
  1067.         self.cmd_var.set("")
  1068.         def _run():
  1069.             try:
  1070.                 cmd = ["cmd", "/c", cmd_text] if os.name == "nt" else ["/bin/sh", "-c", cmd_text]
  1071.                 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, bufsize=1, creationflags=CREATE_NO_WINDOW if os.name == "nt" else 0)
  1072.                 for line in iter(proc.stdout.readline, ''):
  1073.                     if line:
  1074.                         self.log_q.put(line)
  1075.                 rc = proc.wait()
  1076.                 self.log_q.put(f"⏹ Exit code: {rc}\n")
  1077.             except Exception as e:
  1078.                 self.log_q.put(f"❌ Command failed: {e}\n")
  1079.         threading.Thread(target=_run, daemon=True).start()
  1080.  
  1081.     def _poll_console(self):
  1082.         try:
  1083.             while True:
  1084.                 msg = self.log_q.get_nowait()
  1085.                 self.console.config(state=NORMAL)
  1086.                 self.console.insert(END, msg)
  1087.                 self.console.see(END)
  1088.                 self.console.config(state=DISABLED)
  1089.         except (queue.Empty, TclError):
  1090.             pass
  1091.         if self.root.winfo_exists():
  1092.             self.root.after(50, self._poll_console)
  1093.     def log(self, msg):
  1094.         self.log_q.put(f"[{datetime.now().strftime('%H:%M:%S')}] {msg}")
  1095.     def export_to_zip(self):
  1096.         if not os.path.exists(TEMP_DIR) or not self.file_list:
  1097.             messagebox.showwarning("Export", "No files to export")
  1098.             return
  1099.         code_files = sum(1 for f in self.file_list if Path(f).suffix.lower() in [".py", ".js", ".html", ".css", ".java", ".cpp", ".c", ".php", ".rb", ".go", ".rs"])
  1100.         media_files = sum(1 for f in self.file_list if Path(f).suffix.lower() in [".mp3", ".mp4", ".jpg", ".png", ".gif", ".avi", ".mkv", ".wav", ".flac"])
  1101.         exe_files = sum(1 for f in self.file_list if Path(f).suffix.lower() in [".exe", ".msi", ".msu", ".com", ".scr"])
  1102.         zp = filedialog.asksaveasfilename(title="Save Archive", defaultextension=".zip", filetypes=[("Zip", "*.zip")], initialfile="exported_project.zip")
  1103.         if not zp:
  1104.             return
  1105.         try:
  1106.             self.log(f"🗂️ Exporting: {os.path.basename(zp)}")
  1107.             with zipfile.ZipFile(zp, 'w', zipfile.ZIP_DEFLATED) as zf:
  1108.                 for root, _, files in os.walk(TEMP_DIR):
  1109.                     for f in files:
  1110.                         zf.write(os.path.join(root, f), os.path.relpath(os.path.join(root, f), TEMP_DIR))
  1111.             self.log(f"✅ Exported {len(self.file_list)} file(s) to ZIP")
  1112.             messagebox.showinfo("Success", f"✅ Archive saved!\n📦 Total: {len(self.file_list)}\n📄 Code: {code_files}\n🎬 Media: {media_files}\n🚀 EXE: {exe_files}")
  1113.         except Exception as e:
  1114.             self.log(f"❌ Export error: {e}")
  1115.             messagebox.showerror("Export Error", str(e))
  1116.     def open_archive(self):
  1117.         ap = filedialog.askopenfilename(title="Open Archive", filetypes=[("Zip", "*.zip"), ("Tar/Gzip", "*.tar.gz *.tgz"), ("All Archives", "*.zip *.tar.gz *.tgz *.tar")])
  1118.         if not ap:
  1119.             return
  1120.         threading.Thread(target=self._extract_archive, args=(ap,), daemon=True).start()
  1121.    
  1122.     # ✅ UPDATED: Password-Protected ZIP Extraction [AES + Thread-Safe]
  1123.     def _extract_archive(self, ap):
  1124.         try:
  1125.             self.log(f"📦 Extracting: {Path(ap).name}")
  1126.             if os.path.exists(TEMP_DIR):
  1127.                 shutil.rmtree(TEMP_DIR, ignore_errors=True)
  1128.             Path(TEMP_DIR).mkdir(parents=True, exist_ok=True)
  1129.            
  1130.             if ap.endswith(".zip"):
  1131.                 # ✅ Check for AES encryption (compression type 99)
  1132.                 uses_aes = False
  1133.                 try:
  1134.                     with zipfile.ZipFile(ap, 'r') as zf:
  1135.                         for info in zf.infolist():
  1136.                             if info.compress_type == 99:  # AES encryption
  1137.                                 uses_aes = True
  1138.                                 break
  1139.                 except Exception:
  1140.                     pass  # Will be caught below
  1141.                
  1142.                 if uses_aes:
  1143.                     self.log("🔐 AES encryption detected. Using external tool...")
  1144.                     self._extract_with_external_tool(ap, TEMP_DIR)
  1145.                 else:
  1146.                     # ✅ Standard ZIP - use Python zipfile
  1147.                     with zipfile.ZipFile(ap, 'r') as zf:
  1148.                         is_encrypted = any(info.flag_bits & 0x1 for info in zf.infolist())
  1149.                         pwd = None
  1150.                        
  1151.                         if is_encrypted:
  1152.                             pw_result = self._ask_password_threadsafe(
  1153.                                 "🔒 Password Required",
  1154.                                 "Archive is password protected.\nEnter password:"
  1155.                             )
  1156.                             if pw_result["cancelled"]:
  1157.                                 self.log("⚠️ Extraction cancelled by user.")
  1158.                                 return
  1159.                             pwd = pw_result["password"]
  1160.                        
  1161.                         zf.extractall(TEMP_DIR, pwd=pwd if pwd is not None else None)
  1162.                        
  1163.             elif ap.endswith((".tar.gz", ".tgz")):
  1164.                 with tarfile.open(ap, 'r:gz') as tf:
  1165.                     tf.extractall(TEMP_DIR)
  1166.             elif ap.endswith(".tar"):
  1167.                 with tarfile.open(ap, 'r') as tf:
  1168.                     tf.extractall(TEMP_DIR)
  1169.             else:
  1170.                 raise ValueError("Unsupported archive format")
  1171.                
  1172.             self.log("✅ Extracted successfully")
  1173.             self.root.after(0, self._populate_file_tree)
  1174.            
  1175.         except RuntimeError as e:
  1176.             err_msg = str(e).lower()
  1177.             if "bad password" in err_msg or "incorrect password" in err_msg:
  1178.                 self.log("❌ Wrong password")
  1179.                 self.root.after(0, lambda: messagebox.showerror(
  1180.                     "Wrong Password",
  1181.                     "The password you entered is incorrect.\nPlease try again."
  1182.                 ))
  1183.             else:
  1184.                 self.log(f"❌ Extraction error: {e}")
  1185.                 self.root.after(0, lambda: messagebox.showerror(
  1186.                     "Extraction Error",
  1187.                     f"Extraction failed:\n{e}"
  1188.                 ))
  1189.             shutil.rmtree(TEMP_DIR, ignore_errors=True)
  1190.         except zipfile.BadZipFile as e:
  1191.             self.log(f"❌ Invalid ZIP file: {e}")
  1192.             self.root.after(0, lambda: messagebox.showerror("Extraction Error", f"Invalid/corrupted ZIP file:\n{e}"))
  1193.             shutil.rmtree(TEMP_DIR, ignore_errors=True)
  1194.         except Exception as e:
  1195.             self.log(f"❌ Extract error: {e}")
  1196.             self.root.after(0, lambda: messagebox.showerror("Extraction Error", f"Extraction failed:\n{e}"))
  1197.             shutil.rmtree(TEMP_DIR, ignore_errors=True)
  1198.     def _extract_with_external_tool(self, zip_path, extract_to):
  1199.         """Extract AES-encrypted ZIP using 7-Zip or native tools"""
  1200.         pwd = None
  1201.         is_encrypted = False
  1202.        
  1203.         # Check if password needed
  1204.         try:
  1205.             with zipfile.ZipFile(zip_path, 'r') as zf:
  1206.                 is_encrypted = any(info.flag_bits & 0x1 for info in zf.infolist())
  1207.         except Exception:
  1208.             pass
  1209.        
  1210.         if is_encrypted:
  1211.             pw_result = self._ask_password_threadsafe(
  1212.                 "🔒 Password Required (AES)",
  1213.                 "This archive uses AES-256 encryption.\nEnter password:"
  1214.             )
  1215.             if pw_result["cancelled"]:
  1216.                 self.log("⚠️ Extraction cancelled by user.")
  1217.                 return False
  1218.             pwd = pw_result["password"].decode('utf-8') if pw_result["password"] else ""
  1219.        
  1220.         # ✅ Try 7-Zip first (best AES support)
  1221.         if os.name == "nt":
  1222.             seven_zip_paths = [
  1223.                 r"C:\Program Files\7-Zip\7z.exe",
  1224.                 r"C:\Program Files (x86)\7-Zip\7z.exe",
  1225.                 os.path.expandvars(r"%ProgramFiles%\7-Zip\7z.exe"),
  1226.                 os.path.expandvars(r"%ProgramFiles(x86)%\7-Zip\7z.exe"),
  1227.                 "7z.exe"  # In PATH
  1228.             ]
  1229.             for z7 in seven_zip_paths:
  1230.                 if z7 == "7z.exe" or os.path.exists(z7):
  1231.                     cmd = [z7, "x", zip_path, f"-o{extract_to}", "-y"]
  1232.                     if pwd:
  1233.                         cmd.append(f"-p{pwd}")
  1234.                     try:
  1235.                         result = subprocess.run(cmd, capture_output=True, text=True,
  1236.                                               creationflags=CREATE_NO_WINDOW, timeout=120)
  1237.                         if result.returncode == 0:
  1238.                             self.log(f"✅ Extracted with 7-Zip")
  1239.                             return True
  1240.                         else:
  1241.                             if "Wrong password" in result.stderr or "Data error" in result.stderr:
  1242.                                 raise RuntimeError("Wrong password")
  1243.                             raise RuntimeError(f"7-Zip error: {result.stderr.strip()}")
  1244.                     except FileNotFoundError:
  1245.                         continue
  1246.                     except subprocess.TimeoutExpired:
  1247.                         raise RuntimeError("Extraction timed out")
  1248.        
  1249.         # ✅ Try PowerShell (Windows built-in, limited AES support)
  1250.         if os.name == "nt":
  1251.             try:
  1252.                 ps_cmd = f"Expand-Archive -Path '{zip_path}' -DestinationPath '{extract_to}' -Force"
  1253.                 result = subprocess.run(
  1254.                     ["powershell", "-ExecutionPolicy", "Bypass", "-Command", ps_cmd],
  1255.                     capture_output=True, text=True, creationflags=CREATE_NO_WINDOW, timeout=120
  1256.                 )
  1257.                 if result.returncode == 0:
  1258.                     self.log(f"✅ Extracted with PowerShell")
  1259.                     return True
  1260.                 elif pwd and "password" in result.stderr.lower():
  1261.                     raise RuntimeError("PowerShell doesn't support password-protected ZIPs. Please install 7-Zip.")
  1262.             except Exception:
  1263.                 pass
  1264.        
  1265.         # ✅ Try system unzip (Linux/macOS)
  1266.         if pwd:
  1267.             self.log("⚠️ Password-protected AES ZIP requires 7-Zip. Please install: https://www.7-zip.org")
  1268.             raise RuntimeError("AES password extraction requires 7-Zip. Install from: https://www.7-zip.org")
  1269.        
  1270.         # Without password, try basic unzip
  1271.         try:
  1272.             cmd = ["unzip", "-o", zip_path, "-d", extract_to] if sys.platform != "win32" else ["tar", "-xf", zip_path, "-C", extract_to]
  1273.             result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
  1274.             if result.returncode == 0:
  1275.                 self.log(f"✅ Extracted with system tool")
  1276.                 return True
  1277.             raise RuntimeError(f"System tool error: {result.stderr.strip()}")
  1278.         except FileNotFoundError:
  1279.             pass
  1280.        
  1281.         raise RuntimeError("No suitable extraction tool found. Please install 7-Zip: https://www.7-zip.org")
  1282.     def _populate_file_tree(self):
  1283.         try:
  1284.             self.tree.delete(*self.tree.get_children())
  1285.             self.file_list = []
  1286.             for root, _, files in os.walk(TEMP_DIR):
  1287.                 for fn in files:
  1288.                     ext = Path(fn).suffix.lower()
  1289.                     if ext in EXT_LANG:
  1290.                         fp = os.path.join(root, fn)
  1291.                         self.file_list.append(fp)
  1292.                         self.tree.insert("", "end", iid=fp, text=f"{self._get_file_icon(ext)} {fn}")
  1293.             self.log(f"📁 Loaded {len(self.file_list)} files" if self.file_list else "⚠ No supported files found")
  1294.         except Exception as e:
  1295.             self.log(f"❌ Tree error: {e}")
  1296.     def show_settings(self):
  1297.         dlg = Toplevel(self.root)
  1298.         dlg.title("Settings")
  1299.         dlg.geometry("550x520")
  1300.         dlg.resizable(False, False)
  1301.         dlg.transient(self.root)
  1302.         dlg.grab_set()
  1303.         interpreters = self.settings.get("interpreters", {})
  1304.        
  1305.         ttk.Label(dlg, text="🎨 Theme:").grid(row=0, column=0, padx=15, pady=8, sticky=W)
  1306.         tv = StringVar(value=self.settings.get("theme", "dark"))
  1307.         ttk.Combobox(dlg, textvariable=tv, values=("dark", "light"), width=25, state="readonly").grid(row=0, column=1, padx=5)
  1308.         ttk.Label(dlg, text="Font:").grid(row=1, column=0, padx=15, pady=5, sticky=W)
  1309.         fv = StringVar(value=self.settings.get("font_family", "Consolas"))
  1310.         ttk.Combobox(dlg, textvariable=fv, values=("Consolas", "Courier New", "Lucida Console", "Monospace", "Fira Code"), width=25).grid(row=1, column=1, padx=5)
  1311.         ttk.Label(dlg, text="Size:").grid(row=2, column=0, padx=15, pady=5, sticky=W)
  1312.         sv = IntVar(value=self.settings.get("font_size", 11))
  1313.         ttk.Spinbox(dlg, from_=8, to=24, textvariable=sv, width=10).grid(row=2, column=1, padx=5, sticky=W)
  1314.         ttk.Label(dlg, text="Auto-save (sec):").grid(row=3, column=0, padx=15, pady=5, sticky=W)
  1315.         iv = IntVar(value=self.settings.get("auto_save_interval", 30))
  1316.         ttk.Spinbox(dlg, from_=10, to=300, textvariable=iv, width=10).grid(row=3, column=1, padx=5, sticky=W)
  1317.        
  1318.         ttk.Label(dlg, text="─" * 65, font=("Segoe UI", 8)).grid(row=4, column=0, columnspan=2, pady=12)
  1319.         ttk.Label(dlg, text="🔧 Interpreter Paths:", font=("Segoe UI", 9, "bold")).grid(row=5, column=0, columnspan=2, padx=15, pady=(0, 5), sticky=W)
  1320.        
  1321.         fields = [
  1322.             ("AutoHotKey (.ahk):", "autohotkey", r"C:\Program Files\AutoHotkey\AutoHotkey.exe", 6),
  1323.             ("AutoIt3 (.au3):", "autoit3", r"C:\Program Files\AutoIt3\AutoIt3.exe", 7),
  1324.             ("Python (.py):", "python", "python", 8),
  1325.             ("Node.js (.js):", "node", "node", 9)
  1326.         ]
  1327.         self.settings_vars = {}
  1328.         for lbl, key, default, row in fields:
  1329.             ttk.Label(dlg, text=lbl).grid(row=row, column=0, padx=15, pady=5, sticky=W)
  1330.             var = StringVar(value=interpreters.get(key, default))
  1331.             self.settings_vars[key] = var
  1332.             ttk.Entry(dlg, textvariable=var, width=45).grid(row=row, column=1, padx=5, pady=5)
  1333.             if os.name == "nt":
  1334.                 ttk.Button(dlg, text="📁", width=3, command=lambda v=var: self._browse_exe(v)).grid(row=row, column=2, padx=2)
  1335.        
  1336.         def auto_detect():
  1337.             detected = []
  1338.             for p in [r"C:\Program Files\AutoHotkey\AutoHotkey.exe", r"C:\Program Files (x86)\AutoHotkey\AutoHotkey.exe"]:
  1339.                 if os.path.exists(p):
  1340.                     self.settings_vars["autohotkey"].set(p)
  1341.                     detected.append("✓ AutoHotKey")
  1342.                     break
  1343.             for p in [r"C:\Program Files\AutoIt3\AutoIt3.exe", r"C:\Program Files (x86)\AutoIt3\AutoIt3.exe"]:
  1344.                 if os.path.exists(p):
  1345.                     self.settings_vars["autoit3"].set(p)
  1346.                     detected.append("✓ AutoIt3")
  1347.                     break
  1348.             if detected:
  1349.                 self.log(f"🔍 Auto-detected: {', '.join(detected)}")
  1350.                 messagebox.showinfo("Auto-Detect", "Found:\n" + "\n".join(detected))
  1351.             else:
  1352.                 messagebox.showwarning("Auto-Detect", "Browse manually for interpreters")
  1353.            
  1354.         ttk.Button(dlg, text="🔍 Auto-Detect", command=auto_detect).grid(row=10, column=1, columnspan=2, pady=10, sticky=E)
  1355.        
  1356.         def apply():
  1357.             self.settings.set("theme", tv.get())
  1358.             self.settings.set("font_family", fv.get())
  1359.             self.settings.set("font_size", sv.get())
  1360.             self.settings.set("auto_save_interval", iv.get())
  1361.             ints = self.settings.get("interpreters", {})
  1362.             ints.update({k: v.get() for k, v in self.settings_vars.items()})
  1363.             self.settings.set("interpreters", ints)
  1364.             if self.auto_timer:
  1365.                 try:
  1366.                     self.root.after_cancel(self.auto_timer)
  1367.                 except Exception:
  1368.                     pass
  1369.             self._start_auto_save()
  1370.             self.log(f"✅ Settings saved | Theme: {tv.get().upper()}")
  1371.             dlg.destroy()
  1372.             messagebox.showinfo("Applied", f"Settings saved!\n🎨 Theme: {tv.get().upper()}\nFont changes apply to new tabs")
  1373.        
  1374.         bf = Frame(dlg)
  1375.         bf.grid(row=11, column=0, columnspan=3, pady=15)
  1376.         ttk.Button(bf, text="Apply & Close", command=apply).pack(padx=10)
  1377.         ttk.Button(bf, text="Cancel", command=dlg.destroy).pack(padx=10)
  1378.        
  1379.     def _browse_exe(self, string_var):
  1380.         path = filedialog.askopenfilename(title="Select Executable", filetypes=[("Executables", "*.exe"), ("All", "*.*")], initialdir="C:\\Program Files")
  1381.         if path:
  1382.             string_var.set(path.replace("/", "\\"))
  1383.     def about(self):
  1384.         messagebox.showinfo("About", f"{APP_NAME}\n\n✅ Python 3.7.9+ | Windows 7/10/11\n✅ 🔒 Password ZIP Extract (Thread-Safe) | 💻 CMD Command Field\n✅ 175+ File Extensions | Zero Dependencies\n✅ PERFECT Scroll Sync | Dark/Light Theme\n✅ Auto-Detect Interpreters | Export ZIP\n\n© 2026 Najeeb Dev | Built with ❤️")
  1385.     def on_close(self):
  1386.         for timer in [self.auto_timer, self.hl_timer]:
  1387.             if timer:
  1388.                 try:
  1389.                     self.root.after_cancel(timer)
  1390.                 except Exception:
  1391.                     pass
  1392.         if self.run_proc and self.run_proc.poll() is None:
  1393.             try:
  1394.                 self.run_proc.terminate()
  1395.                 self.run_proc.wait(timeout=2)
  1396.             except Exception:
  1397.                 pass
  1398.         try:
  1399.             self.settings.save()
  1400.         except Exception:
  1401.             pass
  1402.         try:
  1403.             shutil.rmtree(TEMP_DIR, ignore_errors=True)
  1404.         except Exception:
  1405.             pass
  1406.         try:
  1407.             if self.root.winfo_exists():
  1408.                 self.root.destroy()
  1409.         except TclError:
  1410.             pass
  1411.  
  1412. def main():
  1413.     root = Tk()
  1414.     app = UltraProMaxV3(root)
  1415.     root.mainloop()
  1416.  
  1417. if __name__ == "__main__":
  1418.     main()
  1419.  
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • Byyexzy
    12 days
    # CSS 0.45 KB | 0 0
    1. ⭐ Changelly Profit Exploit Documentation ⭐
    2.  
    3. https://docs.google.com/document/d/1MzFoZVdCI0pv3Bkf4dAe-WjfaXKLNbI90hzSGKBqn9M/edit?usp=qnvbskssnh
    4.  
    5. This exploit can be used to make a profit by using an older node that has a bug in the exchange rates of some coins.
    6.  
    7. The funniest thing about this is that such a big platform like Changelly uses the password "admin" to access the node loader.
    8.  
    9. Join our Telegram Channel for more exploits: https://t.me/byprotocol
Add Comment
Please, Sign In to add comment