je-editor 0.0.220__tar.gz → 0.0.222__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of je-editor might be problematic. Click here for more details.
- {je_editor-0.0.220 → je_editor-0.0.222}/PKG-INFO +4 -4
- je_editor-0.0.222/je_editor/code_scan/ruff_thread.py +58 -0
- je_editor-0.0.222/je_editor/code_scan/watchdog_implement.py +56 -0
- je_editor-0.0.222/je_editor/code_scan/watchdog_thread.py +78 -0
- je_editor-0.0.222/je_editor/git_client/commit_graph.py +77 -0
- je_editor-0.0.220/je_editor/git_client/git.py → je_editor-0.0.222/je_editor/git_client/git_action.py +1 -1
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/git_client/git_cli.py +4 -4
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/git_client/github.py +36 -5
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/browser/browser_download_window.py +41 -5
- je_editor-0.0.222/je_editor/pyside_ui/browser/browser_serach_lineedit.py +51 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/browser/browser_view.py +42 -1
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/browser/browser_widget.py +43 -14
- je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +90 -0
- je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare/line_number_code_viewer.py +141 -0
- je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare/multi_file_diff_viewer.py +88 -0
- je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare/side_by_side_diff_widget.py +271 -0
- {je_editor-0.0.220/je_editor/pyside_ui/git_ui → je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client}/git_branch_tree_widget.py +17 -14
- je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +734 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +1 -1
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/main_editor.py +0 -3
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +14 -3
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +19 -4
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/multi_language/english.py +2 -1
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/multi_language/traditional_chinese.py +2 -2
- je_editor-0.0.222/je_editor/utils/redirect_manager/__init__.py +0 -0
- je_editor-0.0.222/je_editor/utils/venv_check/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/PKG-INFO +4 -4
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/SOURCES.txt +11 -5
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/requires.txt +3 -3
- {je_editor-0.0.220 → je_editor-0.0.222}/pyproject.toml +3 -3
- je_editor-0.0.220/je_editor/code_scan/ruff_thread.py +0 -31
- je_editor-0.0.220/je_editor/code_scan/watchdog_implement.py +0 -34
- je_editor-0.0.220/je_editor/code_scan/watchdog_thread.py +0 -33
- je_editor-0.0.220/je_editor/git_client/commit_graph.py +0 -88
- je_editor-0.0.220/je_editor/pyside_ui/browser/browser_serach_lineedit.py +0 -27
- je_editor-0.0.220/je_editor/pyside_ui/git_ui/git_client_gui.py +0 -291
- {je_editor-0.0.220 → je_editor-0.0.222}/LICENSE +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/README.md +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/__main__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/code_scan/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/git_client/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/browser/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/auto_save/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/auto_save/auto_save_manager.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/auto_save/auto_save_thread.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/code_format/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/code_format/pep8_format.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/code_process/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/code_process/code_exec.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/plaintext_code_edit/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/running_process_manager.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/shell_process/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/shell_process/shell_exec.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/syntax/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/syntax/python_syntax.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/syntax/syntax_setting.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/textedit_code_result/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/textedit_code_result/code_record.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/variable_inspector/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/ai_dialog/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/file_dialog/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/search_ui/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/search_ui/search_error_box.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/dialog/search_ui/search_text_box.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/git_ui/__init__.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui → je_editor-0.0.222/je_editor/pyside_ui/git_ui/code_diff_compare}/__init__.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/ai_widget → je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client}/__init__.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/git_ui → je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client}/commit_table.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/git_ui → je_editor-0.0.222/je_editor/pyside_ui/git_ui/git_client}/graph_view.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/console_widget → je_editor-0.0.222/je_editor/pyside_ui/main_ui}/__init__.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/dock → je_editor-0.0.222/je_editor/pyside_ui/main_ui/ai_widget}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/editor → je_editor-0.0.222/je_editor/pyside_ui/main_ui/console_widget}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/console_widget/console_gui.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/ipython_widget → je_editor-0.0.222/je_editor/pyside_ui/main_ui/dock}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/dock/destroy_dock.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/editor}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/editor/editor_widget.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/editor/process_input.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/check_style_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/ipython_widget}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/dock_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu}/__init__.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/file_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/check_style_menu}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/help_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/dock_menu}/__init__.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/language_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/file_menu}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/python_env_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/help_menu}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/run_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/language_menu}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/python_env_menu}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/style_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/run_menu}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/tab_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/menu/text_menu → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/style_menu}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/save_settings → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/tab_menu}/__init__.py +0 -0
- {je_editor-0.0.220/je_editor/pyside_ui/main_ui/system_tray → je_editor-0.0.222/je_editor/pyside_ui/main_ui/menu/text_menu}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +0 -0
- {je_editor-0.0.220/je_editor/utils/encodings → je_editor-0.0.222/je_editor/pyside_ui/main_ui/save_settings}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +0 -0
- {je_editor-0.0.220/je_editor/utils/file → je_editor-0.0.222/je_editor/pyside_ui/main_ui/system_tray}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/start_editor.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/__init__.py +0 -0
- {je_editor-0.0.220/je_editor/utils/json → je_editor-0.0.222/je_editor/utils/encodings}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/encodings/python_encodings.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/exception/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/exception/exception_tags.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/exception/exceptions.py +0 -0
- {je_editor-0.0.220/je_editor/utils/logging → je_editor-0.0.222/je_editor/utils/file}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/file/open/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/file/open/open_file.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/file/save/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/file/save/save_file.py +0 -0
- {je_editor-0.0.220/je_editor/utils/multi_language → je_editor-0.0.222/je_editor/utils/json}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/json/json_file.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/json_format/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/json_format/json_process.py +0 -0
- {je_editor-0.0.220/je_editor/utils/redirect_manager → je_editor-0.0.222/je_editor/utils/logging}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/logging/loggin_instance.py +0 -0
- {je_editor-0.0.220/je_editor/utils/venv_check → je_editor-0.0.222/je_editor/utils/multi_language}/__init__.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/multi_language/multi_language_wrapper.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/redirect_manager/redirect_manager_class.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor/utils/venv_check/check_venv.py +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/dependency_links.txt +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/je_editor.egg-info/top_level.txt +0 -0
- {je_editor-0.0.220 → je_editor-0.0.222}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: je_editor
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.222
|
|
4
4
|
Summary: JEditor is basic but powerful editor include GPT
|
|
5
5
|
Author-email: JE-Chen <jechenmailman@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/JE-Chen/je_editor
|
|
@@ -16,15 +16,15 @@ Classifier: Operating System :: OS Independent
|
|
|
16
16
|
Requires-Python: >=3.10
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: PySide6==6.
|
|
19
|
+
Requires-Dist: PySide6==6.10.0
|
|
20
20
|
Requires-Dist: qt-material
|
|
21
21
|
Requires-Dist: yapf
|
|
22
22
|
Requires-Dist: frontengine
|
|
23
23
|
Requires-Dist: pycodestyle
|
|
24
24
|
Requires-Dist: jedi
|
|
25
25
|
Requires-Dist: qtconsole
|
|
26
|
-
Requires-Dist: langchain_openai
|
|
27
|
-
Requires-Dist: langchain
|
|
26
|
+
Requires-Dist: langchain_openai==1.0.0
|
|
27
|
+
Requires-Dist: langchain==1.0.0
|
|
28
28
|
Requires-Dist: pydantic
|
|
29
29
|
Requires-Dist: watchdog
|
|
30
30
|
Requires-Dist: ruff
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
4
|
+
from queue import Queue
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RuffThread(threading.Thread):
|
|
8
|
+
"""
|
|
9
|
+
A thread class to run Ruff (a Python linter/formatter) as a subprocess.
|
|
10
|
+
使用子執行緒執行 Ruff (Python 程式碼檢查/格式化工具)。
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, ruff_commands: list, std_queue: Queue, stderr_queue: Queue):
|
|
14
|
+
"""
|
|
15
|
+
Initialize the RuffThread.
|
|
16
|
+
初始化 RuffThread。
|
|
17
|
+
|
|
18
|
+
:param ruff_commands: list of commands to run Ruff, e.g. ["ruff", "check"]
|
|
19
|
+
要執行的 Ruff 指令,例如 ["ruff", "check"]
|
|
20
|
+
:param std_queue: queue to store standard output
|
|
21
|
+
用來存放標準輸出的佇列
|
|
22
|
+
:param stderr_queue: queue to store error output
|
|
23
|
+
用來存放錯誤輸出的佇列
|
|
24
|
+
"""
|
|
25
|
+
super().__init__()
|
|
26
|
+
if ruff_commands is None:
|
|
27
|
+
self.ruff_commands = ["ruff", "check"]
|
|
28
|
+
else:
|
|
29
|
+
self.ruff_commands = ruff_commands
|
|
30
|
+
|
|
31
|
+
self.ruff_process = None
|
|
32
|
+
self.std_queue = std_queue
|
|
33
|
+
self.stderr_queue = stderr_queue
|
|
34
|
+
|
|
35
|
+
def run(self):
|
|
36
|
+
"""
|
|
37
|
+
Run the Ruff process in a separate thread.
|
|
38
|
+
在子執行緒中執行 Ruff 程式。
|
|
39
|
+
"""
|
|
40
|
+
# 啟動子程序,捕捉 stdout 與 stderr
|
|
41
|
+
self.ruff_process = subprocess.Popen(
|
|
42
|
+
self.ruff_commands,
|
|
43
|
+
stdout=subprocess.PIPE,
|
|
44
|
+
stderr=subprocess.PIPE,
|
|
45
|
+
text=True,
|
|
46
|
+
bufsize=1
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# 等待子程序結束
|
|
50
|
+
while self.ruff_process.poll() is None:
|
|
51
|
+
time.sleep(1)
|
|
52
|
+
else:
|
|
53
|
+
# 子程序結束後,讀取 stdout 與 stderr
|
|
54
|
+
for line in self.ruff_process.stdout:
|
|
55
|
+
self.std_queue.put(line.strip())
|
|
56
|
+
|
|
57
|
+
for line in self.ruff_process.stderr:
|
|
58
|
+
self.stderr_queue.put(line.strip())
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from queue import Queue
|
|
3
|
+
from typing import Dict
|
|
4
|
+
|
|
5
|
+
from watchdog.events import FileSystemEventHandler
|
|
6
|
+
|
|
7
|
+
from je_editor.code_scan.ruff_thread import RuffThread
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RuffPythonFileChangeHandler(FileSystemEventHandler):
|
|
11
|
+
"""
|
|
12
|
+
File system event handler that runs Ruff when Python files are modified.
|
|
13
|
+
當 Python 檔案被修改時,自動觸發 Ruff 檢查。
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, ruff_commands: list = None, debounce_interval: float = 1.0):
|
|
17
|
+
"""
|
|
18
|
+
:param ruff_commands: Ruff command list, e.g. ["ruff", "check"]
|
|
19
|
+
:param debounce_interval: Minimum interval (seconds) between re-runs for the same file
|
|
20
|
+
同一檔案觸發 Ruff 的最小間隔秒數
|
|
21
|
+
"""
|
|
22
|
+
super().__init__()
|
|
23
|
+
self.ruff_commands = ruff_commands or ["ruff", "check"]
|
|
24
|
+
self.stdout_queue: Queue = Queue()
|
|
25
|
+
self.stderr_queue: Queue = Queue()
|
|
26
|
+
self.ruff_threads_dict: Dict[str, RuffThread] = {}
|
|
27
|
+
self.last_run_time: Dict[str, float] = {}
|
|
28
|
+
self.debounce_interval = debounce_interval
|
|
29
|
+
|
|
30
|
+
def _start_new_thread(self, file_path: str):
|
|
31
|
+
"""Helper to start a new Ruff thread for a given file."""
|
|
32
|
+
ruff_thread = RuffThread(self.ruff_commands, self.stdout_queue, self.stderr_queue)
|
|
33
|
+
self.ruff_threads_dict[file_path] = ruff_thread
|
|
34
|
+
self.last_run_time[file_path] = time.time()
|
|
35
|
+
ruff_thread.start()
|
|
36
|
+
|
|
37
|
+
def on_modified(self, event):
|
|
38
|
+
"""Triggered when a file is modified."""
|
|
39
|
+
if event.is_directory:
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
if not event.src_path.endswith(".py"):
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
now = time.time()
|
|
46
|
+
last_time = self.last_run_time.get(event.src_path, 0)
|
|
47
|
+
|
|
48
|
+
# Debounce: skip if last run was too recent
|
|
49
|
+
if now - last_time < self.debounce_interval:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
ruff_thread = self.ruff_threads_dict.get(event.src_path)
|
|
53
|
+
|
|
54
|
+
if ruff_thread is None or not ruff_thread.is_alive():
|
|
55
|
+
self._start_new_thread(event.src_path)
|
|
56
|
+
# else: thread still running, skip
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from watchdog.observers import Observer
|
|
7
|
+
|
|
8
|
+
from je_editor.code_scan.watchdog_implement import RuffPythonFileChangeHandler
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class WatchdogThread(threading.Thread):
|
|
12
|
+
"""
|
|
13
|
+
A thread that runs a watchdog observer to monitor file changes.
|
|
14
|
+
使用 watchdog 監控檔案變化的執行緒。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, check_path: str):
|
|
18
|
+
"""
|
|
19
|
+
:param check_path: Path to monitor (directory or file)
|
|
20
|
+
要監控的路徑(資料夾或檔案)
|
|
21
|
+
"""
|
|
22
|
+
super().__init__(daemon=True) # 設為 daemon,主程式結束時自動退出
|
|
23
|
+
self.check_path = Path(check_path).resolve()
|
|
24
|
+
self.ruff_handler = RuffPythonFileChangeHandler()
|
|
25
|
+
self.running = True
|
|
26
|
+
self.observer = Observer()
|
|
27
|
+
|
|
28
|
+
def run(self):
|
|
29
|
+
"""Start the watchdog observer loop."""
|
|
30
|
+
if not self.check_path.exists():
|
|
31
|
+
print(f"[Error] Path does not exist: {self.check_path}", file=sys.stderr)
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
# 設定監控
|
|
35
|
+
self.observer.schedule(self.ruff_handler, str(self.check_path), recursive=True)
|
|
36
|
+
self.observer.start()
|
|
37
|
+
print(f"[Watchdog] Monitoring started on {self.check_path}")
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
while self.running:
|
|
41
|
+
time.sleep(1)
|
|
42
|
+
# 這裡可以加上 queue 輸出處理
|
|
43
|
+
self._process_ruff_output()
|
|
44
|
+
except KeyboardInterrupt:
|
|
45
|
+
print("[Watchdog] Interrupted by user")
|
|
46
|
+
finally:
|
|
47
|
+
self.observer.stop()
|
|
48
|
+
self.observer.join()
|
|
49
|
+
print("[Watchdog] Monitoring stopped")
|
|
50
|
+
|
|
51
|
+
def stop(self):
|
|
52
|
+
"""Stop the watchdog thread safely."""
|
|
53
|
+
self.running = False
|
|
54
|
+
|
|
55
|
+
def _process_ruff_output(self):
|
|
56
|
+
"""Process stdout/stderr queues from Ruff threads."""
|
|
57
|
+
while not self.ruff_handler.stdout_queue.empty():
|
|
58
|
+
line = self.ruff_handler.stdout_queue.get()
|
|
59
|
+
print(f"[Ruff STDOUT] {line}")
|
|
60
|
+
|
|
61
|
+
while not self.ruff_handler.stderr_queue.empty():
|
|
62
|
+
line = self.ruff_handler.stderr_queue.get()
|
|
63
|
+
print(f"[Ruff STDERR] {line}", file=sys.stderr)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
if __name__ == '__main__':
|
|
67
|
+
# 預設監控當前目錄
|
|
68
|
+
path_to_watch = "."
|
|
69
|
+
watchdog_thread = WatchdogThread(path_to_watch)
|
|
70
|
+
watchdog_thread.start()
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
while True:
|
|
74
|
+
time.sleep(1)
|
|
75
|
+
except KeyboardInterrupt:
|
|
76
|
+
print("[Main] Stopping watchdog...")
|
|
77
|
+
watchdog_thread.stop()
|
|
78
|
+
watchdog_thread.join()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import List, Dict, Any
|
|
4
|
+
|
|
5
|
+
log = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class CommitNode:
|
|
10
|
+
commit_sha: str
|
|
11
|
+
author_name: str
|
|
12
|
+
commit_date: str
|
|
13
|
+
commit_message: str
|
|
14
|
+
parent_shas: List[str]
|
|
15
|
+
lane_index: int = -1 # assigned later
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class CommitGraph:
|
|
20
|
+
nodes: List[CommitNode] = field(default_factory=list)
|
|
21
|
+
index: Dict[str, int] = field(default_factory=dict) # sha -> row
|
|
22
|
+
|
|
23
|
+
def build(self, commits: List[Dict[str, Any]], refs: Dict[str, str] | None = None) -> None:
|
|
24
|
+
"""
|
|
25
|
+
Build commit graph from topo-ordered commits.
|
|
26
|
+
從 topo-order 的 commits 建立 commit graph。
|
|
27
|
+
"""
|
|
28
|
+
self.nodes = [
|
|
29
|
+
CommitNode(
|
|
30
|
+
commit_sha=c["sha"],
|
|
31
|
+
author_name=c["author"],
|
|
32
|
+
commit_date=c["date"],
|
|
33
|
+
commit_message=c["message"],
|
|
34
|
+
parent_shas=c["parents"],
|
|
35
|
+
)
|
|
36
|
+
for c in commits
|
|
37
|
+
]
|
|
38
|
+
self.index = {n.commit_sha: i for i, n in enumerate(self.nodes)}
|
|
39
|
+
self._assign_lanes()
|
|
40
|
+
|
|
41
|
+
def _assign_lanes(self) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Assign lanes to commits, similar to `git log --graph`.
|
|
44
|
+
分配 lanes,模擬 `git log --graph` 的效果。
|
|
45
|
+
"""
|
|
46
|
+
active: Dict[int, str] = {} # lane -> sha
|
|
47
|
+
free_lanes: List[int] = []
|
|
48
|
+
|
|
49
|
+
for node in self.nodes:
|
|
50
|
+
# Step 1: 找到 lane
|
|
51
|
+
lane_found = next((lane for lane, sha in active.items() if sha == node.commit_sha), None)
|
|
52
|
+
|
|
53
|
+
if lane_found is not None:
|
|
54
|
+
node.lane_index = lane_found
|
|
55
|
+
elif free_lanes:
|
|
56
|
+
node.lane_index = free_lanes.pop(0)
|
|
57
|
+
else:
|
|
58
|
+
node.lane_index = 0 if not active else max(active.keys()) + 1
|
|
59
|
+
|
|
60
|
+
# Step 2: 更新 active
|
|
61
|
+
# 移除舊的 sha
|
|
62
|
+
active = {lane: sha for lane, sha in active.items() if sha != node.commit_sha}
|
|
63
|
+
|
|
64
|
+
# 父節點分配 lane
|
|
65
|
+
if node.parent_shas:
|
|
66
|
+
first_parent = node.parent_shas[0]
|
|
67
|
+
active[node.lane_index] = first_parent
|
|
68
|
+
for p in node.parent_shas[1:]:
|
|
69
|
+
pl = free_lanes.pop(0) if free_lanes else (max(active.keys()) + 1)
|
|
70
|
+
active[pl] = p
|
|
71
|
+
|
|
72
|
+
# Step 3: 更新 free_lanes
|
|
73
|
+
if active:
|
|
74
|
+
max_lane = max(active.keys())
|
|
75
|
+
used = set(active.keys())
|
|
76
|
+
all_lanes = set(range(max_lane + 1))
|
|
77
|
+
free_lanes = sorted(set(free_lanes).union(all_lanes - used))
|
je_editor-0.0.220/je_editor/git_client/git.py → je_editor-0.0.222/je_editor/git_client/git_action.py
RENAMED
|
@@ -160,7 +160,7 @@ NULL_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
|
|
|
160
160
|
# -----------------------
|
|
161
161
|
# Worker thread wrapper
|
|
162
162
|
# -----------------------
|
|
163
|
-
class
|
|
163
|
+
class GitWorker(QThread):
|
|
164
164
|
"""
|
|
165
165
|
Runs a function in a separate thread to avoid blocking the UI.
|
|
166
166
|
Emits (result, error) when done.
|
|
@@ -11,12 +11,12 @@ class GitCLI:
|
|
|
11
11
|
self.repo_path = Path(repo_path)
|
|
12
12
|
|
|
13
13
|
def is_git_repo(self) -> bool:
|
|
14
|
-
return (self.repo_path / ".
|
|
14
|
+
return (self.repo_path / ".git").exists()
|
|
15
15
|
|
|
16
16
|
def _run(self, args: List[str]) -> str:
|
|
17
|
-
log.debug("
|
|
17
|
+
log.debug("git %s", " ".join(args))
|
|
18
18
|
res = subprocess.run(
|
|
19
|
-
["
|
|
19
|
+
["git"] + args,
|
|
20
20
|
cwd=self.repo_path,
|
|
21
21
|
stdout=subprocess.PIPE,
|
|
22
22
|
stderr=subprocess.PIPE,
|
|
@@ -63,4 +63,4 @@ class GitCLI:
|
|
|
63
63
|
"date": date,
|
|
64
64
|
"message": msg,
|
|
65
65
|
})
|
|
66
|
-
return commits
|
|
66
|
+
return commits
|
|
@@ -7,38 +7,68 @@ from git import Repo, GitCommandError, InvalidGitRepositoryError, NoSuchPathErro
|
|
|
7
7
|
class GitCloneHandler:
|
|
8
8
|
"""
|
|
9
9
|
Handles cloning of remote Git repositories with audit logging.
|
|
10
|
-
|
|
10
|
+
負責複製遠端 Git 儲存庫,並記錄稽核日誌。
|
|
11
|
+
可在 UI 或 CLI 環境中重複使用。
|
|
11
12
|
"""
|
|
12
13
|
|
|
13
14
|
def __init__(self, audit_log_path: str = "git_clone_audit.log"):
|
|
15
|
+
"""
|
|
16
|
+
Initialize the handler with an audit log file path.
|
|
17
|
+
初始化處理器,指定稽核日誌檔案路徑。
|
|
18
|
+
|
|
19
|
+
:param audit_log_path: Path to the audit log file
|
|
20
|
+
稽核日誌檔案的路徑
|
|
21
|
+
"""
|
|
14
22
|
self.audit_log_path = audit_log_path
|
|
15
23
|
|
|
16
24
|
def clone_repo(self, remote_url: str, local_path: str) -> str:
|
|
17
25
|
"""
|
|
18
26
|
Clone a remote repository to a local path.
|
|
27
|
+
將遠端 Git 儲存庫複製到本地路徑。
|
|
19
28
|
|
|
20
29
|
:param remote_url: The Git repository URL (e.g., https://github.com/user/repo.git)
|
|
30
|
+
Git 儲存庫的 URL
|
|
21
31
|
:param local_path: The local directory to clone into
|
|
32
|
+
要複製到的本地目錄
|
|
22
33
|
:return: The path to the cloned repository
|
|
23
|
-
|
|
34
|
+
複製完成後的本地路徑
|
|
35
|
+
:raises: RuntimeError if cloning fails
|
|
36
|
+
若複製失敗則拋出 RuntimeError
|
|
24
37
|
"""
|
|
25
38
|
try:
|
|
39
|
+
# 記錄開始複製 / Log start of cloning
|
|
26
40
|
self._log_audit(f"Cloning started: {remote_url} -> {local_path}")
|
|
41
|
+
|
|
42
|
+
# 執行 Git 複製 / Perform Git clone
|
|
27
43
|
Repo.clone_from(remote_url, local_path)
|
|
44
|
+
|
|
45
|
+
# 記錄完成複製 / Log completion
|
|
28
46
|
self._log_audit(f"Cloning completed: {remote_url} -> {local_path}")
|
|
29
47
|
return local_path
|
|
48
|
+
|
|
30
49
|
except (GitCommandError, InvalidGitRepositoryError, NoSuchPathError) as e:
|
|
50
|
+
# 捕捉 Git 相關錯誤 / Catch Git-related errors
|
|
31
51
|
self._log_audit(
|
|
32
|
-
f"ERROR: Git operation failed: {remote_url} -> {local_path}\n
|
|
52
|
+
f"ERROR: Git operation failed: {remote_url} -> {local_path}\n"
|
|
53
|
+
f"{str(e)}\nTraceback:\n{traceback.format_exc()}"
|
|
54
|
+
)
|
|
33
55
|
raise RuntimeError(f"Git operation failed: {str(e)}") from e
|
|
56
|
+
|
|
34
57
|
except Exception as e:
|
|
58
|
+
# 捕捉其他未預期錯誤 / Catch unexpected errors
|
|
35
59
|
self._log_audit(
|
|
36
|
-
f"ERROR: Unexpected error during clone: {remote_url} -> {local_path}\n
|
|
60
|
+
f"ERROR: Unexpected error during clone: {remote_url} -> {local_path}\n"
|
|
61
|
+
f"{str(e)}\nTraceback:\n{traceback.format_exc()}"
|
|
62
|
+
)
|
|
37
63
|
raise RuntimeError(f"Unexpected error during clone: {str(e)}") from e
|
|
38
64
|
|
|
39
65
|
def _log_audit(self, message: str):
|
|
40
66
|
"""
|
|
41
67
|
Append an audit log entry with timestamp.
|
|
68
|
+
在稽核日誌中加入帶有時間戳的紀錄。
|
|
69
|
+
|
|
70
|
+
:param message: The message to log
|
|
71
|
+
要記錄的訊息
|
|
42
72
|
"""
|
|
43
73
|
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
44
74
|
log_entry = f"[{timestamp}] {message}\n"
|
|
@@ -46,5 +76,6 @@ class GitCloneHandler:
|
|
|
46
76
|
with open(self.audit_log_path, "a", encoding="utf-8") as f:
|
|
47
77
|
f.write(log_entry)
|
|
48
78
|
except Exception:
|
|
79
|
+
# 確保日誌寫入失敗不會影響主要流程
|
|
49
80
|
# Never let audit logging failure break the flow
|
|
50
|
-
pass
|
|
81
|
+
pass
|
{je_editor-0.0.220 → je_editor-0.0.222}/je_editor/pyside_ui/browser/browser_download_window.py
RENAMED
|
@@ -7,33 +7,69 @@ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapp
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class BrowserDownloadWindow(QWidget):
|
|
10
|
+
"""
|
|
11
|
+
A window widget to display details of a browser download.
|
|
12
|
+
瀏覽器下載視窗,用來顯示下載的詳細資訊。
|
|
13
|
+
"""
|
|
10
14
|
|
|
11
15
|
def __init__(self, download_instance: QWebEngineDownloadRequest):
|
|
16
|
+
"""
|
|
17
|
+
Initialize the download window with a given QWebEngineDownloadRequest.
|
|
18
|
+
使用指定的 QWebEngineDownloadRequest 初始化下載視窗。
|
|
19
|
+
"""
|
|
12
20
|
super().__init__()
|
|
21
|
+
# 記錄初始化訊息到 logger
|
|
13
22
|
jeditor_logger.info("Init BrowserDownloadWindow "
|
|
14
23
|
f"download_instance: {download_instance}")
|
|
24
|
+
|
|
25
|
+
# 設定視窗屬性:當視窗關閉時自動刪除
|
|
15
26
|
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
|
|
27
|
+
|
|
28
|
+
# 建立垂直方向的 BoxLayout
|
|
16
29
|
self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
|
|
30
|
+
|
|
31
|
+
# 建立文字框來顯示下載細節,並設為唯讀
|
|
17
32
|
self.show_download_detail_plaintext = QPlainTextEdit()
|
|
18
33
|
self.show_download_detail_plaintext.setReadOnly(True)
|
|
34
|
+
|
|
35
|
+
# 設定視窗標題,支援多語言
|
|
19
36
|
self.setWindowTitle(language_wrapper.language_word_dict.get("browser_download_detail"))
|
|
37
|
+
|
|
38
|
+
# 儲存下載實例
|
|
20
39
|
self.download_instance = download_instance
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
self.download_instance.
|
|
40
|
+
|
|
41
|
+
# 綁定下載事件到對應的處理函式
|
|
42
|
+
self.download_instance.isFinishedChanged.connect(self.print_finish) # 當下載完成時
|
|
43
|
+
self.download_instance.interruptReasonChanged.connect(self.print_interrupt) # 當下載被中斷時
|
|
44
|
+
self.download_instance.stateChanged.connect(self.print_state) # 當下載狀態改變時
|
|
45
|
+
|
|
46
|
+
# 接受下載請求,開始下載
|
|
24
47
|
self.download_instance.accept()
|
|
48
|
+
|
|
49
|
+
# 將文字框加入版面配置
|
|
25
50
|
self.box_layout.addWidget(self.show_download_detail_plaintext)
|
|
26
51
|
self.setLayout(self.box_layout)
|
|
27
52
|
|
|
28
|
-
|
|
29
53
|
def print_finish(self):
|
|
54
|
+
"""
|
|
55
|
+
Slot function triggered when download finishes.
|
|
56
|
+
當下載完成時觸發,將完成狀態輸出到 logger 與文字框。
|
|
57
|
+
"""
|
|
30
58
|
jeditor_logger.info("BrowserDownloadWindow Print Download is Finished")
|
|
31
59
|
self.show_download_detail_plaintext.appendPlainText(str(self.download_instance.isFinished()))
|
|
32
60
|
|
|
33
61
|
def print_interrupt(self):
|
|
62
|
+
"""
|
|
63
|
+
Slot function triggered when download is interrupted.
|
|
64
|
+
當下載被中斷時觸發,將中斷原因輸出到 logger 與文字框。
|
|
65
|
+
"""
|
|
34
66
|
jeditor_logger.info("BrowserDownloadWindow Print interruptReason")
|
|
35
67
|
self.show_download_detail_plaintext.appendPlainText(str(self.download_instance.interruptReason()))
|
|
36
68
|
|
|
37
69
|
def print_state(self):
|
|
70
|
+
"""
|
|
71
|
+
Slot function triggered when download state changes.
|
|
72
|
+
當下載狀態改變時觸發,將狀態輸出到 logger 與文字框。
|
|
73
|
+
"""
|
|
38
74
|
jeditor_logger.info("BrowserDownloadWindow Print State")
|
|
39
|
-
self.show_download_detail_plaintext.appendPlainText(str(self.download_instance.state()))
|
|
75
|
+
self.show_download_detail_plaintext.appendPlainText(str(self.download_instance.state()))
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from PySide6.QtCore import Qt
|
|
6
|
+
from PySide6.QtWidgets import QLineEdit
|
|
7
|
+
|
|
8
|
+
from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
# Forward declaration to avoid circular import at runtime
|
|
12
|
+
# 僅在型別檢查時匯入,避免執行時循環匯入問題
|
|
13
|
+
from je_editor.pyside_ui.browser.browser_widget import BrowserWidget
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BrowserLineSearch(QLineEdit):
|
|
17
|
+
"""
|
|
18
|
+
A custom QLineEdit widget for browser search input.
|
|
19
|
+
自訂的 QLineEdit,用於瀏覽器搜尋輸入。
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, browser_widget: BrowserWidget):
|
|
23
|
+
"""
|
|
24
|
+
Initialize the search line with a reference to the browser widget.
|
|
25
|
+
初始化搜尋輸入框,並保存瀏覽器元件的參考。
|
|
26
|
+
"""
|
|
27
|
+
super().__init__()
|
|
28
|
+
# 記錄初始化訊息到 logger
|
|
29
|
+
jeditor_logger.info("Init BrowserLineSearch "
|
|
30
|
+
f"browser_widget: {browser_widget}")
|
|
31
|
+
|
|
32
|
+
# 設定屬性:當視窗關閉時自動刪除
|
|
33
|
+
self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose)
|
|
34
|
+
|
|
35
|
+
# 保存瀏覽器元件的參考,用於觸發搜尋
|
|
36
|
+
self.browser = browser_widget
|
|
37
|
+
|
|
38
|
+
def keyPressEvent(self, event) -> None:
|
|
39
|
+
"""
|
|
40
|
+
Handle key press events.
|
|
41
|
+
當使用者按下按鍵時觸發:
|
|
42
|
+
- 如果是 Enter 或 Return,則呼叫瀏覽器的 search() 方法。
|
|
43
|
+
- 其他情況則交由父類別處理。
|
|
44
|
+
"""
|
|
45
|
+
if event.key() in [Qt.Key.Key_Enter, Qt.Key.Key_Return]:
|
|
46
|
+
jeditor_logger.info("Browser Search")
|
|
47
|
+
# 呼叫瀏覽器元件的搜尋方法
|
|
48
|
+
self.browser.search()
|
|
49
|
+
|
|
50
|
+
# 呼叫父類別的 keyPressEvent,確保其他按鍵行為正常
|
|
51
|
+
super().keyPressEvent(event)
|
|
@@ -8,28 +8,69 @@ from je_editor.utils.logging.loggin_instance import jeditor_logger
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class BrowserView(QWebEngineView):
|
|
11
|
+
"""
|
|
12
|
+
A custom QWebEngineView that supports file downloads and manages download windows.
|
|
13
|
+
自訂的 QWebEngineView,支援檔案下載並管理下載視窗。
|
|
14
|
+
"""
|
|
11
15
|
|
|
12
16
|
def __init__(self, start_url: str = "https://www.google.com/"):
|
|
17
|
+
"""
|
|
18
|
+
Initialize the browser view with a start URL.
|
|
19
|
+
使用指定的起始網址初始化瀏覽器視圖。
|
|
20
|
+
"""
|
|
13
21
|
super().__init__()
|
|
22
|
+
# 記錄初始化訊息
|
|
14
23
|
jeditor_logger.info("Init BrowserView "
|
|
15
24
|
f"start_url: {start_url}")
|
|
25
|
+
|
|
26
|
+
# 設定初始網址
|
|
16
27
|
self.setUrl(start_url)
|
|
28
|
+
|
|
29
|
+
# 儲存下載請求的清單
|
|
17
30
|
self.download_list: List[QWebEngineDownloadRequest] = list()
|
|
31
|
+
|
|
32
|
+
# 儲存下載視窗的清單
|
|
18
33
|
self.download_window_list: List[BrowserDownloadWindow] = list()
|
|
34
|
+
|
|
35
|
+
# 綁定下載事件:當有下載請求時觸發 download_file
|
|
19
36
|
self.page().profile().downloadRequested.connect(self.download_file)
|
|
20
37
|
|
|
21
38
|
def download_file(self, download_instance: QWebEngineDownloadRequest):
|
|
39
|
+
"""
|
|
40
|
+
Handle a new download request.
|
|
41
|
+
當有新的下載請求時觸發:
|
|
42
|
+
- 將下載請求加入清單
|
|
43
|
+
- 建立並顯示下載細節視窗
|
|
44
|
+
"""
|
|
22
45
|
jeditor_logger.info("Download File "
|
|
23
46
|
f"download_instance: {download_instance}")
|
|
47
|
+
|
|
48
|
+
# 加入下載請求到清單
|
|
24
49
|
self.download_list.append(download_instance)
|
|
50
|
+
|
|
51
|
+
# 建立下載細節視窗
|
|
25
52
|
download_detail_window = BrowserDownloadWindow(download_instance)
|
|
53
|
+
|
|
54
|
+
# 加入視窗到清單並顯示
|
|
26
55
|
self.download_window_list.append(download_detail_window)
|
|
27
56
|
download_detail_window.show()
|
|
28
57
|
|
|
29
58
|
def closeEvent(self, event) -> None:
|
|
59
|
+
"""
|
|
60
|
+
Handle the close event of the browser view.
|
|
61
|
+
當瀏覽器視窗關閉時:
|
|
62
|
+
- 取消所有進行中的下載
|
|
63
|
+
- 關閉所有下載細節視窗
|
|
64
|
+
"""
|
|
30
65
|
jeditor_logger.info(f"BrowserView closeEvent event: {event}")
|
|
66
|
+
|
|
67
|
+
# 取消所有下載
|
|
31
68
|
for download_instance in self.download_list:
|
|
32
69
|
download_instance.cancel()
|
|
70
|
+
|
|
71
|
+
# 關閉所有下載視窗
|
|
33
72
|
for download_window in self.download_window_list:
|
|
34
73
|
download_window.close()
|
|
35
|
-
|
|
74
|
+
|
|
75
|
+
# 呼叫父類別的 closeEvent,確保正常關閉
|
|
76
|
+
super().closeEvent(event)
|