From 51da712fda71a4a033b01024c2289cf6f6e09629 Mon Sep 17 00:00:00 2001 From: 0velia <130620218+0velia@users.noreply.github.com> Date: Fri, 10 Jul 2026 23:12:15 +0700 Subject: [PATCH] Add files via upload --- generator/app.py | 30 +++++ generator/build.py | 101 ++++++++++++++ generator/generator.html | 267 +++++++++++++++++++++++++++++++++++++ generator/requirements.txt | 3 + 4 files changed, 401 insertions(+) create mode 100644 generator/app.py create mode 100644 generator/build.py create mode 100644 generator/generator.html create mode 100644 generator/requirements.txt diff --git a/generator/app.py b/generator/app.py new file mode 100644 index 0000000..56bbedd --- /dev/null +++ b/generator/app.py @@ -0,0 +1,30 @@ +import os +import sys +import webview + +def get_resource_path(relative_path): + """ Get the absolute path to the resource, working correctly both + during local development and after compilation inside PyInstaller """ + try: + # PyInstaller creates a temporary folder and stores the path in _MEIPASS + base_path = sys._MEIPASS + except Exception: + base_path = os.path.dirname(os.path.realpath(__file__)) + return os.path.join(base_path, relative_path) + +if __name__ == "__main__": + # Locate the embedded HTML generator file + html_path = get_resource_path("generator.html") + + # Create the standalone window environment + window = webview.create_window( + title="Universal Dropdown Node Generator", + url=html_path, + width=900, + height=850, + resizable=True + ) + + # Force the local OS renderer (WebView2 Edge Chromium on Windows) + # so the interface looks identical and functions smoothly offline on all PCs + webview.start(gui="cef" if os.name != "nt" else "mshtml" if not html_path else None) \ No newline at end of file diff --git a/generator/build.py b/generator/build.py new file mode 100644 index 0000000..a205556 --- /dev/null +++ b/generator/build.py @@ -0,0 +1,101 @@ +import os +import sys +import subprocess +import shutil + +def run_command(command, env=None): + """Helper to run system commands and stream the output to the console""" + process = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + shell=True, + text=True, + env=env + ) + while True: + output = process.stdout.readline() + if output == '' and process.poll() is not None: + break + if output: + print(output.strip()) + return process.poll() + +if __name__ == "__main__": + current_dir = os.path.dirname(os.path.realpath(__file__)) + venv_dir = os.path.join(current_dir, "builder_env") + + # Calculate the root folder path (my_universal_dropdowns) + # Moving up two levels from: .../my_universal_dropdowns/generator/build.py + root_dir = os.path.abspath(os.path.join(current_dir, "..")) + final_exe_path = os.path.join(root_dir, "Node Builder Assistant.exe") + + print("ð Starting One-Click Standalone Executable Builder...\n") + + # 1. Create Virtual Environment + if not os.path.exists(venv_dir): + print("ðŠ Step 1: Creating isolated virtual environment...") + if run_command(f'"{sys.executable}" -m venv builder_env') != 0: + print("â Failed to create virtual environment.") + sys.exit(1) + else: + print("âš Virtual environment already exists, skipping creation.") + + # Determine paths inside venv based on OS + if os.name == 'nt': # Windows + pip_path = os.path.join(venv_dir, "Scripts", "pip.exe") + pyinstaller_path = os.path.join(venv_dir, "Scripts", "pyinstaller.exe") + else: # Mac/Linux fallback + pip_path = os.path.join(venv_dir, "bin", "pip") + pyinstaller_path = os.path.join(venv_dir, "bin", "pyinstaller") + + # 2. Upgrade pip and install requirements + print("\nð¥ Step 2: Installing requirements into isolated environment...") + run_command(f'"{pip_path}" install --upgrade pip') + + req_file = os.path.join(current_dir, "requirements.txt") + if os.path.exists(req_file): + if run_command(f'"{pip_path}" install -r "{req_file}"') != 0: + print("â Failed to install dependencies.") + sys.exit(1) + else: + print("â requirements.txt not found! Please create it first.") + sys.exit(1) + + # 3. Compile with PyInstaller + print("\nð ïž Step 3: Compiling hyper-lean executable via PyInstaller...") + build_cmd = f'"{pyinstaller_path}" --clean --noconsole --onefile --add-data "generator.html;." app.py' + + if run_command(build_cmd) == 0: + print("\nâ Compilation successful!") + + # 4. Relocate and Rename the Executable + print("\nð Step 4: Moving and renaming executable to root directory...") + compiled_exe = os.path.join(current_dir, "dist", "app.exe") + + try: + # Overwrite if it already exists from a previous build + if os.path.exists(final_exe_path): + os.remove(final_exe_path) + + shutil.move(compiled_exe, final_exe_path) + print(f"âš Successfully deployed to: {final_exe_path}") + except Exception as e: + print(f"â Failed to move or rename executable: {e}") + + # 5. Clean up temporary build clutter + print("\næ« Step 5: Cleaning up temporary build clutter...") + build_folder = os.path.join(current_dir, "build") + dist_folder = os.path.join(current_dir, "dist") + spec_file = os.path.join(current_dir, "app.spec") + + if os.path.exists(build_folder): + shutil.rmtree(build_folder) + if os.path.exists(dist_folder): + shutil.rmtree(dist_folder) + if os.path.exists(spec_file): + os.remove(spec_file) + + print("âš Temporary build cache deleted. Process complete!") + else: + print("\nâ PyInstaller compilation failed.") \ No newline at end of file diff --git a/generator/generator.html b/generator/generator.html new file mode 100644 index 0000000..a592ae9 --- /dev/null +++ b/generator/generator.html @@ -0,0 +1,267 @@ + + +
+ + +The generated node aggregates all dropdown choices into a single comma-separated output. The node scans a subfolder named lists inside your custom node directory:
"), and no backslashes (\). Validated on upload.
+