pygpt-net 2.4.38__py3-none-any.whl → 2.4.40__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 (105) hide show
  1. CHANGELOG.md +19 -0
  2. README.md +25 -2
  3. pygpt_net/CHANGELOG.txt +19 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/controller/__init__.py +7 -3
  6. pygpt_net/controller/audio/__init__.py +9 -1
  7. pygpt_net/controller/calendar/__init__.py +3 -1
  8. pygpt_net/controller/chat/input.py +2 -1
  9. pygpt_net/controller/chat/render.py +8 -5
  10. pygpt_net/controller/ctx/__init__.py +33 -25
  11. pygpt_net/controller/ctx/common.py +3 -2
  12. pygpt_net/controller/debug/__init__.py +13 -2
  13. pygpt_net/controller/dialogs/confirm.py +2 -2
  14. pygpt_net/controller/kernel/__init__.py +2 -1
  15. pygpt_net/controller/lang/custom.py +2 -7
  16. pygpt_net/controller/lang/mapping.py +2 -2
  17. pygpt_net/controller/layout.py +2 -2
  18. pygpt_net/controller/notepad.py +14 -10
  19. pygpt_net/controller/theme/nodes.py +2 -5
  20. pygpt_net/controller/tools/__init__.py +37 -1
  21. pygpt_net/controller/ui/__init__.py +1 -5
  22. pygpt_net/controller/ui/tabs.py +295 -60
  23. pygpt_net/core/command.py +3 -1
  24. pygpt_net/core/ctx/__init__.py +16 -2
  25. pygpt_net/core/ctx/container.py +18 -11
  26. pygpt_net/core/ctx/output.py +86 -67
  27. pygpt_net/core/debug/tabs.py +5 -2
  28. pygpt_net/core/filesystem/url.py +7 -3
  29. pygpt_net/core/render/base.py +14 -3
  30. pygpt_net/core/render/markdown/renderer.py +3 -1
  31. pygpt_net/core/render/plain/renderer.py +3 -3
  32. pygpt_net/core/render/web/body.py +10 -4
  33. pygpt_net/core/render/web/renderer.py +213 -41
  34. pygpt_net/core/tabs/__init__.py +268 -98
  35. pygpt_net/core/tabs/tab.py +16 -4
  36. pygpt_net/core/web.py +127 -1
  37. pygpt_net/data/config/config.json +12 -5
  38. pygpt_net/data/config/models.json +3 -3
  39. pygpt_net/data/config/modes.json +3 -3
  40. pygpt_net/data/css/web-blocks.css +18 -0
  41. pygpt_net/data/css/web-blocks.light.css +7 -0
  42. pygpt_net/data/css/web-chatgpt.css +8 -0
  43. pygpt_net/data/css/web-chatgpt_wide.css +8 -0
  44. pygpt_net/data/icons/split_screen.svg +1 -0
  45. pygpt_net/data/locale/locale.de.ini +3 -0
  46. pygpt_net/data/locale/locale.en.ini +6 -1
  47. pygpt_net/data/locale/locale.es.ini +3 -0
  48. pygpt_net/data/locale/locale.fr.ini +3 -0
  49. pygpt_net/data/locale/locale.it.ini +3 -0
  50. pygpt_net/data/locale/locale.pl.ini +4 -1
  51. pygpt_net/data/locale/locale.uk.ini +3 -0
  52. pygpt_net/data/locale/locale.zh.ini +3 -0
  53. pygpt_net/data/locale/plugin.cmd_web.de.ini +2 -0
  54. pygpt_net/data/locale/plugin.cmd_web.en.ini +20 -10
  55. pygpt_net/data/locale/plugin.cmd_web.es.ini +2 -0
  56. pygpt_net/data/locale/plugin.cmd_web.fr.ini +2 -0
  57. pygpt_net/data/locale/plugin.cmd_web.it.ini +2 -0
  58. pygpt_net/data/locale/plugin.cmd_web.pl.ini +2 -0
  59. pygpt_net/data/locale/plugin.cmd_web.uk.ini +2 -0
  60. pygpt_net/data/locale/plugin.cmd_web.zh.ini +2 -0
  61. pygpt_net/icons.qrc +1 -0
  62. pygpt_net/icons_rc.py +165 -136
  63. pygpt_net/item/ctx.py +46 -24
  64. pygpt_net/plugin/audio_input/simple.py +4 -2
  65. pygpt_net/plugin/audio_output/__init__.py +4 -1
  66. pygpt_net/plugin/base/plugin.py +18 -4
  67. pygpt_net/plugin/cmd_code_interpreter/__init__.py +39 -37
  68. pygpt_net/plugin/cmd_code_interpreter/runner.py +25 -12
  69. pygpt_net/plugin/cmd_web/__init__.py +46 -6
  70. pygpt_net/plugin/cmd_web/config.py +74 -48
  71. pygpt_net/plugin/cmd_web/websearch.py +61 -28
  72. pygpt_net/plugin/cmd_web/worker.py +79 -13
  73. pygpt_net/provider/core/config/patch.py +29 -1
  74. pygpt_net/provider/core/ctx/base.py +4 -1
  75. pygpt_net/provider/core/ctx/db_sqlite/__init__.py +10 -1
  76. pygpt_net/provider/core/ctx/db_sqlite/storage.py +22 -1
  77. pygpt_net/tools/__init__.py +9 -1
  78. pygpt_net/tools/base.py +15 -1
  79. pygpt_net/tools/code_interpreter/__init__.py +174 -75
  80. pygpt_net/tools/code_interpreter/ui/dialogs.py +21 -103
  81. pygpt_net/tools/code_interpreter/ui/widgets.py +284 -9
  82. pygpt_net/tools/html_canvas/__init__.py +78 -23
  83. pygpt_net/tools/html_canvas/ui/dialogs.py +46 -62
  84. pygpt_net/tools/html_canvas/ui/widgets.py +96 -3
  85. pygpt_net/ui/base/context_menu.py +2 -2
  86. pygpt_net/ui/layout/chat/input.py +10 -18
  87. pygpt_net/ui/layout/chat/output.py +26 -44
  88. pygpt_net/ui/layout/ctx/ctx_list.py +13 -4
  89. pygpt_net/ui/layout/toolbox/footer.py +18 -2
  90. pygpt_net/ui/main.py +2 -2
  91. pygpt_net/ui/menu/debug.py +11 -1
  92. pygpt_net/ui/widget/filesystem/explorer.py +2 -2
  93. pygpt_net/ui/widget/lists/context.py +26 -5
  94. pygpt_net/ui/widget/tabs/Input.py +2 -2
  95. pygpt_net/ui/widget/tabs/body.py +2 -1
  96. pygpt_net/ui/widget/tabs/layout.py +195 -0
  97. pygpt_net/ui/widget/tabs/output.py +209 -55
  98. pygpt_net/ui/widget/textarea/html.py +11 -1
  99. pygpt_net/ui/widget/textarea/output.py +10 -1
  100. pygpt_net/ui/widget/textarea/web.py +49 -9
  101. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/METADATA +26 -3
  102. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/RECORD +105 -103
  103. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/LICENSE +0 -0
  104. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/WHEEL +0 -0
  105. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/entry_points.txt +0 -0
@@ -6,10 +6,15 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.07 23:00:00 #
9
+ # Updated Date: 2024.12.12 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
+ from PySide6.QtGui import QAction, QIcon
13
+ from PySide6.QtWidgets import QTabWidget, QMenu
14
+
12
15
  from pygpt_net.core.tabs.tab import Tab
16
+ from pygpt_net.utils import trans
17
+
13
18
 
14
19
  class Tools:
15
20
  def __init__(self, window=None):
@@ -44,6 +49,37 @@ class Tools:
44
49
  if idx is not None:
45
50
  self.window.controller.ui.tabs.switch_tab_by_idx(idx)
46
51
 
52
+ def append_tab_menu(
53
+ self,
54
+ parent: QTabWidget,
55
+ menu: QMenu,
56
+ idx: int,
57
+ column_idx: int
58
+ ) -> QMenu:
59
+ """
60
+ Append tab menu
61
+
62
+ :param parent: parent widget
63
+ :param menu: menu
64
+ :param idx: tab index
65
+ :param column_idx: column index
66
+ :return: tab add submenu
67
+ """
68
+ submenu = menu.addMenu(QIcon(":/icons/add.svg"), trans("action.tab.add.tool"))
69
+ tools = self.window.tools.get_all()
70
+ for id in tools:
71
+ tool = tools[id]
72
+ if not tool.has_tab:
73
+ continue
74
+ icon = tool.tab_icon
75
+ title = trans(tool.tab_title)
76
+ action = QAction(QIcon(icon), title, parent)
77
+ action.triggered.connect(
78
+ lambda idx=idx, column_idx=column_idx, id=id: parent.add_tab(idx, column_idx, Tab.TAB_TOOL, id)
79
+ )
80
+ submenu.addAction(action)
81
+ return submenu
82
+
47
83
  def get_tab_tools(self) -> dict:
48
84
  """
49
85
  Get tab tools
@@ -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: 2024.11.21 20:00:00 #
9
+ # Updated Date: 2024.12.09 23:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtGui import QColor
@@ -47,10 +47,6 @@ class UI:
47
47
  self.update()
48
48
  self.init_toolbox()
49
49
 
50
- def pre_setup(self):
51
- """Post setup UI"""
52
- self.tabs.setup()
53
-
54
50
  def update(self):
55
51
  """Update all elements"""
56
52
 
@@ -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: 2024.11.20 21:00:00 #
9
+ # Updated Date: 2024.12.12 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import QTimer
@@ -29,40 +29,91 @@ class Tabs:
29
29
  self.initialized = False
30
30
  self.appended = False
31
31
  self.current = 0
32
+ self.column_idx = 0
33
+ self.tmp_column_idx = 0
34
+ self.locked = False
35
+ self.col = {}
32
36
 
33
37
  def setup(self):
34
38
  """Setup tabs"""
35
39
  self.window.core.tabs.load()
36
40
  self.window.controller.notepad.load()
41
+ self.setup_options()
37
42
  self.initialized = True
38
43
 
39
- def add(self, type: int, title: str, icon=None, reference=None, data_id=None):
40
- """
41
- Add tab
44
+ def setup_options(self):
45
+ """Setup options"""
46
+ state = self.window.core.config.get("layout.split", False)
47
+ self.window.ui.nodes['layout.split'].setChecked(state)
48
+ if not state:
49
+ self.window.ui.splitters['columns'].setSizes([1, 0])
50
+
51
+ def add(
52
+ self,
53
+ type: int,
54
+ title: str,
55
+ icon=None,
56
+ child=None,
57
+ data_id=None,
58
+ tool_id=None,
59
+ ):
60
+ """
61
+ Add a new tab
42
62
 
43
63
  :param type: Tab type
44
64
  :param title: Tab title
45
65
  :param icon: Tab icon
46
- :param reference: Tab reference
47
- :param data_id: Tab data ID
48
- """
49
- self.window.core.tabs.add(type, title, icon, reference, data_id)
50
-
51
- def append(self, type: int, idx: int):
52
- """
53
- Append tab in place
66
+ :param child: Tab child (child widget)
67
+ :param data_id: Tab data ID (child data ID)
68
+ :param tool_id: Tool ID
69
+ """
70
+ self.window.core.tabs.add(
71
+ type=type,
72
+ title=title,
73
+ icon=icon,
74
+ child=child,
75
+ data_id=data_id,
76
+ tool_id=tool_id
77
+ )
78
+
79
+ def append(
80
+ self,
81
+ type: int,
82
+ tool_id: str = None,
83
+ idx: int = 0,
84
+ column_idx: int = 0
85
+ ):
86
+ """
87
+ Append tab at tab index
54
88
 
55
89
  :param type: Tab type
90
+ :param tool_id: Tool ID
56
91
  :param idx: Tab index
92
+ :param column_idx: Column index
57
93
  """
58
94
  self.appended = True # lock reload in previous tab
59
- self.window.core.tabs.append(type, idx)
60
- self.switch_tab_by_idx(idx + 1) # switch to new tab
95
+ self.column_idx = column_idx # switch to column
96
+ tab = self.window.core.tabs.append(
97
+ type=type,
98
+ idx=idx,
99
+ column_idx=column_idx,
100
+ tool_id=tool_id
101
+ )
102
+ self.switch_tab_by_idx(tab.idx, column_idx) # switch to new tab
61
103
 
62
104
  def reload_titles(self):
63
105
  """Reload tab titles"""
64
106
  self.window.core.tabs.reload_titles()
65
107
 
108
+ def update_current(self):
109
+ """Update current tab"""
110
+ curr_tab = self.get_current_tab()
111
+ curr_column = self.get_current_column_idx()
112
+ if curr_column not in self.col:
113
+ self.col[curr_column] = -1
114
+ if curr_tab is not None:
115
+ self.col[curr_column] = curr_tab.pid
116
+
66
117
  def reload(self):
67
118
  """Reload tabs"""
68
119
  self.window.core.tabs.reload()
@@ -78,15 +129,16 @@ class Tabs:
78
129
  else:
79
130
  self.window.ui.nodes['output_plain'][pid].setVisible(False)
80
131
  self.window.ui.nodes['output'][pid].setVisible(True)
81
- self.switch_tab(Tab.TAB_CHAT)
132
+ #self.switch_tab(Tab.TAB_CHAT)
82
133
 
83
- def on_tab_changed(self, idx: int):
134
+ def on_tab_changed(self, idx: int, column_idx: int = 0):
84
135
  """
85
136
  Output tab changed
86
137
 
87
138
  :param idx: tab index
139
+ :param column_idx: column index
88
140
  """
89
- tab = self.window.core.tabs.get_tab_by_index(idx)
141
+ tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
90
142
  if tab is None:
91
143
  self.appended = False
92
144
  return
@@ -99,43 +151,57 @@ class Tabs:
99
151
  if meta is not None:
100
152
  self.window.controller.ctx.load(meta.id) # reload
101
153
 
102
-
103
154
  prev_tab = self.current
155
+ prev_column = self.column_idx
104
156
  self.current = idx
157
+ self.column_idx = column_idx
105
158
  self.window.controller.ui.mode.update()
106
159
  self.window.controller.ui.vision.update()
107
160
 
108
161
  # check type
109
162
  if tab.type == Tab.TAB_NOTEPAD:
110
163
  self.window.controller.notepad.opened_once = True
111
- self.window.controller.notepad.on_open(idx)
164
+ self.window.controller.notepad.on_open(idx, column_idx)
112
165
  elif tab.type == Tab.TAB_CHAT:
113
- pid_meta = self.window.core.ctx.output.get_meta(tab.pid)
114
- meta = self.window.core.ctx.get_meta_by_id(pid_meta)
166
+ # get meta for selected tab, if not loaded yet then append meta here
167
+ meta_id = self.window.core.ctx.output.prepare_meta(tab)
168
+ meta = self.window.core.ctx.get_meta_by_id(meta_id)
115
169
  if meta is not None:
116
- self.window.controller.ctx.load(pid_meta) # reload renderer
170
+ self.window.controller.ctx.load(meta.id) # reload renderer
117
171
  elif tab.type == Tab.TAB_TOOL_PAINTER:
118
172
  if self.window.core.config.get('vision.capture.enabled'):
119
173
  self.window.controller.camera.enable_capture()
120
174
 
121
- if prev_tab != idx:
175
+ if prev_tab != idx or prev_column != column_idx:
122
176
  self.window.dispatch(AppEvent(AppEvent.TAB_SELECTED)) # app event
123
177
 
124
- def get_current_idx(self) -> int:
178
+ self.window.controller.ui.update()
179
+ self.update_current()
180
+
181
+ def get_current_idx(self, column_idx: int = 0) -> int:
125
182
  """
126
183
  Get current tab index
127
184
 
185
+ :param column_idx: column index
128
186
  :return: tab index
129
187
  """
130
188
  return self.current
131
189
 
190
+ def get_current_column_idx(self) -> int:
191
+ """
192
+ Get current column index
193
+
194
+ :return: column index
195
+ """
196
+ return self.column_idx
197
+
132
198
  def get_current_tab(self) -> Tab or None:
133
199
  """
134
200
  Get current tab
135
201
 
136
202
  :return: tab
137
203
  """
138
- return self.window.core.tabs.get_tab_by_index(self.current)
204
+ return self.window.core.tabs.get_tab_by_index(self.get_current_idx(), self.column_idx)
139
205
 
140
206
  def get_current_type(self) -> int or None:
141
207
  """
@@ -143,7 +209,7 @@ class Tabs:
143
209
 
144
210
  :return: tab type
145
211
  """
146
- tab = self.window.core.tabs.get_tab_by_index(self.current)
212
+ tab = self.window.core.tabs.get_tab_by_index(self.get_current_idx(), self.column_idx)
147
213
  if tab is None:
148
214
  return None
149
215
  return tab.type
@@ -154,7 +220,7 @@ class Tabs:
154
220
 
155
221
  :return: tab PID
156
222
  """
157
- tab = self.window.core.tabs.get_tab_by_index(self.current)
223
+ tab = self.window.core.tabs.get_tab_by_index(self.get_current_idx(), self.column_idx)
158
224
  if tab is None:
159
225
  return None
160
226
  return tab.pid
@@ -166,7 +232,7 @@ class Tabs:
166
232
  :param idx: tab index
167
233
  :return: tab type
168
234
  """
169
- tab = self.window.core.tabs.get_tab_by_index(idx)
235
+ tab = self.window.core.tabs.get_tab_by_index(idx, self.column_idx)
170
236
  if tab is None:
171
237
  return None
172
238
  return tab.type
@@ -178,68 +244,118 @@ class Tabs:
178
244
  :param type: tab type
179
245
  :return: tab index
180
246
  """
181
- return self.window.core.tabs.get_min_idx_by_type(type)
247
+ return self.window.core.tabs.get_min_idx_by_type(type, self.column_idx)
182
248
 
183
- def on_tab_clicked(self, idx: int):
249
+ def on_column_changed(self):
250
+ """Column changed event"""
251
+ if self.locked:
252
+ return
253
+ tab = self.window.core.tabs.get_tab_by_index(self.current, self.column_idx)
254
+ if tab is None:
255
+ return
256
+ current_ctx = self.window.core.ctx.get_current()
257
+ if current_ctx is not None and current_ctx != tab.data_id:
258
+ self.window.controller.ctx.select_on_list_only(tab.data_id)
259
+ self.window.controller.ui.update()
260
+ self.update_current()
261
+
262
+ def on_tab_clicked(self, idx: int, column_idx: int = 0):
184
263
  """
185
264
  Tab click event
186
265
 
187
266
  :param idx: tab index
267
+ :param column_idx: column index
268
+ """
269
+ self.current = idx
270
+ self.column_idx = column_idx
271
+ self.on_column_changed()
272
+ self.update_current()
273
+
274
+ def on_column_focus(self, idx: int):
275
+ """
276
+ Column focus event
277
+
278
+ :param idx: column index
188
279
  """
189
- pass
280
+ self.column_idx = idx
281
+ self.on_column_changed()
282
+ self.update_current()
190
283
 
191
- def on_tab_dbl_clicked(self, idx: int):
284
+ def on_tab_dbl_clicked(self, idx: int, column_idx: int = 0):
192
285
  """
193
286
  Tab double click event
194
287
 
195
288
  :param idx: tab index
289
+ :param column_idx: column index
196
290
  """
197
- pass
291
+ self.column_idx = column_idx
292
+ self.on_tab_changed(idx, column_idx)
293
+ self.update_current()
198
294
 
199
- def on_tab_closed(self, idx: int):
295
+ def on_tab_closed(self, idx: int, column_idx: int = 0):
200
296
  """
201
297
  Tab close event
202
298
 
203
299
  :param idx: tab index
300
+ :param column_idx: column index
204
301
  """
205
- self.window.core.tabs.remove_tab_by_idx(idx)
302
+ if self.locked:
303
+ return
304
+ self.window.core.tabs.remove_tab_by_idx(idx, column_idx)
305
+ self.update_current()
206
306
 
207
- def on_tab_moved(self, idx: int):
307
+ def on_tab_moved(self, idx: int, column_idx: int = 0):
208
308
  """
209
309
  Tab moved event
210
310
 
211
311
  :param idx: tab index
312
+ :param column_idx: column index
212
313
  """
314
+ if self.locked:
315
+ return
213
316
  self.window.core.tabs.update()
317
+ self.update_current()
214
318
 
215
- def close(self, idx: int):
319
+ def close(self, idx: int, column_idx: int = 0):
216
320
  """
217
321
  Close tab
218
322
 
219
323
  :param idx: tab index
324
+ :param column_idx: column index
220
325
  """
221
- self.on_tab_closed(idx)
326
+ self.on_tab_closed(idx, column_idx)
327
+ self.update_current()
222
328
 
223
- def close_all(self, type: int, force: bool = False):
329
+ def close_all(
330
+ self,
331
+ type: int,
332
+ column_idx: int = 0,
333
+ force: bool = False
334
+ ):
224
335
  """
225
336
  Close all tabs
226
337
 
227
338
  :param type: tab type
339
+ :param column_idx: column index
228
340
  :param force: force close
229
341
  """
230
342
  if not force:
343
+ self.tmp_column_idx = column_idx
231
344
  self.window.ui.dialogs.confirm(
232
345
  type='tab.close_all',
233
346
  id=type,
234
347
  msg=trans('tab.close_all.confirm'),
235
348
  )
236
349
  return
237
- self.window.core.tabs.remove_all_by_type(type)
350
+ column_idx = self.tmp_column_idx
351
+ self.window.core.tabs.remove_all_by_type(type, column_idx)
352
+ self.update_current()
238
353
 
239
354
  def next_tab(self):
240
355
  """Switch to next tab"""
241
- current = self.window.ui.tabs['output'].currentIndex()
242
- all = len(self.window.ui.tabs['output'].children())
356
+ tabs = self.window.ui.layout.get_active_tabs()
357
+ current = tabs.currentIndex()
358
+ all = len(tabs.children())
243
359
  next = current + 1
244
360
  if next >= all:
245
361
  next = 0
@@ -247,8 +363,9 @@ class Tabs:
247
363
 
248
364
  def prev_tab(self):
249
365
  """Switch to previous tab"""
250
- current = self.window.ui.tabs['output'].currentIndex()
251
- all = len(self.window.ui.tabs['output'].children())
366
+ tabs = self.window.ui.layout.get_active_tabs()
367
+ current = tabs.currentIndex()
368
+ all = len(tabs.children())
252
369
  prev = current - 1
253
370
  if prev < 0:
254
371
  prev = all - 1
@@ -264,14 +381,16 @@ class Tabs:
264
381
  if idx is not None:
265
382
  self.switch_tab_by_idx(idx)
266
383
 
267
- def switch_tab_by_idx(self, idx: int):
384
+ def switch_tab_by_idx(self, idx: int, column_idx: int = 0):
268
385
  """
269
386
  Switch tab by index
270
387
 
271
388
  :param idx: tab index
389
+ :param column_idx: column index
272
390
  """
273
- self.window.ui.tabs['output'].setCurrentIndex(idx)
274
- self.on_tab_changed(idx)
391
+ tabs = self.window.ui.layout.get_tabs_by_idx(column_idx)
392
+ tabs.setCurrentIndex(idx)
393
+ self.on_tab_changed(idx, column_idx)
275
394
 
276
395
  def get_current_tab_name(self) -> str:
277
396
  """
@@ -279,7 +398,8 @@ class Tabs:
279
398
 
280
399
  :return: tab name
281
400
  """
282
- return self.window.ui.tabs['output'].tabText(self.current)
401
+ tabs = self.window.ui.layout.get_active_tabs()
402
+ return tabs.tabText(self.current)
283
403
 
284
404
  def get_current_tab_name_for_audio(self) -> str:
285
405
  """
@@ -309,18 +429,20 @@ class Tabs:
309
429
  """
310
430
  Update tab tooltip
311
431
 
312
- :param tooltip: tooltip
432
+ :param tooltip: tooltip text
313
433
  """
314
- self.window.ui.tabs['output'].setTabToolTip(self.current, tooltip)
434
+ tabs = self.window.ui.layout.get_active_tabs()
435
+ tabs.setTabToolTip(self.current, tooltip)
315
436
 
316
- def rename(self, idx: int):
437
+ def rename(self, idx: int, column_idx: int = 0):
317
438
  """
318
- Rename tab
439
+ Rename tab (show dialog)
319
440
 
320
441
  :param idx: tab idx
442
+ :param column_idx: column idx
321
443
  """
322
444
  # get tab
323
- tab = self.window.core.tabs.get_tab_by_index(idx)
445
+ tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
324
446
  if tab is None:
325
447
  return
326
448
  # set dialog and show
@@ -329,7 +451,12 @@ class Tabs:
329
451
  self.window.ui.dialog['rename'].current = idx
330
452
  self.window.ui.dialog['rename'].show()
331
453
 
332
- def update_name(self, idx: int, name: str, close: bool = True):
454
+ def update_name(
455
+ self,
456
+ idx: int,
457
+ name: str,
458
+ close: bool = True
459
+ ):
333
460
  """
334
461
  Update tab title
335
462
 
@@ -349,7 +476,11 @@ class Tabs:
349
476
  """
350
477
  self.update_name(self.current, name)
351
478
 
352
- def update_title(self, idx: int, title: str):
479
+ def update_title(
480
+ self,
481
+ idx: int,
482
+ title: str
483
+ ):
353
484
  """
354
485
  Update tab title
355
486
 
@@ -359,8 +490,9 @@ class Tabs:
359
490
  # check if current tab is chat
360
491
  if self.get_current_type() != Tab.TAB_CHAT:
361
492
  return
493
+ tabs = self.window.ui.layout.get_active_tabs()
362
494
  tooltip = title
363
- self.window.ui.tabs['output'].setTabToolTip(idx, tooltip)
495
+ tabs.setTabToolTip(idx, tooltip)
364
496
  if len(title) > 8:
365
497
  title = title[:8] + '...' # truncate to max 8 chars
366
498
  self.window.core.tabs.update_title(idx, title, tooltip)
@@ -383,7 +515,110 @@ class Tabs:
383
515
  if idx is not None:
384
516
  self.switch_tab_by_idx(idx)
385
517
 
386
- def new_tab(self):
387
- """Handle [+} button"""
388
- idx = self.get_current_idx()
389
- self.append(Tab.TAB_CHAT, idx)
518
+ def new_tab(self, column_idx: int = 0):
519
+ """
520
+ Handle [+] button
521
+
522
+ :param column_idx: column index
523
+ """
524
+ idx = self.get_current_idx(column_idx)
525
+ self.append(
526
+ type=Tab.TAB_CHAT,
527
+ tool_id=None,
528
+ idx=idx,
529
+ column_idx=column_idx
530
+ )
531
+
532
+ def restore_data(self):
533
+ """Restore tab data"""
534
+ data = self.window.core.config.get("tabs.opened", [])
535
+ if not data:
536
+ self.switch_tab_by_idx(0, 0)
537
+ return
538
+
539
+ # reverse order, second column is first
540
+ data = dict(reversed(list(data.items())))
541
+ for col_idx in data:
542
+ tab_idx = data[col_idx]
543
+ self.switch_tab_by_idx(int(tab_idx), int(col_idx))
544
+
545
+ # set default column to 0
546
+ self.column_idx = 0
547
+ self.on_column_changed()
548
+
549
+ def move_tab(self, idx: int, column_idx: int, new_column_idx: int):
550
+ """
551
+ Move tab to another column
552
+
553
+ :param idx: tab index
554
+ :param column_idx: column index
555
+ :param new_column_idx: new column index
556
+ """
557
+ self.locked = True
558
+ tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
559
+ self.window.core.tabs.move_tab(tab, new_column_idx)
560
+ self.locked = False
561
+ # switch to new column
562
+ self.column_idx = new_column_idx
563
+ self.on_column_changed()
564
+ # switch to new tab
565
+ self.switch_tab_by_idx(tab.idx, new_column_idx)
566
+
567
+ def toggle_split_screen(self, state):
568
+ """
569
+ Toggle split screen mode
570
+
571
+ :param state: True if split screen is enabled
572
+ """
573
+ if state:
574
+ # self.rightWidget.show()
575
+ self.window.ui.splitters['columns'].setSizes([1, 1])
576
+ else:
577
+ # self.rightWidget.hide()
578
+ self.window.ui.splitters['columns'].setSizes([1, 0])
579
+ # set to first column
580
+ self.column_idx = 0
581
+ self.on_column_changed()
582
+ self.window.core.config.set("layout.split", state)
583
+ self.window.core.config.save()
584
+
585
+ def is_current_by_type(self, type: int) -> bool:
586
+ """
587
+ Check if one of current tabs is of given type
588
+
589
+ :param type: tab type
590
+ :return: True if one of tab is of given type
591
+ """
592
+ for col in self.col:
593
+ pid = self.col[col]
594
+ tab = self.window.core.tabs.get_tab_by_pid(pid)
595
+ if tab is not None and tab.type == type:
596
+ return True
597
+
598
+ def is_current_tool(self, tool_id: str) -> bool:
599
+ """
600
+ Check if one of current tabs is of given tool ID
601
+
602
+ :param tool_id: tool ID
603
+ :return: True if one of tab is of given tool ID
604
+ """
605
+ for col in self.col:
606
+ pid = self.col[col]
607
+ tab = self.window.core.tabs.get_tab_by_pid(pid)
608
+ if tab is not None and tab.tool_id == tool_id:
609
+ return True
610
+
611
+ def switch_to_first_chat(self):
612
+ """Switch to first chat tab"""
613
+ if self.is_current_by_type(Tab.TAB_CHAT):
614
+ return
615
+ # abort if active tab is chat
616
+ if self.get_current_type() == Tab.TAB_CHAT:
617
+ return
618
+ # find first chat tab
619
+ for col in self.col:
620
+ pid = self.col[col]
621
+ tab = self.window.core.tabs.get_tab_by_pid(pid)
622
+ if tab is not None and tab.type == Tab.TAB_CHAT:
623
+ self.switch_tab_by_idx(tab.idx, col)
624
+ return
pygpt_net/core/command.py CHANGED
@@ -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: 2024.11.21 20:00:00 #
9
+ # Updated Date: 2024.12.13 08:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -18,6 +18,7 @@ from pygpt_net.core.types import (
18
18
  MODE_COMPLETION,
19
19
  MODE_LANGCHAIN,
20
20
  MODE_LLAMA_INDEX,
21
+ MODE_AUDIO,
21
22
  )
22
23
  from pygpt_net.core.events import Event
23
24
  from pygpt_net.item.ctx import CtxItem
@@ -537,6 +538,7 @@ class Command:
537
538
  MODE_LLAMA_INDEX,
538
539
  MODE_LANGCHAIN,
539
540
  MODE_COMPLETION,
541
+ MODE_AUDIO,
540
542
  ]
541
543
  mode = self.window.core.config.get('mode')
542
544
  if mode in disabled_modes: