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: