pygpt-net 2.6.20__py3-none-any.whl → 2.6.22__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.
- pygpt_net/CHANGELOG.txt +13 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +3 -1
- pygpt_net/controller/__init__.py +4 -8
- pygpt_net/controller/access/voice.py +2 -2
- pygpt_net/controller/agent/agent.py +130 -2
- pygpt_net/controller/agent/experts.py +93 -96
- pygpt_net/controller/agent/llama.py +2 -1
- pygpt_net/controller/assistant/assistant.py +18 -1
- pygpt_net/controller/assistant/batch.py +2 -3
- pygpt_net/controller/assistant/editor.py +2 -2
- pygpt_net/controller/assistant/files.py +2 -3
- pygpt_net/controller/assistant/store.py +2 -2
- pygpt_net/controller/attachment/attachment.py +17 -1
- pygpt_net/controller/audio/audio.py +2 -2
- pygpt_net/controller/camera/camera.py +15 -7
- pygpt_net/controller/chat/chat.py +2 -2
- pygpt_net/controller/chat/common.py +50 -33
- pygpt_net/controller/chat/image.py +67 -77
- pygpt_net/controller/chat/input.py +94 -166
- pygpt_net/controller/chat/output.py +83 -140
- pygpt_net/controller/chat/response.py +83 -102
- pygpt_net/controller/chat/text.py +116 -149
- pygpt_net/controller/ctx/common.py +2 -1
- pygpt_net/controller/ctx/ctx.py +87 -6
- pygpt_net/controller/files/files.py +13 -1
- pygpt_net/controller/idx/idx.py +26 -2
- pygpt_net/controller/idx/indexer.py +85 -76
- pygpt_net/controller/kernel/reply.py +53 -66
- pygpt_net/controller/kernel/stack.py +16 -16
- pygpt_net/controller/lang/lang.py +52 -34
- pygpt_net/controller/model/importer.py +3 -2
- pygpt_net/controller/model/model.py +62 -3
- pygpt_net/controller/notepad/notepad.py +86 -84
- pygpt_net/controller/plugins/settings.py +3 -4
- pygpt_net/controller/settings/editor.py +4 -4
- pygpt_net/controller/settings/profile.py +105 -124
- pygpt_net/controller/theme/menu.py +154 -57
- pygpt_net/controller/theme/nodes.py +51 -44
- pygpt_net/controller/theme/theme.py +33 -9
- pygpt_net/controller/tools/tools.py +2 -2
- pygpt_net/controller/ui/tabs.py +2 -3
- pygpt_net/controller/ui/ui.py +16 -2
- pygpt_net/core/agents/observer/evaluation.py +3 -3
- pygpt_net/core/agents/provider.py +25 -3
- pygpt_net/core/agents/runner.py +4 -1
- pygpt_net/core/agents/runners/llama_workflow.py +19 -7
- pygpt_net/core/agents/runners/loop.py +3 -1
- pygpt_net/core/agents/runners/openai_workflow.py +17 -3
- pygpt_net/core/agents/tools.py +4 -1
- pygpt_net/core/bridge/context.py +34 -37
- pygpt_net/core/ctx/container.py +13 -12
- pygpt_net/core/ctx/ctx.py +1 -1
- pygpt_net/core/ctx/output.py +7 -4
- pygpt_net/core/db/database.py +2 -2
- pygpt_net/core/debug/console/console.py +2 -2
- pygpt_net/core/debug/debug.py +12 -1
- pygpt_net/core/dispatcher/dispatcher.py +24 -1
- pygpt_net/core/events/app.py +7 -7
- pygpt_net/core/events/control.py +26 -26
- pygpt_net/core/events/event.py +6 -3
- pygpt_net/core/events/kernel.py +2 -2
- pygpt_net/core/events/render.py +13 -13
- pygpt_net/core/experts/experts.py +76 -82
- pygpt_net/core/experts/worker.py +12 -12
- pygpt_net/core/filesystem/actions.py +1 -2
- pygpt_net/core/models/models.py +5 -1
- pygpt_net/core/models/ollama.py +14 -5
- pygpt_net/core/render/plain/helpers.py +2 -5
- pygpt_net/core/render/plain/renderer.py +26 -30
- pygpt_net/core/render/web/body.py +1 -1
- pygpt_net/core/render/web/helpers.py +2 -2
- pygpt_net/core/render/web/renderer.py +4 -4
- pygpt_net/core/settings/settings.py +43 -13
- pygpt_net/core/tabs/tabs.py +20 -13
- pygpt_net/core/types/__init__.py +2 -1
- pygpt_net/core/types/agent.py +4 -4
- pygpt_net/core/types/base.py +19 -0
- pygpt_net/core/types/console.py +6 -6
- pygpt_net/core/types/mode.py +8 -8
- pygpt_net/core/types/multimodal.py +3 -3
- pygpt_net/core/types/openai.py +2 -1
- pygpt_net/data/config/config.json +5 -5
- pygpt_net/data/config/models.json +19 -3
- pygpt_net/data/config/settings.json +14 -14
- pygpt_net/data/locale/locale.de.ini +4 -1
- pygpt_net/data/locale/locale.en.ini +6 -3
- pygpt_net/data/locale/locale.es.ini +4 -1
- pygpt_net/data/locale/locale.fr.ini +4 -1
- pygpt_net/data/locale/locale.it.ini +4 -1
- pygpt_net/data/locale/locale.pl.ini +5 -4
- pygpt_net/data/locale/locale.uk.ini +4 -1
- pygpt_net/data/locale/locale.zh.ini +4 -1
- pygpt_net/item/ctx.py +256 -240
- pygpt_net/item/model.py +59 -116
- pygpt_net/item/preset.py +122 -105
- pygpt_net/plugin/twitter/plugin.py +2 -2
- pygpt_net/provider/agents/llama_index/workflow/planner.py +3 -3
- pygpt_net/provider/agents/openai/agent.py +4 -12
- pygpt_net/provider/agents/openai/agent_b2b.py +10 -15
- pygpt_net/provider/agents/openai/agent_planner.py +4 -4
- pygpt_net/provider/agents/openai/agent_with_experts.py +3 -7
- pygpt_net/provider/agents/openai/agent_with_experts_feedback.py +4 -8
- pygpt_net/provider/agents/openai/agent_with_feedback.py +4 -8
- pygpt_net/provider/agents/openai/bot_researcher.py +2 -18
- pygpt_net/provider/agents/openai/bots/__init__.py +0 -0
- pygpt_net/provider/agents/openai/bots/research_bot/__init__.py +0 -0
- pygpt_net/provider/agents/openai/bots/research_bot/agents/__init__.py +0 -0
- pygpt_net/provider/agents/openai/bots/research_bot/agents/planner_agent.py +1 -1
- pygpt_net/provider/agents/openai/bots/research_bot/agents/search_agent.py +1 -0
- pygpt_net/provider/agents/openai/bots/research_bot/agents/writer_agent.py +1 -1
- pygpt_net/provider/agents/openai/bots/research_bot/manager.py +1 -10
- pygpt_net/provider/agents/openai/evolve.py +5 -9
- pygpt_net/provider/agents/openai/supervisor.py +4 -8
- pygpt_net/provider/core/config/patch.py +10 -3
- pygpt_net/provider/core/ctx/db_sqlite/utils.py +43 -43
- pygpt_net/provider/core/model/patch.py +11 -1
- pygpt_net/provider/core/preset/json_file.py +47 -49
- pygpt_net/provider/gpt/agents/experts.py +2 -2
- pygpt_net/tools/audio_transcriber/ui/dialogs.py +44 -54
- pygpt_net/tools/code_interpreter/body.py +1 -2
- pygpt_net/tools/code_interpreter/tool.py +7 -4
- pygpt_net/tools/code_interpreter/ui/html.py +1 -3
- pygpt_net/tools/code_interpreter/ui/widgets.py +2 -3
- pygpt_net/tools/html_canvas/ui/widgets.py +1 -3
- pygpt_net/tools/image_viewer/ui/dialogs.py +40 -37
- pygpt_net/tools/indexer/ui/widgets.py +2 -4
- pygpt_net/tools/media_player/tool.py +2 -5
- pygpt_net/tools/media_player/ui/widgets.py +60 -36
- pygpt_net/tools/text_editor/ui/widgets.py +18 -19
- pygpt_net/tools/translator/ui/widgets.py +39 -35
- pygpt_net/ui/base/context_menu.py +9 -4
- pygpt_net/ui/dialog/db.py +1 -3
- pygpt_net/ui/dialog/models.py +1 -3
- pygpt_net/ui/dialog/models_importer.py +2 -4
- pygpt_net/ui/dialogs.py +34 -30
- pygpt_net/ui/layout/chat/attachments.py +72 -84
- pygpt_net/ui/layout/chat/attachments_ctx.py +40 -44
- pygpt_net/ui/layout/chat/attachments_uploaded.py +36 -39
- pygpt_net/ui/layout/chat/calendar.py +100 -70
- pygpt_net/ui/layout/chat/chat.py +23 -17
- pygpt_net/ui/layout/chat/input.py +95 -118
- pygpt_net/ui/layout/chat/output.py +100 -162
- pygpt_net/ui/layout/chat/painter.py +89 -61
- pygpt_net/ui/layout/ctx/ctx_list.py +43 -52
- pygpt_net/ui/layout/status.py +23 -14
- pygpt_net/ui/layout/toolbox/agent.py +27 -38
- pygpt_net/ui/layout/toolbox/agent_llama.py +42 -45
- pygpt_net/ui/layout/toolbox/assistants.py +42 -38
- pygpt_net/ui/layout/toolbox/computer_env.py +32 -23
- pygpt_net/ui/layout/toolbox/footer.py +13 -16
- pygpt_net/ui/layout/toolbox/image.py +18 -21
- pygpt_net/ui/layout/toolbox/indexes.py +46 -89
- pygpt_net/ui/layout/toolbox/mode.py +20 -7
- pygpt_net/ui/layout/toolbox/model.py +12 -10
- pygpt_net/ui/layout/toolbox/presets.py +68 -52
- pygpt_net/ui/layout/toolbox/prompt.py +31 -58
- pygpt_net/ui/layout/toolbox/toolbox.py +25 -21
- pygpt_net/ui/layout/toolbox/vision.py +20 -22
- pygpt_net/ui/main.py +2 -4
- pygpt_net/ui/menu/about.py +64 -84
- pygpt_net/ui/menu/audio.py +87 -63
- pygpt_net/ui/menu/config.py +121 -127
- pygpt_net/ui/menu/debug.py +69 -76
- pygpt_net/ui/menu/file.py +32 -35
- pygpt_net/ui/menu/menu.py +2 -3
- pygpt_net/ui/menu/plugins.py +69 -33
- pygpt_net/ui/menu/theme.py +45 -46
- pygpt_net/ui/menu/tools.py +56 -60
- pygpt_net/ui/menu/video.py +20 -25
- pygpt_net/ui/tray.py +1 -2
- pygpt_net/ui/widget/audio/bar.py +1 -3
- pygpt_net/ui/widget/audio/input_button.py +3 -4
- pygpt_net/ui/widget/calendar/select.py +1 -2
- pygpt_net/ui/widget/dialog/base.py +12 -9
- pygpt_net/ui/widget/dialog/editor_file.py +20 -23
- pygpt_net/ui/widget/dialog/find.py +25 -24
- pygpt_net/ui/widget/dialog/profile.py +57 -53
- pygpt_net/ui/widget/draw/painter.py +62 -93
- pygpt_net/ui/widget/element/button.py +42 -30
- pygpt_net/ui/widget/element/checkbox.py +23 -15
- pygpt_net/ui/widget/element/group.py +6 -5
- pygpt_net/ui/widget/element/labels.py +1 -2
- pygpt_net/ui/widget/filesystem/explorer.py +93 -102
- pygpt_net/ui/widget/image/display.py +1 -2
- pygpt_net/ui/widget/lists/assistant.py +1 -2
- pygpt_net/ui/widget/lists/attachment.py +1 -2
- pygpt_net/ui/widget/lists/attachment_ctx.py +1 -2
- pygpt_net/ui/widget/lists/context.py +2 -4
- pygpt_net/ui/widget/lists/index.py +1 -2
- pygpt_net/ui/widget/lists/model.py +1 -2
- pygpt_net/ui/widget/lists/model_editor.py +1 -2
- pygpt_net/ui/widget/lists/model_importer.py +1 -2
- pygpt_net/ui/widget/lists/preset.py +1 -2
- pygpt_net/ui/widget/lists/preset_plugins.py +1 -2
- pygpt_net/ui/widget/lists/profile.py +1 -2
- pygpt_net/ui/widget/lists/uploaded.py +1 -2
- pygpt_net/ui/widget/option/checkbox.py +2 -4
- pygpt_net/ui/widget/option/checkbox_list.py +1 -4
- pygpt_net/ui/widget/option/cmd.py +1 -4
- pygpt_net/ui/widget/option/dictionary.py +25 -28
- pygpt_net/ui/widget/option/input.py +1 -3
- pygpt_net/ui/widget/tabs/Input.py +16 -12
- pygpt_net/ui/widget/tabs/body.py +5 -3
- pygpt_net/ui/widget/tabs/layout.py +36 -25
- pygpt_net/ui/widget/tabs/output.py +96 -74
- pygpt_net/ui/widget/textarea/calendar_note.py +1 -2
- pygpt_net/ui/widget/textarea/editor.py +41 -73
- pygpt_net/ui/widget/textarea/find.py +11 -10
- pygpt_net/ui/widget/textarea/html.py +3 -6
- pygpt_net/ui/widget/textarea/input.py +63 -64
- pygpt_net/ui/widget/textarea/notepad.py +54 -38
- pygpt_net/ui/widget/textarea/output.py +65 -54
- pygpt_net/ui/widget/textarea/search_input.py +5 -4
- pygpt_net/ui/widget/textarea/web.py +2 -4
- pygpt_net/ui/widget/vision/camera.py +2 -31
- {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/METADATA +25 -154
- {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/RECORD +218 -217
- {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.20.dist-info → pygpt_net-2.6.22.dist-info}/entry_points.txt +0 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.08.
|
|
9
|
+
# Updated Date: 2025.08.24 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -77,22 +77,19 @@ class Profile:
|
|
|
77
77
|
|
|
78
78
|
if save_current:
|
|
79
79
|
print("Saving all settings in current profile...")
|
|
80
|
-
self.window.controller.settings.save_all(force=True)
|
|
81
|
-
# self.window.controller.layout.save() # save layout state
|
|
80
|
+
self.window.controller.settings.save_all(force=True)
|
|
82
81
|
self.window.core.config.profile.set_current(uuid)
|
|
83
82
|
|
|
84
|
-
# switch to profile workdir
|
|
85
83
|
path = self.window.core.config.profile.get_current_workdir()
|
|
86
84
|
if path and os.path.exists(path):
|
|
87
85
|
self.window.controller.settings.workdir.update(
|
|
88
86
|
path,
|
|
89
87
|
force=True,
|
|
90
88
|
profile_name=profile['name']
|
|
91
|
-
)
|
|
89
|
+
)
|
|
92
90
|
else:
|
|
93
91
|
self.after_update(profile['name'])
|
|
94
92
|
|
|
95
|
-
|
|
96
93
|
def after_update(
|
|
97
94
|
self,
|
|
98
95
|
name: str,
|
|
@@ -113,13 +110,11 @@ class Profile:
|
|
|
113
110
|
"""Select current profile on list"""
|
|
114
111
|
current = self.window.core.config.profile.get_current()
|
|
115
112
|
profiles = self.get_profiles()
|
|
116
|
-
idx
|
|
117
|
-
for uuid in profiles:
|
|
113
|
+
for idx, uuid in enumerate(profiles):
|
|
118
114
|
if uuid == current:
|
|
119
115
|
index = self.window.ui.models['profile.list'].index(idx, 0)
|
|
120
116
|
self.window.ui.nodes['profile.list'].setCurrentIndex(index)
|
|
121
117
|
break
|
|
122
|
-
idx += 1
|
|
123
118
|
|
|
124
119
|
def get_profiles(self) -> Dict[str, Dict[str, Any]]:
|
|
125
120
|
"""
|
|
@@ -131,15 +126,16 @@ class Profile:
|
|
|
131
126
|
|
|
132
127
|
def new(self):
|
|
133
128
|
"""New profile dialog"""
|
|
134
|
-
self.window.ui
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
129
|
+
ui = self.window.ui
|
|
130
|
+
ui.nodes['dialog.profile.checkbox.switch'].setVisible(True)
|
|
131
|
+
ui.dialog['profile.item'].checkboxes.setVisible(False)
|
|
132
|
+
ui.dialog['profile.item'].id = 'profile'
|
|
133
|
+
ui.dialog['profile.item'].uuid = None
|
|
134
|
+
ui.dialog['profile.item'].mode = 'create'
|
|
135
|
+
ui.dialog['profile.item'].path = ""
|
|
136
|
+
ui.dialog['profile.item'].input.setText("")
|
|
137
|
+
ui.dialog['profile.item'].prepare()
|
|
138
|
+
ui.dialog['profile.item'].show()
|
|
143
139
|
|
|
144
140
|
def edit(self, uuid: str):
|
|
145
141
|
"""
|
|
@@ -147,19 +143,20 @@ class Profile:
|
|
|
147
143
|
|
|
148
144
|
:param uuid: profile UUID
|
|
149
145
|
"""
|
|
150
|
-
self.window.ui
|
|
146
|
+
ui = self.window.ui
|
|
147
|
+
ui.nodes['dialog.profile.checkbox.switch'].setVisible(False)
|
|
151
148
|
profile = self.window.core.config.profile.get(uuid)
|
|
152
|
-
|
|
149
|
+
ui.dialog['profile.item'].checkboxes.setVisible(False)
|
|
153
150
|
if profile is None:
|
|
154
|
-
|
|
151
|
+
ui.dialogs.alert("Profile not found!")
|
|
155
152
|
return
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
153
|
+
ui.dialog['profile.item'].id = 'profile'
|
|
154
|
+
ui.dialog['profile.item'].uuid = uuid
|
|
155
|
+
ui.dialog['profile.item'].mode = 'edit'
|
|
156
|
+
ui.dialog['profile.item'].path = profile['workdir'].replace("%HOME%", str(Path.home()))
|
|
157
|
+
ui.dialog['profile.item'].input.setText(profile['name'])
|
|
158
|
+
ui.dialog['profile.item'].prepare()
|
|
159
|
+
ui.dialog['profile.item'].show()
|
|
163
160
|
|
|
164
161
|
def open(self, force: bool = False):
|
|
165
162
|
"""
|
|
@@ -207,84 +204,80 @@ class Profile:
|
|
|
207
204
|
:param path: profile workdir path
|
|
208
205
|
:param uuid: profile UUID (update and duplicate only)
|
|
209
206
|
"""
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
207
|
+
ui = self.window.ui
|
|
208
|
+
fs = self.window.core.filesystem
|
|
209
|
+
cfg = self.window.core.config.profile
|
|
210
|
+
current = cfg.get_current()
|
|
211
|
+
name_stripped = name.strip()
|
|
212
|
+
path_stripped = path.strip()
|
|
213
|
+
if name_stripped == "":
|
|
214
|
+
ui.dialogs.alert(trans("dialog.profile.alert.name.empty"))
|
|
213
215
|
return
|
|
214
|
-
if
|
|
215
|
-
|
|
216
|
+
if path_stripped == "":
|
|
217
|
+
ui.dialogs.alert(trans("dialog.profile.alert.path.empty"))
|
|
216
218
|
return
|
|
217
|
-
|
|
218
|
-
|
|
219
|
+
|
|
220
|
+
p = Path(path_stripped)
|
|
221
|
+
if not p.is_dir():
|
|
222
|
+
ui.dialogs.alert(trans("dialog.profile.alert.path.not_exists"))
|
|
219
223
|
return
|
|
220
224
|
|
|
221
|
-
if not
|
|
222
|
-
if not
|
|
223
|
-
|
|
225
|
+
if not fs.is_directory_empty(path_stripped):
|
|
226
|
+
if not fs.is_workdir_in_path(path_stripped):
|
|
227
|
+
ui.dialogs.alert(trans("dialog.profile.alert.duplicate.not_empty"))
|
|
224
228
|
return
|
|
225
229
|
|
|
226
230
|
if mode == 'create':
|
|
227
|
-
|
|
228
|
-
uuid = self.window.core.config.profile.add(name, path)
|
|
231
|
+
uuid = cfg.add(name_stripped, path_stripped)
|
|
229
232
|
self.window.update_status(trans("dialog.profile.status.created"))
|
|
230
|
-
if
|
|
233
|
+
if ui.nodes['dialog.profile.checkbox.switch'].isChecked():
|
|
231
234
|
QTimer.singleShot(100, lambda: self.after_create(uuid))
|
|
232
235
|
return
|
|
233
236
|
|
|
234
237
|
elif mode == 'edit':
|
|
235
|
-
|
|
236
|
-
profile = self.window.core.config.profile.get(uuid)
|
|
238
|
+
profile = cfg.get(uuid)
|
|
237
239
|
old_path = profile['workdir'].replace("%HOME%", str(Path.home()))
|
|
238
|
-
|
|
240
|
+
cfg.update_profile(uuid, name_stripped, path_stripped)
|
|
239
241
|
self.window.update_status(trans("dialog.profile.status.updated"))
|
|
240
|
-
|
|
241
|
-
# if current profile and path was changed then reload:
|
|
242
242
|
if uuid == current:
|
|
243
243
|
self.window.ui.update_title()
|
|
244
|
-
if old_path !=
|
|
244
|
+
if old_path != path_stripped:
|
|
245
245
|
self.switch(uuid, force=True)
|
|
246
246
|
|
|
247
247
|
elif mode == 'duplicate':
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
self.window.ui.dialogs.alert(trans("dialog.workdir.change.empty.alert"))
|
|
248
|
+
if not fs.is_directory_empty(path_stripped):
|
|
249
|
+
ui.dialogs.alert(trans("dialog.workdir.change.empty.alert"))
|
|
251
250
|
return
|
|
252
251
|
|
|
253
252
|
profiles = self.get_profiles()
|
|
254
253
|
if uuid not in profiles:
|
|
255
|
-
|
|
254
|
+
ui.dialogs.alert(trans("dialog.profile.alert.src.empty"))
|
|
256
255
|
return
|
|
257
256
|
profile = profiles[uuid]
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
self.window.ui.dialogs.alert(trans("dialog.profile.alert.path.same"))
|
|
258
|
+
if profile['workdir'].replace("%HOME%", str(Path.home())) == path_stripped:
|
|
259
|
+
ui.dialogs.alert(trans("dialog.profile.alert.path.same"))
|
|
262
260
|
return
|
|
263
261
|
|
|
264
|
-
# check free space
|
|
265
262
|
include_datadir = self.is_include_datadir()
|
|
266
263
|
include_db = self.is_include_db()
|
|
267
264
|
src_path = profile['workdir'].replace("%HOME%", str(Path.home()))
|
|
268
|
-
space_required =
|
|
265
|
+
space_required = fs.get_directory_size(src_path, human_readable=False)
|
|
269
266
|
if not include_datadir:
|
|
270
|
-
space_required -=
|
|
267
|
+
space_required -= fs.get_datadir_size(src_path, human_readable=False)
|
|
271
268
|
if not include_db:
|
|
272
|
-
space_required -=
|
|
273
|
-
space_free =
|
|
269
|
+
space_required -= fs.get_db_size(src_path, human_readable=False)
|
|
270
|
+
space_free = fs.get_free_disk_space(path_stripped, human_readable=False)
|
|
274
271
|
if space_required > space_free:
|
|
275
|
-
|
|
276
|
-
required=
|
|
277
|
-
free=
|
|
272
|
+
ui.dialogs.alert(trans("dialog.workdir.result.no_free_space").format(
|
|
273
|
+
required=fs.sizeof_fmt(space_required),
|
|
274
|
+
free=fs.sizeof_fmt(space_free),
|
|
278
275
|
))
|
|
279
276
|
return
|
|
280
277
|
|
|
281
|
-
|
|
282
|
-
self.duplicate(uuid, name, path)
|
|
278
|
+
self.duplicate(uuid, name_stripped, path_stripped)
|
|
283
279
|
self.window.update_status(trans("dialog.profile.status.duplicated"))
|
|
284
|
-
# if self.window.ui.nodes['dialog.profile.checkbox.switch'].isChecked():
|
|
285
|
-
# self.switch(uuid, force=True)
|
|
286
280
|
|
|
287
|
-
# close dialog and update list
|
|
288
281
|
self.window.ui.dialogs.close('profile.item')
|
|
289
282
|
self.update_menu()
|
|
290
283
|
self.update_list()
|
|
@@ -362,7 +355,6 @@ class Profile:
|
|
|
362
355
|
if uuid in profiles:
|
|
363
356
|
profile = profiles[uuid]
|
|
364
357
|
name = profile['name']
|
|
365
|
-
# remove profile
|
|
366
358
|
if self.window.core.config.profile.remove(uuid):
|
|
367
359
|
self.window.update_status(trans("dialog.profile.status.removed") + ": " + name)
|
|
368
360
|
self.update_list()
|
|
@@ -397,18 +389,20 @@ class Profile:
|
|
|
397
389
|
"""
|
|
398
390
|
uuid = self.get_id_by_idx(idx)
|
|
399
391
|
profile = self.window.core.config.profile.get(uuid)
|
|
400
|
-
self.window.ui.dialog['profile.item']
|
|
392
|
+
dialog = self.window.ui.dialog['profile.item']
|
|
393
|
+
dialog.checkboxes.setVisible(True)
|
|
401
394
|
if profile is None:
|
|
402
395
|
self.window.ui.dialogs.alert("Profile not found!")
|
|
403
396
|
return
|
|
404
397
|
self.window.ui.nodes['dialog.profile.checkbox.switch'].setVisible(True)
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
398
|
+
|
|
399
|
+
dialog.id = 'profile'
|
|
400
|
+
dialog.uuid = uuid
|
|
401
|
+
dialog.mode = 'duplicate'
|
|
402
|
+
dialog.path = ""
|
|
403
|
+
dialog.input.setText(profile['name'] + " - copy")
|
|
404
|
+
dialog.prepare()
|
|
405
|
+
dialog.show()
|
|
412
406
|
|
|
413
407
|
def delete_all(self, uuid: str):
|
|
414
408
|
"""
|
|
@@ -514,61 +508,48 @@ class Profile:
|
|
|
514
508
|
"""Setup profile menu"""
|
|
515
509
|
profiles = self.window.core.config.profile.get_all()
|
|
516
510
|
current = self.window.core.config.profile.get_current()
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
511
|
+
ui_menu = self.window.ui.menu
|
|
512
|
+
suffix = trans("profile.current.suffix")
|
|
513
|
+
for uuid, profile in profiles.items():
|
|
514
|
+
if uuid not in ui_menu['config.profiles']:
|
|
520
515
|
name = profile['name']
|
|
521
|
-
checked =
|
|
522
|
-
if
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
self.window.
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
checkable=True,
|
|
529
|
-
)
|
|
530
|
-
self.window.ui.menu['config.profiles'][uuid].setChecked(checked)
|
|
531
|
-
self.window.ui.menu['config.profiles'][uuid].triggered.connect(
|
|
532
|
-
lambda checked=True, uuid=uuid: self.window.controller.settings.profile.switch(uuid))
|
|
533
|
-
self.window.ui.menu['config.profile'].addAction(self.window.ui.menu['config.profiles'][uuid])
|
|
516
|
+
checked = uuid == current
|
|
517
|
+
text = f"{name} {suffix}" if checked else name
|
|
518
|
+
action = QAction(text, self.window, checkable=True)
|
|
519
|
+
action.setChecked(checked)
|
|
520
|
+
action.triggered.connect(lambda checked=False, u=uuid: self.window.controller.settings.profile.switch(u))
|
|
521
|
+
ui_menu['config.profiles'][uuid] = action
|
|
522
|
+
ui_menu['config.profile'].addAction(action)
|
|
534
523
|
|
|
535
524
|
def update_menu(self):
|
|
536
525
|
"""Update menu"""
|
|
537
526
|
profiles = self.window.core.config.profile.get_all()
|
|
538
527
|
current = self.window.core.config.profile.get_current()
|
|
539
|
-
|
|
528
|
+
ui_menu = self.window.ui.menu
|
|
529
|
+
profile_actions = ui_menu['config.profiles']
|
|
530
|
+
suffix = trans("profile.current.suffix")
|
|
531
|
+
|
|
532
|
+
for uuid in list(profile_actions.keys()):
|
|
540
533
|
if uuid in profiles:
|
|
541
534
|
name = profiles[uuid]['name']
|
|
542
|
-
|
|
543
|
-
if
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
for uuid in list(profiles.keys()):
|
|
551
|
-
if uuid not in self.window.ui.menu['config.profiles']:
|
|
552
|
-
profile = profiles[uuid]
|
|
535
|
+
is_current = uuid == current
|
|
536
|
+
text = f"{name} {suffix}" if is_current else name
|
|
537
|
+
action = profile_actions[uuid]
|
|
538
|
+
action.setText(text)
|
|
539
|
+
action.setChecked(is_current)
|
|
540
|
+
|
|
541
|
+
for uuid, profile in profiles.items():
|
|
542
|
+
if uuid not in profile_actions:
|
|
553
543
|
name = profile['name']
|
|
554
|
-
|
|
555
|
-
if
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
self.window.
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
self.window.ui.menu['config.profiles'][uuid].setChecked(checked)
|
|
564
|
-
self.window.ui.menu['config.profiles'][uuid].triggered.connect(
|
|
565
|
-
lambda checked=True, uuid=uuid: self.window.controller.settings.profile.switch(uuid)
|
|
566
|
-
)
|
|
567
|
-
self.window.ui.menu['config.profile'].addAction(self.window.ui.menu['config.profiles'][uuid])
|
|
568
|
-
|
|
569
|
-
# remove non-exist profiles
|
|
570
|
-
for uuid in list(self.window.ui.menu['config.profiles'].keys()):
|
|
544
|
+
is_current = uuid == current
|
|
545
|
+
text = f"{name} {suffix}" if is_current else name
|
|
546
|
+
action = QAction(text, self.window, checkable=True)
|
|
547
|
+
action.setChecked(is_current)
|
|
548
|
+
action.triggered.connect(lambda checked=False, u=uuid: self.window.controller.settings.profile.switch(u))
|
|
549
|
+
profile_actions[uuid] = action
|
|
550
|
+
ui_menu['config.profile'].addAction(action)
|
|
551
|
+
|
|
552
|
+
for uuid in list(profile_actions.keys()):
|
|
571
553
|
if uuid not in profiles:
|
|
572
|
-
|
|
573
|
-
del
|
|
574
|
-
|
|
554
|
+
ui_menu['config.profile'].removeAction(profile_actions[uuid])
|
|
555
|
+
del profile_actions[uuid]
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
|
7
7
|
# MIT License #
|
|
8
8
|
# Created By : Marcin Szczygliński #
|
|
9
|
-
# Updated Date: 2025.
|
|
9
|
+
# Updated Date: 2025.08.24 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
|
-
from PySide6.QtGui import QAction
|
|
12
|
+
from PySide6.QtGui import QAction, QActionGroup
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class Menu:
|
|
@@ -20,19 +20,65 @@ class Menu:
|
|
|
20
20
|
:param window: Window instance
|
|
21
21
|
"""
|
|
22
22
|
self.window = window
|
|
23
|
-
self.density_values =
|
|
23
|
+
self.density_values = (-2, -1, 0, 1, 2)
|
|
24
24
|
self.loaded = False
|
|
25
25
|
self.syntax_loaded = False
|
|
26
26
|
self.density_loaded = False
|
|
27
27
|
|
|
28
|
+
self._style_group = None
|
|
29
|
+
self._theme_group = None
|
|
30
|
+
self._density_group = None
|
|
31
|
+
self._syntax_group = None
|
|
32
|
+
|
|
33
|
+
def _on_style_triggered(self, action):
|
|
34
|
+
"""
|
|
35
|
+
Handle style change triggered by menu action
|
|
36
|
+
|
|
37
|
+
:param action: QAction instance
|
|
38
|
+
"""
|
|
39
|
+
self.window.controller.theme.toggle_style(action.data())
|
|
40
|
+
|
|
41
|
+
def _on_theme_triggered(self, action):
|
|
42
|
+
"""
|
|
43
|
+
Handle theme change triggered by menu action
|
|
44
|
+
|
|
45
|
+
:param action: QAction instance
|
|
46
|
+
"""
|
|
47
|
+
self.window.controller.theme.toggle_theme_by_menu(action.data())
|
|
48
|
+
|
|
49
|
+
def _on_syntax_triggered(self, action):
|
|
50
|
+
"""
|
|
51
|
+
Handle syntax highlight change triggered by menu action
|
|
52
|
+
|
|
53
|
+
:param action: QAction instance
|
|
54
|
+
"""
|
|
55
|
+
self.window.controller.theme.toggle_syntax(action.data(), update_menu=True)
|
|
56
|
+
|
|
57
|
+
def _on_density_triggered(self, action):
|
|
58
|
+
"""
|
|
59
|
+
Handle layout density change triggered by menu action
|
|
60
|
+
|
|
61
|
+
:param action: QAction instance
|
|
62
|
+
"""
|
|
63
|
+
self.window.controller.theme.toggle_option_by_menu('layout.density', action.data())
|
|
64
|
+
|
|
28
65
|
def setup_list(self):
|
|
29
66
|
"""Setup menu list"""
|
|
30
|
-
# setup themes list menu
|
|
31
67
|
if self.loaded:
|
|
32
68
|
return
|
|
33
69
|
|
|
34
|
-
|
|
35
|
-
|
|
70
|
+
w = self.window
|
|
71
|
+
menu = w.ui.menu
|
|
72
|
+
common = w.controller.theme.common
|
|
73
|
+
|
|
74
|
+
if self._style_group is None:
|
|
75
|
+
self._style_group = QActionGroup(w)
|
|
76
|
+
self._style_group.setExclusive(True)
|
|
77
|
+
self._style_group.triggered.connect(self._on_style_triggered)
|
|
78
|
+
|
|
79
|
+
styles = common.get_styles_list()
|
|
80
|
+
menu_style_dict = menu['theme_style']
|
|
81
|
+
menu_style = menu['theme.style']
|
|
36
82
|
for style in styles:
|
|
37
83
|
style_id = style.lower()
|
|
38
84
|
title = style.replace('_', ' ').title()
|
|
@@ -40,90 +86,141 @@ class Menu:
|
|
|
40
86
|
title = "ChatGPT"
|
|
41
87
|
elif title == "Chatgpt Wide":
|
|
42
88
|
title = "ChatGPT (wide)"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
self.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
89
|
+
act = QAction(title, w, checkable=True)
|
|
90
|
+
act.setData(style_id)
|
|
91
|
+
menu_style_dict[style_id] = act
|
|
92
|
+
self._style_group.addAction(act)
|
|
93
|
+
menu_style.addAction(act)
|
|
94
|
+
|
|
95
|
+
if self._theme_group is None:
|
|
96
|
+
self._theme_group = QActionGroup(w)
|
|
97
|
+
self._theme_group.setExclusive(True)
|
|
98
|
+
self._theme_group.triggered.connect(self._on_theme_triggered)
|
|
99
|
+
|
|
100
|
+
themes = common.get_themes_list()
|
|
101
|
+
themes += common.get_custom_themes_list()
|
|
52
102
|
themes.sort()
|
|
53
|
-
for theme in themes:
|
|
54
|
-
name = self.window.controller.theme.common.translate(theme)
|
|
55
|
-
self.window.ui.menu['theme'][theme] = QAction(name, self.window, checkable=True)
|
|
56
|
-
self.window.ui.menu['theme'][theme].triggered.connect(
|
|
57
|
-
lambda checked=None, theme=theme: self.window.controller.theme.toggle(theme))
|
|
58
103
|
|
|
59
|
-
|
|
104
|
+
menu_theme_dict = menu['theme']
|
|
105
|
+
menu_dark = menu['theme.dark']
|
|
106
|
+
menu_light = menu['theme.light']
|
|
107
|
+
for theme in themes:
|
|
108
|
+
name = common.translate(theme)
|
|
109
|
+
act = QAction(name, w, checkable=True)
|
|
110
|
+
act.setData(theme)
|
|
111
|
+
menu_theme_dict[theme] = act
|
|
112
|
+
self._theme_group.addAction(act)
|
|
60
113
|
if theme.startswith('dark'):
|
|
61
|
-
|
|
114
|
+
menu_dark.addAction(act)
|
|
62
115
|
elif theme.startswith('light'):
|
|
63
|
-
|
|
116
|
+
menu_light.addAction(act)
|
|
64
117
|
|
|
65
118
|
self.loaded = True
|
|
66
119
|
|
|
67
120
|
def setup_syntax(self):
|
|
68
121
|
"""Setup syntax menu"""
|
|
69
|
-
|
|
122
|
+
w = self.window
|
|
123
|
+
menu = w.ui.menu
|
|
124
|
+
|
|
125
|
+
styles = w.controller.chat.render.web_renderer.body.highlight.get_styles()
|
|
70
126
|
styles.sort()
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
127
|
+
|
|
128
|
+
if self.syntax_loaded:
|
|
129
|
+
existing = sorted(menu['theme_syntax'].keys())
|
|
130
|
+
if existing == styles:
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
menu_syntax_dict = menu['theme_syntax']
|
|
134
|
+
menu_syntax = menu['theme.syntax']
|
|
135
|
+
|
|
136
|
+
for act in menu_syntax_dict.values():
|
|
137
|
+
menu_syntax.removeAction(act)
|
|
138
|
+
act.deleteLater()
|
|
139
|
+
menu_syntax_dict.clear()
|
|
140
|
+
|
|
141
|
+
if self._syntax_group is None:
|
|
142
|
+
self._syntax_group = QActionGroup(w)
|
|
143
|
+
self._syntax_group.setExclusive(True)
|
|
144
|
+
self._syntax_group.triggered.connect(self._on_syntax_triggered)
|
|
145
|
+
|
|
75
146
|
for style in styles:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
self.
|
|
147
|
+
act = QAction(style, w, checkable=True)
|
|
148
|
+
act.setData(style)
|
|
149
|
+
menu_syntax_dict[style] = act
|
|
150
|
+
self._syntax_group.addAction(act)
|
|
151
|
+
menu_syntax.addAction(act)
|
|
152
|
+
|
|
153
|
+
self.syntax_loaded = True
|
|
80
154
|
|
|
81
155
|
def setup_density(self):
|
|
82
156
|
"""Setup menu list"""
|
|
83
157
|
if self.density_loaded:
|
|
84
158
|
return
|
|
85
|
-
|
|
86
|
-
|
|
159
|
+
|
|
160
|
+
w = self.window
|
|
161
|
+
menu = w.ui.menu
|
|
162
|
+
|
|
163
|
+
if self._density_group is None:
|
|
164
|
+
self._density_group = QActionGroup(w)
|
|
165
|
+
self._density_group.setExclusive(True)
|
|
166
|
+
self._density_group.triggered.connect(self._on_density_triggered)
|
|
167
|
+
|
|
168
|
+
current_density = w.core.config.get('layout.density')
|
|
169
|
+
menu_density_dict = menu['theme.layout.density']
|
|
170
|
+
menu_density = menu['theme.density']
|
|
171
|
+
|
|
87
172
|
for value in self.density_values:
|
|
88
173
|
name = str(value)
|
|
89
174
|
if value > 0:
|
|
90
175
|
name = '+' + name
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
self.
|
|
176
|
+
act = QAction(name, w, checkable=True)
|
|
177
|
+
act.setData(value)
|
|
178
|
+
menu_density_dict[value] = act
|
|
179
|
+
self._density_group.addAction(act)
|
|
180
|
+
menu_density.addAction(act)
|
|
95
181
|
if value == current_density:
|
|
96
|
-
|
|
182
|
+
act.setChecked(True)
|
|
183
|
+
|
|
97
184
|
self.density_loaded = True
|
|
98
185
|
|
|
99
186
|
def update_density(self):
|
|
100
187
|
"""Update layout density menu"""
|
|
101
188
|
current_density = self.window.core.config.get('layout.density')
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
189
|
+
items = self.window.ui.menu['theme.layout.density']
|
|
190
|
+
act = items.get(current_density)
|
|
191
|
+
if act is not None:
|
|
192
|
+
act.setChecked(True)
|
|
193
|
+
else:
|
|
194
|
+
for a in items.values():
|
|
195
|
+
a.setChecked(False)
|
|
106
196
|
|
|
107
197
|
def update_list(self):
|
|
108
198
|
"""Update theme list menu"""
|
|
109
|
-
# styles
|
|
110
199
|
current_style = self.window.core.config.get('theme.style')
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
|
|
200
|
+
style_items = self.window.ui.menu['theme_style']
|
|
201
|
+
act = style_items.get(current_style)
|
|
202
|
+
if act is not None:
|
|
203
|
+
act.setChecked(True)
|
|
204
|
+
else:
|
|
205
|
+
for a in style_items.values():
|
|
206
|
+
a.setChecked(False)
|
|
115
207
|
|
|
116
|
-
# color themes
|
|
117
208
|
current_theme = self.window.core.config.get('theme')
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if
|
|
121
|
-
|
|
209
|
+
theme_items = self.window.ui.menu['theme']
|
|
210
|
+
act = theme_items.get(current_theme)
|
|
211
|
+
if act is not None:
|
|
212
|
+
act.setChecked(True)
|
|
213
|
+
else:
|
|
214
|
+
for a in theme_items.values():
|
|
215
|
+
a.setChecked(False)
|
|
122
216
|
|
|
123
217
|
def update_syntax(self):
|
|
124
218
|
"""Update code syntax highlight menu"""
|
|
125
219
|
current = self.window.core.config.get('render.code_syntax')
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if
|
|
129
|
-
|
|
220
|
+
items = self.window.ui.menu['theme_syntax']
|
|
221
|
+
act = items.get(current)
|
|
222
|
+
if act is not None:
|
|
223
|
+
act.setChecked(True)
|
|
224
|
+
else:
|
|
225
|
+
for a in items.values():
|
|
226
|
+
a.setChecked(False)
|