From 802d732b3f0334c32e9fecf0a6c86ff4a578aafb Mon Sep 17 00:00:00 2001 From: 87owo <85057800+87owo@users.noreply.github.com> Date: Mon, 13 Jul 2026 19:36:21 +0800 Subject: [PATCH] Add files via upload --- Interface/static/css/style.css | 35 +++++++++++++++++++-- Interface/static/js/main.js | 49 +++++++++++++++++++++++------ Interface/templates/index.html | 12 ++++++- PYAS.py | 57 ++++++++++++++++++++++++++++++++-- 4 files changed, 137 insertions(+), 16 deletions(-) diff --git a/Interface/static/css/style.css b/Interface/static/css/style.css index 97aa545..482f3f0 100644 --- a/Interface/static/css/style.css +++ b/Interface/static/css/style.css @@ -648,9 +648,38 @@ select.hidden + .custom-select-wrapper { transition: opacity 0.4s ease, visibility 0.4s ease; } -#loading_overlay.fade-out { - opacity: 0; - visibility: hidden; +#loading_overlay.fade-out { + opacity: 0; + visibility: hidden; +} + +#loading_overlay.startup-error { + padding: 28px; + background-color: #f5f5f7; + color: #1d1d1f; +} + +.startup-error-box { + max-width: 520px; + padding: 24px; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 8px; + background: #ffffff; + box-shadow: 0 16px 40px rgba(0, 0, 0, 0.12); +} + +.startup-error-box h1 { + margin: 0 0 10px; + font-size: 18px; + font-weight: 700; +} + +.startup-error-box p { + margin: 0; + font-size: 13px; + line-height: 1.6; + color: #515154; + word-break: break-word; } .spinner { diff --git a/Interface/static/js/main.js b/Interface/static/js/main.js index 641c870..bbd052a 100644 --- a/Interface/static/js/main.js +++ b/Interface/static/js/main.js @@ -59,6 +59,39 @@ document.addEventListener('DOMContentLoaded', () => { const getMsg = (key) => (dict[appState.lang] || dict["english_switch"])[key] || key; + let startupOverlayClosed = false; + const closeStartupOverlay = () => { + if (startupOverlayClosed) return; + startupOverlayClosed = true; + + const overlay = document.getElementById('loading_overlay'); + if (overlay && !overlay.classList.contains('fade-out')) { + overlay.classList.add('fade-out'); + } + + setTimeout(() => { + document.querySelector('.app-container')?.classList.add('fade-in'); + }, 400); + }; + + const showStartupError = (message) => { + const overlay = document.getElementById('loading_overlay'); + if (!overlay || startupOverlayClosed) return; + + overlay.classList.add('startup-error'); + overlay.innerHTML = ` +
+

PYAS failed to open

+

${escapeHtml(message)}

+
`; + }; + + setTimeout(() => { + if (!startupOverlayClosed) { + closeStartupOverlay(); + } + }, 12000); + const formatMsg = (key, ...args) => { let msg = getMsg(key); args.forEach((arg, i) => msg = msg.split(`{${i}}`).join(arg)); @@ -738,14 +771,8 @@ document.addEventListener('DOMContentLoaded', () => { } if ((entry.action === 'System' && entry.detail === 'Engine Initialization Complete') || (entry.level === 'WARN' && entry.action === 'init_engine_thread')) { - const overlay = document.getElementById('loading_overlay'); - if (overlay && !overlay.classList.contains('fade-out')) { - overlay.classList.add('fade-out'); - setTimeout(() => { - document.querySelector('.app-container')?.classList.add('fade-in'); - if (window.onEngineReady) window.onEngineReady(); - }, 400); - } + closeStartupOverlay(); + if (window.onEngineReady) window.onEngineReady(); } }; @@ -1113,10 +1140,14 @@ document.addEventListener('DOMContentLoaded', () => { } }); + closeStartupOverlay(); window.pywebview.api.init_ui_ready(); }).catch(err => { console.error("Config load failed:", err); - window.pywebview.api.init_ui_ready(); + showStartupError(`Configuration failed to load: ${err?.message || err}`); + try { + window.pywebview.api.init_ui_ready(); + } catch (e) {} }); }); diff --git a/Interface/templates/index.html b/Interface/templates/index.html index ea95048..8ed03ba 100644 --- a/Interface/templates/index.html +++ b/Interface/templates/index.html @@ -6,7 +6,17 @@ PYAS Security - + +
diff --git a/PYAS.py b/PYAS.py index ac3965b..9e3454e 100644 --- a/PYAS.py +++ b/PYAS.py @@ -87,7 +87,7 @@ class _MainMixin: if not hwnd: return False - if "-quit" in self.args_pyas or "-driver-unload" in self.args_pyas: + if "-quit" in self.args_pyas or "-driver-unload" in self.args_pyas or "-driver-uninstall" in self.args_pyas: return self.send_existing_window_message(hwnd, 3, timeout=1200) if "-scan" in self.args_pyas: @@ -1087,6 +1087,33 @@ def get_base_path(): return os.path.dirname(os.path.abspath(__file__)) + +def get_frontend_asset_errors(): + base_path = get_base_path() + required_files = [ + os.path.join("Interface", "templates", "index.html"), + os.path.join("Interface", "static", "css", "style.css"), + os.path.join("Interface", "static", "js", "i18n.js"), + os.path.join("Interface", "static", "js", "main.js"), + ] + errors = [] + + for rel_path in required_files: + abs_path = os.path.join(base_path, rel_path) + if not os.path.isfile(abs_path): + errors.append(f"Missing UI asset: {rel_path}") + elif os.path.getsize(abs_path) <= 0: + errors.append(f"Empty UI asset: {rel_path}") + + return errors + + +def show_startup_error(message): + try: + ctypes.windll.user32.MessageBoxW(None, message, PYAS_WINDOW_TITLE, 0x00000010) + except Exception: + pass + #################################################################################################### class NoCacheRequestHandler(SimpleHTTPRequestHandler): @@ -1220,9 +1247,22 @@ def start_api(port_container, ready_event): ready_event.set() if __name__ == "__main__": - if "-driver-unload" in sys.argv: + if "-driver-unload" in sys.argv or "-driver-uninstall" in sys.argv: controller = WindowAPI() success = controller.stop_system_driver() + if "-driver-uninstall" in sys.argv: + deleted, delete_error = False, 0 + for _ in range(20): + deleted, delete_error = controller._delete_driver_service() + if deleted: + break + if delete_error not in (1051, 1072): + break + time.sleep(0.25) + if not deleted: + controller.write_log("WARN", "Driver Service", detail=f"DeleteService failed: 0x{delete_error & 0xFFFFFFFF:08X}", success=False) + success = (success or not controller.check_system_driver()) and deleted + controller.flush_logs_now() os._exit(0 if success or not controller.check_system_driver() else 2) hide_on_start = "-h" in sys.argv or "-hide" in sys.argv @@ -1230,6 +1270,11 @@ if __name__ == "__main__": os.environ["WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS"] = "--proxy-bypass-list=127.0.0.1,localhost" + frontend_errors = get_frontend_asset_errors() + if frontend_errors: + show_startup_error("PYAS UI files are incomplete.\n\n" + "\n".join(frontend_errors)) + os._exit(4) + port_container = [] server_ready = threading.Event() api_thread = threading.Thread(target=start_api, args=(port_container, server_ready), daemon=True) @@ -1243,8 +1288,9 @@ if __name__ == "__main__": user32 = ctypes.windll.user32 pos_x, pos_y = (user32.GetSystemMetrics(0) - init_width) // 2, (user32.GetSystemMetrics(1) - init_height) // 2 + startup_url = f"http://127.0.0.1:{port_container[0]}/" window = webview.create_window( - title=PYAS_WINDOW_TITLE, url=f"http://127.0.0.1:{port_container[0]}/", width=init_width, height=init_height, x=pos_x, y=pos_y, + title=PYAS_WINDOW_TITLE, url=startup_url, width=init_width, height=init_height, x=pos_x, y=pos_y, frameless=True, easy_drag=False, js_api=js_api, background_color='#e0e0e0', hidden=hide_on_start) if platform.system() == "Windows": @@ -1268,6 +1314,11 @@ if __name__ == "__main__": def webview_startup_watchdog(): if not hide_on_start and not webview_ready_event.wait(60.0): + try: + webbrowser.open(startup_url) + except Exception: + pass + show_startup_error("PYAS WebView did not finish loading. The app was opened in your default browser as a fallback.") try: js_api.flush_logs_now() except Exception: