je-editor 0.0.104__py3-none-any.whl → 0.0.228__py3-none-any.whl

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.
Files changed (151) hide show
  1. je_editor/__init__.py +26 -21
  2. je_editor/__main__.py +1 -1
  3. je_editor/code_scan/ruff_thread.py +58 -0
  4. je_editor/code_scan/watchdog_implement.py +56 -0
  5. je_editor/code_scan/watchdog_thread.py +78 -0
  6. je_editor/git_client/commit_graph.py +77 -0
  7. je_editor/git_client/git_action.py +175 -0
  8. je_editor/git_client/git_cli.py +66 -0
  9. je_editor/pyside_ui/browser/browser_download_window.py +75 -0
  10. je_editor/pyside_ui/browser/browser_serach_lineedit.py +51 -0
  11. je_editor/pyside_ui/browser/browser_view.py +87 -0
  12. je_editor/pyside_ui/browser/browser_widget.py +103 -0
  13. je_editor/pyside_ui/browser/main_browser_widget.py +85 -0
  14. je_editor/pyside_ui/code/auto_save/auto_save_manager.py +60 -0
  15. je_editor/pyside_ui/code/auto_save/auto_save_thread.py +59 -0
  16. je_editor/pyside_ui/code/code_format/pep8_format.py +130 -0
  17. je_editor/pyside_ui/code/code_process/code_exec.py +267 -0
  18. je_editor/pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py +412 -0
  19. je_editor/pyside_ui/code/running_process_manager.py +48 -0
  20. je_editor/pyside_ui/code/shell_process/shell_exec.py +236 -0
  21. je_editor/pyside_ui/code/syntax/python_syntax.py +99 -0
  22. je_editor/pyside_ui/code/syntax/syntax_setting.py +95 -0
  23. je_editor/pyside_ui/code/textedit_code_result/code_record.py +75 -0
  24. je_editor/pyside_ui/code/variable_inspector/inspector_gui.py +172 -0
  25. je_editor/pyside_ui/dialog/ai_dialog/set_ai_dialog.py +71 -0
  26. je_editor/pyside_ui/dialog/file_dialog/create_file_dialog.py +68 -0
  27. je_editor/pyside_ui/dialog/file_dialog/open_file_dialog.py +111 -0
  28. je_editor/pyside_ui/dialog/file_dialog/save_file_dialog.py +67 -0
  29. je_editor/pyside_ui/dialog/search_ui/search_error_box.py +49 -0
  30. je_editor/pyside_ui/dialog/search_ui/search_text_box.py +49 -0
  31. je_editor/pyside_ui/git_ui/code_diff_compare/code_diff_viewer_widget.py +90 -0
  32. je_editor/pyside_ui/git_ui/code_diff_compare/line_number_code_viewer.py +141 -0
  33. je_editor/pyside_ui/git_ui/code_diff_compare/multi_file_diff_viewer.py +88 -0
  34. je_editor/pyside_ui/git_ui/code_diff_compare/side_by_side_diff_widget.py +284 -0
  35. je_editor/pyside_ui/git_ui/git_client/commit_table.py +65 -0
  36. je_editor/pyside_ui/git_ui/git_client/git_branch_tree_widget.py +156 -0
  37. je_editor/pyside_ui/git_ui/git_client/git_client_gui.py +799 -0
  38. je_editor/pyside_ui/git_ui/git_client/graph_view.py +218 -0
  39. je_editor/pyside_ui/main_ui/ai_widget/ai_config.py +34 -0
  40. je_editor/pyside_ui/main_ui/ai_widget/ask_thread.py +36 -0
  41. je_editor/pyside_ui/main_ui/ai_widget/chat_ui.py +147 -0
  42. je_editor/pyside_ui/main_ui/ai_widget/langchain_interface.py +84 -0
  43. je_editor/pyside_ui/main_ui/console_widget/console_gui.py +162 -0
  44. je_editor/pyside_ui/main_ui/console_widget/qprocess_adapter.py +84 -0
  45. je_editor/pyside_ui/main_ui/dock/__init__.py +0 -0
  46. je_editor/pyside_ui/main_ui/dock/destroy_dock.py +50 -0
  47. je_editor/pyside_ui/main_ui/editor/__init__.py +0 -0
  48. je_editor/pyside_ui/main_ui/editor/editor_widget.py +301 -0
  49. je_editor/pyside_ui/main_ui/editor/editor_widget_dock.py +70 -0
  50. je_editor/pyside_ui/main_ui/editor/process_input.py +101 -0
  51. je_editor/pyside_ui/main_ui/ipython_widget/__init__.py +0 -0
  52. je_editor/pyside_ui/main_ui/ipython_widget/rich_jupyter.py +78 -0
  53. je_editor/pyside_ui/main_ui/main_editor.py +369 -0
  54. je_editor/pyside_ui/main_ui/menu/__init__.py +0 -0
  55. je_editor/pyside_ui/main_ui/menu/check_style_menu/__init__.py +0 -0
  56. je_editor/pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +104 -0
  57. je_editor/pyside_ui/main_ui/menu/dock_menu/__init__.py +0 -0
  58. je_editor/pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py +208 -0
  59. je_editor/pyside_ui/main_ui/menu/file_menu/__init__.py +0 -0
  60. je_editor/pyside_ui/main_ui/menu/file_menu/build_file_menu.py +186 -0
  61. je_editor/pyside_ui/main_ui/menu/help_menu/__init__.py +0 -0
  62. je_editor/pyside_ui/main_ui/menu/help_menu/build_help_menu.py +100 -0
  63. je_editor/pyside_ui/main_ui/menu/language_menu/__init__.py +0 -0
  64. je_editor/pyside_ui/main_ui/menu/language_menu/build_language_server.py +89 -0
  65. je_editor/pyside_ui/main_ui/menu/python_env_menu/__init__.py +0 -0
  66. je_editor/pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py +238 -0
  67. je_editor/pyside_ui/main_ui/menu/run_menu/__init__.py +0 -0
  68. je_editor/pyside_ui/main_ui/menu/run_menu/build_run_menu.py +160 -0
  69. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/__init__.py +0 -0
  70. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +109 -0
  71. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +101 -0
  72. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +98 -0
  73. je_editor/pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py +41 -0
  74. je_editor/pyside_ui/main_ui/menu/set_menu_bar.py +63 -0
  75. je_editor/pyside_ui/main_ui/menu/style_menu/__init__.py +0 -0
  76. je_editor/pyside_ui/main_ui/menu/style_menu/build_style_menu.py +73 -0
  77. je_editor/pyside_ui/main_ui/menu/tab_menu/__init__.py +0 -0
  78. je_editor/pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py +275 -0
  79. je_editor/pyside_ui/main_ui/menu/text_menu/__init__.py +0 -0
  80. je_editor/pyside_ui/main_ui/menu/text_menu/build_text_menu.py +135 -0
  81. je_editor/pyside_ui/main_ui/save_settings/__init__.py +0 -0
  82. je_editor/pyside_ui/main_ui/save_settings/setting_utils.py +33 -0
  83. je_editor/pyside_ui/main_ui/save_settings/user_color_setting_file.py +103 -0
  84. je_editor/pyside_ui/main_ui/save_settings/user_setting_file.py +58 -0
  85. je_editor/pyside_ui/main_ui/system_tray/__init__.py +0 -0
  86. je_editor/pyside_ui/main_ui/system_tray/extend_system_tray.py +90 -0
  87. je_editor/start_editor.py +32 -8
  88. je_editor/utils/encodings/python_encodings.py +100 -97
  89. je_editor/utils/exception/exception_tags.py +11 -11
  90. je_editor/utils/file/open/open_file.py +38 -22
  91. je_editor/utils/file/save/save_file.py +40 -16
  92. je_editor/utils/json/json_file.py +36 -15
  93. je_editor/utils/json_format/json_process.py +38 -2
  94. je_editor/utils/logging/__init__.py +0 -0
  95. je_editor/utils/logging/loggin_instance.py +57 -0
  96. je_editor/utils/multi_language/__init__.py +0 -0
  97. je_editor/utils/multi_language/english.py +221 -0
  98. je_editor/utils/multi_language/multi_language_wrapper.py +54 -0
  99. je_editor/utils/multi_language/traditional_chinese.py +214 -0
  100. je_editor/utils/redirect_manager/redirect_manager_class.py +67 -25
  101. je_editor/utils/venv_check/__init__.py +0 -0
  102. je_editor/utils/venv_check/check_venv.py +51 -0
  103. je_editor-0.0.228.dist-info/METADATA +99 -0
  104. je_editor-0.0.228.dist-info/RECORD +140 -0
  105. {je_editor-0.0.104.dist-info → je_editor-0.0.228.dist-info}/WHEEL +1 -1
  106. {je_editor-0.0.104.dist-info → je_editor-0.0.228.dist-info/licenses}/LICENSE +1 -1
  107. je_editor/pyside_ui/auto_save/auto_save_thread.py +0 -34
  108. je_editor/pyside_ui/code_editor/code_edit_plaintext.py +0 -143
  109. je_editor/pyside_ui/code_process/code_exec.py +0 -190
  110. je_editor/pyside_ui/code_result/code_record.py +0 -39
  111. je_editor/pyside_ui/colors/global_color.py +0 -4
  112. je_editor/pyside_ui/file_dialog/open_file_dialog.py +0 -27
  113. je_editor/pyside_ui/file_dialog/save_file_dialog.py +0 -24
  114. je_editor/pyside_ui/main_ui/editor_main_ui/main_editor.py +0 -183
  115. je_editor/pyside_ui/main_ui_setting/ui_setting.py +0 -36
  116. je_editor/pyside_ui/menu/menu_bar/check_style_menu/build_check_style_menu.py +0 -44
  117. je_editor/pyside_ui/menu/menu_bar/file_menu/build_file_menu.py +0 -30
  118. je_editor/pyside_ui/menu/menu_bar/help_menu/build_help_menu.py +0 -39
  119. je_editor/pyside_ui/menu/menu_bar/run_menu/build_run_menu.py +0 -102
  120. je_editor/pyside_ui/menu/menu_bar/set_menu_bar.py +0 -24
  121. je_editor/pyside_ui/menu/menu_bar/venv_menu/build_venv_menu.py +0 -74
  122. je_editor/pyside_ui/search_ui/search_error_box.py +0 -20
  123. je_editor/pyside_ui/search_ui/search_text_box.py +0 -20
  124. je_editor/pyside_ui/shell_process/shell_exec.py +0 -157
  125. je_editor/pyside_ui/syntax/python_syntax.py +0 -99
  126. je_editor/pyside_ui/treeview/project_treeview/set_project_treeview.py +0 -47
  127. je_editor/pyside_ui/user_setting/user_setting_file.py +0 -23
  128. je_editor-0.0.104.dist-info/METADATA +0 -84
  129. je_editor-0.0.104.dist-info/RECORD +0 -69
  130. /je_editor/{pyside_ui/auto_save → code_scan}/__init__.py +0 -0
  131. /je_editor/{pyside_ui/code_editor → git_client}/__init__.py +0 -0
  132. /je_editor/pyside_ui/{code_process → browser}/__init__.py +0 -0
  133. /je_editor/pyside_ui/{code_result → code}/__init__.py +0 -0
  134. /je_editor/pyside_ui/{colors → code/auto_save}/__init__.py +0 -0
  135. /je_editor/pyside_ui/{file_dialog → code/code_format}/__init__.py +0 -0
  136. /je_editor/pyside_ui/{main_ui/editor_main_ui → code/code_process}/__init__.py +0 -0
  137. /je_editor/pyside_ui/{main_ui_setting → code/plaintext_code_edit}/__init__.py +0 -0
  138. /je_editor/pyside_ui/{menu → code/shell_process}/__init__.py +0 -0
  139. /je_editor/pyside_ui/{menu/menu_bar → code/syntax}/__init__.py +0 -0
  140. /je_editor/pyside_ui/{menu/menu_bar/check_style_menu → code/textedit_code_result}/__init__.py +0 -0
  141. /je_editor/pyside_ui/{menu/menu_bar/file_menu → code/variable_inspector}/__init__.py +0 -0
  142. /je_editor/pyside_ui/{menu/menu_bar/help_menu → dialog}/__init__.py +0 -0
  143. /je_editor/pyside_ui/{menu/menu_bar/run_menu → dialog/ai_dialog}/__init__.py +0 -0
  144. /je_editor/pyside_ui/{menu/menu_bar/venv_menu → dialog/file_dialog}/__init__.py +0 -0
  145. /je_editor/pyside_ui/{search_ui → dialog/search_ui}/__init__.py +0 -0
  146. /je_editor/pyside_ui/{shell_process → git_ui}/__init__.py +0 -0
  147. /je_editor/pyside_ui/{syntax → git_ui/code_diff_compare}/__init__.py +0 -0
  148. /je_editor/pyside_ui/{treeview → git_ui/git_client}/__init__.py +0 -0
  149. /je_editor/pyside_ui/{treeview/project_treeview → main_ui/ai_widget}/__init__.py +0 -0
  150. /je_editor/pyside_ui/{user_setting → main_ui/console_widget}/__init__.py +0 -0
  151. {je_editor-0.0.104.dist-info → je_editor-0.0.228.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,172 @@
1
+ import ast
2
+
3
+ from PySide6.QtCore import QAbstractTableModel, Qt, QTimer, QSortFilterProxyModel
4
+ from PySide6.QtWidgets import QTableView, QVBoxLayout, QWidget, QLineEdit, QLabel
5
+
6
+ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
7
+
8
+
9
+ class VariableModel(QAbstractTableModel):
10
+ """
11
+ 變數模型:負責管理與顯示 Python 全域變數
12
+ Variable model: manages and displays Python global variables
13
+ """
14
+
15
+ def __init__(self, parent=None):
16
+ super().__init__(parent)
17
+ self.variables = [] # 儲存變數資訊 [名稱, 型別, 值字串, 真實值]
18
+ # Store variable info [name, type, repr(value), actual value]
19
+
20
+ def update_data(self):
21
+ """
22
+ 更新變數清單,從全域變數中擷取
23
+ Update variable list from globals()
24
+ """
25
+ parent_widget = self.parent()
26
+ # 避免在 table 正在互動時更新,造成衝突
27
+ # Avoid updating while table is in active state
28
+ if parent_widget and getattr(parent_widget, "table", None):
29
+ if parent_widget.table.state() != QTableView.State.NoState:
30
+ return
31
+
32
+ vars_dict = globals()
33
+ self.beginResetModel()
34
+ self.variables = [
35
+ [name, type(value).__name__, repr(value), value]
36
+ for name, value in vars_dict.items()
37
+ if not name.startswith("__") # 過濾內建變數 / filter out built-in variables
38
+ ]
39
+ self.endResetModel()
40
+
41
+ def rowCount(self, parent=None):
42
+ # 回傳變數數量 / return number of variables
43
+ return len(self.variables)
44
+
45
+ def columnCount(self, parent=None):
46
+ # 固定三欄:名稱、型別、值 / fixed 3 columns: name, type, value
47
+ return 3
48
+
49
+ def data(self, index, role=Qt.ItemDataRole.DisplayRole):
50
+ # 提供表格顯示的資料
51
+ # Provide data for table display
52
+ if not index.isValid() or not (0 <= index.row() < len(self.variables)):
53
+ return None
54
+ if role == Qt.ItemDataRole.DisplayRole:
55
+ return self.variables[index.row()][index.column()]
56
+ return None
57
+
58
+ def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole):
59
+ # 設定表頭文字 (支援多語系)
60
+ # Set header labels (multi-language support)
61
+ if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
62
+ return [
63
+ language_wrapper.language_word_dict.get("variable_inspector_var_name"),
64
+ language_wrapper.language_word_dict.get("variable_inspector_var_type"),
65
+ language_wrapper.language_word_dict.get("variable_inspector_var_value")
66
+ ][section]
67
+ return None
68
+
69
+ def flags(self, index):
70
+ # 設定欄位屬性,僅允許「值」欄可編輯
71
+ # Set column flags, only "value" column is editable
72
+ if not index.isValid() or not (0 <= index.row() < len(self.variables)):
73
+ return Qt.ItemFlag.NoItemFlags
74
+ base = super().flags(index)
75
+ if index.column() == 2: # 只允許編輯值欄 / only value column editable
76
+ base |= Qt.ItemFlag.ItemIsEditable
77
+ return base
78
+
79
+ def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
80
+ """
81
+ 更新變數值,並同步到全域變數
82
+ Update variable value and sync to globals()
83
+ """
84
+ if role == Qt.ItemDataRole.EditRole and index.column() == 2:
85
+
86
+ if value == "":
87
+ return False
88
+
89
+ old_repr = self.variables[index.row()][2]
90
+ if str(value) == old_repr:
91
+ return False
92
+
93
+ var_name = self.variables[index.row()][0]
94
+ try:
95
+ # 嘗試將輸入轉換為 Python 物件
96
+ # Try to evaluate input as Python object
97
+ new_value = ast.literal_eval(value)
98
+ except Exception:
99
+ # 若失敗則當作字串處理
100
+ # If failed, treat as string
101
+ new_value = value
102
+
103
+ if new_value == self.variables[index.row()][3]:
104
+ return False
105
+
106
+ # 更新全域變數與模型資料
107
+ # Update globals and model data
108
+ globals()[var_name] = new_value
109
+ self.variables[index.row()][2] = repr(new_value)
110
+ self.variables[index.row()][3] = new_value
111
+ self.dataChanged.emit(index, index, [Qt.ItemDataRole.DisplayRole])
112
+ return True
113
+ return False
114
+
115
+
116
+ class VariableProxy(QSortFilterProxyModel):
117
+ """
118
+ 過濾代理模型:支援搜尋與編輯轉發
119
+ Proxy model: supports filtering and forwards editing
120
+ """
121
+
122
+ def setData(self, index, value, role=Qt.ItemDataRole.EditRole):
123
+ # 將編輯操作轉發到原始模型
124
+ # Forward editing to source model
125
+ return self.sourceModel().setData(
126
+ self.mapToSource(index), value, role
127
+ )
128
+
129
+
130
+ class VariableInspector(QWidget):
131
+ """
132
+ 變數檢視器:提供 GUI 介面顯示與搜尋全域變數
133
+ Variable inspector: GUI interface to display and search global variables
134
+ """
135
+
136
+ def __init__(self):
137
+ super().__init__()
138
+ self.setWindowTitle(language_wrapper.language_word_dict.get("variable_inspector_title"))
139
+ layout = QVBoxLayout(self)
140
+
141
+ # 搜尋框 / search box
142
+ self.search_box = QLineEdit()
143
+ self.search_box.setPlaceholderText(language_wrapper.language_word_dict.get("variable_inspector_search"))
144
+ layout.addWidget(QLabel(language_wrapper.language_word_dict.get("variable_inspector_search")))
145
+ layout.addWidget(self.search_box)
146
+
147
+ # 模型與代理模型 / model and proxy model
148
+ self.model = VariableModel(parent=self)
149
+ self.proxy_model = VariableProxy(self)
150
+ self.proxy_model.setSourceModel(self.model)
151
+ self.proxy_model.setFilterCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive) # 不分大小寫 / case-insensitive
152
+ self.proxy_model.setFilterKeyColumn(0) # 僅針對變數名稱過濾 / filter by variable name
153
+
154
+ # 表格顯示 / table view
155
+ self.table = QTableView()
156
+ self.table.setModel(self.proxy_model)
157
+ self.table.horizontalHeader().setStretchLastSection(True)
158
+ self.table.horizontalHeader().setSectionResizeMode(
159
+ self.table.horizontalHeader().ResizeMode.Stretch
160
+ )
161
+ layout.addWidget(self.table, stretch=1)
162
+
163
+ # 綁定搜尋框輸入事件 / bind search box input
164
+ self.search_box.textChanged.connect(
165
+ self.proxy_model.setFilterFixedString
166
+ )
167
+
168
+ # 定時更新變數清單 (每 500ms)
169
+ # Periodically update variable list (every 500ms)
170
+ self.timer = QTimer(self)
171
+ self.timer.timeout.connect(self.model.update_data)
172
+ self.timer.start(500)
@@ -0,0 +1,71 @@
1
+ from PySide6.QtWidgets import QWidget, QLineEdit, QPushButton, QMessageBox, QGridLayout, QLabel
2
+
3
+ from je_editor.pyside_ui.main_ui.ai_widget.ai_config import ai_config
4
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
5
+ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
6
+
7
+
8
+ class SetAIDialog(QWidget):
9
+ """
10
+ 設定 AI 模型的對話框
11
+ Dialog for configuring AI model settings
12
+ """
13
+
14
+ def __init__(self):
15
+ jeditor_logger.info("Init SetAIDialog")
16
+ super().__init__()
17
+
18
+ # Base URL 輸入欄位 / Base URL input field
19
+ self.base_url_label = QLabel(language_wrapper.language_word_dict.get("base_url_label"))
20
+ self.base_url_input = QLineEdit()
21
+
22
+ # API Key 輸入欄位 / API Key input field
23
+ self.api_key_label = QLabel(language_wrapper.language_word_dict.get("api_key_label"))
24
+ self.api_key_input = QLineEdit()
25
+
26
+ # Chat Model 輸入欄位 / Chat model input field
27
+ self.chat_model_label = QLabel(language_wrapper.language_word_dict.get("ai_model_label"))
28
+ self.chat_model_input = QLineEdit()
29
+
30
+ # 新增 AI 設定按鈕 / Button to add AI configuration
31
+ self.add_ai_info_button = QPushButton()
32
+ self.add_ai_info_button.setText(language_wrapper.language_word_dict.get("add_ai_model_pushbutton"))
33
+ self.add_ai_info_button.clicked.connect(self.update_ai_config)
34
+
35
+ # 使用 GridLayout 排版 / Use GridLayout for layout
36
+ self.grid_layout = QGridLayout()
37
+ self.grid_layout.addWidget(self.base_url_label, 0, 0)
38
+ self.grid_layout.addWidget(self.base_url_input, 0, 1)
39
+ self.grid_layout.addWidget(self.api_key_label, 1, 0)
40
+ self.grid_layout.addWidget(self.api_key_input, 1, 1)
41
+ self.grid_layout.addWidget(self.chat_model_label, 2, 0)
42
+ self.grid_layout.addWidget(self.chat_model_input, 2, 1)
43
+ self.grid_layout.addWidget(self.add_ai_info_button, 3, 1)
44
+
45
+ # 設定視窗標題 / Set window title
46
+ self.setWindowTitle(language_wrapper.language_word_dict.get("add_ai_model_title"))
47
+ self.setLayout(self.grid_layout)
48
+
49
+ def update_ai_config(self):
50
+ """
51
+ 更新 AI 設定,將使用者輸入的 base_url、api_key、chat_model
52
+ 儲存到 ai_config.choosable_ai 中
53
+ Update AI configuration with user inputs (base_url, api_key, chat_model)
54
+ """
55
+ base_url = self.base_url_input.text().strip()
56
+ api_key = self.api_key_input.text().strip()
57
+ chat_model = self.chat_model_input.text().strip()
58
+
59
+ if base_url and chat_model:
60
+ # 更新設定字典 / Update configuration dictionary
61
+ ai_config.choosable_ai.update(
62
+ {"AI_model": {"base_url": base_url, "api_key": api_key, "chat_model": chat_model}}
63
+ )
64
+ else:
65
+ # 若缺少必要欄位,顯示警告訊息框
66
+ # Show warning message box if required fields are missing
67
+ QMessageBox.warning(
68
+ self,
69
+ language_wrapper.language_word_dict.get("set_ai_model_warring_title"),
70
+ language_wrapper.language_word_dict.get("set_ai_model_warring_text")
71
+ )
@@ -0,0 +1,68 @@
1
+ from PySide6.QtWidgets import QWidget, QBoxLayout, QLineEdit, QPushButton, QHBoxLayout, QMessageBox
2
+
3
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
4
+ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
5
+
6
+
7
+ class CreateFileDialog(QWidget):
8
+ """
9
+ 建立新檔案的對話框
10
+ Dialog for creating a new file
11
+ """
12
+
13
+ def __init__(self):
14
+ jeditor_logger.info("Init CreateFileDialog")
15
+ super().__init__()
16
+
17
+ # 垂直佈局 (上到下)
18
+ # Vertical layout (top to bottom)
19
+ self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
20
+
21
+ # 檔案名稱輸入框 / File name input field
22
+ self.file_name_input = QLineEdit()
23
+
24
+ # 建立檔案按鈕 / Create file button
25
+ self.create_file_button = QPushButton()
26
+ self.create_file_button.setText(language_wrapper.language_word_dict.get("create_file_dialog_pushbutton"))
27
+ self.create_file_button.clicked.connect(self.create_file)
28
+
29
+ # 水平佈局 (放置按鈕) / Horizontal layout (for button)
30
+ self.box_h_layout = QHBoxLayout()
31
+ self.box_h_layout.addWidget(self.create_file_button)
32
+
33
+ # 將元件加入主佈局 / Add widgets to main layout
34
+ self.box_layout.addWidget(self.file_name_input)
35
+ self.box_layout.addLayout(self.box_h_layout)
36
+
37
+ # 設定視窗標題 / Set window title
38
+ self.setWindowTitle(language_wrapper.language_word_dict.get("create_file_dialog_pushbutton"))
39
+ self.setLayout(self.box_layout)
40
+
41
+ def create_file(self):
42
+ """
43
+ 建立檔案的邏輯:
44
+ 1. 檢查輸入是否為空
45
+ 2. 若為空,顯示警告訊息
46
+ 3. 否則建立新檔案並關閉對話框
47
+ File creation logic:
48
+ 1. Check if input is empty
49
+ 2. If empty, show warning message
50
+ 3. Otherwise, create new file and close dialog
51
+ """
52
+ jeditor_logger.info("CreateFileDialog create_file")
53
+ file_name = self.file_name_input.text().strip()
54
+
55
+ if file_name == "":
56
+ # 若未輸入檔名,顯示提示訊息
57
+ # Show warning if no file name is entered
58
+ create_file_message_box = QMessageBox(self)
59
+ create_file_message_box.setText(
60
+ language_wrapper.language_word_dict.get("input_file_name_dialog_pushbutton")
61
+ )
62
+ create_file_message_box.show()
63
+ else:
64
+ # 建立新檔案 (若存在則覆蓋)
65
+ # Create new file (overwrite if exists)
66
+ with open(file_name, "w+") as file:
67
+ file.write("")
68
+ self.close() # 關閉對話框 / close dialog
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import sys
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING
7
+
8
+ from je_editor.pyside_ui.code.auto_save.auto_save_manager import init_new_auto_save_thread, file_is_open_manager_dict
9
+ from je_editor.pyside_ui.main_ui.save_settings.user_setting_file import user_setting_dict, read_user_setting
10
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
11
+ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
12
+ from je_editor.utils.venv_check.check_venv import check_and_choose_venv
13
+
14
+ if TYPE_CHECKING:
15
+ # 僅在型別檢查時匯入,避免循環依賴
16
+ # Only imported during type checking to avoid circular imports
17
+ from je_editor.pyside_ui.main_ui.main_editor import EditorMain
18
+
19
+ from PySide6.QtWidgets import QFileDialog
20
+
21
+ from je_editor.pyside_ui.main_ui.editor.editor_widget import EditorWidget
22
+ from je_editor.utils.file.open.open_file import read_file
23
+
24
+
25
+ def choose_file_get_open_file_path(parent_qt_instance: EditorMain) -> None:
26
+ """
27
+ 開啟檔案並將內容載入編輯器
28
+ Open file and set code edit content
29
+ :param parent_qt_instance: Pyside 主視窗 / Pyside parent
30
+ :return: None
31
+ """
32
+ jeditor_logger.info("open_file_dialog.py choose_file_get_open_file_path"
33
+ f" parent_qt_instance: {parent_qt_instance}")
34
+ widget = parent_qt_instance.tab_widget.currentWidget()
35
+ if isinstance(widget, EditorWidget):
36
+ # 開啟檔案選擇對話框 / Open file dialog
37
+ file_path = QFileDialog().getOpenFileName(
38
+ parent=parent_qt_instance,
39
+ dir=str(Path.cwd()),
40
+ filter="""Python file (*.py);;
41
+ HTML file (*.html);;
42
+ File (*.*)"""
43
+ )[0]
44
+
45
+ if file_path is not None and file_path != "":
46
+ # 檢查檔案是否已經開啟 / Check if file already opened
47
+ if file_is_open_manager_dict.get(str(Path(file_path)), None) is not None:
48
+ widget.tab_manager.setCurrentWidget(
49
+ widget.tab_manager.findChild(EditorWidget, str(Path(file_path).name)))
50
+ return
51
+ else:
52
+ # 記錄已開啟檔案 / Register opened file
53
+ file_is_open_manager_dict.update({file_path: str(Path(file_path).name)})
54
+
55
+ # 設定目前檔案路徑 / Set current file path
56
+ widget.current_file = file_path
57
+ # 讀取檔案內容 / Read file content
58
+ file_content = read_file(file_path)[1]
59
+ widget.code_edit.setPlainText(file_content)
60
+
61
+ # 啟動自動儲存執行緒 / Start auto-save thread
62
+ if widget.current_file is not None and widget.code_save_thread is None:
63
+ init_new_auto_save_thread(widget.current_file, widget)
64
+ else:
65
+ widget.code_save_thread.file = widget.current_file
66
+
67
+ # 更新使用者設定中的最後開啟檔案 / Update last opened file in user settings
68
+ user_setting_dict.update({"last_file": str(widget.current_file)})
69
+ # 更新分頁標題 / Rename tab title
70
+ widget.rename_self_tab()
71
+
72
+
73
+ def choose_dir_get_dir_path(parent_qt_instance: EditorMain) -> None:
74
+ """
75
+ 選擇資料夾並更新工作目錄與專案樹
76
+ Choose directory and update working dir and project tree
77
+ """
78
+ jeditor_logger.info("open_file_dialog.py choose_dir_get_dir_path"
79
+ f" parent_qt_instance: {parent_qt_instance}")
80
+ dir_path = QFileDialog().getExistingDirectory(parent=parent_qt_instance, )
81
+ if dir_path != "":
82
+ check_path = Path(dir_path)
83
+ else:
84
+ return
85
+
86
+ if check_path.exists() and check_path.is_dir():
87
+ # 更新工作目錄 / Update working directory
88
+ parent_qt_instance.working_dir = dir_path
89
+ os.chdir(dir_path)
90
+
91
+ # 更新所有編輯器的專案樹與環境檢查 / Update project tree and check env for all editors
92
+ for code_editor in range(parent_qt_instance.tab_widget.count()):
93
+ widget = parent_qt_instance.tab_widget.widget(code_editor)
94
+ if isinstance(widget, EditorWidget):
95
+ widget.project_treeview.setRootIndex(widget.project_treeview_model.index(dir_path))
96
+ widget.code_edit.check_env()
97
+
98
+ # 設定虛擬環境路徑 / Set virtual environment path
99
+ if sys.platform in ["win32", "cygwin", "msys"]:
100
+ venv_path = Path(os.getcwd() + "/venv/Scripts")
101
+ else:
102
+ venv_path = Path(os.getcwd() + "/venv/bin")
103
+
104
+ parent_qt_instance.python_compiler = check_and_choose_venv(venv_path)
105
+
106
+ # 重新讀取使用者設定並套用啟動設定 / Reload user settings and apply startup settings
107
+ read_user_setting()
108
+ parent_qt_instance.startup_setting()
109
+
110
+ # 重設語言設定 / Reset language
111
+ language_wrapper.reset_language(user_setting_dict.get("language", "English"))
@@ -0,0 +1,67 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING
6
+
7
+ from je_editor.pyside_ui.code.auto_save.auto_save_manager import file_is_open_manager_dict
8
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
9
+
10
+ if TYPE_CHECKING:
11
+ # 僅在型別檢查時匯入,避免循環依賴
12
+ # Only imported during type checking to avoid circular imports
13
+ from je_editor.pyside_ui.main_ui.main_editor import EditorMain
14
+
15
+ from PySide6.QtWidgets import QFileDialog
16
+
17
+ from je_editor.pyside_ui.main_ui.editor.editor_widget import EditorWidget
18
+ from je_editor.utils.file.save.save_file import write_file
19
+
20
+
21
+ def choose_file_get_save_file_path(parent_qt_instance: EditorMain) -> bool:
22
+ """
23
+ 開啟「另存新檔」對話框,將編輯器內容儲存到檔案
24
+ Open "Save As" dialog and save editor content to file
25
+ :param parent_qt_instance: Pyside 主視窗 / Pyside parent
26
+ :return: 是否成功儲存檔案 / whether file was saved successfully
27
+ """
28
+ jeditor_logger.info("save_file_dialog.py choose_file_get_save_file_path"
29
+ f" parent_qt_instance: {parent_qt_instance}")
30
+
31
+ # 取得目前分頁的編輯器元件
32
+ # Get current tab's editor widget
33
+ widget = parent_qt_instance.tab_widget.currentWidget()
34
+
35
+ if isinstance(widget, EditorWidget):
36
+ # 開啟檔案儲存對話框 / Open file save dialog
37
+ file_path = QFileDialog().getSaveFileName(
38
+ parent=parent_qt_instance,
39
+ dir=os.getcwd(),
40
+ filter="""Python file (*.py);;
41
+ HTML file (*.html);;
42
+ File (*.*)"""
43
+ )[0]
44
+
45
+ # 確認使用者有選擇檔案路徑 / Ensure user selected a file path
46
+ if file_path is not None and file_path != "":
47
+ # 更新目前檔案路徑 / Update current file path
48
+ widget.current_file = file_path
49
+
50
+ # 將編輯器內容寫入檔案 / Write editor content to file
51
+ write_file(file_path, widget.code_edit.toPlainText())
52
+
53
+ # 更新已開啟檔案管理字典 / Update opened file manager dictionary
54
+ path = Path(file_path)
55
+ file_is_open_manager_dict.update({str(path): str(path.name)})
56
+
57
+ # 若有自動儲存執行緒,更新其監控的檔案與編輯器
58
+ # If auto-save thread exists, update its file and editor reference
59
+ if widget.code_save_thread is not None:
60
+ widget.code_save_thread.file = file_path
61
+ widget.code_save_thread.editor = widget.code_edit
62
+
63
+ # 更新分頁標題 / Update tab title
64
+ widget.rename_self_tab()
65
+ return True
66
+ return False
67
+ return False
@@ -0,0 +1,49 @@
1
+ from PySide6.QtWidgets import QWidget, QLineEdit, QPushButton, QBoxLayout, QHBoxLayout
2
+
3
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
4
+ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
5
+
6
+
7
+ class SearchResultBox(QWidget):
8
+ """
9
+ 搜尋結果對話框
10
+ Search result dialog box
11
+ """
12
+
13
+ def __init__(self):
14
+ jeditor_logger.info("Init SearchResultBox")
15
+ super().__init__()
16
+
17
+ # 垂直佈局 (上到下)
18
+ # Vertical layout (top to bottom)
19
+ self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
20
+
21
+ # 搜尋輸入框 / Search input field
22
+ self.search_input = QLineEdit()
23
+
24
+ # 下一個搜尋按鈕 / Next search button
25
+ self.search_next_button = QPushButton()
26
+ self.search_next_button.setText(language_wrapper.language_word_dict.get("search_next_dialog_pushbutton"))
27
+
28
+ # 上一個搜尋按鈕 / Previous search button
29
+ self.search_back_button = QPushButton()
30
+ self.search_back_button.setText(language_wrapper.language_word_dict.get("search_back_dialog_pushbutton"))
31
+
32
+ # 水平佈局 (放置前後搜尋按鈕)
33
+ # Horizontal layout (for back/next buttons)
34
+ self.box_h_layout = QHBoxLayout()
35
+ self.box_h_layout.addWidget(self.search_back_button)
36
+ self.box_h_layout.addWidget(self.search_next_button)
37
+
38
+ # 將元件加入主佈局
39
+ # Add widgets to main layout
40
+ self.box_layout.addWidget(self.search_input)
41
+ self.box_layout.addLayout(self.box_h_layout)
42
+
43
+ # 設定視窗標題
44
+ # Set window title
45
+ self.setWindowTitle("Search Result")
46
+
47
+ # 套用主佈局
48
+ # Apply main layout
49
+ self.setLayout(self.box_layout)
@@ -0,0 +1,49 @@
1
+ from PySide6.QtWidgets import QWidget, QLineEdit, QPushButton, QBoxLayout, QHBoxLayout
2
+
3
+ from je_editor.utils.logging.loggin_instance import jeditor_logger
4
+ from je_editor.utils.multi_language.multi_language_wrapper import language_wrapper
5
+
6
+
7
+ class SearchBox(QWidget):
8
+ """
9
+ 搜尋框元件
10
+ Search box widget
11
+ """
12
+
13
+ def __init__(self):
14
+ jeditor_logger.info("Init SearchBox")
15
+ super().__init__()
16
+
17
+ # 垂直佈局 (上到下)
18
+ # Vertical layout (top to bottom)
19
+ self.box_layout = QBoxLayout(QBoxLayout.Direction.TopToBottom)
20
+
21
+ # 搜尋輸入框 / Search input field
22
+ self.search_input = QLineEdit()
23
+
24
+ # 下一個搜尋按鈕 / Next search button
25
+ self.search_next_button = QPushButton()
26
+ self.search_next_button.setText(language_wrapper.language_word_dict.get("search_next_dialog_pushbutton"))
27
+
28
+ # 上一個搜尋按鈕 / Previous search button
29
+ self.search_back_button = QPushButton()
30
+ self.search_back_button.setText(language_wrapper.language_word_dict.get("search_back_dialog_pushbutton"))
31
+
32
+ # 水平佈局 (放置前後搜尋按鈕)
33
+ # Horizontal layout (for back/next buttons)
34
+ self.box_h_layout = QHBoxLayout()
35
+ self.box_h_layout.addWidget(self.search_back_button)
36
+ self.box_h_layout.addWidget(self.search_next_button)
37
+
38
+ # 將元件加入主佈局
39
+ # Add widgets to main layout
40
+ self.box_layout.addWidget(self.search_input)
41
+ self.box_layout.addLayout(self.box_h_layout)
42
+
43
+ # 設定視窗標題 (支援多語系)
44
+ # Set window title (multi-language support)
45
+ self.setWindowTitle(language_wrapper.language_word_dict.get("search_box_dialog_title"))
46
+
47
+ # 套用主佈局
48
+ # Apply main layout
49
+ self.setLayout(self.box_layout)
@@ -0,0 +1,90 @@
1
+ from PySide6.QtGui import QAction, QActionGroup
2
+ from PySide6.QtWidgets import (
3
+ QWidget, QVBoxLayout, QMenuBar, QFileDialog, QMessageBox
4
+ )
5
+ from git import Repo
6
+
7
+ from je_editor.pyside_ui.git_ui.code_diff_compare.multi_file_diff_viewer import MultiFileDiffViewer
8
+
9
+
10
+ class DiffViewerWidget(QWidget):
11
+ """
12
+ Git Diff Viewer Application
13
+ Git 差異檢視器應用程式
14
+ """
15
+
16
+ def __init__(self):
17
+ super().__init__()
18
+ self.setWindowTitle("Git Diff Viewer")
19
+
20
+ # === Main diff viewer widget / 主要的差異檢視元件 ===
21
+ self.viewer = MultiFileDiffViewer()
22
+
23
+ # === Menu bar / 選單列 ===
24
+ self.menubar = QMenuBar(self)
25
+ file_menu = self.menubar.addMenu("File") # File 選單
26
+ view_menu = self.menubar.addMenu("View") # View 選單
27
+
28
+ # --- File → Open Git Repo ---
29
+ # Action to open a Git repository
30
+ # 開啟 Git 專案的動作
31
+ open_repo_action = QAction("Open Git Repo", self)
32
+ open_repo_action.triggered.connect(self.open_repo)
33
+ file_menu.addAction(open_repo_action)
34
+
35
+ # === View → Theme switching (exclusive) / 主題切換(單選模式) ===
36
+ theme_group = QActionGroup(self)
37
+ theme_group.setExclusive(True) # 確保只能選擇一個主題 / ensure only one theme can be selected
38
+
39
+ dark_action = QAction("Dark Mode", self, checkable=True) # 深色模式 / dark mode
40
+ light_action = QAction("Light Mode", self, checkable=True) # 淺色模式 / light mode
41
+
42
+ theme_group.addAction(dark_action)
43
+ theme_group.addAction(light_action)
44
+
45
+ # Default to dark mode / 預設為深色模式
46
+ dark_action.setChecked(True)
47
+ self.viewer.set_dark_theme()
48
+
49
+ # Connect theme actions / 綁定主題切換事件
50
+ dark_action.triggered.connect(lambda: self.set_theme("dark"))
51
+ light_action.triggered.connect(lambda: self.set_theme("light"))
52
+
53
+ view_menu.addAction(dark_action)
54
+ view_menu.addAction(light_action)
55
+
56
+ # === Layout / 版面配置 ===
57
+ layout = QVBoxLayout(self)
58
+ layout.setMenuBar(self.menubar) # 把選單列放在上方 / put menu bar at the top
59
+ layout.addWidget(self.viewer) # 把差異檢視器放在主要區域 / add diff viewer in main area
60
+
61
+ def open_repo(self):
62
+ """
63
+ Open a Git repository and display its diff.
64
+ 開啟一個 Git 專案並顯示差異。
65
+ """
66
+ path = QFileDialog.getExistingDirectory(self, "Select Git Repository")
67
+ if not path:
68
+ return
69
+ try:
70
+ repo = Repo(path) # 嘗試載入 Git 專案 / try to load Git repo
71
+ diff_text = repo.git.diff() # 取得差異文字 / get diff text
72
+ if not diff_text.strip():
73
+ # 如果沒有差異,顯示提示訊息 / show info if no changes
74
+ QMessageBox.information(self, "Info", "No changes in repo.")
75
+ else:
76
+ # 如果有差異,顯示在 viewer 中 / show diff in viewer
77
+ self.viewer.set_diff_text(diff_text)
78
+ except Exception as e:
79
+ # 如果開啟失敗,顯示錯誤訊息 / show error if failed
80
+ QMessageBox.critical(self, "Error", f"Failed to open repo:\n{e}")
81
+
82
+ def set_theme(self, mode: str):
83
+ """
84
+ Switch between dark and light themes.
85
+ 切換深色與淺色主題。
86
+ """
87
+ if mode == "dark":
88
+ self.viewer.set_dark_theme()
89
+ else:
90
+ self.viewer.set_light_theme()