#!/usr/bin/env python3 """ CADDesigner React Web UI startup script. """ import argparse import os from pathlib import Path import shutil import subprocess import sys project_root = Path(__file__).parent frontend_root = project_root / "frontend" def resolve_browser_host(host: str) -> str: """Convert a bind address to a display address better suited for browser access.""" if host in {"0.0.0.0", "::"}: return "localhost" return host def resolve_api_url(api_url: str | None) -> str: """Resolve the API address that the React UI should connect to.""" if api_url: return api_url.rstrip("/") env_api_url = os.getenv("CADDESIGNER_API_URL") if env_api_url: return env_api_url.rstrip("/") return "http://127.0.0.1:8000" def resolve_package_manager() -> tuple[str, str]: """Prefer pnpm, then fall back to npm.""" pnpm_cmd = shutil.which("pnpm") if pnpm_cmd: return pnpm_cmd, "pnpm" npm_cmd = shutil.which("npm") if npm_cmd: return npm_cmd, "npm" raise RuntimeError("Neither pnpm nor npm was found. Please install a Node.js package manager first.") def ensure_frontend_ready(debug: bool = False) -> tuple[str, str]: """Ensure React frontend dependencies are ready and return package manager information.""" if not frontend_root.is_dir(): raise RuntimeError(f"React frontend directory does not exist: {frontend_root}") package_manager_cmd, package_manager_name = resolve_package_manager() if not (frontend_root / "node_modules").exists(): print( f"React frontend dependencies are not installed yet; running {package_manager_name} install ..." ) install_result = subprocess.run( [package_manager_cmd, "install"], cwd=frontend_root, ) if install_result.returncode != 0: raise RuntimeError( f"{package_manager_name} install failed; unable to start React UI" ) if debug: print("React frontend dependencies installed successfully") return package_manager_cmd, package_manager_name def launch_react_app(args) -> None: """Start the standalone React Web UI (Vite).""" package_manager_cmd, package_manager_name = ensure_frontend_ready(debug=args.debug) api_url = resolve_api_url(args.api_url) display_host = resolve_browser_host(args.host) env = os.environ.copy() env["VITE_API_BASE_URL"] = api_url env["CADDESIGNER_API_PROXY_TARGET"] = api_url print("Starting CADDesigner React Web UI...") print("=" * 60) print(f"React UI address: http://{display_host}:{args.port}") print(f"API proxy target: {api_url}") print(f"Frontend package manager: {package_manager_name}") print(f"Frontend directory: {frontend_root}") if args.debug: print("Debug mode: enabled") print("Press Ctrl+C to stop the service") print("=" * 60) extra_args_separator = ["--"] if package_manager_name == "npm" else [] cmd = [ package_manager_cmd, "run", "dev", *extra_args_separator, "--host", args.host, "--port", str(args.port), "--strictPort", ] os.chdir(frontend_root) os.execvpe(package_manager_cmd, cmd, env) def main(): """Main function.""" parser = argparse.ArgumentParser( description="CADDesigner React Web UI launcher", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Usage examples: # Start React UI with the default configuration %(prog)s # Customize the listening address %(prog)s --host 0.0.0.0 --port 7861 # Connect to a remote API server; React forwards requests through the proxy %(prog)s --api-url http://192.168.1.100:8000 Environment variable: CADDESIGNER_API_URL API server address (default: http://localhost:8000) """, ) parser.add_argument( "--host", default="0.0.0.0", help="Web UI server host address (default: 0.0.0.0)", ) parser.add_argument( "--port", type=int, default=7860, help="Web UI server port (default: 7860)", ) parser.add_argument( "--api-url", default=None, help="CADDesigner API server address (default: http://localhost:8000)", ) parser.add_argument( "--debug", action="store_true", help="Enable debug mode", ) args = parser.parse_args() try: launch_react_app(args) except KeyboardInterrupt: print("\nUser canceled; exiting...") except Exception as exc: print(f"Startup failed: {exc}") if args.debug: import traceback traceback.print_exc() sys.exit(1) if __name__ == "__main__": main()