pygpt-net 2.6.3__py3-none-any.whl → 2.6.6__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 (78) hide show
  1. pygpt_net/CHANGELOG.txt +14 -0
  2. pygpt_net/__init__.py +2 -2
  3. pygpt_net/app.py +6 -1
  4. pygpt_net/config.py +55 -65
  5. pygpt_net/controller/__init__.py +5 -2
  6. pygpt_net/controller/chat/chat.py +38 -35
  7. pygpt_net/controller/chat/render.py +144 -217
  8. pygpt_net/controller/chat/stream.py +52 -25
  9. pygpt_net/controller/config/config.py +39 -42
  10. pygpt_net/controller/config/field/checkbox.py +16 -12
  11. pygpt_net/controller/config/field/checkbox_list.py +36 -31
  12. pygpt_net/controller/config/field/cmd.py +51 -57
  13. pygpt_net/controller/config/field/combo.py +33 -16
  14. pygpt_net/controller/config/field/dictionary.py +48 -55
  15. pygpt_net/controller/config/field/input.py +50 -32
  16. pygpt_net/controller/config/field/slider.py +40 -45
  17. pygpt_net/controller/config/field/textarea.py +20 -6
  18. pygpt_net/controller/config/placeholder.py +110 -231
  19. pygpt_net/controller/ctx/common.py +48 -49
  20. pygpt_net/controller/ctx/ctx.py +24 -4
  21. pygpt_net/controller/lang/mapping.py +57 -95
  22. pygpt_net/controller/lang/plugins.py +64 -55
  23. pygpt_net/controller/lang/settings.py +39 -38
  24. pygpt_net/controller/layout/layout.py +11 -2
  25. pygpt_net/controller/plugins/plugins.py +19 -1
  26. pygpt_net/controller/settings/profile.py +16 -4
  27. pygpt_net/controller/ui/mode.py +107 -125
  28. pygpt_net/core/bridge/bridge.py +5 -5
  29. pygpt_net/core/command/command.py +149 -219
  30. pygpt_net/core/ctx/ctx.py +94 -146
  31. pygpt_net/core/debug/debug.py +48 -58
  32. pygpt_net/core/models/models.py +74 -112
  33. pygpt_net/core/modes/modes.py +13 -21
  34. pygpt_net/core/plugins/plugins.py +154 -177
  35. pygpt_net/core/presets/presets.py +103 -176
  36. pygpt_net/core/render/web/body.py +50 -39
  37. pygpt_net/core/render/web/renderer.py +154 -251
  38. pygpt_net/core/text/utils.py +28 -44
  39. pygpt_net/core/tokens/tokens.py +104 -203
  40. pygpt_net/data/config/config.json +3 -3
  41. pygpt_net/data/config/models.json +3 -3
  42. pygpt_net/item/ctx.py +141 -139
  43. pygpt_net/plugin/agent/plugin.py +2 -1
  44. pygpt_net/plugin/audio_output/plugin.py +5 -2
  45. pygpt_net/plugin/base/plugin.py +77 -93
  46. pygpt_net/plugin/bitbucket/plugin.py +3 -2
  47. pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
  48. pygpt_net/plugin/cmd_custom/plugin.py +3 -2
  49. pygpt_net/plugin/cmd_files/plugin.py +3 -2
  50. pygpt_net/plugin/cmd_history/plugin.py +3 -2
  51. pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
  52. pygpt_net/plugin/cmd_serial/plugin.py +3 -2
  53. pygpt_net/plugin/cmd_system/plugin.py +3 -6
  54. pygpt_net/plugin/cmd_web/plugin.py +3 -2
  55. pygpt_net/plugin/experts/plugin.py +2 -2
  56. pygpt_net/plugin/facebook/plugin.py +3 -4
  57. pygpt_net/plugin/github/plugin.py +4 -2
  58. pygpt_net/plugin/google/plugin.py +3 -3
  59. pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
  60. pygpt_net/plugin/mailer/plugin.py +3 -5
  61. pygpt_net/plugin/openai_vision/plugin.py +3 -2
  62. pygpt_net/plugin/real_time/plugin.py +52 -60
  63. pygpt_net/plugin/slack/plugin.py +3 -4
  64. pygpt_net/plugin/telegram/plugin.py +3 -4
  65. pygpt_net/plugin/twitter/plugin.py +3 -4
  66. pygpt_net/tools/code_interpreter/tool.py +0 -1
  67. pygpt_net/tools/translator/tool.py +1 -1
  68. pygpt_net/ui/layout/ctx/ctx_list.py +10 -6
  69. pygpt_net/ui/main.py +46 -30
  70. pygpt_net/ui/tray.py +61 -60
  71. pygpt_net/ui/widget/lists/context.py +2 -2
  72. pygpt_net/ui/widget/textarea/web.py +161 -48
  73. pygpt_net/utils.py +8 -1
  74. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/METADATA +16 -2
  75. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/RECORD +78 -78
  76. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/LICENSE +0 -0
  77. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/WHEEL +0 -0
  78. {pygpt_net-2.6.3.dist-info → pygpt_net-2.6.6.dist-info}/entry_points.txt +0 -0
@@ -6,12 +6,12 @@
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.15 03:00:00 #
9
+ # Updated Date: 2025.08.16 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Optional
13
13
 
14
- from PySide6.QtCore import QTimer
14
+ from PySide6.QtCore import QTimer, QSignalBlocker
15
15
  from PySide6.QtWidgets import QApplication
16
16
 
17
17
  from pygpt_net.core.tabs.tab import Tab
@@ -30,31 +30,27 @@ class Common:
30
30
  self.window = window
31
31
  self.summarizer = Summarizer(window)
32
32
 
33
+ def _update_ctx_no_scroll(self):
34
+ self.window.controller.ctx.update(no_scroll=True)
35
+
33
36
  def update_label_by_current(self):
34
37
  """Update ctx label from current ctx"""
35
38
  mode = self.window.core.ctx.get_mode()
36
-
37
- # if no ctx mode then use current mode
38
39
  if mode is None:
39
40
  mode = self.window.core.config.get('mode')
40
41
 
41
42
  label = trans('mode.' + mode)
42
43
 
43
- # append assistant name to ctx name label
44
44
  if mode == 'assistant':
45
- id = self.window.core.ctx.get_assistant()
46
- assistant = self.window.core.assistants.get_by_id(id)
45
+ assistants = self.window.core.assistants
46
+ assistant_id = self.window.core.ctx.get_assistant()
47
+ assistant = assistants.get_by_id(assistant_id) if assistant_id is not None else None
48
+ if assistant is None:
49
+ assistant_id = self.window.core.config.get('assistant')
50
+ assistant = assistants.get_by_id(assistant_id)
47
51
  if assistant is not None:
48
- # get ctx assistant
49
- label += ' (' + assistant.name + ')'
50
- else:
51
- # get current assistant
52
- id = self.window.core.config.get('assistant')
53
- assistant = self.window.core.assistants.get_by_id(id)
54
- if assistant is not None:
55
- label += ' (' + assistant.name + ')'
52
+ label = f'{label} ({assistant.name})'
56
53
 
57
- # update ctx label
58
54
  self.window.controller.ui.update_ctx_label(label)
59
55
 
60
56
  def update_label(
@@ -74,9 +70,7 @@ class Common:
74
70
  if mode == 'assistant' and assistant_id is not None:
75
71
  assistant = self.window.core.assistants.get_by_id(assistant_id)
76
72
  if assistant is not None:
77
- label += ' (' + assistant.name + ')'
78
-
79
- # update ctx label
73
+ label = f'{label} ({assistant.name})'
80
74
  self.window.controller.ui.update_ctx_label(label)
81
75
 
82
76
  def duplicate(self, meta_id: int):
@@ -87,11 +81,9 @@ class Common:
87
81
  """
88
82
  new_id = self.window.core.ctx.duplicate(meta_id)
89
83
  if new_id is not None:
90
- self.window.core.attachments.context.duplicate(meta_id, new_id) # copy attachments
91
- self.window.update_status(
92
- "Context duplicated, new ctx id: {}".format(new_id)
93
- )
94
- QTimer.singleShot(100, lambda: self.window.controller.ctx.update(no_scroll=True))
84
+ self.window.core.attachments.context.duplicate(meta_id, new_id)
85
+ self.window.update_status(f"Context duplicated, new ctx id: {new_id}")
86
+ QTimer.singleShot(100, self._update_ctx_no_scroll)
95
87
 
96
88
  def dismiss_rename(self):
97
89
  """Dismiss rename dialog"""
@@ -103,34 +95,42 @@ class Common:
103
95
 
104
96
  :param meta: CtxMeta instance
105
97
  """
106
- data_id = None
107
- title = None
108
- if meta:
109
- data_id = meta.id
110
- title = meta.name
98
+ data_id = meta.id if meta else None
99
+ title = meta.name if meta else None
111
100
  self.window.controller.ui.tabs.focus_by_type(Tab.TAB_CHAT, data_id=data_id, title=title, meta=meta)
112
101
 
113
102
  def restore_display_filter(self):
114
103
  """Restore display filter"""
115
- self.window.ui.nodes['filter.ctx.radio.all'].setChecked(False)
116
- self.window.ui.nodes['filter.ctx.radio.pinned'].setChecked(False)
117
- self.window.ui.nodes['filter.ctx.radio.indexed'].setChecked(False)
104
+ nodes = self.window.ui.nodes
105
+ all_radio = nodes['filter.ctx.radio.all']
106
+ pinned_radio = nodes['filter.ctx.radio.pinned']
107
+ indexed_radio = nodes['filter.ctx.radio.indexed']
108
+
109
+ try:
110
+ b1 = QSignalBlocker(all_radio)
111
+ b2 = QSignalBlocker(pinned_radio)
112
+ b3 = QSignalBlocker(indexed_radio)
113
+ except:
114
+ pass
115
+
116
+ all_radio.setChecked(False)
117
+ pinned_radio.setChecked(False)
118
+ indexed_radio.setChecked(False)
118
119
 
119
120
  if self.window.core.config.has('ctx.records.filter'):
120
- filter = self.window.core.config.get('ctx.records.filter')
121
- self.toggle_display_filter(filter)
121
+ filter_value = self.window.core.config.get('ctx.records.filter')
122
+ self.toggle_display_filter(filter_value)
122
123
 
123
- if filter == 'pinned':
124
- self.window.ui.nodes['filter.ctx.radio.pinned'].setChecked(True)
125
- elif filter == 'indexed':
126
- self.window.ui.nodes['filter.ctx.radio.indexed'].setChecked(True)
124
+ if filter_value == 'pinned':
125
+ pinned_radio.setChecked(True)
126
+ elif filter_value == 'indexed':
127
+ indexed_radio.setChecked(True)
127
128
  else:
128
- self.window.ui.nodes['filter.ctx.radio.all'].setChecked(True)
129
+ all_radio.setChecked(True)
129
130
  else:
130
- self.window.ui.nodes['filter.ctx.radio.all'].setChecked(True)
131
+ all_radio.setChecked(True)
131
132
  self.toggle_display_filter('all')
132
133
 
133
- # restore filters labels
134
134
  self.restore_filters_labels()
135
135
 
136
136
  def restore_filters_labels(self):
@@ -146,17 +146,16 @@ class Common:
146
146
 
147
147
  :param filter: Filter
148
148
  """
149
- filters = {}
150
149
  if filter == 'pinned':
151
- filters['is_important'] = {
152
- "mode": "=",
153
- "value": 1,
150
+ filters = {
151
+ 'is_important': {"mode": "=", "value": 1}
154
152
  }
155
153
  elif filter == 'indexed':
156
- filters['indexed_ts'] = {
157
- "mode": ">",
158
- "value": 0,
154
+ filters = {
155
+ 'indexed_ts': {"mode": ">", "value": 0}
159
156
  }
157
+ else:
158
+ filters = {}
160
159
 
161
160
  self.window.core.config.set("ctx.records.filter", filter)
162
161
  self.window.core.ctx.clear_tmp_meta()
@@ -169,7 +168,7 @@ class Common:
169
168
 
170
169
  :param id: context list idx
171
170
  """
172
- value = "@" + str(id)
171
+ value = f"@{id}"
173
172
  self.window.controller.chat.common.append_to_input(value, separator=" ")
174
173
  QApplication.clipboard().setText(value)
175
174
 
@@ -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.15 03:00:00 #
9
+ # Updated Date: 2025.08.16 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Optional, List
@@ -244,6 +244,19 @@ class Ctx:
244
244
  """Clean memory"""
245
245
  self.window.core.gpt.close() # clear gpt client
246
246
 
247
+ def new_in_group(
248
+ self,
249
+ group_id: Optional[int] = None,
250
+ force: bool = False
251
+ ):
252
+ """
253
+ Create new ctx in group
254
+
255
+ :param group_id: group ID
256
+ :param force: force context creation
257
+ """
258
+ QTimer.singleShot(10, lambda: self.new(force, group_id))
259
+
247
260
  def new(
248
261
  self,
249
262
  force: bool = False,
@@ -330,6 +343,7 @@ class Ctx:
330
343
  self.window.ui.contexts.ctx_list.update(
331
344
  'ctx.list',
332
345
  self.window.core.ctx.get_meta(reload),
346
+ expand=False,
333
347
  )
334
348
 
335
349
  def refresh(self, restore_model: bool = True):
@@ -948,7 +962,10 @@ class Ctx:
948
962
  self.window.core.ctx.update_meta_group_id(meta_id, group_id)
949
963
  self.group_id = group_id
950
964
  if update:
951
- self.update(no_scroll=True)
965
+ QTimer.singleShot(
966
+ 100,
967
+ lambda: self.update()
968
+ )
952
969
 
953
970
  def remove_from_group(self, meta_id):
954
971
  """
@@ -958,7 +975,10 @@ class Ctx:
958
975
  """
959
976
  self.window.core.ctx.update_meta_group_id(meta_id, None)
960
977
  self.group_id = None
961
- self.update(no_scroll=True)
978
+ QTimer.singleShot(
979
+ 100,
980
+ lambda: self.update(no_scroll=True)
981
+ )
962
982
 
963
983
  def new_group(
964
984
  self,
@@ -1001,7 +1021,7 @@ class Ctx:
1001
1021
  "Group '{}' created.".format(name)
1002
1022
  )
1003
1023
  self.window.ui.dialog['create'].close()
1004
- self.select_group(id)
1024
+ # self.select_group(id)
1005
1025
  self.group_id = id
1006
1026
 
1007
1027
  def rename_group(
@@ -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.02 20:00:00 #
9
+ # Updated Date: 2025.08.15 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from typing import Dict
@@ -24,76 +24,61 @@ class Mapping:
24
24
  self.window = window
25
25
  self.mapping = {}
26
26
 
27
+ def _apply_map(self, items, targets, getter_name: str, setter_name: str):
28
+ t = trans
29
+ get = getattr
30
+ for k, key in items.items():
31
+ w = targets.get(k)
32
+ if w is None:
33
+ continue
34
+ try:
35
+ v = t(key)
36
+ getter = get(w, getter_name, None)
37
+ setter = get(w, setter_name, None)
38
+ if setter is None:
39
+ continue
40
+ if getter:
41
+ try:
42
+ if getter() == v:
43
+ continue
44
+ except Exception:
45
+ pass
46
+ setter(v)
47
+ except Exception:
48
+ pass
49
+
27
50
  def apply(self):
28
51
  """Apply mapped keys"""
29
-
30
- # load locale mapping
31
- if len(self.mapping) == 0:
52
+ if not self.mapping:
32
53
  self.mapping = self.get_mapping()
33
54
 
34
- # nodes labels
35
- for k in self.mapping['nodes']:
36
- if k in self.window.ui.nodes:
55
+ ui = self.window.ui
56
+ m = self.mapping
57
+
58
+ self._apply_map(m['nodes'], ui.nodes, 'text', 'setText')
59
+ self._apply_map(m['menu.title'], ui.menu, 'title', 'setTitle')
60
+ self._apply_map(m['menu.text'], ui.menu, 'text', 'setText')
61
+ self._apply_map(m['menu.tooltip'], ui.menu, 'toolTip', 'setToolTip')
62
+ self._apply_map(m['dialog.title'], ui.dialog, 'windowTitle', 'setWindowTitle')
63
+ self._apply_map(m['tooltip'], ui.nodes, 'toolTip', 'setToolTip')
64
+ self._apply_map(m['placeholder'], ui.nodes, 'placeholderText', 'setPlaceholderText')
65
+
66
+ tab_tools = self.window.controller.tools.get_tab_tools()
67
+ t = trans
68
+ for k, v in tab_tools.items():
69
+ w = ui.menu.get(k)
70
+ if w is None:
71
+ continue
72
+ try:
73
+ val = t("output.tab." + v[0])
37
74
  try:
38
- self.window.ui.nodes[k].setText(trans(self.mapping['nodes'][k]))
39
- except:
40
- pass
41
-
42
- # menu title
43
- for k in self.mapping['menu.title']:
44
- if k in self.window.ui.menu:
45
- try:
46
- self.window.ui.menu[k].setTitle(trans(self.mapping['menu.title'][k]))
47
- except:
48
- pass
49
-
50
- # menu text
51
- for k in self.mapping['menu.text']:
52
- if k in self.window.ui.menu:
53
- try:
54
- self.window.ui.menu[k].setText(trans(self.mapping['menu.text'][k]))
55
- except:
56
- pass
57
-
58
- # menu tooltip
59
- for k in self.mapping['menu.tooltip']:
60
- if k in self.window.ui.menu:
61
- try:
62
- self.window.ui.menu[k].setToolTip(trans(self.mapping['menu.tooltip'][k]))
63
- except:
64
- pass
65
-
66
- # dialog title
67
- for k in self.mapping['dialog.title']:
68
- if k in self.window.ui.dialog:
69
- try:
70
- self.window.ui.dialog[k].setWindowTitle(trans(self.mapping['dialog.title'][k]))
71
- except:
72
- pass
73
-
74
- # tooltip
75
- for k in self.mapping['tooltip']:
76
- if k in self.window.ui.nodes:
77
- try:
78
- self.window.ui.nodes[k].setToolTip(trans(self.mapping['tooltip'][k]))
79
- except:
80
- pass
81
-
82
- # placeholder
83
- for k in self.mapping['placeholder']:
84
- if k in self.window.ui.nodes:
85
- try:
86
- self.window.ui.nodes[k].setPlaceholderText(trans(self.mapping['placeholder'][k]))
87
- except:
88
- pass
89
-
90
- # menu tab tools
91
- for k in self.window.controller.tools.get_tab_tools():
92
- if k in self.window.ui.menu:
93
- try:
94
- self.window.ui.menu[k].setText(trans("output.tab." + self.window.controller.tools.get_tab_tools()[k][0]))
95
- except:
75
+ if hasattr(w, 'text') and w.text() == val:
76
+ continue
77
+ except Exception:
96
78
  pass
79
+ w.setText(val)
80
+ except Exception:
81
+ pass
97
82
 
98
83
  def get_mapping(self) -> Dict[str, Dict[str, str]]:
99
84
  """
@@ -132,8 +117,6 @@ class Mapping:
132
117
  nodes['preset.presets.label'] = 'toolbox.presets.label'
133
118
  nodes['preset.agents.label'] = 'toolbox.agents.label'
134
119
  nodes['preset.experts.label'] = 'toolbox.experts.label'
135
- # nodes['preset.presets.new'] = 'preset.new'
136
- # nodes['preset.clear'] = 'preset.clear'
137
120
  nodes['preset.use'] = 'preset.use'
138
121
  nodes['cmd.enabled'] = 'cmd.enabled'
139
122
  nodes['toolbox.prompt.label'] = 'toolbox.prompt'
@@ -146,7 +129,6 @@ class Mapping:
146
129
  nodes["agent.auto_stop"] = "toolbox.agent.auto_stop.label"
147
130
  nodes["agent.continue"] = "toolbox.agent.continue.label"
148
131
  nodes['layout.split'] = "layout.split"
149
- # nodes["indexes.new"] = "idx.new"
150
132
 
151
133
  # input
152
134
  nodes['input.label'] = 'input.label'
@@ -171,7 +153,6 @@ class Mapping:
171
153
 
172
154
  # assistants
173
155
  nodes['assistants.label'] = 'toolbox.assistants.label'
174
- # nodes['assistants.new'] = 'assistant.new'
175
156
  nodes['assistants.import'] = 'assistant.import'
176
157
  nodes['assistant.btn.save'] = 'dialog.assistant.btn.save'
177
158
  nodes['assistant.btn.close'] = 'dialog.assistant.btn.close'
@@ -190,13 +171,7 @@ class Mapping:
190
171
  nodes['assistant.store.btn.close'] = 'dialog.assistant.store.btn.close'
191
172
  nodes['assistant.store.hide_thread'] = 'assistant.store.hide_threads'
192
173
 
193
- # nodes['assistant.id_tip'] = 'assistant.new.id_tip'
194
- # nodes['assistant.api.tip'] = 'assistant.api.tip'
195
-
196
174
  # vision
197
- # nodes['vision.capture.enable'] = 'vision.capture.enable'
198
- # nodes['vision.capture.auto'] = 'vision.capture.auto'
199
- # nodes['vision.capture.label'] = 'vision.capture.options.title'
200
175
  nodes['inline.vision'] = 'inline.vision'
201
176
 
202
177
  # dialog: plugin settings
@@ -241,7 +216,7 @@ class Mapping:
241
216
 
242
217
  # extra settings
243
218
  nodes["idx.api.warning"] = "settings.llama.extra.api.warning"
244
- nodes["idx.db.settings.legend"] = "settings.llama.extra.btn.idx_head"
219
+ nodes["idx.db.settings.legend.head"] = "settings.llama.extra.btn.idx_head"
245
220
 
246
221
  # start
247
222
  nodes['start.title'] = 'dialog.start.title.text'
@@ -290,7 +265,6 @@ class Mapping:
290
265
  nodes['dialog.find.btn.clear'] = 'dialog.find.btn.clear'
291
266
  nodes['dialog.find.btn.find_prev'] = 'dialog.find.btn.find_prev'
292
267
  nodes['dialog.find.btn.find_next'] = 'dialog.find.btn.find_next'
293
- nodes['dialog.find.btn.find_prev'] = 'dialog.find.btn.find_prev'
294
268
 
295
269
  # dialog: profile
296
270
  nodes['dialog.profile.item.btn.dismiss'] = 'dialog.profile.item.btn.dismiss'
@@ -316,10 +290,9 @@ class Mapping:
316
290
  nodes['tip.toolbox.presets'] = 'tip.toolbox.presets'
317
291
  nodes['tip.toolbox.prompt'] = 'tip.toolbox.prompt'
318
292
  nodes['tip.toolbox.assistants'] = 'tip.toolbox.assistants'
319
- # nodes['tip.toolbox.indexes'] = 'tip.toolbox.indexes'
320
293
  nodes['tip.toolbox.ctx'] = 'tip.toolbox.ctx'
321
294
  nodes['tip.toolbox.mode'] = 'tip.toolbox.mode'
322
- nodes['plugin.settings.cmd.footer'] = 'cmd.tip' # plugin settings cmd footer
295
+ nodes['plugin.settings.cmd.footer'] = 'cmd.tip'
323
296
 
324
297
  # tool: indexer
325
298
  nodes['tool.indexer.idx.label'] = 'tool.indexer.idx'
@@ -344,7 +317,6 @@ class Mapping:
344
317
  nodes['tool.indexer.ctx.header.tip'] = 'tool.indexer.tab.ctx.tip'
345
318
  nodes['tool.indexer.browse.header.tip'] = 'tool.indexer.tab.browse.tip'
346
319
 
347
- # menu title
348
320
  menu_title = {}
349
321
  menu_title['menu.app'] = 'menu.file'
350
322
  menu_title['menu.config'] = 'menu.config'
@@ -368,7 +340,6 @@ class Mapping:
368
340
  menu_title['menu.tools'] = 'menu.tools'
369
341
  menu_title['menu.donate'] = 'menu.info.donate'
370
342
 
371
- # menu text
372
343
  menu_text = {}
373
344
  menu_text['app.ctx.new'] = 'menu.file.new'
374
345
  menu_text['app.ctx.group.new'] = 'menu.file.group.new'
@@ -408,8 +379,8 @@ class Mapping:
408
379
  menu_text['video.capture'] = 'menu.video.capture'
409
380
  menu_text['video.capture.auto'] = 'menu.video.capture.auto'
410
381
 
411
- # debug menu
412
- if 'menu.debug' in self.window.ui.menu:
382
+ ui_menu = self.window.ui.menu
383
+ if 'menu.debug' in ui_menu:
413
384
  menu_text['debug.config'] = 'menu.debug.config'
414
385
  menu_text['debug.context'] = 'menu.debug.context'
415
386
  menu_text['debug.presets'] = 'menu.debug.presets'
@@ -424,7 +395,6 @@ class Mapping:
424
395
  menu_text['debug.logger'] = 'menu.debug.logger'
425
396
  menu_text['debug.app.log'] = 'menu.debug.app.log'
426
397
 
427
- # dialog titles
428
398
  dialog_title = {}
429
399
  dialog_title['info.about'] = 'dialog.about.title'
430
400
  dialog_title['info.changelog'] = 'dialog.changelog.title'
@@ -445,13 +415,10 @@ class Mapping:
445
415
  dialog_title['profile.item'] = 'dialog.profile.item.editor'
446
416
  dialog_title['tool.indexer'] = 'tool.indexer.title'
447
417
 
448
- # tooltips
449
418
  tooltips = {}
450
419
  tooltips['prompt.context'] = 'tip.tokens.ctx'
451
420
  tooltips['input.counter'] = 'tip.tokens.input'
452
421
  tooltips['inline.vision'] = 'vision.checkbox.tooltip'
453
- # tooltips['vision.capture.enable'] = 'vision.capture.enable.tooltip'
454
- # tooltips['vision.capture.auto'] = 'vision.capture.auto.tooltip'
455
422
  tooltips['cmd.enabled'] = 'cmd.tip'
456
423
  tooltips['icon.video.capture'] = 'icon.video.capture'
457
424
  tooltips['icon.audio.output'] = 'icon.audio.output'
@@ -459,17 +426,14 @@ class Mapping:
459
426
  tooltips['assistant.store.btn.refresh_status'] = 'dialog.assistant.store.btn.refresh_status'
460
427
  tooltips['agent.llama.loop.score'] = 'toolbox.agent.llama.loop.score.tooltip'
461
428
 
462
- # menu tooltips
463
429
  menu_tooltips = {}
464
430
  menu_tooltips['video.capture'] = 'vision.capture.enable.tooltip'
465
431
  menu_tooltips['video.capture.auto'] = 'vision.capture.auto.tooltip'
466
432
 
467
- # placeholders
468
433
  placeholders = {}
469
434
  placeholders['ctx.search'] = 'ctx.list.search.placeholder'
470
435
  placeholders['interpreter.input'] = 'interpreter.input.placeholder'
471
436
 
472
- # mapping
473
437
  mapping = {}
474
438
  mapping['nodes'] = nodes
475
439
  mapping['menu.title'] = menu_title
@@ -479,10 +443,8 @@ class Mapping:
479
443
  mapping['tooltip'] = tooltips
480
444
  mapping['placeholder'] = placeholders
481
445
 
482
- # tools
483
446
  tool_mappings = self.window.tools.get_lang_mappings()
484
- for type in tool_mappings:
485
- for k in tool_mappings[type]:
486
- mapping[type][k] = tool_mappings[type][k]
447
+ for t, d in tool_mappings.items():
448
+ mapping.setdefault(t, {}).update(d)
487
449
 
488
- return mapping
450
+ return mapping
@@ -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.07.22 00:00:00 #
9
+ # Updated Date: 2025.08.15 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from pygpt_net.utils import trans
@@ -27,71 +27,80 @@ class Plugins:
27
27
  # plugins: info
28
28
  self.window.controller.plugins.update_info()
29
29
 
30
- # reload all domains (plugin locale files)
31
- ids = self.window.core.plugins.plugins.keys()
32
- for id in ids:
33
- plugin = self.window.core.plugins.plugins[id]
30
+ plugins_dict = self.window.core.plugins.plugins
31
+ plugin_ids = tuple(plugins_dict.keys())
32
+ win = self.window
33
+ ui = win.ui
34
+ ui_nodes = ui.nodes
35
+ ui_tabs = ui.tabs
36
+ settings_tab = ui_tabs['plugin.settings']
37
+ ui_menu_plugins = ui.menu.get('plugins', {})
38
+ ui_config = ui.config
39
+ ctrl_plugins = win.controller.plugins
40
+
41
+ for plugin_id in plugin_ids:
42
+ plugin = plugins_dict[plugin_id]
34
43
  if not plugin.use_locale:
35
44
  continue
36
- domain = 'plugin.{}'.format(id)
45
+ domain = f'plugin.{plugin_id}'
37
46
  trans('', True, domain)
38
47
 
39
- # apply to plugin settings
40
- for id in ids:
41
- plugin = self.window.core.plugins.plugins[id]
48
+ for plugin_id in plugin_ids:
49
+ plugin = plugins_dict[plugin_id]
42
50
  if not plugin.use_locale:
43
51
  continue
44
- domain = 'plugin.{}'.format(id)
52
+ domain = f'plugin.{plugin_id}'
45
53
 
46
- # set name, translate if localization is enabled
47
54
  name_txt = trans('plugin.name', False, domain)
48
55
 
49
- # set description, translate if localization is enabled
50
- desc_key = 'plugin.settings.' + id + '.desc'
51
- desc_txt = trans('plugin.description', False, domain)
52
- if desc_key in self.window.ui.nodes:
53
- self.window.ui.nodes[desc_key].setText(desc_txt)
56
+ plugin_settings_desc_key = f'plugin.settings.{plugin_id}.desc'
57
+ if plugin_settings_desc_key in ui_nodes:
58
+ desc_txt = trans('plugin.description', False, domain)
59
+ ui_nodes[plugin_settings_desc_key].setText(desc_txt)
54
60
 
55
- # update tab name
56
- tab_idx = self.window.controller.plugins.get_tab_idx(id)
57
- # update tab name
61
+ tab_idx = ctrl_plugins.get_tab_idx(plugin_id)
58
62
  if tab_idx is not None:
59
- self.window.ui.tabs['plugin.settings'].setTabText(tab_idx, name_txt)
63
+ settings_tab.setTabText(tab_idx, name_txt)
60
64
 
61
- if id in self.window.ui.menu['plugins']:
62
- self.window.ui.menu['plugins'][id].setText(name_txt)
65
+ if plugin_id in ui_menu_plugins:
66
+ ui_menu_plugins[plugin_id].setText(name_txt)
63
67
 
64
68
  options = plugin.setup()
65
- option_ids = options.keys()
66
- for option_id in option_ids:
67
- # prepare element nodes keys
68
- label_key = 'plugin.' + id + '.' + option_id + '.label'
69
- desc_key = 'plugin.' + id + '.' + option_id + '.desc'
70
-
71
- # update options label, description and tooltip
72
- label_str = trans(option_id + '.label', False, domain)
73
- desc_str = trans(option_id + '.description', False, domain)
74
- tooltip_str = trans(option_id + '.tooltip', False, domain)
75
-
76
- if tooltip_str == option_id + '.tooltip':
77
- tooltip_str = desc_str
78
- if label_key in self.window.ui.nodes:
79
- self.window.ui.nodes[label_key].setText(label_str)
80
- if desc_key in self.window.ui.nodes:
81
- self.window.ui.nodes[desc_key].setText(desc_str)
82
- self.window.ui.nodes[desc_key].setToolTip(tooltip_str)
83
-
84
- if options[option_id]['type'] == 'bool':
85
- # update checkbox label
86
- if domain in self.window.ui.config and option_id in self.window.ui.config[domain]:
87
- try:
88
- if hasattr(self.window.ui.config[domain][option_id], 'setText'):
89
- self.window.ui.config[domain][option_id].setText(label_str)
90
- self.window.ui.config[domain][option_id].box.setText(label_str)
91
- except Exception as e:
92
- pass
93
-
94
- # update settings dialog list
95
- idx = self.window.ui.tabs['plugin.settings'].currentIndex()
96
- self.window.plugin_settings.update_list('plugin.list', self.window.core.plugins.plugins)
97
- self.window.controller.plugins.set_by_tab(idx)
69
+ if not options:
70
+ continue
71
+
72
+ cfg_domain = ui_config.get(domain)
73
+ for option_id, option in options.items():
74
+ label_key = f'plugin.{plugin_id}.{option_id}.label'
75
+ desc_key = f'plugin.{plugin_id}.{option_id}.desc'
76
+
77
+ is_bool = option.get('type') == 'bool'
78
+ need_label = (label_key in ui_nodes) or (is_bool and cfg_domain and option_id in cfg_domain)
79
+ need_desc = desc_key in ui_nodes
80
+
81
+ label_str = None
82
+ if need_label:
83
+ label_str = trans(f'{option_id}.label', False, domain)
84
+
85
+ if need_desc:
86
+ desc_str = trans(f'{option_id}.description', False, domain)
87
+ tooltip_str = trans(f'{option_id}.tooltip', False, domain)
88
+ if tooltip_str == f'{option_id}.tooltip':
89
+ tooltip_str = desc_str
90
+ ui_nodes[desc_key].setText(desc_str)
91
+ ui_nodes[desc_key].setToolTip(tooltip_str)
92
+
93
+ if label_key in ui_nodes and label_str is not None:
94
+ ui_nodes[label_key].setText(label_str)
95
+
96
+ if is_bool and cfg_domain and option_id in cfg_domain and label_str is not None:
97
+ widget = cfg_domain[option_id]
98
+ if hasattr(widget, 'setText'):
99
+ widget.setText(label_str)
100
+ box = getattr(widget, 'box', None)
101
+ if box and hasattr(box, 'setText'):
102
+ box.setText(label_str)
103
+
104
+ idx = settings_tab.currentIndex()
105
+ win.plugin_settings.update_list('plugin.list', plugins_dict)
106
+ ctrl_plugins.set_by_tab(idx)