Najeebsk

ALL-SCRIPTS-ZIP-PRO.pyw

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