Add files via upload
This commit is contained in:
1 parent
fb7a68ebbb
commit
6d088d05c2
3 files changed
+256
-115
No files matched your search
+89
-34
@@ -35,6 +35,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "此選項可以修復系統登錄檔項目",
|
||||
"block_popup": "彈窗攔截",
|
||||
"block_popup_desc": "此選項可以選擇指定窗口攔截",
|
||||
"popup_adaptive": "視窗特徵匹配",
|
||||
"popup_reselect": "請重新選取",
|
||||
"popup_fallback": "標題/所選視窗",
|
||||
"popup_capture_failed": "無法取得所選視窗,請確認它仍開啟且權限允許存取,再重試。",
|
||||
"popup_add_failed": "添加規則失敗,請重試並查看日誌。",
|
||||
"list_white": "白名單",
|
||||
"list_white_desc": "此選項可以查看並管理白名單",
|
||||
"list_quarantine": "隔離區",
|
||||
@@ -120,9 +125,9 @@ window.AppI18n = {
|
||||
"col_class": "類別",
|
||||
"col_title": "標題",
|
||||
"col_repair": "修復項目",
|
||||
"btn_select_all": "全選",
|
||||
"ctx_open_loc": "打開檔案所在位置",
|
||||
"ctx_copy_cell": "複製當前欄位文字",
|
||||
"btn_select_all": "全選",
|
||||
"ctx_open_loc": "打開檔案所在位置",
|
||||
"ctx_copy_cell": "複製當前欄位文字",
|
||||
"msg_too_many": "(資料過多,僅顯示前 {0} 筆以確保效能流暢)",
|
||||
"res_scan_comp": "掃描完成",
|
||||
"res_virus_det": "發現病毒",
|
||||
@@ -225,6 +230,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "此选项可以修复系统注册表项目",
|
||||
"block_popup": "弹窗拦截",
|
||||
"block_popup_desc": "此选项可以选择指定窗口拦截",
|
||||
"popup_adaptive": "窗口特征匹配",
|
||||
"popup_reselect": "请重新选择",
|
||||
"popup_fallback": "标题/所选窗口",
|
||||
"popup_capture_failed": "无法取得所选窗口,请确认它仍打开且权限允许访问,再重试。",
|
||||
"popup_add_failed": "添加规则失败,请重试并查看日志。",
|
||||
"list_white": "白名单",
|
||||
"list_white_desc": "此选项可以查看并管理白名单",
|
||||
"list_quarantine": "隔离区",
|
||||
@@ -310,9 +320,9 @@ window.AppI18n = {
|
||||
"col_class": "类别",
|
||||
"col_title": "标题",
|
||||
"col_repair": "修复项目",
|
||||
"btn_select_all": "全选",
|
||||
"ctx_open_loc": "打开文件所在位置",
|
||||
"ctx_copy_cell": "复制当前字段文本",
|
||||
"btn_select_all": "全选",
|
||||
"ctx_open_loc": "打开文件所在位置",
|
||||
"ctx_copy_cell": "复制当前字段文本",
|
||||
"msg_too_many": "(数据过多,仅显示前 {0} 条以确保性能流畅)",
|
||||
"res_scan_comp": "扫描完成",
|
||||
"res_virus_det": "发现病毒",
|
||||
@@ -415,6 +425,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "Repair system registry items",
|
||||
"block_popup": "Popup Blocker",
|
||||
"block_popup_desc": "Block specific popup windows",
|
||||
"popup_adaptive": "Window matching",
|
||||
"popup_reselect": "Reselect window",
|
||||
"popup_fallback": "Title / selected window",
|
||||
"popup_capture_failed": "Cannot access the selected window. Check that it is still open and access is allowed, then retry.",
|
||||
"popup_add_failed": "Could not save the popup rule. Retry and check the logs.",
|
||||
"list_white": "Whitelist",
|
||||
"list_white_desc": "View and manage the whitelist",
|
||||
"list_quarantine": "Quarantine",
|
||||
@@ -500,9 +515,9 @@ window.AppI18n = {
|
||||
"col_class": "Class",
|
||||
"col_title": "Title",
|
||||
"col_repair": "Repair Items",
|
||||
"btn_select_all": "Select All",
|
||||
"ctx_open_loc": "Open file location",
|
||||
"ctx_copy_cell": "Copy current field text",
|
||||
"btn_select_all": "Select All",
|
||||
"ctx_open_loc": "Open file location",
|
||||
"ctx_copy_cell": "Copy current field text",
|
||||
"msg_too_many": "(Too much data, showing only the top {0} records for performance)",
|
||||
"res_scan_comp": "Scan Completed",
|
||||
"res_virus_det": "Virus Detected",
|
||||
@@ -605,6 +620,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "システムレジストリ項目を修復します",
|
||||
"block_popup": "ポップアップブロック",
|
||||
"block_popup_desc": "特定のポップアップウィンドウをブロックします",
|
||||
"popup_adaptive": "ウィンドウ照合",
|
||||
"popup_reselect": "再選択が必要",
|
||||
"popup_fallback": "タイトル/選択ウィンドウ",
|
||||
"popup_capture_failed": "選択したウィンドウにアクセスできません。開いていることと権限を確認し、再試行してください。",
|
||||
"popup_add_failed": "ルールを保存できませんでした。再試行してログを確認してください。",
|
||||
"list_white": "ホワイトリスト",
|
||||
"list_white_desc": "ホワイトリストを表示および管理します",
|
||||
"list_quarantine": "隔離",
|
||||
@@ -690,9 +710,9 @@ window.AppI18n = {
|
||||
"col_class": "クラス",
|
||||
"col_title": "タイトル",
|
||||
"col_repair": "修復項目",
|
||||
"btn_select_all": "すべて選択",
|
||||
"ctx_open_loc": "ファイルの場所を開く",
|
||||
"ctx_copy_cell": "現在のフィールドのテキストをコピー",
|
||||
"btn_select_all": "すべて選択",
|
||||
"ctx_open_loc": "ファイルの場所を開く",
|
||||
"ctx_copy_cell": "現在のフィールドのテキストをコピー",
|
||||
"msg_too_many": "(データが多すぎるため、パフォーマンスを確保するために上位 {0} 件のみを表示します)",
|
||||
"res_scan_comp": "スキャン完了",
|
||||
"res_virus_det": "ウイルスを検出",
|
||||
@@ -795,6 +815,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "시스템 레지스트리 항목을 복구합니다",
|
||||
"block_popup": "팝업 차단",
|
||||
"block_popup_desc": "특정 팝업 창을 차단합니다",
|
||||
"popup_adaptive": "창 특성 일치",
|
||||
"popup_reselect": "다시 선택 필요",
|
||||
"popup_fallback": "제목 / 선택한 창",
|
||||
"popup_capture_failed": "선택한 창에 접근할 수 없습니다. 창이 열려 있고 접근 권한이 있는지 확인하고 다시 시도하세요.",
|
||||
"popup_add_failed": "규칙을 저장하지 못했습니다. 다시 시도하고 로그를 확인하세요.",
|
||||
"list_white": "화이트리스트",
|
||||
"list_white_desc": "화이트리스트를 보고 관리합니다",
|
||||
"list_quarantine": "격리소",
|
||||
@@ -880,9 +905,9 @@ window.AppI18n = {
|
||||
"col_class": "클래스",
|
||||
"col_title": "제목",
|
||||
"col_repair": "복구 항목",
|
||||
"btn_select_all": "전체 선택",
|
||||
"ctx_open_loc": "파일 위치 열기",
|
||||
"ctx_copy_cell": "현재 필드 텍스트 복사",
|
||||
"btn_select_all": "전체 선택",
|
||||
"ctx_open_loc": "파일 위치 열기",
|
||||
"ctx_copy_cell": "현재 필드 텍스트 복사",
|
||||
"msg_too_many": "(데이터가 너무 많아 성능 유지를 위해 상위 {0}개만 표시합니다)",
|
||||
"res_scan_comp": "스캔 완료",
|
||||
"res_virus_det": "바이러스 발견",
|
||||
@@ -985,6 +1010,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "Répare les éléments du registre système",
|
||||
"block_popup": "Bloqueur de fenêtres contextuelles",
|
||||
"block_popup_desc": "Bloque les fenêtres contextuelles spécifiques",
|
||||
"popup_adaptive": "Correspondance fenêtre",
|
||||
"popup_reselect": "Resélection requise",
|
||||
"popup_fallback": "Titre / fenêtre sélectionnée",
|
||||
"popup_capture_failed": "Impossible d’accéder à la fenêtre sélectionnée. Vérifiez qu’elle est ouverte et accessible, puis réessayez.",
|
||||
"popup_add_failed": "Impossible d’enregistrer la règle. Réessayez et consultez les journaux.",
|
||||
"list_white": "Liste blanche",
|
||||
"list_white_desc": "Afficher et gérer la liste blanche",
|
||||
"list_quarantine": "Quarantaine",
|
||||
@@ -1070,9 +1100,9 @@ window.AppI18n = {
|
||||
"col_class": "Classe",
|
||||
"col_title": "Titre",
|
||||
"col_repair": "Éléments de réparation",
|
||||
"btn_select_all": "Tout sélectionner",
|
||||
"ctx_open_loc": "Ouvrir l'emplacement du fichier",
|
||||
"ctx_copy_cell": "Copier le texte du champ actuel",
|
||||
"btn_select_all": "Tout sélectionner",
|
||||
"ctx_open_loc": "Ouvrir l'emplacement du fichier",
|
||||
"ctx_copy_cell": "Copier le texte du champ actuel",
|
||||
"msg_too_many": "(Trop de données, affichage des {0} premiers enregistrements pour des raisons de performances)",
|
||||
"res_scan_comp": "Analyse terminée",
|
||||
"res_virus_det": "Virus détecté",
|
||||
@@ -1175,6 +1205,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "Repara elementos del registro del sistema",
|
||||
"block_popup": "Bloqueador de ventanas emergentes",
|
||||
"block_popup_desc": "Bloquea ventanas emergentes específicas",
|
||||
"popup_adaptive": "Coincidencia de ventana",
|
||||
"popup_reselect": "Seleccionar de nuevo",
|
||||
"popup_fallback": "Título / ventana seleccionada",
|
||||
"popup_capture_failed": "No se puede acceder a la ventana seleccionada. Compruebe que sigue abierta y tiene acceso, y reinténtelo.",
|
||||
"popup_add_failed": "No se pudo guardar la regla. Reinténtelo y consulte los registros.",
|
||||
"list_white": "Lista blanca",
|
||||
"list_white_desc": "Ver y administrar la lista blanca",
|
||||
"list_quarantine": "Cuarentena",
|
||||
@@ -1260,9 +1295,9 @@ window.AppI18n = {
|
||||
"col_class": "Clase",
|
||||
"col_title": "Título",
|
||||
"col_repair": "Elementos de reparación",
|
||||
"btn_select_all": "Seleccionar todo",
|
||||
"ctx_open_loc": "Abrir ubicación de archivo",
|
||||
"ctx_copy_cell": "Copiar texto del campo actual",
|
||||
"btn_select_all": "Seleccionar todo",
|
||||
"ctx_open_loc": "Abrir ubicación de archivo",
|
||||
"ctx_copy_cell": "Copiar texto del campo actual",
|
||||
"msg_too_many": "(Demasiados datos, mostrando solo los primeros {0} registros para mantener el rendimiento)",
|
||||
"res_scan_comp": "Escaneo completado",
|
||||
"res_virus_det": "Virus detectado",
|
||||
@@ -1365,6 +1400,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "सिस्टम रजिस्ट्री आइटम की मरम्मत करें",
|
||||
"block_popup": "पॉपअप ब्लॉकर",
|
||||
"block_popup_desc": "विशिष्ट पॉपअप विंडो ब्लॉक करें",
|
||||
"popup_adaptive": "विंडो मिलान",
|
||||
"popup_reselect": "दोबारा चुनें",
|
||||
"popup_fallback": "शीर्षक / चयनित विंडो",
|
||||
"popup_capture_failed": "चयनित विंडो तक पहुँच नहीं है। विंडो खुली है और अनुमति है, यह जाँचकर फिर प्रयास करें।",
|
||||
"popup_add_failed": "नियम सहेजा नहीं जा सका। फिर प्रयास करें और लॉग देखें।",
|
||||
"list_white": "श्वेतसूची",
|
||||
"list_white_desc": "श्वेतसूची देखें और प्रबंधित करें",
|
||||
"list_quarantine": "संगरोध",
|
||||
@@ -1450,9 +1490,9 @@ window.AppI18n = {
|
||||
"col_class": "वर्ग",
|
||||
"col_title": "शीर्षक",
|
||||
"col_repair": "मरम्मत आइटम",
|
||||
"btn_select_all": "सभी चुनें",
|
||||
"ctx_open_loc": "फ़ाइल स्थान खोलें",
|
||||
"ctx_copy_cell": "वर्तमान फ़ील्ड का टेक्स्ट कॉपी करें",
|
||||
"btn_select_all": "सभी चुनें",
|
||||
"ctx_open_loc": "फ़ाइल स्थान खोलें",
|
||||
"ctx_copy_cell": "वर्तमान फ़ील्ड का टेक्स्ट कॉपी करें",
|
||||
"msg_too_many": "(बहुत अधिक डेटा, प्रदर्शन सुनिश्चित करने के लिए केवल शीर्ष {0} रिकॉर्ड दिखा रहा है)",
|
||||
"res_scan_comp": "स्कैन पूरा हुआ",
|
||||
"res_virus_det": "वायरस का पता चला",
|
||||
@@ -1555,6 +1595,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "إصلاح عناصر سجل النظام",
|
||||
"block_popup": "حظر النوافذ المنبثقة",
|
||||
"block_popup_desc": "حظر نوافذ منبثقة معينة",
|
||||
"popup_adaptive": "مطابقة النافذة",
|
||||
"popup_reselect": "أعد اختيار النافذة",
|
||||
"popup_fallback": "العنوان / النافذة المحددة",
|
||||
"popup_capture_failed": "تعذر الوصول إلى النافذة المحددة. تأكد من أنها مفتوحة وأن الوصول مسموح ثم أعد المحاولة.",
|
||||
"popup_add_failed": "تعذر حفظ القاعدة. أعد المحاولة وتحقق من السجلات.",
|
||||
"list_white": "القائمة البيضاء",
|
||||
"list_white_desc": "عرض وإدارة القائمة البيضاء",
|
||||
"list_quarantine": "الحجر الصحي",
|
||||
@@ -1640,9 +1685,9 @@ window.AppI18n = {
|
||||
"col_class": "الفئة",
|
||||
"col_title": "العنوان",
|
||||
"col_repair": "عناصر الإصلاح",
|
||||
"btn_select_all": "تحديد الكل",
|
||||
"ctx_open_loc": "فتح موقع الملف",
|
||||
"ctx_copy_cell": "نسخ نص الحقل الحالي",
|
||||
"btn_select_all": "تحديد الكل",
|
||||
"ctx_open_loc": "فتح موقع الملف",
|
||||
"ctx_copy_cell": "نسخ نص الحقل الحالي",
|
||||
"msg_too_many": "(بيانات كثيرة جدًا، يتم عرض أول {0} سجل فقط لضمان الأداء)",
|
||||
"res_scan_comp": "اكتمل الفحص",
|
||||
"res_virus_det": "تم اكتشاف فيروس",
|
||||
@@ -1745,6 +1790,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "Восстановление элементов системного реестра",
|
||||
"block_popup": "Блокировщик всплывающих окон",
|
||||
"block_popup_desc": "Блокировка определенных всплывающих окон",
|
||||
"popup_adaptive": "Сопоставление окон",
|
||||
"popup_reselect": "Выберите заново",
|
||||
"popup_fallback": "Заголовок / выбранное окно",
|
||||
"popup_capture_failed": "Нет доступа к выбранному окну. Проверьте, что оно открыто и доступ разрешён, и повторите попытку.",
|
||||
"popup_add_failed": "Не удалось сохранить правило. Повторите попытку и проверьте журнал.",
|
||||
"list_white": "Белый список",
|
||||
"list_white_desc": "Просмотр и управление белым списком",
|
||||
"list_quarantine": "Карантин",
|
||||
@@ -1830,9 +1880,9 @@ window.AppI18n = {
|
||||
"col_class": "Класс",
|
||||
"col_title": "Заголовок",
|
||||
"col_repair": "Элементы восстановления",
|
||||
"btn_select_all": "Выбрать все",
|
||||
"ctx_open_loc": "Открыть расположение файла",
|
||||
"ctx_copy_cell": "Копировать текст текущего поля",
|
||||
"btn_select_all": "Выбрать все",
|
||||
"ctx_open_loc": "Открыть расположение файла",
|
||||
"ctx_copy_cell": "Копировать текст текущего поля",
|
||||
"msg_too_many": "(Слишком много данных, показаны только первые {0} записей для обеспечения производительности)",
|
||||
"res_scan_comp": "Сканирование завершено",
|
||||
"res_virus_det": "Обнаружен вирус",
|
||||
@@ -1935,6 +1985,11 @@ window.AppI18n = {
|
||||
"repair_sys_desc": "Popravlja elemente sistemskega registra",
|
||||
"block_popup": "Blokator pojavnih oken",
|
||||
"block_popup_desc": "Blokira določena pojavna okna",
|
||||
"popup_adaptive": "Ujemanje oken",
|
||||
"popup_reselect": "Izberite znova",
|
||||
"popup_fallback": "Naslov / izbrano okno",
|
||||
"popup_capture_failed": "Do izbranega okna ni mogoče dostopati. Preverite, da je odprto in dostop dovoljen, ter poskusite znova.",
|
||||
"popup_add_failed": "Pravila ni bilo mogoče shraniti. Poskusite znova in preverite dnevnike.",
|
||||
"list_white": "Seznam dovoljenih",
|
||||
"list_white_desc": "Ogled in upravljanje seznama dovoljenih",
|
||||
"list_quarantine": "Karantena",
|
||||
@@ -2020,9 +2075,9 @@ window.AppI18n = {
|
||||
"col_class": "Razred",
|
||||
"col_title": "Naslov",
|
||||
"col_repair": "Predmeti za popravilo",
|
||||
"btn_select_all": "Izberi vse",
|
||||
"ctx_open_loc": "Odpri mesto datoteke",
|
||||
"ctx_copy_cell": "Kopiraj besedilo trenutnega polja",
|
||||
"btn_select_all": "Izberi vse",
|
||||
"ctx_open_loc": "Odpri mesto datoteke",
|
||||
"ctx_copy_cell": "Kopiraj besedilo trenutnega polja",
|
||||
"msg_too_many": "(Preveč podatkov, prikazanih je samo prvih {0} zapisov za zagotavljanje zmogljivosti)",
|
||||
"res_scan_comp": "Skeniranje končano",
|
||||
"res_virus_det": "Zaznan virus",
|
||||
@@ -2104,4 +2159,4 @@ window.AppI18n = {
|
||||
russian_switch: "ru",
|
||||
slovenian_switch: "sl"
|
||||
}
|
||||
};
|
||||
};
|
||||
+119
-49
@@ -172,7 +172,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
pathOnly: [{key: 'path', label: 'col_path', flex: 1}],
|
||||
fileList: [{key: 'time_str', label: 'col_date', flex: 1.5}, {key: 'path', label: 'col_path', flex: 5}],
|
||||
junk: [{key: 'path', label: 'col_path', flex: 3}, {key: 'sizeStr', label: 'col_size', flex: 1}],
|
||||
popup: [{key: 'exe', label: 'col_prog', flex: 1}, {key: 'class', label: 'col_class', flex: 1}, {key: 'title', label: 'col_title', flex: 2}],
|
||||
popup: [{key: 'mode', label: 'col_status', flex: 0.7, isI18n: true}, {key: 'exe', label: 'col_prog', flex: 1}, {key: 'class', label: 'col_class', flex: 1}, {key: 'title', label: 'col_title', flex: 2}],
|
||||
repair: [{key: 'display', label: 'col_repair', flex: 1, isI18n: true}, {key: 'path', label: 'col_path', flex: 3}],
|
||||
netmon: [{key: 'name', label: 'col_name', flex: 1.5}, {key: 'pid', label: 'col_pid', flex: 0.5}, {key: 'downStr', label: 'col_down_speed', flex: 1}, {key: 'upStr', label: 'col_up_speed', flex: 1}, {key: 'connCount', label: 'col_conn_count', flex: 0.8}]
|
||||
};
|
||||
@@ -468,7 +468,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
selectAllCb.indeterminate = (checkedCount > 0 && checkedCount < cData.length);
|
||||
};
|
||||
|
||||
const MAX_ITEMS = 2000;
|
||||
const MAX_ITEMS = 1000;
|
||||
const limitData = displayData.slice(0, MAX_ITEMS);
|
||||
|
||||
let htmlContent = limitData.map(item => {
|
||||
@@ -499,7 +499,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
widget.renderData(state.filterText, isInit);
|
||||
};
|
||||
|
||||
const getCheckedValues = (selector) => Array.from(document.querySelectorAll(`${selector} .manage-list-item input[type="checkbox"]:checked`)).map(cb => cb.value);
|
||||
const getCheckedValues = (selector) => {
|
||||
if (selector === '#log_export_window') {
|
||||
const widget = document.querySelector(`${selector} .manage-widget`);
|
||||
if (widget?.gridState) return Array.from(widget.gridState.checkedSet);
|
||||
}
|
||||
return Array.from(document.querySelectorAll(`${selector} .manage-list-item input[type="checkbox"]:checked`)).map(cb => cb.value);
|
||||
};
|
||||
const formatSize = (bytes) => {
|
||||
if (bytes === 0) return '0 B';
|
||||
const k = 1024, sizes = ['B', 'KB', 'MB', 'GB'], i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
@@ -704,7 +710,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
renderDataGrid('#whitelist_window', mapList(cfg.white_list), cols.fileList, 'path');
|
||||
renderDataGrid('#quarantine_window', mapList(cfg.quarantine), cols.fileList, 'path');
|
||||
renderDataGrid('#custom_protect_window', mapList(cfg.custom_rule), cols.fileList, 'path');
|
||||
const popupRules = (cfg.block_list || []).map(item => ({ exe: item.exe || '*', class: item.class || '*', title: item.title || '*', value: item.exe || item.title }));
|
||||
const popupRules = (cfg.block_list || []).map((item, index) => ({ mode: item.schema_version === 2 ? (item.match_mode === 'title' ? 'popup_fallback' : 'popup_adaptive') : 'popup_fallback', exe: item.exe || '*', class: item.class || '*', title: item.title || '*', value: item.id || `legacy:${index}` }));
|
||||
renderDataGrid('#popup_window', popupRules, cols.popup, 'value');
|
||||
});
|
||||
};
|
||||
@@ -819,7 +825,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (entry.operate !== null) parts.push(`Op: ${entry.operate}`);
|
||||
parts.push(`Success: ${entry.success}`);
|
||||
let val = logWidget.value + parts.join(' | ') + '\n';
|
||||
if (val.length > 20000) val = val.substring(val.indexOf('\n', val.length - 15000) + 1);
|
||||
if (val.length > 100000) {
|
||||
const newline = val.indexOf('\n', val.length - 90000);
|
||||
val = newline >= 0 && newline < val.length - 1 ? val.slice(newline + 1) : val.slice(-90000);
|
||||
}
|
||||
logWidget.value = val;
|
||||
logWidget.scrollTop = logWidget.scrollHeight;
|
||||
}
|
||||
@@ -1008,19 +1017,36 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
if (val === 'add') {
|
||||
if (id === 'popup_window') {
|
||||
const wrapper = document.getElementById(selectId).nextElementSibling;
|
||||
const triggerText = wrapper.querySelector('.custom-select-text');
|
||||
const originalI18n = triggerText.getAttribute('data-i18n');
|
||||
const originalText = triggerText.textContent;
|
||||
|
||||
triggerText.setAttribute('data-i18n', 'msg_click_target');
|
||||
triggerText.textContent = getMsg('msg_click_target');
|
||||
|
||||
window.pywebview.api.capture_popup_window().then(rule => {
|
||||
triggerText.setAttribute('data-i18n', originalI18n || 'btn_select');
|
||||
triggerText.textContent = originalText;
|
||||
if (rule) window.pywebview.api.add_popup_rule(rule).then(ok => ok && refreshConfigLists());
|
||||
const select = document.getElementById(selectId);
|
||||
if (select.dataset.capturing === 'true') return;
|
||||
select.dataset.capturing = 'true';
|
||||
const triggerText = select.nextElementSibling?.querySelector('.custom-select-text');
|
||||
const originalI18n = triggerText?.getAttribute('data-i18n');
|
||||
const originalText = triggerText?.textContent;
|
||||
if (triggerText) {
|
||||
triggerText.setAttribute('data-i18n', 'msg_click_target');
|
||||
triggerText.textContent = getMsg('msg_click_target');
|
||||
}
|
||||
let failureMessage = 'popup_capture_failed';
|
||||
Promise.resolve().then(() => window.pywebview.api.capture_popup_window()).then(async rule => {
|
||||
if (rule?.error) throw new Error(rule.error);
|
||||
if (!rule) return;
|
||||
failureMessage = 'popup_add_failed';
|
||||
const ok = await window.pywebview.api.add_popup_rule(rule);
|
||||
if (!ok) throw new Error('Popup rule was not saved');
|
||||
await refreshConfigLists();
|
||||
}).catch(error => {
|
||||
console.error('Popup capture/add failed', error);
|
||||
return window.pywebview.api.show_alert(getMsg('title_prompt'), getMsg(failureMessage), 'warning');
|
||||
}).catch(error => console.error('Popup error notification failed', error)).finally(() => {
|
||||
select.dataset.capturing = 'false';
|
||||
if (triggerText) {
|
||||
triggerText.setAttribute('data-i18n', originalI18n || 'btn_select');
|
||||
triggerText.textContent = originalText;
|
||||
}
|
||||
updateCustomSelectUI(selectId, 'none');
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
const fTypes = id === 'custom_protect_window' ? ['JSON Files (*.json)'] : null;
|
||||
const currentListKey = id === 'custom_protect_window' ? 'custom_rule' : listKey;
|
||||
@@ -1051,7 +1077,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
} else if (val === 'remove') {
|
||||
const checked = getCheckedValues(`#${id}`);
|
||||
if (checked.length > 0) window.pywebview.api.remove_list_items(listKey, checked).then(refreshConfigLists);
|
||||
if (checked.length > 0) {
|
||||
const remove = id === 'popup_window' ? window.pywebview.api.remove_popup_rules(checked) : window.pywebview.api.remove_list_items(listKey, checked);
|
||||
remove.then(refreshConfigLists);
|
||||
}
|
||||
} else if (val === 'quarantine' || val === 'whitelist') {
|
||||
const checked = getCheckedValues(`#${id}`);
|
||||
if (checked.length > 0) {
|
||||
@@ -1220,51 +1249,92 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
renderDataGrid('#netmon_window', [], cols.netmon, 'pid', false);
|
||||
|
||||
let pywebviewInitStarted = false;
|
||||
let pywebviewInitReady = false;
|
||||
let pywebviewInitAttempts = 0;
|
||||
let pywebviewInitTimer = null;
|
||||
const pywebviewInitDeadline = Date.now() + 30000;
|
||||
|
||||
const initializePywebview = () => {
|
||||
if (pywebviewInitStarted || !window.pywebview?.api) return;
|
||||
const reportStartupError = (stage, message) => {
|
||||
const text = String(message);
|
||||
console.error(`PYAS startup ${stage}:`, text);
|
||||
if (window.pywebview?.api?.report_ui_error) {
|
||||
window.pywebview.api.report_ui_error(stage, text).catch(() => {});
|
||||
}
|
||||
};
|
||||
|
||||
const schedulePywebviewInit = (delay) => {
|
||||
clearTimeout(pywebviewInitTimer);
|
||||
pywebviewInitTimer = setTimeout(initializePywebview, delay);
|
||||
};
|
||||
|
||||
const initializePywebview = async () => {
|
||||
if (pywebviewInitStarted || pywebviewInitReady || pywebviewInitAttempts >= 3) return;
|
||||
if (!window.pywebview?.api) {
|
||||
if (Date.now() < pywebviewInitDeadline) schedulePywebviewInit(250);
|
||||
else showStartupError("The Python interface bridge is unavailable. PYAS will attempt recovery.");
|
||||
return;
|
||||
}
|
||||
pywebviewInitStarted = true;
|
||||
|
||||
const configTimeout = new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error("Configuration request timed out")), 10000);
|
||||
pywebviewInitAttempts += 1;
|
||||
clearTimeout(pywebviewInitTimer);
|
||||
let timeoutId;
|
||||
let timedOut = false;
|
||||
let stage = 'get_config';
|
||||
const timeout = new Promise((_, reject) => {
|
||||
timeoutId = setTimeout(() => {
|
||||
timedOut = true;
|
||||
reject(new Error("Python interface initialization timed out"));
|
||||
}, 8000);
|
||||
});
|
||||
|
||||
Promise.race([window.pywebview.api.get_config(), configTimeout]).then(cfg => {
|
||||
const theme = cfg.theme || "white_switch";
|
||||
const lang = cfg.language || "english_switch";
|
||||
appState.firstLaunch = cfg.first_launch;
|
||||
applyTheme(theme);
|
||||
translateText(lang);
|
||||
updateCustomSelectUI('theme_select', theme);
|
||||
updateCustomSelectUI('lang_select', lang);
|
||||
|
||||
const switchMap = ["process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
|
||||
if (appState.firstLaunch) {
|
||||
toggle.checked = false;
|
||||
toggle.disabled = true;
|
||||
} else if (switchMap[index]) {
|
||||
toggle.checked = !!cfg[switchMap[index]];
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
for (const error of (window.pyasStartupErrors || []).splice(0)) {
|
||||
reportStartupError(error.stage, error.message);
|
||||
}
|
||||
await Promise.race([(async () => {
|
||||
const cfg = await window.pywebview.api.get_config();
|
||||
if (timedOut) return;
|
||||
if (!cfg || typeof cfg !== 'object') throw new Error("Invalid configuration response");
|
||||
stage = 'ui_apply';
|
||||
const theme = cfg.theme || "white_switch";
|
||||
const lang = cfg.language || "english_switch";
|
||||
appState.firstLaunch = cfg.first_launch;
|
||||
applyTheme(theme);
|
||||
translateText(lang);
|
||||
updateCustomSelectUI('theme_select', theme);
|
||||
updateCustomSelectUI('lang_select', lang);
|
||||
const switchMap = ["process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
|
||||
if (appState.firstLaunch) {
|
||||
toggle.checked = false;
|
||||
toggle.disabled = true;
|
||||
} else if (switchMap[index]) {
|
||||
toggle.checked = !!cfg[switchMap[index]];
|
||||
}
|
||||
});
|
||||
stage = 'init_ui_ready';
|
||||
await window.pywebview.api.init_ui_ready();
|
||||
})(), timeout]);
|
||||
pywebviewInitReady = true;
|
||||
closeStartupOverlay();
|
||||
window.pywebview.api.init_ui_ready();
|
||||
}).catch(err => {
|
||||
} catch (err) {
|
||||
reportStartupError(stage, err?.message || err);
|
||||
showStartupError(`Interface initialization failed (${stage}): ${err?.message || err}`);
|
||||
if (pywebviewInitAttempts < 3) schedulePywebviewInit(500 * pywebviewInitAttempts);
|
||||
} finally {
|
||||
clearTimeout(timeoutId);
|
||||
pywebviewInitStarted = false;
|
||||
console.error("Config load failed:", err);
|
||||
showStartupError(`Configuration failed to load: ${err?.message || err}`);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('pywebviewready', initializePywebview);
|
||||
initializePywebview();
|
||||
|
||||
|
||||
window.onEngineReady = async () => {
|
||||
if (!appState.firstLaunch) return;
|
||||
await window.pywebview.api.update_config("first_launch", false);
|
||||
const switchMap = ["process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
const seq = ["cloud_switch", "autostart_switch", "suffix_switch", "process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "context_switch"];
|
||||
const seq = ["autostart_switch", "suffix_switch", "process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "context_switch"];
|
||||
|
||||
for (const key of seq) {
|
||||
const index = switchMap.indexOf(key);
|
||||
|
||||
@@ -5,18 +5,34 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PYAS Security</title>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<script>
|
||||
window.pyasStartupErrors = [];
|
||||
window.addEventListener('error', function (event) {
|
||||
var target = event.target;
|
||||
window.pyasStartupErrors.push({stage: 'javascript-or-resource', message: event.message || (target && (target.src || target.href)) || 'Unknown script error'});
|
||||
}, true);
|
||||
window.addEventListener('unhandledrejection', function (event) {
|
||||
window.pyasStartupErrors.push({stage: 'promise', message: String(event.reason)});
|
||||
});
|
||||
window.addEventListener('pywebviewready', function () {
|
||||
if (!window.pywebview || !window.pywebview.api || !window.pywebview.api.report_ui_error) return;
|
||||
window.pyasStartupErrors.splice(0).forEach(function (error) {
|
||||
window.pywebview.api.report_ui_error(error.stage, error.message).catch(function () {});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script defer src="/static/js/i18n.js"></script>
|
||||
<script defer src="/static/js/main.js"></script>
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
window.setTimeout(function () {
|
||||
var overlay = document.getElementById('loading_overlay');
|
||||
if (overlay && !overlay.classList.contains('fade-out') && !overlay.classList.contains('startup-error')) {
|
||||
overlay.classList.add('startup-error');
|
||||
overlay.innerHTML = '<div class="startup-error-box"><h1>PYAS failed to open</h1><p>WebView2 initialization timed out. PYAS will retry automatically.</p></div>';
|
||||
}
|
||||
}, 20000);
|
||||
});
|
||||
<script defer src="/static/js/main.js"></script>
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
window.setTimeout(function () {
|
||||
var overlay = document.getElementById('loading_overlay');
|
||||
if (overlay && !overlay.classList.contains('fade-out') && !overlay.classList.contains('startup-error')) {
|
||||
overlay.classList.add('startup-error');
|
||||
overlay.innerHTML = '<div class="startup-error-box"><h1>PYAS failed to open</h1><p>WebView2 initialization timed out. PYAS will retry automatically.</p></div>';
|
||||
}
|
||||
}, 20000);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
@@ -67,20 +83,20 @@
|
||||
<div class="state-widget">
|
||||
<svg viewBox="0 0 24 24" class="state-icon theme-svg" style="width: 120px; height: 120px;" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path><polyline points="9 12 11 14 15 10"></polyline></svg>
|
||||
<h2 class="state-title" data-i18n>此裝置已受到防護</h2>
|
||||
</div>
|
||||
<div class="quick-actions">
|
||||
<div class="list-item" style="cursor: pointer;" id="quick_scan_btn" data-action="scan">
|
||||
<svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><circle cx="10" cy="10" r="6"/><path d="m14.5 14.5 5 5M8 10l1.5 1.5L12 8.5"/></svg>
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>智能掃描</h2><p data-i18n>此選項可以快速掃描裝置安全性</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quick-actions">
|
||||
<div class="list-item" style="cursor: pointer;" id="quick_scan_btn" data-action="scan">
|
||||
<svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><circle cx="10" cy="10" r="6"/><path d="m14.5 14.5 5 5M8 10l1.5 1.5L12 8.5"/></svg>
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>智能掃描</h2><p data-i18n>此選項可以快速掃描裝置安全性</p>
|
||||
</div>
|
||||
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
|
||||
</div>
|
||||
<div class="list-item" style="cursor: pointer;" data-target="log_export_window">
|
||||
<svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9zM14 3v6h6"/><path d="M8 13h4m-4 4h3m5-5v6m-3-3 3 3 3-3"/></svg>
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>日誌導出</h2><p data-i18n>此選項可以篩選日誌並匯出存檔</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-item" style="cursor: pointer;" data-target="log_export_window">
|
||||
<svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9zM14 3v6h6"/><path d="M8 13h4m-4 4h3m5-5v6m-3-3 3 3 3-3"/></svg>
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>日誌導出</h2><p data-i18n>此選項可以篩選日誌並匯出存檔</p>
|
||||
</div>
|
||||
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -197,9 +213,9 @@
|
||||
<h2 data-i18n>垃圾清理</h2><p data-i18n>此選項可以清理暫存垃圾檔案</p>
|
||||
</div>
|
||||
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
|
||||
</div>
|
||||
<div class="list-item" style="cursor: pointer;" data-target="repair_window">
|
||||
<svg class="item-icon item-icon-repair" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M14 6a5 5 0 0 0-6 6l-5 5a2.1 2.1 0 0 0 3 3l5-5a5 5 0 0 0 6-6l-3 3-3-3 3-3z"/></svg>
|
||||
</div>
|
||||
<div class="list-item" style="cursor: pointer;" data-target="repair_window">
|
||||
<svg class="item-icon item-icon-repair" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M14 6a5 5 0 0 0-6 6l-5 5a2.1 2.1 0 0 0 3 3l5-5a5 5 0 0 0 6-6l-3 3-3-3 3-3z"/></svg>
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>系統修復</h2><p data-i18n>此選項可以修復系統登錄檔項目</p>
|
||||
</div>
|
||||
@@ -257,7 +273,7 @@
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>雲端服務</h2><p data-i18n>此選項可以自動上報可疑檔案</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
<label class="toggle-switch"><input type="checkbox" data-config-key="cloud_switch"><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M3 4h18l-7 8v7l-4 2v-9L3 4z"/></svg>
|
||||
@@ -271,7 +287,7 @@
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>開機啟動</h2><p data-i18n>此選項可以設定開機自動執行</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
<label class="toggle-switch"><input type="checkbox" checked data-config-key="autostart_switch"><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<svg class="item-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M7 7h10M7 11h3"/><circle cx="13" cy="14" r="3"/><path d="m15.2 16.2 2.8 2.8"/></svg>
|
||||
@@ -327,14 +343,14 @@
|
||||
<section id="about_window" class="page">
|
||||
<div class="list-widget">
|
||||
<div class="list-item">
|
||||
<svg class="item-icon item-icon-compact" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"></path></svg>
|
||||
<svg class="item-icon item-icon-compact" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"></path></svg>
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>產品金鑰</h2><p data-i18n>此選項可以啟用產品金鑰</p>
|
||||
</div>
|
||||
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<svg class="item-icon item-icon-compact" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
|
||||
<svg class="item-icon item-icon-compact" aria-hidden="true" focusable="false" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>官方網站</h2><p data-i18n>此選項可以前往官方網站</p>
|
||||
</div>
|
||||
@@ -621,4 +637,4 @@ IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE L
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
Reference in new issue
Block a user