pygpt-net 2.4.38__py3-none-any.whl → 2.4.39__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.
- CHANGELOG.md +6 -0
- README.md +7 -1
- pygpt_net/CHANGELOG.txt +6 -0
- pygpt_net/__init__.py +2 -2
- pygpt_net/controller/__init__.py +3 -1
- pygpt_net/controller/calendar/__init__.py +3 -1
- pygpt_net/controller/chat/render.py +8 -5
- pygpt_net/controller/ctx/__init__.py +32 -24
- pygpt_net/controller/ctx/common.py +3 -2
- pygpt_net/controller/dialogs/confirm.py +2 -2
- pygpt_net/controller/lang/custom.py +2 -7
- pygpt_net/controller/lang/mapping.py +2 -2
- pygpt_net/controller/layout.py +2 -2
- pygpt_net/controller/notepad.py +8 -5
- pygpt_net/controller/ui/tabs.py +201 -58
- pygpt_net/core/ctx/__init__.py +11 -1
- pygpt_net/core/ctx/container.py +16 -9
- pygpt_net/core/ctx/output.py +86 -67
- pygpt_net/core/debug/tabs.py +3 -2
- pygpt_net/core/filesystem/url.py +7 -3
- pygpt_net/core/render/base.py +14 -3
- pygpt_net/core/render/markdown/renderer.py +3 -1
- pygpt_net/core/render/plain/renderer.py +3 -3
- pygpt_net/core/render/web/body.py +9 -3
- pygpt_net/core/render/web/renderer.py +7 -5
- pygpt_net/core/tabs/__init__.py +180 -71
- pygpt_net/core/tabs/tab.py +13 -4
- pygpt_net/data/config/config.json +11 -5
- pygpt_net/data/config/models.json +3 -3
- pygpt_net/data/config/modes.json +3 -3
- pygpt_net/data/locale/locale.de.ini +3 -0
- pygpt_net/data/locale/locale.en.ini +3 -0
- pygpt_net/data/locale/locale.es.ini +3 -0
- pygpt_net/data/locale/locale.fr.ini +3 -0
- pygpt_net/data/locale/locale.it.ini +3 -0
- pygpt_net/data/locale/locale.pl.ini +4 -1
- pygpt_net/data/locale/locale.uk.ini +3 -0
- pygpt_net/data/locale/locale.zh.ini +3 -0
- pygpt_net/plugin/audio_input/simple.py +4 -2
- pygpt_net/provider/core/config/patch.py +8 -1
- pygpt_net/provider/core/ctx/base.py +4 -1
- pygpt_net/provider/core/ctx/db_sqlite/__init__.py +10 -1
- pygpt_net/provider/core/ctx/db_sqlite/storage.py +22 -1
- pygpt_net/ui/layout/chat/input.py +10 -18
- pygpt_net/ui/layout/chat/output.py +26 -44
- pygpt_net/ui/layout/toolbox/footer.py +18 -2
- pygpt_net/ui/widget/tabs/layout.py +195 -0
- pygpt_net/ui/widget/tabs/output.py +124 -35
- pygpt_net/ui/widget/textarea/html.py +11 -1
- pygpt_net/ui/widget/textarea/output.py +10 -1
- pygpt_net/ui/widget/textarea/web.py +49 -9
- {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.dist-info}/METADATA +8 -2
- {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.dist-info}/RECORD +56 -55
- {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.dist-info}/LICENSE +0 -0
- {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.dist-info}/WHEEL +0 -0
- {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.39.dist-info}/entry_points.txt +0 -0
@@ -49,6 +49,8 @@ action.tab.add.notepad = Ajouter un nouveau bloc-notes
|
|
49
49
|
action.tab.close = Fermer
|
50
50
|
action.tab.close_all.chat = Fermer tous les chats
|
51
51
|
action.tab.close_all.notepad = Fermer tous les blocs-notes
|
52
|
+
action.tab.move.left = Déplacer vers la colonne de gauche
|
53
|
+
action.tab.move.right = Déplacer vers la colonne de droite
|
52
54
|
action.touch = Créer un fichier...
|
53
55
|
action.truncate = Tout effacer
|
54
56
|
action.undo = Annuler
|
@@ -548,6 +550,7 @@ ipython.docker.build.finish = Succès! L'image Docker pour IPython a été const
|
|
548
550
|
ipython.docker.build.start = Construction de l'image Docker... veuillez patienter...
|
549
551
|
ipython.docker.install = L'exécution d'IPython nécessite l'installation de Docker. Docker n'a pas été détecté sur ce système. Veuillez d'abord installer Docker ; vous pouvez trouver les instructions ici : https://docs.docker.com/engine/install/. L'exécution de la commande actuelle a été mise en pause.
|
550
552
|
ipython.image.build = L'image Docker pour IPython n'a pas encore été construite. Cela va se produire maintenant, et cela peut prendre un certain temps (mais c'est une procédure unique). L'exécution de la commande a été mise en pause. Une fois l'image construite, veuillez exécuter à nouveau la commande.
|
553
|
+
layout.split = Écran scindé (bêta)
|
551
554
|
menu.audio = Audio / Voix
|
552
555
|
menu.audio.cache.clear = Effacer le cache audio...
|
553
556
|
menu.audio.control.global = Contrôle vocal (global)
|
@@ -49,6 +49,8 @@ action.tab.add.notepad = Aggiungi un nuovo blocco note
|
|
49
49
|
action.tab.close = Chiudi
|
50
50
|
action.tab.close_all.chat = Chiudi tutte le chat
|
51
51
|
action.tab.close_all.notepad = Chiudi tutti i blocchi note
|
52
|
+
action.tab.move.left = Sposta nella colonna di sinistra
|
53
|
+
action.tab.move.right = Sposta nella colonna di destra
|
52
54
|
action.touch = Crea file...
|
53
55
|
action.truncate = Cancellare tutto
|
54
56
|
action.undo = Annulla
|
@@ -548,6 +550,7 @@ ipython.docker.build.finish = Successo! L'immagine Docker per IPython è stata c
|
|
548
550
|
ipython.docker.build.start = Creazione dell'immagine Docker... per favore aspetta...
|
549
551
|
ipython.docker.install = L'esecuzione di IPython richiede l'installazione di Docker. Docker non è stato rilevato su questo sistema. Si prega di installare prima Docker; puoi trovare le istruzioni qui: https://docs.docker.com/engine/install/. L'esecuzione del comando corrente è stata messa in pausa.
|
550
552
|
ipython.image.build = L'immagine Docker per IPython non è stata ancora costruita. Questo avverrà ora e potrebbe richiedere del tempo (ma è una procedura una tantum). L'esecuzione del comando è stata messa in pausa. Una volta costruita l'immagine, esegui nuovamente il comando.
|
553
|
+
layout.split = Schermo diviso (beta)
|
551
554
|
menu.audio = Audio / Voce
|
552
555
|
menu.audio.cache.clear = Cancella cache audio...
|
553
556
|
menu.audio.control.global = Controllo vocale (globale)
|
@@ -49,6 +49,8 @@ action.tab.add.notepad = Dodaj nowy notatnik
|
|
49
49
|
action.tab.close = Zamknij
|
50
50
|
action.tab.close_all.chat = Zamknij wszystkie czaty
|
51
51
|
action.tab.close_all.notepad = Zamknij wszystkie notatniki
|
52
|
+
action.tab.move.left = Przenieś do lewej kolumny
|
53
|
+
action.tab.move.right = Przenieś do prawej kolumny
|
52
54
|
action.touch = Utwórz plik...
|
53
55
|
action.truncate = Wyczyść wszystko
|
54
56
|
action.undo = Cofnij
|
@@ -548,6 +550,7 @@ ipython.docker.build.finish = Sukces! Obraz Docker dla IPython został zbudowany
|
|
548
550
|
ipython.docker.build.start = Budowanie obrazu Dockera... proszę czekać...
|
549
551
|
ipython.docker.install = Uruchomienie IPython wymaga zainstalowania Dockera. Docker nie został wykryty na tym systemie. Proszę najpierw zainstalować Dockera; instrukcje można znaleźć tutaj: https://docs.docker.com/engine/install/. Wykonywanie bieżącego polecenia zostało wstrzymane.
|
550
552
|
ipython.image.build = Obraz Dockera dla IPython nie został jeszcze zbudowany. Nastąpi to teraz i może to potrwać chwilę (ale to jednorazowa procedura). Wykonywanie polecenia zostało wstrzymane. Po zbudowaniu obrazu możesz ponownie poprosić o wykonanie polecenia.
|
553
|
+
layout.split = Podziel ekran (beta)
|
551
554
|
menu.audio = Audio / Mowa
|
552
555
|
menu.audio.cache.clear = Wyczyść pamięć podręczną audio...
|
553
556
|
menu.audio.control.global = Kontrola głosowa (globalna)
|
@@ -759,7 +762,7 @@ settings.audio.input.channels.desc = Kanały wejściowe, domyślnie: 1
|
|
759
762
|
settings.audio.input.device = Urządzenie Wejściowe Audio
|
760
763
|
settings.audio.input.device.desc = Wybierz urządzenie audio dla wejścia mikrofonu.
|
761
764
|
settings.audio.input.rate = Częstotliwość próbkowania
|
762
|
-
settings.audio.input.rate.desc = Częstotliwość próbkowania, domyślnie: 44100
|
765
|
+
settings.audio.input.rate.desc = Częstotliwość próbkowania, domyślnie: 44100
|
763
766
|
settings.check_updates = Sprawdź aktualizacje przy starcie
|
764
767
|
settings.check_updates.bg = Sprawdź aktualizacje w tle
|
765
768
|
settings.cmd.field.desc = Włącz narzędzie `{cmd}`.
|
@@ -49,6 +49,8 @@ action.tab.add.notepad = Додати новий блокнот
|
|
49
49
|
action.tab.close = Закрити
|
50
50
|
action.tab.close_all.chat = Закрити всі чати
|
51
51
|
action.tab.close_all.notepad = Закрити всі блокноти
|
52
|
+
action.tab.move.left = Перемістити в ліву колонку
|
53
|
+
action.tab.move.right = Перемістити в праву колонку
|
52
54
|
action.touch = Створити файл...
|
53
55
|
action.truncate = Очистити все
|
54
56
|
action.undo = Скасувати
|
@@ -547,6 +549,7 @@ ipython.docker.build.finish = Успіх! Образ Docker для IPython ст
|
|
547
549
|
ipython.docker.build.start = Створення образу Docker... будь ласка, зачекайте...
|
548
550
|
ipython.docker.install = Для запуску IPython необхідно встановити Docker. Docker не було виявлено на цій системі. Будь ласка, спочатку встановіть Docker; інструкції можна знайти тут: https://docs.docker.com/engine/install/. Виконання поточної команди було призупинено.
|
549
551
|
ipython.image.build = Docker-образ для IPython ще не був створений. Це відбудеться зараз, і це може зайняти деякий час (але це одноразова процедура). Виконання команди було призупинено. Після створення образу, будь ласка, виконайте команду знову.
|
552
|
+
layout.split = Розділити екран (бета)
|
550
553
|
menu.audio = Аудіо / Голос
|
551
554
|
menu.audio.cache.clear = Очистити кеш аудіо...
|
552
555
|
menu.audio.control.global = Контроль голосу (глобальний)
|
@@ -50,6 +50,8 @@ action.tab.add.notepad = 添加新记事本
|
|
50
50
|
action.tab.close = 关闭
|
51
51
|
action.tab.close_all.chat = 关闭所有聊天
|
52
52
|
action.tab.close_all.notepad = 关闭所有记事本
|
53
|
+
action.tab.move.left = 移到左列
|
54
|
+
action.tab.move.right = 移到右列
|
53
55
|
action.touch = 創建文件...
|
54
56
|
action.truncate = 截斷
|
55
57
|
action.undo = 撤銷
|
@@ -621,6 +623,7 @@ ipython.docker.build.finish = 成功!IPython 的 Docker 镜像已构建完成
|
|
621
623
|
ipython.docker.build.start = 正在构建 Docker 镜像... 请稍候...
|
622
624
|
ipython.docker.install = 运行 IPython 需要安装 Docker。系统上未检测到 Docker。请先安装 Docker;您可以在此处找到说明:https://docs.docker.com/engine/install/。当前命令的执行已暂停。
|
623
625
|
ipython.image.build = IPython 的 Docker 镜像尚未构建。这将立即发生,可能需要一段时间(但这是一次性过程)。命令的执行已暂停。镜像构建完成后,请再次执行命令。
|
626
|
+
layout.split = 分屏 (测试版)
|
624
627
|
menu.audio = 音頻/語音
|
625
628
|
menu.audio.cache.clear = 清除音频缓存...
|
626
629
|
menu.audio.control.global = 语音控制(全局)
|
@@ -151,9 +151,11 @@ class Simple:
|
|
151
151
|
self.plugin.window.dispatch(AppEvent(AppEvent.VOICE_CONTROL_STOPPED)) # app event
|
152
152
|
return
|
153
153
|
wf = wave.open(path, 'wb')
|
154
|
-
|
154
|
+
channels = int(self.plugin.window.core.config.get('audio.input.channels', 1))
|
155
|
+
rate = int(self.plugin.window.core.config.get('audio.input.rate', 44100))
|
156
|
+
wf.setnchannels(channels)
|
155
157
|
wf.setsampwidth(self.p.get_sample_size(pyaudio.paInt16))
|
156
|
-
wf.setframerate(
|
158
|
+
wf.setframerate(rate)
|
157
159
|
wf.writeframes(b''.join(self.frames))
|
158
160
|
wf.close()
|
159
161
|
self.plugin.handle_thread(True) # handle transcription in simple mode
|
@@ -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.12.
|
9
|
+
# Updated Date: 2024.12.09 03:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import copy
|
@@ -1748,6 +1748,13 @@ class Patch:
|
|
1748
1748
|
self.window.core.updater.patch_css('style.light.css', True) # force update
|
1749
1749
|
updated = True
|
1750
1750
|
|
1751
|
+
# < 2.4.39
|
1752
|
+
if old < parse_version("2.4.39"):
|
1753
|
+
print("Migrating config from < 2.4.39...")
|
1754
|
+
if 'layout.split' not in data:
|
1755
|
+
data["layout.split"] = False
|
1756
|
+
updated = True
|
1757
|
+
|
1751
1758
|
# update file
|
1752
1759
|
migrated = False
|
1753
1760
|
if updated:
|
@@ -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.
|
9
|
+
# Updated Date: 2024.12.09 00:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
from packaging.version import Version
|
@@ -71,6 +71,9 @@ class BaseProvider:
|
|
71
71
|
def get_meta_indexed(self):
|
72
72
|
pass
|
73
73
|
|
74
|
+
def get_item_by_id(self, id: int) -> CtxItem:
|
75
|
+
pass
|
76
|
+
|
74
77
|
def dump(self, ctx: CtxItem):
|
75
78
|
pass
|
76
79
|
|
@@ -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.
|
9
|
+
# Updated Date: 2024.12.09 00:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import time
|
@@ -118,6 +118,15 @@ class DbSqliteProvider(BaseProvider):
|
|
118
118
|
"""
|
119
119
|
return self.storage.get_last_meta_id()
|
120
120
|
|
121
|
+
def get_item_by_id(self, id: int) -> CtxItem:
|
122
|
+
"""
|
123
|
+
Get ctx item by ID
|
124
|
+
|
125
|
+
:param id: ctx item ID
|
126
|
+
:return: ctx item
|
127
|
+
"""
|
128
|
+
return self.storage.get_item_by_id(id)
|
129
|
+
|
121
130
|
def load(self, id: int) -> list:
|
122
131
|
"""
|
123
132
|
Load items for ctx ID
|
@@ -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.
|
9
|
+
# Updated Date: 2024.12.09 00:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
from datetime import datetime
|
@@ -189,6 +189,27 @@ class Storage:
|
|
189
189
|
items[meta.id] = meta
|
190
190
|
return items
|
191
191
|
|
192
|
+
def get_item_by_id(self, id: int) -> CtxItem:
|
193
|
+
"""
|
194
|
+
Return ctx item by ID
|
195
|
+
|
196
|
+
:return: CtxItem
|
197
|
+
"""
|
198
|
+
stmt = text("""
|
199
|
+
SELECT * FROM ctx_item WHERE id = :id
|
200
|
+
""").bindparams(id=id)
|
201
|
+
db = self.window.core.db.get_db()
|
202
|
+
with db.connect() as conn:
|
203
|
+
result = conn.execute(stmt)
|
204
|
+
row = result.fetchone()
|
205
|
+
if row:
|
206
|
+
item = CtxItem()
|
207
|
+
unpack_item(item, row._asdict())
|
208
|
+
meta = self.get_meta_by_id(item.meta_id) # append meta
|
209
|
+
item.meta = meta
|
210
|
+
return item
|
211
|
+
return None
|
212
|
+
|
192
213
|
def get_meta_by_root_id_and_preset_id(self, root_id: int, preset_id: str) -> dict:
|
193
214
|
"""
|
194
215
|
Return dict with indexed CtxMeta objects, indexed by ID
|
@@ -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.
|
9
|
+
# Updated Date: 2024.12.09 03:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
from PySide6.QtCore import Qt
|
@@ -153,14 +153,6 @@ class Input:
|
|
153
153
|
"""
|
154
154
|
# header (input label + input counter)
|
155
155
|
self.window.ui.nodes['input.label'] = HelpLabel(trans("input.label"))
|
156
|
-
self.window.ui.nodes['input.counter'] = QLabel("")
|
157
|
-
self.window.ui.nodes['input.counter'].setToolTip(trans('tip.tokens.input'))
|
158
|
-
|
159
|
-
# inline vision
|
160
|
-
self.window.ui.nodes['inline.vision'] = HelpLabel(trans('inline.vision'))
|
161
|
-
self.window.ui.nodes['inline.vision'].setVisible(False)
|
162
|
-
self.window.ui.nodes['inline.vision'].setContentsMargins(0, 0, 0, 0)
|
163
|
-
self.window.ui.nodes['inline.vision'].setToolTip(trans('vision.checkbox.tooltip'))
|
164
156
|
|
165
157
|
# plugin audio input addon
|
166
158
|
self.window.ui.plugin_addon['audio.input'] = AudioInput(self.window)
|
@@ -168,10 +160,10 @@ class Input:
|
|
168
160
|
|
169
161
|
grid = QGridLayout()
|
170
162
|
|
171
|
-
left_layout = QHBoxLayout()
|
172
|
-
left_layout.addWidget(self.window.ui.nodes['input.label'])
|
173
|
-
left_layout.addWidget(self.window.ui.nodes['inline.vision'])
|
174
|
-
left_layout.addStretch(1)
|
163
|
+
#left_layout = QHBoxLayout()
|
164
|
+
#left_layout.addWidget(self.window.ui.nodes['input.label'])
|
165
|
+
#left_layout.addWidget(self.window.ui.nodes['inline.vision'])
|
166
|
+
#left_layout.addStretch(1)
|
175
167
|
|
176
168
|
center_layout = QHBoxLayout()
|
177
169
|
center_layout.addStretch()
|
@@ -179,13 +171,13 @@ class Input:
|
|
179
171
|
center_layout.addWidget(self.window.ui.plugin_addon['audio.input.btn'])
|
180
172
|
center_layout.addStretch()
|
181
173
|
|
182
|
-
right_layout = QHBoxLayout()
|
183
|
-
right_layout.addStretch(1)
|
184
|
-
right_layout.addWidget(self.window.ui.nodes['input.counter'])
|
174
|
+
#right_layout = QHBoxLayout()
|
175
|
+
#right_layout.addStretch(1)
|
176
|
+
#right_layout.addWidget(self.window.ui.nodes['input.counter'])
|
185
177
|
|
186
|
-
grid.addLayout(left_layout, 0, 0)
|
178
|
+
#grid.addLayout(left_layout, 0, 0)
|
187
179
|
grid.addLayout(center_layout, 0, 1, alignment=Qt.AlignCenter)
|
188
|
-
grid.addLayout(right_layout, 0, 2, alignment=Qt.AlignRight)
|
180
|
+
#grid.addLayout(right_layout, 0, 2, alignment=Qt.AlignRight)
|
189
181
|
|
190
182
|
grid.setContentsMargins(0, 0, 0, 0)
|
191
183
|
return grid
|
@@ -6,18 +6,17 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.09 03:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
from PySide6.QtCore import Qt
|
13
|
-
from PySide6.QtGui import QIcon
|
14
13
|
from PySide6.QtWidgets import QVBoxLayout, QLabel, QHBoxLayout, QCheckBox, QWidget, QSizePolicy, QPushButton, \
|
15
|
-
QGridLayout,
|
14
|
+
QGridLayout, QWidgetItem, QLayout
|
16
15
|
|
17
16
|
from pygpt_net.ui.widget.audio.output import AudioOutput
|
18
17
|
from pygpt_net.ui.widget.element.labels import ChatStatusLabel, IconLabel, HelpLabel
|
19
|
-
from pygpt_net.ui.widget.
|
20
|
-
from pygpt_net.ui.widget.
|
18
|
+
from pygpt_net.ui.widget.anims.loader import Loading
|
19
|
+
from pygpt_net.ui.widget.tabs.layout import OutputLayout
|
21
20
|
|
22
21
|
from .explorer import Explorer
|
23
22
|
from .input import Input
|
@@ -28,6 +27,7 @@ from pygpt_net.utils import trans
|
|
28
27
|
import pygpt_net.icons_rc
|
29
28
|
|
30
29
|
|
30
|
+
|
31
31
|
class Output:
|
32
32
|
def __init__(self, window=None):
|
33
33
|
"""
|
@@ -47,46 +47,15 @@ class Output:
|
|
47
47
|
|
48
48
|
:return: QWidget
|
49
49
|
"""
|
50
|
-
#
|
51
|
-
self.window.ui.
|
52
|
-
|
53
|
-
#
|
54
|
-
plus_button = QPushButton(QIcon(":/icons/add.svg"), "")
|
55
|
-
plus_button.setFixedSize(30, 25)
|
56
|
-
plus_button.setFlat(True)
|
57
|
-
plus_button.clicked.connect(self.window.controller.ui.tabs.new_tab)
|
58
|
-
plus_button.setObjectName('tab-add')
|
59
|
-
plus_button.setProperty('tabAdd', True)
|
60
|
-
plus_button.setToolTip(trans('action.tab.add.chat'))
|
61
|
-
|
62
|
-
# Add the button to the top right corner of the tab bar
|
63
|
-
self.window.ui.tabs['output'].setCornerWidget(plus_button, corner=Qt.TopRightCorner)
|
64
|
-
|
65
|
-
# create empty tabs
|
50
|
+
# prepare columns
|
51
|
+
self.window.ui.layout = OutputLayout(self.window)
|
52
|
+
|
53
|
+
# create empty containers for chat output
|
66
54
|
self.window.ui.nodes['output'] = {}
|
67
55
|
self.window.ui.nodes['output_plain'] = {}
|
68
56
|
|
69
|
-
# connect signals
|
70
|
-
self.window.ui.tabs['output'].currentChanged.connect(
|
71
|
-
self.window.controller.ui.tabs.on_tab_changed
|
72
|
-
)
|
73
|
-
self.window.ui.tabs['output'].tabBarClicked.connect(
|
74
|
-
self.window.controller.ui.tabs.on_tab_clicked
|
75
|
-
)
|
76
|
-
self.window.ui.tabs['output'].tabBarDoubleClicked.connect(
|
77
|
-
self.window.controller.ui.tabs.on_tab_dbl_clicked
|
78
|
-
)
|
79
|
-
self.window.ui.tabs['output'].tabCloseRequested.connect(
|
80
|
-
self.window.controller.ui.tabs.on_tab_closed
|
81
|
-
)
|
82
|
-
|
83
|
-
# tab bar signals
|
84
|
-
self.window.ui.tabs['output'].tabBar().tabMoved.connect(
|
85
|
-
self.window.controller.ui.tabs.on_tab_moved
|
86
|
-
)
|
87
|
-
|
88
57
|
layout = QVBoxLayout()
|
89
|
-
layout.addWidget(self.window.ui.
|
58
|
+
layout.addWidget(self.window.ui.layout)
|
90
59
|
layout.addLayout(self.setup_bottom())
|
91
60
|
layout.setContentsMargins(0, 5, 0, 0)
|
92
61
|
|
@@ -177,18 +146,29 @@ class Output:
|
|
177
146
|
self.window.ui.nodes['prompt.context'].setToolTip(trans('tip.tokens.ctx'))
|
178
147
|
self.window.ui.nodes['prompt.context'].setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
179
148
|
|
149
|
+
self.window.ui.nodes['input.counter'] = ChatStatusLabel("")
|
150
|
+
self.window.ui.nodes['input.counter'].setToolTip(trans('tip.tokens.input'))
|
151
|
+
self.window.ui.nodes['input.counter'].setWordWrap(False)
|
152
|
+
|
180
153
|
# plugin audio output addon
|
181
154
|
self.window.ui.plugin_addon['audio.output'] = AudioOutput(self.window)
|
182
155
|
|
183
156
|
# schedule
|
184
157
|
self.window.ui.plugin_addon['schedule'] = ChatStatusLabel("")
|
185
158
|
|
159
|
+
# inline vision
|
160
|
+
self.window.ui.nodes['inline.vision'] = HelpLabel(trans('inline.vision'))
|
161
|
+
self.window.ui.nodes['inline.vision'].setVisible(False)
|
162
|
+
self.window.ui.nodes['inline.vision'].setContentsMargins(0, 0, 0, 0)
|
163
|
+
self.window.ui.nodes['inline.vision'].setToolTip(trans('vision.checkbox.tooltip'))
|
164
|
+
|
186
165
|
opts_layout = QHBoxLayout()
|
187
166
|
# opts_layout.setSpacing(2) #
|
188
167
|
opts_layout.setContentsMargins(0, 0, 0, 0)
|
189
168
|
opts_layout.addWidget(self.window.ui.nodes['output.timestamp'])
|
190
169
|
# opts_layout.addWidget(self.window.ui.nodes['output.edit'])
|
191
170
|
opts_layout.addWidget(self.window.ui.nodes['output.raw'])
|
171
|
+
opts_layout.addWidget(self.window.ui.nodes['inline.vision'])
|
192
172
|
opts_layout.setAlignment(Qt.AlignLeft)
|
193
173
|
|
194
174
|
left_layout = QHBoxLayout()
|
@@ -210,6 +190,8 @@ class Output:
|
|
210
190
|
right_layout.addWidget(self.window.ui.nodes['chat.model'])
|
211
191
|
right_layout.addWidget(QLabel(" "))
|
212
192
|
right_layout.addWidget(self.window.ui.nodes['prompt.context'])
|
193
|
+
right_layout.addWidget(QLabel(" "))
|
194
|
+
right_layout.addWidget(self.window.ui.nodes['input.counter'])
|
213
195
|
right_layout.setContentsMargins(0, 0, 0, 0)
|
214
196
|
|
215
197
|
left_widget = QWidget()
|
@@ -224,14 +206,14 @@ class Output:
|
|
224
206
|
|
225
207
|
left_layout = QHBoxLayout()
|
226
208
|
left_layout.addWidget(left_widget)
|
227
|
-
left_layout.addStretch(1)
|
209
|
+
#left_layout.addStretch(1)
|
228
210
|
left_layout.setContentsMargins(0, 0, 0, 0)
|
229
211
|
|
230
212
|
center_layout = QHBoxLayout()
|
231
|
-
center_layout.addStretch()
|
213
|
+
#center_layout.addStretch()
|
232
214
|
center_layout.addWidget(self.window.ui.nodes['anim.loading'])
|
233
215
|
center_layout.addStretch()
|
234
|
-
center_layout.setContentsMargins(
|
216
|
+
center_layout.setContentsMargins(20, 0, 0, 0)
|
235
217
|
|
236
218
|
right_layout = QHBoxLayout()
|
237
219
|
right_layout.addStretch(1)
|
@@ -6,14 +6,14 @@
|
|
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.
|
9
|
+
# Updated Date: 2024.12.09 03:00:00 #
|
10
10
|
# ================================================== #
|
11
11
|
|
12
12
|
import os
|
13
13
|
|
14
14
|
from PySide6.QtCore import QSize
|
15
15
|
from PySide6.QtGui import QIcon
|
16
|
-
from PySide6.QtWidgets import QVBoxLayout, QLabel, QPushButton, QWidget, QSizePolicy
|
16
|
+
from PySide6.QtWidgets import QVBoxLayout, QLabel, QPushButton, QWidget, QSizePolicy, QHBoxLayout
|
17
17
|
|
18
18
|
from pygpt_net.ui.widget.textarea.name import NameInput
|
19
19
|
from pygpt_net.ui.widget.option.slider import OptionSlider
|
@@ -25,6 +25,7 @@ from .agent_llama import AgentLlama
|
|
25
25
|
from .image import Image
|
26
26
|
from .indexes import Indexes
|
27
27
|
from .vision import Vision
|
28
|
+
from ...widget.option.toggle_label import ToggleLabel
|
28
29
|
|
29
30
|
|
30
31
|
class Footer:
|
@@ -75,6 +76,21 @@ class Footer:
|
|
75
76
|
rows.addWidget(self.window.ui.nodes['voice.control.btn'])
|
76
77
|
rows.setContentsMargins(2, 0, 0, 0)
|
77
78
|
|
79
|
+
self.window.ui.nodes['layout.split'] = ToggleLabel(trans('layout.split'), label_position="left",
|
80
|
+
icon=":/icons/window.svg")
|
81
|
+
self.window.ui.nodes['layout.split'].box.stateChanged.connect(
|
82
|
+
lambda: self.window.controller.ui.tabs.toggle_split_screen(self.window.ui.nodes['layout.split'].box.isChecked())
|
83
|
+
)
|
84
|
+
split_layout = QHBoxLayout()
|
85
|
+
|
86
|
+
split_layout.addWidget(QLabel(""))
|
87
|
+
split_layout.addStretch(1)
|
88
|
+
split_layout.addWidget(self.window.ui.nodes['layout.split'])
|
89
|
+
split_layout.setContentsMargins(5, 0, 15, 0)
|
90
|
+
split_widget = QWidget()
|
91
|
+
split_widget.setLayout(split_layout)
|
92
|
+
rows.addWidget(split_widget)
|
93
|
+
|
78
94
|
# logo
|
79
95
|
logo_button = self.setup_logo()
|
80
96
|
|
@@ -0,0 +1,195 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# ================================================== #
|
4
|
+
# This file is a part of PYGPT package #
|
5
|
+
# Website: https://pygpt.net #
|
6
|
+
# GitHub: https://github.com/szczyglis-dev/py-gpt #
|
7
|
+
# MIT License #
|
8
|
+
# Created By : Marcin Szczygliński #
|
9
|
+
# Updated Date: 2024.12.09 03:00:00 #
|
10
|
+
# ================================================== #
|
11
|
+
|
12
|
+
from PySide6.QtCore import Qt, QObject, QEvent
|
13
|
+
from PySide6.QtWidgets import QTabWidget, QWidget, QVBoxLayout, QSplitter, QSizePolicy
|
14
|
+
|
15
|
+
from pygpt_net.ui.widget.tabs.output import OutputTabs
|
16
|
+
|
17
|
+
|
18
|
+
class OutputColumn(QWidget):
|
19
|
+
def __init__(self, window=None):
|
20
|
+
"""
|
21
|
+
Output column
|
22
|
+
|
23
|
+
:param window: window instance
|
24
|
+
"""
|
25
|
+
super(OutputColumn, self).__init__(window)
|
26
|
+
self.window = window
|
27
|
+
self.idx = -1
|
28
|
+
self.tabs = OutputTabs(self.window, column=self)
|
29
|
+
self.layout = QVBoxLayout()
|
30
|
+
self.layout.addWidget(self.tabs)
|
31
|
+
self.layout.setContentsMargins(0, 0, 0, 0)
|
32
|
+
self.setLayout(self.layout)
|
33
|
+
self.filter = FocusEventFilter(self, self.on_focus)
|
34
|
+
self.installEventFilter(self.filter)
|
35
|
+
self.setFocusPolicy(Qt.StrongFocus)
|
36
|
+
|
37
|
+
def on_focus(self, widget):
|
38
|
+
"""
|
39
|
+
On widget clicked
|
40
|
+
|
41
|
+
:param widget: widget
|
42
|
+
"""
|
43
|
+
self.window.controller.ui.tabs.on_column_focus(self.idx)
|
44
|
+
widget.setFocus()
|
45
|
+
|
46
|
+
def set_idx(self, idx: int):
|
47
|
+
"""
|
48
|
+
Set index
|
49
|
+
|
50
|
+
:param idx: int
|
51
|
+
"""
|
52
|
+
self.idx = idx
|
53
|
+
|
54
|
+
def get_idx(self) -> int:
|
55
|
+
"""
|
56
|
+
Get index
|
57
|
+
|
58
|
+
:return: int
|
59
|
+
"""
|
60
|
+
return self.idx
|
61
|
+
|
62
|
+
def set_tabs(self, tabs: QTabWidget):
|
63
|
+
"""
|
64
|
+
Set tabs widget
|
65
|
+
|
66
|
+
:param tabs: QTabWidget
|
67
|
+
"""
|
68
|
+
self.tabs = tabs
|
69
|
+
|
70
|
+
def get_tabs(self) -> OutputTabs:
|
71
|
+
"""
|
72
|
+
Get tabs
|
73
|
+
|
74
|
+
:return: OutputTabs
|
75
|
+
"""
|
76
|
+
return self.tabs
|
77
|
+
|
78
|
+
|
79
|
+
class OutputLayout(QWidget):
|
80
|
+
def __init__(self, window=None):
|
81
|
+
"""
|
82
|
+
Output layout
|
83
|
+
|
84
|
+
:param window: window instance
|
85
|
+
"""
|
86
|
+
super(OutputLayout, self).__init__(window)
|
87
|
+
self.window = window
|
88
|
+
self.columns = []
|
89
|
+
|
90
|
+
column1 = OutputColumn(self.window)
|
91
|
+
column2 = OutputColumn(self.window)
|
92
|
+
self.add_column(column1)
|
93
|
+
self.add_column(column2)
|
94
|
+
|
95
|
+
self.splitter = QSplitter(Qt.Horizontal)
|
96
|
+
self.splitter.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
97
|
+
for column in self.columns:
|
98
|
+
self.splitter.addWidget(column)
|
99
|
+
|
100
|
+
self.window.ui.splitters['columns'] = self.splitter
|
101
|
+
|
102
|
+
self.layout = QVBoxLayout()
|
103
|
+
self.layout.addWidget(self.splitter, stretch=1)
|
104
|
+
self.layout.setContentsMargins(0, 0, 0, 0)
|
105
|
+
self.setLayout(self.layout)
|
106
|
+
|
107
|
+
def get_next_idx(self) -> int:
|
108
|
+
"""
|
109
|
+
Get next index
|
110
|
+
|
111
|
+
:return: int
|
112
|
+
"""
|
113
|
+
return len(self.columns)
|
114
|
+
|
115
|
+
def add_column(self, column: OutputColumn):
|
116
|
+
"""
|
117
|
+
Add column
|
118
|
+
|
119
|
+
:param column: OutputColumn
|
120
|
+
"""
|
121
|
+
idx = self.get_next_idx()
|
122
|
+
column.set_idx(idx)
|
123
|
+
self.columns.append(column)
|
124
|
+
|
125
|
+
def get_tabs_by_idx(self, idx: int) -> OutputTabs:
|
126
|
+
"""
|
127
|
+
Get tabs by column index
|
128
|
+
|
129
|
+
:param idx: int
|
130
|
+
:return: OutputTabs
|
131
|
+
"""
|
132
|
+
for column in self.columns:
|
133
|
+
if column.idx == idx:
|
134
|
+
return column.tabs
|
135
|
+
return None
|
136
|
+
|
137
|
+
def get_active_tabs(self) -> OutputTabs:
|
138
|
+
"""
|
139
|
+
Get active tabs
|
140
|
+
|
141
|
+
:return: OutputTabs
|
142
|
+
"""
|
143
|
+
current = self.window.controller.ui.tabs.get_current_column_idx()
|
144
|
+
for column in self.columns:
|
145
|
+
if column.idx == current:
|
146
|
+
return column.tabs
|
147
|
+
|
148
|
+
def get_column_by_idx(self, idx: int) -> OutputColumn:
|
149
|
+
"""
|
150
|
+
Get column by index
|
151
|
+
|
152
|
+
:param idx: int
|
153
|
+
:return: OutputColumn
|
154
|
+
"""
|
155
|
+
for column in self.columns:
|
156
|
+
if column.idx == idx:
|
157
|
+
return column
|
158
|
+
return None
|
159
|
+
|
160
|
+
def get_active_column(self) -> OutputColumn:
|
161
|
+
"""
|
162
|
+
Get active column
|
163
|
+
|
164
|
+
:return: OutputColumn
|
165
|
+
"""
|
166
|
+
current = self.window.controller.ui.tabs.get_current_column_idx()
|
167
|
+
for column in self.columns:
|
168
|
+
if column.idx == current:
|
169
|
+
return column
|
170
|
+
|
171
|
+
class FocusEventFilter(QObject):
|
172
|
+
def __init__(self, column, callback):
|
173
|
+
"""
|
174
|
+
Column event filter
|
175
|
+
|
176
|
+
:param column: parent column
|
177
|
+
:param callback: callback
|
178
|
+
"""
|
179
|
+
super().__init__()
|
180
|
+
self.column = column
|
181
|
+
self.callback = callback
|
182
|
+
|
183
|
+
def eventFilter(self, obj, event):
|
184
|
+
"""
|
185
|
+
Click event filter
|
186
|
+
|
187
|
+
:param obj: object
|
188
|
+
:param event: event
|
189
|
+
"""
|
190
|
+
if event.type() == QEvent.MouseButtonPress or event.type() == QEvent.FocusIn:
|
191
|
+
widget = obj
|
192
|
+
if widget is not None:
|
193
|
+
self.callback(widget)
|
194
|
+
return False
|
195
|
+
return False
|