pygpt-net 2.6.1__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.
- pygpt_net/CHANGELOG.txt +23 -0
- pygpt_net/__init__.py +3 -3
- pygpt_net/app.py +20 -1
- pygpt_net/config.py +55 -65
- pygpt_net/controller/__init__.py +5 -2
- pygpt_net/controller/calendar/note.py +101 -126
- pygpt_net/controller/chat/chat.py +38 -35
- pygpt_net/controller/chat/render.py +154 -214
- pygpt_net/controller/chat/response.py +5 -3
- pygpt_net/controller/chat/stream.py +92 -27
- pygpt_net/controller/config/config.py +39 -42
- pygpt_net/controller/config/field/checkbox.py +16 -12
- pygpt_net/controller/config/field/checkbox_list.py +36 -31
- pygpt_net/controller/config/field/cmd.py +51 -57
- pygpt_net/controller/config/field/combo.py +33 -16
- pygpt_net/controller/config/field/dictionary.py +48 -55
- pygpt_net/controller/config/field/input.py +50 -32
- pygpt_net/controller/config/field/slider.py +40 -45
- pygpt_net/controller/config/field/textarea.py +20 -6
- pygpt_net/controller/config/placeholder.py +110 -231
- pygpt_net/controller/ctx/common.py +48 -48
- pygpt_net/controller/ctx/ctx.py +91 -132
- pygpt_net/controller/lang/mapping.py +57 -95
- pygpt_net/controller/lang/plugins.py +64 -55
- pygpt_net/controller/lang/settings.py +39 -38
- pygpt_net/controller/layout/layout.py +176 -109
- pygpt_net/controller/mode/mode.py +88 -85
- pygpt_net/controller/model/model.py +73 -73
- pygpt_net/controller/plugins/plugins.py +209 -223
- pygpt_net/controller/plugins/presets.py +54 -55
- pygpt_net/controller/plugins/settings.py +54 -69
- pygpt_net/controller/presets/editor.py +33 -88
- pygpt_net/controller/presets/experts.py +20 -1
- pygpt_net/controller/presets/presets.py +293 -298
- pygpt_net/controller/settings/profile.py +16 -4
- pygpt_net/controller/theme/theme.py +72 -81
- pygpt_net/controller/ui/mode.py +118 -186
- pygpt_net/controller/ui/tabs.py +69 -90
- pygpt_net/controller/ui/ui.py +47 -56
- pygpt_net/controller/ui/vision.py +24 -23
- pygpt_net/core/agents/runner.py +15 -7
- pygpt_net/core/bridge/bridge.py +5 -5
- pygpt_net/core/command/command.py +149 -219
- pygpt_net/core/ctx/ctx.py +94 -146
- pygpt_net/core/debug/debug.py +48 -58
- pygpt_net/core/experts/experts.py +3 -3
- pygpt_net/core/models/models.py +74 -112
- pygpt_net/core/modes/modes.py +13 -21
- pygpt_net/core/plugins/plugins.py +154 -177
- pygpt_net/core/presets/presets.py +103 -176
- pygpt_net/core/render/web/body.py +217 -215
- pygpt_net/core/render/web/renderer.py +330 -474
- pygpt_net/core/text/utils.py +28 -44
- pygpt_net/core/tokens/tokens.py +104 -203
- pygpt_net/data/config/config.json +3 -3
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/locale/locale.de.ini +2 -0
- pygpt_net/data/locale/locale.en.ini +2 -0
- pygpt_net/data/locale/locale.es.ini +2 -0
- pygpt_net/data/locale/locale.fr.ini +2 -0
- pygpt_net/data/locale/locale.it.ini +2 -0
- pygpt_net/data/locale/locale.pl.ini +3 -1
- pygpt_net/data/locale/locale.uk.ini +2 -0
- pygpt_net/data/locale/locale.zh.ini +2 -0
- pygpt_net/item/ctx.py +141 -139
- pygpt_net/plugin/agent/plugin.py +2 -1
- pygpt_net/plugin/audio_output/plugin.py +5 -2
- pygpt_net/plugin/base/plugin.py +101 -85
- pygpt_net/plugin/bitbucket/__init__.py +12 -0
- pygpt_net/plugin/bitbucket/config.py +267 -0
- pygpt_net/plugin/bitbucket/plugin.py +126 -0
- pygpt_net/plugin/bitbucket/worker.py +569 -0
- pygpt_net/plugin/cmd_code_interpreter/plugin.py +3 -2
- pygpt_net/plugin/cmd_custom/plugin.py +3 -2
- pygpt_net/plugin/cmd_files/plugin.py +3 -2
- pygpt_net/plugin/cmd_history/plugin.py +3 -2
- pygpt_net/plugin/cmd_mouse_control/plugin.py +5 -2
- pygpt_net/plugin/cmd_serial/plugin.py +3 -2
- pygpt_net/plugin/cmd_system/plugin.py +3 -6
- pygpt_net/plugin/cmd_web/plugin.py +3 -2
- pygpt_net/plugin/experts/plugin.py +2 -2
- pygpt_net/plugin/facebook/__init__.py +12 -0
- pygpt_net/plugin/facebook/config.py +359 -0
- pygpt_net/plugin/facebook/plugin.py +113 -0
- pygpt_net/plugin/facebook/worker.py +698 -0
- pygpt_net/plugin/github/__init__.py +12 -0
- pygpt_net/plugin/github/config.py +441 -0
- pygpt_net/plugin/github/plugin.py +126 -0
- pygpt_net/plugin/github/worker.py +674 -0
- pygpt_net/plugin/google/__init__.py +12 -0
- pygpt_net/plugin/google/config.py +367 -0
- pygpt_net/plugin/google/plugin.py +126 -0
- pygpt_net/plugin/google/worker.py +826 -0
- pygpt_net/plugin/idx_llama_index/plugin.py +3 -2
- pygpt_net/plugin/mailer/plugin.py +3 -5
- pygpt_net/plugin/openai_vision/plugin.py +3 -2
- pygpt_net/plugin/real_time/plugin.py +52 -60
- pygpt_net/plugin/slack/__init__.py +12 -0
- pygpt_net/plugin/slack/config.py +349 -0
- pygpt_net/plugin/slack/plugin.py +115 -0
- pygpt_net/plugin/slack/worker.py +639 -0
- pygpt_net/plugin/telegram/__init__.py +12 -0
- pygpt_net/plugin/telegram/config.py +308 -0
- pygpt_net/plugin/telegram/plugin.py +117 -0
- pygpt_net/plugin/telegram/worker.py +563 -0
- pygpt_net/plugin/twitter/__init__.py +12 -0
- pygpt_net/plugin/twitter/config.py +491 -0
- pygpt_net/plugin/twitter/plugin.py +125 -0
- pygpt_net/plugin/twitter/worker.py +837 -0
- pygpt_net/provider/agents/llama_index/legacy/openai_assistant.py +35 -3
- pygpt_net/tools/code_interpreter/tool.py +0 -1
- pygpt_net/tools/translator/tool.py +1 -1
- pygpt_net/ui/base/config_dialog.py +86 -100
- pygpt_net/ui/base/context_menu.py +48 -46
- pygpt_net/ui/dialog/preset.py +34 -77
- pygpt_net/ui/layout/ctx/ctx_list.py +10 -6
- pygpt_net/ui/layout/toolbox/presets.py +41 -41
- pygpt_net/ui/main.py +49 -31
- pygpt_net/ui/tray.py +61 -60
- pygpt_net/ui/widget/calendar/select.py +86 -70
- pygpt_net/ui/widget/lists/attachment.py +86 -44
- pygpt_net/ui/widget/lists/base_list_combo.py +85 -33
- pygpt_net/ui/widget/lists/context.py +135 -188
- pygpt_net/ui/widget/lists/preset.py +59 -61
- pygpt_net/ui/widget/textarea/web.py +161 -48
- pygpt_net/utils.py +8 -1
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/METADATA +164 -2
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/RECORD +131 -103
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.1.dist-info → pygpt_net-2.6.6.dist-info}/entry_points.txt +0 -0
pygpt_net/controller/ui/tabs.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
# ================================================== #
|
|
4
4
|
# This file is a part of PYGPT package #
|
|
@@ -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.15 03:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Any, Optional, Tuple
|
|
@@ -43,17 +43,19 @@ class Tabs:
|
|
|
43
43
|
|
|
44
44
|
def setup(self):
|
|
45
45
|
"""Setup tabs"""
|
|
46
|
-
self.window
|
|
47
|
-
|
|
46
|
+
w = self.window
|
|
47
|
+
w.core.tabs.load()
|
|
48
|
+
w.controller.notepad.load()
|
|
48
49
|
self.setup_options()
|
|
49
50
|
self.initialized = True
|
|
50
51
|
|
|
51
52
|
def setup_options(self):
|
|
52
53
|
"""Setup options"""
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
w = self.window
|
|
55
|
+
state = w.core.config.get("layout.split", False)
|
|
56
|
+
w.ui.nodes['layout.split'].setChecked(state)
|
|
55
57
|
if not state:
|
|
56
|
-
|
|
58
|
+
w.ui.splitters['columns'].setSizes([1, 0])
|
|
57
59
|
|
|
58
60
|
def debug(self):
|
|
59
61
|
"""Debug tabs if enabled"""
|
|
@@ -103,15 +105,15 @@ class Tabs:
|
|
|
103
105
|
:param idx: Tab index
|
|
104
106
|
:param column_idx: Column index
|
|
105
107
|
"""
|
|
106
|
-
self.appended = True
|
|
107
|
-
self.column_idx = column_idx
|
|
108
|
+
self.appended = True
|
|
109
|
+
self.column_idx = column_idx
|
|
108
110
|
tab = self.window.core.tabs.append(
|
|
109
111
|
type=type,
|
|
110
112
|
idx=idx,
|
|
111
113
|
column_idx=column_idx,
|
|
112
114
|
tool_id=tool_id
|
|
113
115
|
)
|
|
114
|
-
self.switch_tab_by_idx(tab.idx, column_idx)
|
|
116
|
+
self.switch_tab_by_idx(tab.idx, column_idx)
|
|
115
117
|
self.debug()
|
|
116
118
|
|
|
117
119
|
def reload_titles(self):
|
|
@@ -138,16 +140,21 @@ class Tabs:
|
|
|
138
140
|
|
|
139
141
|
def reload_after(self):
|
|
140
142
|
"""Reload tabs after"""
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
w = self.window
|
|
144
|
+
plain = w.core.config.get("render.plain") is True
|
|
145
|
+
outputs = w.ui.nodes['output']
|
|
146
|
+
outputs_plain = w.ui.nodes['output_plain']
|
|
147
|
+
for pid in outputs:
|
|
148
|
+
out_plain = outputs_plain.get(pid)
|
|
149
|
+
out = outputs.get(pid)
|
|
150
|
+
if out_plain is None or out is None:
|
|
151
|
+
continue
|
|
152
|
+
if plain:
|
|
153
|
+
out_plain.setVisible(True)
|
|
154
|
+
out.setVisible(False)
|
|
155
|
+
else:
|
|
156
|
+
out_plain.setVisible(False)
|
|
157
|
+
out.setVisible(True)
|
|
151
158
|
self.debug()
|
|
152
159
|
|
|
153
160
|
def on_tab_changed(
|
|
@@ -161,7 +168,11 @@ class Tabs:
|
|
|
161
168
|
:param idx: tab index
|
|
162
169
|
:param column_idx: column index
|
|
163
170
|
"""
|
|
164
|
-
|
|
171
|
+
w = self.window
|
|
172
|
+
core = w.core
|
|
173
|
+
tabs_core = core.tabs
|
|
174
|
+
|
|
175
|
+
tab = tabs_core.get_tab_by_index(idx, column_idx)
|
|
165
176
|
if tab is None:
|
|
166
177
|
self.appended = False
|
|
167
178
|
return
|
|
@@ -171,9 +182,9 @@ class Tabs:
|
|
|
171
182
|
if tab.type == Tab.TAB_CHAT:
|
|
172
183
|
self.current = idx
|
|
173
184
|
if self.create_new_on_tab:
|
|
174
|
-
meta =
|
|
185
|
+
meta = w.controller.ctx.new()
|
|
175
186
|
if meta is not None:
|
|
176
|
-
|
|
187
|
+
w.controller.ctx.load(meta.id)
|
|
177
188
|
self.create_new_on_tab = True
|
|
178
189
|
|
|
179
190
|
prev_tab = self.current
|
|
@@ -181,33 +192,31 @@ class Tabs:
|
|
|
181
192
|
|
|
182
193
|
self.current = idx
|
|
183
194
|
self.column_idx = column_idx
|
|
184
|
-
|
|
185
|
-
|
|
195
|
+
w.controller.ui.mode.update()
|
|
196
|
+
w.controller.ui.vision.update()
|
|
186
197
|
|
|
187
|
-
# check type
|
|
188
198
|
if tab.type == Tab.TAB_NOTEPAD:
|
|
189
|
-
|
|
190
|
-
|
|
199
|
+
w.controller.notepad.opened_once = True
|
|
200
|
+
w.controller.notepad.on_open(idx, column_idx)
|
|
191
201
|
elif tab.type == Tab.TAB_CHAT:
|
|
192
|
-
# get meta for selected tab, if not loaded yet then append meta here
|
|
193
202
|
meta_id = tab.data_id
|
|
194
203
|
if meta_id is None:
|
|
195
|
-
meta_id =
|
|
196
|
-
meta =
|
|
204
|
+
meta_id = core.ctx.output.prepare_meta(tab)
|
|
205
|
+
meta = core.ctx.get_meta_by_id(meta_id)
|
|
197
206
|
if meta is not None:
|
|
198
|
-
|
|
207
|
+
w.controller.ctx.load(meta.id)
|
|
199
208
|
elif tab.type == Tab.TAB_TOOL_PAINTER:
|
|
200
|
-
if
|
|
201
|
-
|
|
209
|
+
if core.config.get('vision.capture.enabled'):
|
|
210
|
+
w.controller.camera.enable_capture()
|
|
202
211
|
elif tab.type == Tab.TAB_TOOL_CALENDAR:
|
|
203
|
-
|
|
204
|
-
|
|
212
|
+
w.controller.calendar.update()
|
|
213
|
+
w.controller.calendar.update_ctx_counters()
|
|
205
214
|
|
|
206
215
|
if prev_tab != idx or prev_column != column_idx:
|
|
207
|
-
|
|
216
|
+
w.dispatch(AppEvent(AppEvent.TAB_SELECTED))
|
|
208
217
|
|
|
209
218
|
self.on_changed()
|
|
210
|
-
|
|
219
|
+
w.controller.ui.update()
|
|
211
220
|
self.update_current()
|
|
212
221
|
self.debug()
|
|
213
222
|
|
|
@@ -251,7 +260,6 @@ class Tabs:
|
|
|
251
260
|
:return: tab type
|
|
252
261
|
"""
|
|
253
262
|
tab = self.window.core.tabs.get_tab_by_index(self.get_current_idx(), self.column_idx)
|
|
254
|
-
|
|
255
263
|
if tab is None:
|
|
256
264
|
return None
|
|
257
265
|
return tab.type
|
|
@@ -324,22 +332,20 @@ class Tabs:
|
|
|
324
332
|
"""Column changed event"""
|
|
325
333
|
if self.locked:
|
|
326
334
|
return
|
|
327
|
-
|
|
335
|
+
layout = self.window.ui.layout
|
|
336
|
+
tabs = layout.get_tabs_by_idx(self.column_idx)
|
|
328
337
|
tabs.set_active(True)
|
|
329
338
|
|
|
330
|
-
if self.column_idx == 0
|
|
331
|
-
second_tabs = self.window.ui.layout.get_tabs_by_idx(1)
|
|
332
|
-
else:
|
|
333
|
-
second_tabs = self.window.ui.layout.get_tabs_by_idx(0)
|
|
339
|
+
second_tabs = layout.get_tabs_by_idx(1 if self.column_idx == 0 else 0)
|
|
334
340
|
second_tabs.set_active(False)
|
|
341
|
+
|
|
335
342
|
idx = tabs.currentIndex()
|
|
336
343
|
self.current = idx
|
|
337
344
|
tab = self.window.core.tabs.get_tab_by_index(self.current, self.column_idx)
|
|
338
345
|
if tab is None:
|
|
339
346
|
return
|
|
340
347
|
|
|
341
|
-
|
|
342
|
-
if tab.type == Tab.TAB_CHAT and self.column_idx == 1 and not tab.loaded:
|
|
348
|
+
if tab.type == Tab.TAB_CHAT and self.column_idx == 1 and not getattr(tab, "loaded", False):
|
|
343
349
|
meta = self.window.core.ctx.get_meta_by_id(tab.data_id)
|
|
344
350
|
if meta is not None:
|
|
345
351
|
self.window.controller.ctx.load(meta.id)
|
|
@@ -416,14 +422,14 @@ class Tabs:
|
|
|
416
422
|
return
|
|
417
423
|
|
|
418
424
|
previous_current = self.current
|
|
419
|
-
idx_after = None
|
|
425
|
+
idx_after = None
|
|
420
426
|
if previous_current != idx and self.column_idx == column_idx:
|
|
421
427
|
idx_after = previous_current
|
|
422
428
|
if idx_after > idx:
|
|
423
|
-
idx_after -= 1
|
|
429
|
+
idx_after -= 1
|
|
424
430
|
|
|
425
431
|
if idx_after is None:
|
|
426
|
-
idx_after = self.get_after_close_idx(idx)
|
|
432
|
+
idx_after = self.get_after_close_idx(idx)
|
|
427
433
|
|
|
428
434
|
self.window.core.tabs.remove_tab_by_idx(idx, column_idx)
|
|
429
435
|
if idx_after is not None:
|
|
@@ -496,21 +502,21 @@ class Tabs:
|
|
|
496
502
|
"""Switch to next tab"""
|
|
497
503
|
tabs = self.window.ui.layout.get_active_tabs()
|
|
498
504
|
current = tabs.currentIndex()
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
if
|
|
502
|
-
|
|
503
|
-
self.switch_tab_by_idx(
|
|
505
|
+
total = tabs.count()
|
|
506
|
+
nxt = current + 1
|
|
507
|
+
if nxt >= total:
|
|
508
|
+
nxt = 0
|
|
509
|
+
self.switch_tab_by_idx(nxt)
|
|
504
510
|
|
|
505
511
|
def prev_tab(self):
|
|
506
512
|
"""Switch to previous tab"""
|
|
507
513
|
tabs = self.window.ui.layout.get_active_tabs()
|
|
508
514
|
current = tabs.currentIndex()
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
if
|
|
512
|
-
|
|
513
|
-
self.switch_tab_by_idx(
|
|
515
|
+
total = tabs.count()
|
|
516
|
+
prv = current - 1
|
|
517
|
+
if prv < 0:
|
|
518
|
+
prv = total - 1
|
|
519
|
+
self.switch_tab_by_idx(prv)
|
|
514
520
|
|
|
515
521
|
def switch_tab(self, type: int):
|
|
516
522
|
"""
|
|
@@ -560,14 +566,13 @@ class Tabs:
|
|
|
560
566
|
if tab.type in self.window.core.tabs.titles:
|
|
561
567
|
title = trans(self.window.core.tabs.titles[tab.type])
|
|
562
568
|
|
|
563
|
-
# if more than 1 with this type then attach position info
|
|
564
569
|
num = self.window.core.tabs.count_by_type(tab.type)
|
|
565
570
|
if num > 1:
|
|
566
571
|
order = self.window.core.tabs.get_order_by_idx_and_type(tab.idx, tab.type)
|
|
567
572
|
if order != -1:
|
|
568
|
-
title += " #"
|
|
573
|
+
title += f" #{order}"
|
|
569
574
|
if tab.tooltip is not None and tab.tooltip != "":
|
|
570
|
-
title += " -
|
|
575
|
+
title += f" - {tab.tooltip}"
|
|
571
576
|
return title
|
|
572
577
|
|
|
573
578
|
def update_tooltip(self, tooltip: str):
|
|
@@ -591,11 +596,9 @@ class Tabs:
|
|
|
591
596
|
:param idx: tab idx
|
|
592
597
|
:param column_idx: column idx
|
|
593
598
|
"""
|
|
594
|
-
# get tab
|
|
595
599
|
tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
|
|
596
600
|
if tab is None:
|
|
597
601
|
return
|
|
598
|
-
# set dialog and show
|
|
599
602
|
self.window.ui.dialog['rename'].id = 'tab'
|
|
600
603
|
self.window.ui.dialog['rename'].input.setText(tab.title)
|
|
601
604
|
self.window.ui.dialog['rename'].current = idx
|
|
@@ -638,14 +641,13 @@ class Tabs:
|
|
|
638
641
|
:param idx: tab idx
|
|
639
642
|
:param title: new title
|
|
640
643
|
"""
|
|
641
|
-
# check if current tab is chat
|
|
642
644
|
if self.get_current_type() != Tab.TAB_CHAT:
|
|
643
645
|
return
|
|
644
646
|
tabs = self.window.ui.layout.get_active_tabs()
|
|
645
647
|
tooltip = title
|
|
646
648
|
tabs.setTabToolTip(idx, tooltip)
|
|
647
649
|
if len(title) > self.TAB_CHAT_MAX_CHARS:
|
|
648
|
-
title = title[:self.TAB_CHAT_MAX_CHARS] + '...'
|
|
650
|
+
title = title[:self.TAB_CHAT_MAX_CHARS] + '...'
|
|
649
651
|
self.window.core.tabs.update_title(idx, title, tooltip)
|
|
650
652
|
self.debug()
|
|
651
653
|
|
|
@@ -663,7 +665,6 @@ class Tabs:
|
|
|
663
665
|
|
|
664
666
|
:param meta: context meta
|
|
665
667
|
"""
|
|
666
|
-
# get current tab
|
|
667
668
|
tab = self.get_current_tab()
|
|
668
669
|
if tab is not None and tab.type == Tab.TAB_CHAT:
|
|
669
670
|
tab.data_id = meta.id
|
|
@@ -686,7 +687,6 @@ class Tabs:
|
|
|
686
687
|
|
|
687
688
|
:param column_idx: column index
|
|
688
689
|
"""
|
|
689
|
-
# append at the end of column
|
|
690
690
|
idx = self.window.core.tabs.get_max_idx_by_column(column_idx)
|
|
691
691
|
if idx == -1:
|
|
692
692
|
idx = 0
|
|
@@ -703,14 +703,8 @@ class Tabs:
|
|
|
703
703
|
if not data:
|
|
704
704
|
self.switch_tab_by_idx(0, 0)
|
|
705
705
|
return
|
|
706
|
-
|
|
707
|
-
# reverse order, second column is first
|
|
708
|
-
data = dict(reversed(list(data.items())))
|
|
709
|
-
for col_idx in data:
|
|
710
|
-
tab_idx = data[col_idx]
|
|
706
|
+
for col_idx, tab_idx in reversed(list(data.items())):
|
|
711
707
|
self.switch_tab_by_idx(int(tab_idx), int(col_idx))
|
|
712
|
-
|
|
713
|
-
# set default column to 0
|
|
714
708
|
self.column_idx = 0
|
|
715
709
|
self.on_column_changed()
|
|
716
710
|
self.debug()
|
|
@@ -732,10 +726,8 @@ class Tabs:
|
|
|
732
726
|
tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
|
|
733
727
|
self.window.core.tabs.move_tab(tab, new_column_idx)
|
|
734
728
|
self.locked = False
|
|
735
|
-
# switch to new column
|
|
736
729
|
self.column_idx = new_column_idx
|
|
737
730
|
self.on_column_changed()
|
|
738
|
-
# switch to new tab
|
|
739
731
|
self.switch_tab_by_idx(tab.idx, new_column_idx)
|
|
740
732
|
self.debug()
|
|
741
733
|
|
|
@@ -835,10 +827,8 @@ class Tabs:
|
|
|
835
827
|
"""Switch to first chat tab"""
|
|
836
828
|
if self.is_current_by_type(Tab.TAB_CHAT):
|
|
837
829
|
return
|
|
838
|
-
# abort if active tab is chat
|
|
839
830
|
if self.get_current_type() == Tab.TAB_CHAT:
|
|
840
831
|
return
|
|
841
|
-
# find first chat tab
|
|
842
832
|
for col in self.col:
|
|
843
833
|
pid = self.col[col]
|
|
844
834
|
tab = self.window.core.tabs.get_tab_by_pid(pid)
|
|
@@ -861,10 +851,7 @@ class Tabs:
|
|
|
861
851
|
:param title: new tab name (optional, for chat tab)
|
|
862
852
|
:param meta: context meta (optional, for chat tab)
|
|
863
853
|
"""
|
|
864
|
-
# try to focus tab
|
|
865
854
|
if self.get_current_type() != type:
|
|
866
|
-
|
|
867
|
-
# find the closest tab in current column (on left side)
|
|
868
855
|
current = self.get_current_tab()
|
|
869
856
|
exists = False
|
|
870
857
|
if current:
|
|
@@ -876,11 +863,9 @@ class Tabs:
|
|
|
876
863
|
if exists:
|
|
877
864
|
tab = self.window.core.tabs.get_tab_by_index(idx, column_idx)
|
|
878
865
|
else:
|
|
879
|
-
# if not exists in current col, then find first idx in any column
|
|
880
866
|
tab = self.window.core.tabs.get_first_by_type(type)
|
|
881
867
|
|
|
882
868
|
if tab:
|
|
883
|
-
# if tab is found in current column, switch to it
|
|
884
869
|
tabs = self.window.ui.layout.get_tabs_by_idx(tab.column_idx)
|
|
885
870
|
if tabs:
|
|
886
871
|
idx = tab.idx
|
|
@@ -897,20 +882,16 @@ class Tabs:
|
|
|
897
882
|
self.on_column_focus(tab.column_idx)
|
|
898
883
|
tabs.setCurrentIndex(idx)
|
|
899
884
|
else:
|
|
900
|
-
# if not found in current column, then check in second column
|
|
901
885
|
second_column_idx = 1 if self.column_idx == 0 else 0
|
|
902
|
-
# get current tab from second column
|
|
903
886
|
tabs = self.window.ui.layout.get_tabs_by_idx(second_column_idx)
|
|
904
887
|
second_tabs_idx = tabs.currentIndex()
|
|
905
888
|
second_tab = self.window.core.tabs.get_tab_by_index(second_tabs_idx, second_column_idx)
|
|
906
889
|
if second_tab is not None and second_tab.type == type:
|
|
907
|
-
# switch to second column
|
|
908
890
|
self.on_column_focus(second_column_idx)
|
|
909
891
|
tabs.setCurrentIndex(second_tabs_idx)
|
|
910
892
|
if meta:
|
|
911
893
|
QTimer.singleShot(100, lambda: self.window.controller.ctx.load(meta.id))
|
|
912
894
|
|
|
913
|
-
# if second and split screen disabled, then enable it
|
|
914
895
|
if tab and tab.column_idx == 1:
|
|
915
896
|
if not self.is_split_screen_enabled():
|
|
916
897
|
self.enable_split_screen(update_switch=True)
|
|
@@ -925,7 +906,6 @@ class Tabs:
|
|
|
925
906
|
"""
|
|
926
907
|
return self.window.core.config.get("layout.split", False)
|
|
927
908
|
|
|
928
|
-
|
|
929
909
|
def on_split_screen_changed(self, state: bool):
|
|
930
910
|
"""
|
|
931
911
|
On split screen mode changed
|
|
@@ -960,7 +940,6 @@ class Tabs:
|
|
|
960
940
|
Disable split screen mode
|
|
961
941
|
"""
|
|
962
942
|
self.window.ui.splitters['columns'].setSizes([1, 0])
|
|
963
|
-
# switch to first column
|
|
964
943
|
self.column_idx = 0
|
|
965
944
|
self.on_column_changed()
|
|
966
945
|
self.window.core.config.set("layout.split", False)
|
pygpt_net/controller/ui/ui.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
# ================================================== #
|
|
4
4
|
# This file is a part of PYGPT package #
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.08.15 03:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from typing import Optional
|
|
@@ -45,6 +45,11 @@ class UI:
|
|
|
45
45
|
self.splitter_output_size_input = None
|
|
46
46
|
self.splitter_output_size_files = None
|
|
47
47
|
|
|
48
|
+
self._last_ctx_string = None
|
|
49
|
+
self._last_input_string = None
|
|
50
|
+
self._last_chat_model = None
|
|
51
|
+
self._last_chat_label = None
|
|
52
|
+
|
|
48
53
|
def setup(self):
|
|
49
54
|
"""Setup UI"""
|
|
50
55
|
self.update_font_size()
|
|
@@ -53,23 +58,11 @@ class UI:
|
|
|
53
58
|
|
|
54
59
|
def update(self):
|
|
55
60
|
"""Update all elements"""
|
|
56
|
-
|
|
57
|
-
# update mode, models and presets list
|
|
58
61
|
self.update_toolbox()
|
|
59
|
-
|
|
60
|
-
# update chat label
|
|
61
62
|
self.update_chat_label()
|
|
62
|
-
|
|
63
|
-
# show / hide widgets
|
|
64
63
|
self.mode.update()
|
|
65
|
-
|
|
66
|
-
# update token counters
|
|
67
64
|
self.update_tokens()
|
|
68
|
-
|
|
69
|
-
# update vision
|
|
70
65
|
self.vision.update()
|
|
71
|
-
|
|
72
|
-
# agent status
|
|
73
66
|
self.window.controller.agent.legacy.update()
|
|
74
67
|
|
|
75
68
|
def get_colors(self) -> dict:
|
|
@@ -100,9 +93,10 @@ class UI:
|
|
|
100
93
|
env = "mac"
|
|
101
94
|
else:
|
|
102
95
|
env = "linux"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
96
|
+
node = self.window.ui.nodes["computer_env"]
|
|
97
|
+
index = node.findData(env)
|
|
98
|
+
if index != -1 and node.currentIndex() != index:
|
|
99
|
+
node.setCurrentIndex(index)
|
|
106
100
|
|
|
107
101
|
def on_computer_env_changed(self, env: str):
|
|
108
102
|
"""
|
|
@@ -110,16 +104,19 @@ class UI:
|
|
|
110
104
|
|
|
111
105
|
:param env: selected environment
|
|
112
106
|
"""
|
|
113
|
-
self.window.core.config
|
|
114
|
-
|
|
107
|
+
cfg = self.window.core.config
|
|
108
|
+
if cfg.get("remote_tools.computer_use.env") != env:
|
|
109
|
+
cfg.set("remote_tools.computer_use.env", env)
|
|
110
|
+
cfg.save()
|
|
115
111
|
|
|
116
112
|
def update_toolbox(self):
|
|
117
113
|
"""Update toolbox"""
|
|
118
|
-
self.window.controller
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
114
|
+
ctrl = self.window.controller
|
|
115
|
+
ctrl.mode.update_mode()
|
|
116
|
+
ctrl.model.update()
|
|
117
|
+
ctrl.presets.refresh()
|
|
118
|
+
ctrl.assistant.refresh()
|
|
119
|
+
ctrl.idx.refresh()
|
|
123
120
|
|
|
124
121
|
def format_tokens(self, num: int) -> str:
|
|
125
122
|
"""
|
|
@@ -130,41 +127,31 @@ class UI:
|
|
|
130
127
|
num = int(num)
|
|
131
128
|
if num >= 1_000_000:
|
|
132
129
|
return f"{num // 1_000_000}M"
|
|
133
|
-
|
|
130
|
+
if num >= 1_000:
|
|
134
131
|
return f"{num // 1_000}k"
|
|
135
|
-
|
|
136
|
-
return str(num)
|
|
132
|
+
return str(num)
|
|
137
133
|
|
|
138
134
|
def update_tokens(self):
|
|
139
135
|
"""Update tokens counter in real-time"""
|
|
140
|
-
|
|
136
|
+
ui_nodes = self.window.ui.nodes
|
|
137
|
+
prompt = ui_nodes['input'].toPlainText().strip()
|
|
141
138
|
input_tokens, system_tokens, extra_tokens, ctx_tokens, ctx_len, ctx_len_all, \
|
|
142
139
|
sum_tokens, max_current, threshold = self.window.core.tokens.get_current(prompt)
|
|
143
140
|
attachments_tokens = self.window.controller.chat.attachment.get_current_tokens()
|
|
144
141
|
sum_tokens += attachments_tokens
|
|
145
142
|
|
|
146
|
-
|
|
147
|
-
ctx_string
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
ctx_tokens,
|
|
151
|
-
trans('ctx.tokens')
|
|
152
|
-
)
|
|
153
|
-
self.window.ui.nodes['prompt.context'].setText(ctx_string)
|
|
143
|
+
ctx_string = f"{ctx_len} / {ctx_len_all} - {ctx_tokens} {trans('ctx.tokens')}"
|
|
144
|
+
if ctx_string != self._last_ctx_string:
|
|
145
|
+
ui_nodes['prompt.context'].setText(ctx_string)
|
|
146
|
+
self._last_ctx_string = ctx_string
|
|
154
147
|
|
|
155
148
|
parsed_sum = self.format_tokens(sum_tokens)
|
|
156
149
|
parsed_max_current = self.format_tokens(max_current)
|
|
157
150
|
|
|
158
|
-
input_string = "{} + {} + {} + {} + {} = {} / {}"
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
extra_tokens,
|
|
163
|
-
attachments_tokens,
|
|
164
|
-
parsed_sum,
|
|
165
|
-
parsed_max_current
|
|
166
|
-
)
|
|
167
|
-
self.window.ui.nodes['input.counter'].setText(input_string)
|
|
151
|
+
input_string = f"{input_tokens} + {system_tokens} + {ctx_tokens} + {extra_tokens} + {attachments_tokens} = {parsed_sum} / {parsed_max_current}"
|
|
152
|
+
if input_string != self._last_input_string:
|
|
153
|
+
ui_nodes['input.counter'].setText(input_string)
|
|
154
|
+
self._last_input_string = input_string
|
|
168
155
|
|
|
169
156
|
def store_state(self):
|
|
170
157
|
"""Store UI state"""
|
|
@@ -177,11 +164,10 @@ class UI:
|
|
|
177
164
|
def update_chat_label(self):
|
|
178
165
|
"""Update chat label"""
|
|
179
166
|
model = self.window.core.config.get('model')
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
self.window.ui.nodes['chat.model'].setText(model_str)
|
|
167
|
+
model_str = "" if not model else str(model)
|
|
168
|
+
if model_str != self._last_chat_model:
|
|
169
|
+
self.window.ui.nodes['chat.model'].setText(model_str)
|
|
170
|
+
self._last_chat_model = model_str
|
|
185
171
|
|
|
186
172
|
def update_ctx_label(self, label: Optional[str] = None):
|
|
187
173
|
"""
|
|
@@ -193,19 +179,24 @@ class UI:
|
|
|
193
179
|
allowed = self.window.core.ctx.is_allowed_for_mode(mode)
|
|
194
180
|
if label is None:
|
|
195
181
|
label = ''
|
|
196
|
-
|
|
197
|
-
# add (+) if allowed appending data to this context
|
|
198
182
|
if allowed:
|
|
199
183
|
label += ' (+)'
|
|
200
|
-
|
|
184
|
+
label_str = str(label)
|
|
185
|
+
if label_str != self._last_chat_label:
|
|
186
|
+
self.window.ui.nodes['chat.label'].setText(label_str)
|
|
187
|
+
self._last_chat_label = label_str
|
|
201
188
|
|
|
202
189
|
def show_global_stop(self):
|
|
203
190
|
"""Show global stop button"""
|
|
204
|
-
self.window.ui.nodes['global.stop']
|
|
191
|
+
node = self.window.ui.nodes['global.stop']
|
|
192
|
+
if not node.isVisible():
|
|
193
|
+
node.setVisible(True)
|
|
205
194
|
|
|
206
195
|
def hide_global_stop(self):
|
|
207
196
|
"""Hide global stop button"""
|
|
208
|
-
self.window.ui.nodes['global.stop']
|
|
197
|
+
node = self.window.ui.nodes['global.stop']
|
|
198
|
+
if node.isVisible():
|
|
199
|
+
node.setVisible(False)
|
|
209
200
|
|
|
210
201
|
def on_global_stop(self):
|
|
211
202
|
"""Global stop button action"""
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
# ================================================== #
|
|
4
4
|
# This file is a part of PYGPT package #
|
|
@@ -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.
|
|
9
|
+
# Updated Date: 2025.08.15 03:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from pygpt_net.core.types import (
|
|
@@ -31,7 +31,7 @@ class Vision:
|
|
|
31
31
|
"""
|
|
32
32
|
self.window = window
|
|
33
33
|
|
|
34
|
-
def has_vision(self):
|
|
34
|
+
def has_vision(self) -> bool:
|
|
35
35
|
"""
|
|
36
36
|
Check if vision is available
|
|
37
37
|
|
|
@@ -44,30 +44,34 @@ class Vision:
|
|
|
44
44
|
return True
|
|
45
45
|
if self.window.controller.plugins.is_type_enabled('vision'):
|
|
46
46
|
return True
|
|
47
|
-
if self.is_vision_model() and mode in
|
|
47
|
+
if self.is_vision_model() and mode in (
|
|
48
48
|
MODE_CHAT,
|
|
49
49
|
MODE_LLAMA_INDEX,
|
|
50
50
|
MODE_AGENT,
|
|
51
51
|
MODE_AGENT_OPENAI,
|
|
52
52
|
MODE_RESEARCH,
|
|
53
53
|
MODE_EXPERT,
|
|
54
|
-
|
|
54
|
+
):
|
|
55
55
|
return True
|
|
56
56
|
return False
|
|
57
57
|
|
|
58
|
-
def update(self):
|
|
58
|
+
def update(self) -> None:
|
|
59
59
|
"""Update vision options"""
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
ctrl = self.window.controller
|
|
61
|
+
camera = ctrl.camera
|
|
62
|
+
|
|
63
|
+
if ctrl.painter.is_active():
|
|
64
|
+
camera.setup()
|
|
65
|
+
camera.show_camera()
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
if self.has_vision():
|
|
69
|
+
camera.setup()
|
|
70
|
+
camera.show_camera()
|
|
71
|
+
ctrl.chat.vision.show_inline()
|
|
63
72
|
else:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
self.window.controller.camera.show_camera()
|
|
67
|
-
self.window.controller.chat.vision.show_inline()
|
|
68
|
-
else:
|
|
69
|
-
self.window.controller.camera.hide_camera()
|
|
70
|
-
self.window.controller.chat.vision.hide_inline()
|
|
73
|
+
camera.hide_camera()
|
|
74
|
+
ctrl.chat.vision.hide_inline()
|
|
71
75
|
|
|
72
76
|
def is_vision_model(self) -> bool:
|
|
73
77
|
"""
|
|
@@ -76,10 +80,7 @@ class Vision:
|
|
|
76
80
|
:return: True if vision model
|
|
77
81
|
"""
|
|
78
82
|
model_id = self.window.core.config.get("model")
|
|
79
|
-
if
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
return True
|
|
84
|
-
return False
|
|
85
|
-
|
|
83
|
+
if not model_id:
|
|
84
|
+
return False
|
|
85
|
+
models = self.window.core.models
|
|
86
|
+
return models.has(model_id) and models.get(model_id).is_image_input()
|