pygpt-net 2.6.22__py3-none-any.whl → 2.6.23__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 +8 -0
- pygpt_net/__init__.py +1 -1
- pygpt_net/controller/agent/llama.py +3 -0
- pygpt_net/controller/chat/response.py +4 -0
- pygpt_net/controller/files/files.py +24 -55
- pygpt_net/controller/theme/theme.py +3 -3
- pygpt_net/core/agents/observer/evaluation.py +2 -2
- pygpt_net/core/agents/runners/loop.py +1 -0
- pygpt_net/core/bridge/bridge.py +2 -0
- pygpt_net/core/filesystem/opener.py +261 -0
- pygpt_net/core/filesystem/url.py +13 -10
- pygpt_net/core/platforms/platforms.py +5 -4
- pygpt_net/data/config/config.json +2 -2
- pygpt_net/data/config/models.json +2 -2
- pygpt_net/data/css/web-blocks.dark.css +7 -1
- pygpt_net/data/css/web-blocks.light.css +5 -2
- pygpt_net/data/css/web-chatgpt.dark.css +7 -1
- pygpt_net/data/css/web-chatgpt.light.css +3 -0
- pygpt_net/data/css/web-chatgpt_wide.dark.css +7 -1
- pygpt_net/data/css/web-chatgpt_wide.light.css +3 -0
- pygpt_net/data/locale/locale.de.ini +1 -0
- pygpt_net/data/locale/locale.en.ini +1 -0
- pygpt_net/data/locale/locale.es.ini +1 -0
- pygpt_net/data/locale/locale.fr.ini +1 -0
- pygpt_net/data/locale/locale.it.ini +1 -0
- pygpt_net/data/locale/locale.pl.ini +1 -0
- pygpt_net/data/locale/locale.uk.ini +1 -0
- pygpt_net/data/locale/locale.zh.ini +1 -0
- pygpt_net/provider/core/config/patch.py +12 -1
- pygpt_net/ui/layout/toolbox/agent_llama.py +2 -3
- pygpt_net/ui/widget/tabs/layout.py +6 -4
- pygpt_net/ui/widget/tabs/output.py +348 -13
- pygpt_net/ui/widget/textarea/input.py +74 -8
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.23.dist-info}/METADATA +25 -25
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.23.dist-info}/RECORD +38 -37
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.23.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.23.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.22.dist-info → pygpt_net-2.6.23.dist-info}/entry_points.txt +0 -0
pygpt_net/CHANGELOG.txt
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
2.6.23 (2025-08-25)
|
|
2
|
+
|
|
3
|
+
- Added an inline "Add a new chat" button to the right of the tabs.
|
|
4
|
+
- Added an "Add Attachment" button in the input field.
|
|
5
|
+
- Improved file open in the system's file manager
|
|
6
|
+
- Fixed the restoration of input text color when changing themes from light to dark.
|
|
7
|
+
- Fixed last eval step finish if 100% complete.
|
|
8
|
+
|
|
1
9
|
2.6.22 (2025-08-25)
|
|
2
10
|
|
|
3
11
|
- UI refactor and optimizations.
|
pygpt_net/__init__.py
CHANGED
|
@@ -13,7 +13,7 @@ __author__ = "Marcin Szczygliński"
|
|
|
13
13
|
__copyright__ = "Copyright 2025, Marcin Szczygliński"
|
|
14
14
|
__credits__ = ["Marcin Szczygliński"]
|
|
15
15
|
__license__ = "MIT"
|
|
16
|
-
__version__ = "2.6.
|
|
16
|
+
__version__ = "2.6.23"
|
|
17
17
|
__build__ = "2025-08-25"
|
|
18
18
|
__maintainer__ = "Marcin Szczygliński"
|
|
19
19
|
__github__ = "https://github.com/szczyglis-dev/py-gpt"
|
|
@@ -104,6 +104,9 @@ class Llama:
|
|
|
104
104
|
|
|
105
105
|
def on_end(self):
|
|
106
106
|
"""End of run"""
|
|
107
|
+
self.window.dispatch(KernelEvent(KernelEvent.STATE_IDLE, {
|
|
108
|
+
"id": "agent",
|
|
109
|
+
}))
|
|
107
110
|
self.eval_step = 0 # reset evaluation step
|
|
108
111
|
if self.window.core.config.get("agent.goal.notify"):
|
|
109
112
|
# show notification if enabled and mode is not llama_index
|
|
@@ -269,6 +269,10 @@ class Response:
|
|
|
269
269
|
self.window.update_status(trans("status.agent.reasoning"))
|
|
270
270
|
controller.chat.common.lock_input() # lock input, re-enable stop button
|
|
271
271
|
|
|
272
|
+
if ctx.extra is not None and (isinstance(ctx.extra, dict) and "agent_eval_finish" in ctx.extra):
|
|
273
|
+
controller.agent.llama.on_end(ctx)
|
|
274
|
+
return
|
|
275
|
+
|
|
272
276
|
# agent final response
|
|
273
277
|
if ctx.extra is not None and (isinstance(ctx.extra, dict) and "agent_finish" in ctx.extra):
|
|
274
278
|
controller.agent.llama.on_finish(ctx) # evaluate response and continue if needed
|
|
@@ -6,21 +6,18 @@
|
|
|
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.25 18:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
13
13
|
import os
|
|
14
14
|
import shutil
|
|
15
15
|
from typing import Optional
|
|
16
|
+
from shutil import copy2
|
|
16
17
|
|
|
17
|
-
from PySide6.QtCore import QUrl
|
|
18
|
-
from PySide6.QtGui import QDesktopServices
|
|
19
18
|
from PySide6.QtWidgets import QFileDialog, QApplication
|
|
20
|
-
from showinfm import show_in_file_manager
|
|
21
|
-
from shutil import copy2
|
|
22
|
-
import subprocess
|
|
23
19
|
|
|
20
|
+
from pygpt_net.core.filesystem.opener import Opener
|
|
24
21
|
from pygpt_net.utils import trans
|
|
25
22
|
|
|
26
23
|
|
|
@@ -60,12 +57,12 @@ class Files:
|
|
|
60
57
|
try:
|
|
61
58
|
shutil.rmtree(path) # delete directory with all files
|
|
62
59
|
self.window.update_status(
|
|
63
|
-
"[OK] Deleted directory: {
|
|
60
|
+
f"[OK] Deleted directory: {os.path.basename(path)}"
|
|
64
61
|
)
|
|
65
62
|
self.update_explorer()
|
|
66
63
|
except Exception as e:
|
|
67
64
|
self.window.core.debug.log(e)
|
|
68
|
-
print("Error deleting directory: {} - {}"
|
|
65
|
+
print(f"Error deleting directory: {path} - {e}")
|
|
69
66
|
|
|
70
67
|
def touch_file(
|
|
71
68
|
self,
|
|
@@ -93,12 +90,12 @@ class Files:
|
|
|
93
90
|
filepath = os.path.join(path, name)
|
|
94
91
|
open(filepath, 'a').close()
|
|
95
92
|
self.window.update_status(
|
|
96
|
-
"[OK] Created file: {}"
|
|
93
|
+
f"[OK] Created file: {filepath}"
|
|
97
94
|
)
|
|
98
95
|
self.update_explorer()
|
|
99
96
|
except Exception as e:
|
|
100
97
|
self.window.core.debug.log(e)
|
|
101
|
-
print("Error creating file: {} - {}"
|
|
98
|
+
print(f"Error creating file: {path} - {e}")
|
|
102
99
|
|
|
103
100
|
def delete(
|
|
104
101
|
self,
|
|
@@ -130,12 +127,12 @@ class Files:
|
|
|
130
127
|
try:
|
|
131
128
|
os.remove(path)
|
|
132
129
|
self.window.update_status(
|
|
133
|
-
|
|
130
|
+
f"[OK] Deleted file: {os.path.basename(path)}"
|
|
134
131
|
)
|
|
135
132
|
self.update_explorer()
|
|
136
133
|
except Exception as e:
|
|
137
134
|
self.window.core.debug.log(e)
|
|
138
|
-
print("Error deleting file: {} - {}"
|
|
135
|
+
print(f"Error deleting file: {path} - {e}")
|
|
139
136
|
|
|
140
137
|
def duplicate_local(
|
|
141
138
|
self,
|
|
@@ -172,7 +169,7 @@ class Files:
|
|
|
172
169
|
|
|
173
170
|
if os.path.exists(new_path):
|
|
174
171
|
self.window.update_status(
|
|
175
|
-
"[ERROR] File already exists: {
|
|
172
|
+
f"[ERROR] File already exists: {os.path.basename(new_path)}"
|
|
176
173
|
)
|
|
177
174
|
return
|
|
178
175
|
|
|
@@ -181,12 +178,12 @@ class Files:
|
|
|
181
178
|
else:
|
|
182
179
|
copy2(path, new_path)
|
|
183
180
|
self.window.update_status(
|
|
184
|
-
"[OK] Duplicated file: {
|
|
181
|
+
f"[OK] Duplicated file: {os.path.basename(path)} -> {name}"
|
|
185
182
|
)
|
|
186
183
|
self.update_explorer()
|
|
187
184
|
except Exception as e:
|
|
188
185
|
self.window.core.debug.log(e)
|
|
189
|
-
print("Error duplicating file: {} - {}"
|
|
186
|
+
print(f"Error duplicating file: {path} - {e}")
|
|
190
187
|
|
|
191
188
|
def download_local(self, path: str):
|
|
192
189
|
"""
|
|
@@ -211,11 +208,11 @@ class Files:
|
|
|
211
208
|
else:
|
|
212
209
|
shutil.copy2(path, files[0])
|
|
213
210
|
self.window.update_status(
|
|
214
|
-
"[OK] Downloaded file: {
|
|
211
|
+
f"[OK] Downloaded file: {os.path.basename(path)}"
|
|
215
212
|
)
|
|
216
213
|
except Exception as e:
|
|
217
214
|
self.window.core.debug.log(e)
|
|
218
|
-
print("Error downloading file: {} - {}"
|
|
215
|
+
print(f"Error downloading file: {path} - {e}")
|
|
219
216
|
|
|
220
217
|
def upload_local(
|
|
221
218
|
self,
|
|
@@ -268,9 +265,9 @@ class Files:
|
|
|
268
265
|
num += 1
|
|
269
266
|
except Exception as e:
|
|
270
267
|
self.window.core.debug.log(e)
|
|
271
|
-
print("Error copying file {}: {}"
|
|
268
|
+
print(f"Error copying file {file_path}: {e}")
|
|
272
269
|
if num > 0:
|
|
273
|
-
self.window.update_status("[OK] Uploaded: {} files."
|
|
270
|
+
self.window.update_status(f"[OK] Uploaded: {num} files.")
|
|
274
271
|
self.update_explorer()
|
|
275
272
|
|
|
276
273
|
def rename(self, path: str):
|
|
@@ -299,12 +296,12 @@ class Files:
|
|
|
299
296
|
new_path = os.path.join(os.path.dirname(path), name)
|
|
300
297
|
if os.path.exists(new_path):
|
|
301
298
|
self.window.update_status(
|
|
302
|
-
"[ERROR] File already exists: {
|
|
299
|
+
f"[ERROR] File already exists: {os.path.basename(new_path)}"
|
|
303
300
|
)
|
|
304
301
|
return
|
|
305
302
|
os.rename(path, new_path)
|
|
306
303
|
self.window.update_status(
|
|
307
|
-
|
|
304
|
+
f"[OK] Renamed: {os.path.basename(path)} -> {name}"
|
|
308
305
|
)
|
|
309
306
|
self.window.ui.dialog['rename'].close()
|
|
310
307
|
self.update_explorer()
|
|
@@ -328,24 +325,8 @@ class Files:
|
|
|
328
325
|
|
|
329
326
|
:param path: path to file or directory
|
|
330
327
|
"""
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
url = QUrl.fromLocalFile(path)
|
|
334
|
-
QDesktopServices.openUrl(url)
|
|
335
|
-
else:
|
|
336
|
-
url = QUrl.fromLocalFile(path)
|
|
337
|
-
if not QDesktopServices.openUrl(url):
|
|
338
|
-
subprocess.run(['gio', 'open', path])
|
|
339
|
-
else:
|
|
340
|
-
if not self.window.core.platforms.is_snap():
|
|
341
|
-
path = self.window.core.filesystem.get_path(path)
|
|
342
|
-
url = QUrl.fromLocalFile(path)
|
|
343
|
-
QDesktopServices.openUrl(url)
|
|
344
|
-
else:
|
|
345
|
-
path = self.window.core.filesystem.get_path(path)
|
|
346
|
-
url = QUrl.fromLocalFile(path)
|
|
347
|
-
QDesktopServices.openUrl(url)
|
|
348
|
-
#subprocess.run(['gio', 'open', path])
|
|
328
|
+
path = self.window.core.filesystem.get_path(path)
|
|
329
|
+
Opener.open_path(path, reveal=False)
|
|
349
330
|
|
|
350
331
|
def open_in_file_manager(
|
|
351
332
|
self,
|
|
@@ -359,20 +340,8 @@ class Files:
|
|
|
359
340
|
:param select: select file in file manager
|
|
360
341
|
"""
|
|
361
342
|
path = self.window.core.filesystem.get_path(path)
|
|
362
|
-
if select: # path to file: open containing directory
|
|
363
|
-
path = self.window.core.filesystem.get_path(
|
|
364
|
-
os.path.dirname(path)
|
|
365
|
-
)
|
|
366
|
-
|
|
367
343
|
if os.path.exists(path):
|
|
368
|
-
|
|
369
|
-
url = self.window.core.filesystem.get_url(path)
|
|
370
|
-
QDesktopServices.openUrl(url)
|
|
371
|
-
# show_in_file_manager(path, select)
|
|
372
|
-
else:
|
|
373
|
-
url = QUrl.fromLocalFile(path)
|
|
374
|
-
if not QDesktopServices.openUrl(url):
|
|
375
|
-
subprocess.run(['gio', 'open', path])
|
|
344
|
+
Opener.open_path(path, reveal=select)
|
|
376
345
|
|
|
377
346
|
def make_dir_dialog(self, path: str):
|
|
378
347
|
"""
|
|
@@ -411,7 +380,7 @@ class Files:
|
|
|
411
380
|
return
|
|
412
381
|
os.makedirs(path_dir, exist_ok=True)
|
|
413
382
|
self.window.update_status(
|
|
414
|
-
"[OK] Directory created: {}"
|
|
383
|
+
f"[OK] Directory created: {name}"
|
|
415
384
|
)
|
|
416
385
|
self.update_explorer()
|
|
417
386
|
|
|
@@ -473,9 +442,9 @@ class Files:
|
|
|
473
442
|
:param path: path to file
|
|
474
443
|
"""
|
|
475
444
|
if os.path.isdir(path):
|
|
476
|
-
cmd = "Please list files from directory: {
|
|
445
|
+
cmd = f"Please list files from directory: {self.strip_work_path(path)}"
|
|
477
446
|
else:
|
|
478
|
-
cmd = "Please read this file from current directory: {
|
|
447
|
+
cmd = f"Please read this file from current directory: {self.strip_work_path(path)}"
|
|
479
448
|
self.window.controller.chat.common.append_to_input(cmd)
|
|
480
449
|
|
|
481
450
|
def make_ts_prefix(self) -> str:
|
|
@@ -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.25 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import os
|
|
@@ -50,7 +50,7 @@ class Theme:
|
|
|
50
50
|
|
|
51
51
|
:param name: theme name
|
|
52
52
|
"""
|
|
53
|
-
self.window.update_status(trans("status.
|
|
53
|
+
self.window.update_status(trans("status.reloading"))
|
|
54
54
|
QApplication.processEvents()
|
|
55
55
|
self.toggle(name, force=True)
|
|
56
56
|
self.window.update_status("")
|
|
@@ -62,7 +62,7 @@ class Theme:
|
|
|
62
62
|
:param name: option name
|
|
63
63
|
:param value: option value
|
|
64
64
|
"""
|
|
65
|
-
self.window.update_status(trans("status.
|
|
65
|
+
self.window.update_status(trans("status.reloading"))
|
|
66
66
|
QApplication.processEvents()
|
|
67
67
|
self.toggle_option(name, value)
|
|
68
68
|
self.window.update_status("")
|
|
@@ -212,7 +212,7 @@ class Evaluation:
|
|
|
212
212
|
return prompt.format(
|
|
213
213
|
task=main_task,
|
|
214
214
|
input=last_input,
|
|
215
|
-
output=final_response
|
|
215
|
+
output=final_response,
|
|
216
216
|
)
|
|
217
217
|
|
|
218
218
|
def get_prompt_complete(self, history: List[CtxItem]) -> str:
|
|
@@ -229,7 +229,7 @@ class Evaluation:
|
|
|
229
229
|
return prompt.format(
|
|
230
230
|
task=main_task,
|
|
231
231
|
input=last_input,
|
|
232
|
-
output=final_response
|
|
232
|
+
output=final_response,
|
|
233
233
|
)
|
|
234
234
|
|
|
235
235
|
def get_tools(self) -> List[FunctionTool]:
|
pygpt_net/core/bridge/bridge.py
CHANGED
|
@@ -150,6 +150,7 @@ class Bridge:
|
|
|
150
150
|
# async call
|
|
151
151
|
self.window.core.debug.info("[bridge] Starting worker (async)...")
|
|
152
152
|
self.window.threadpool.start(worker)
|
|
153
|
+
self.worker = worker
|
|
153
154
|
return True
|
|
154
155
|
|
|
155
156
|
def request_next(
|
|
@@ -177,6 +178,7 @@ class Bridge:
|
|
|
177
178
|
|
|
178
179
|
# async call
|
|
179
180
|
self.window.threadpool.start(worker)
|
|
181
|
+
self.worker = worker
|
|
180
182
|
return True
|
|
181
183
|
|
|
182
184
|
def call(
|
|
@@ -0,0 +1,261 @@
|
|
|
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: 2025.08.25 18:00:00 #
|
|
10
|
+
# ================================================== #
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import shutil
|
|
15
|
+
import subprocess
|
|
16
|
+
|
|
17
|
+
from PySide6.QtCore import QUrl
|
|
18
|
+
from PySide6.QtGui import QDesktopServices
|
|
19
|
+
|
|
20
|
+
# QtDBus is optional and may not be available on all systems
|
|
21
|
+
try:
|
|
22
|
+
from PySide6.QtDBus import QDBusInterface, QDBusConnection, QDBusMessage
|
|
23
|
+
HAS_QT_DBUS = True
|
|
24
|
+
except Exception:
|
|
25
|
+
HAS_QT_DBUS = False
|
|
26
|
+
|
|
27
|
+
IS_WINDOWS = sys.platform.startswith("win")
|
|
28
|
+
IS_MAC = sys.platform == "darwin"
|
|
29
|
+
IS_LINUX = sys.platform.startswith("linux")
|
|
30
|
+
|
|
31
|
+
class Opener:
|
|
32
|
+
# ===== public API =====
|
|
33
|
+
@staticmethod
|
|
34
|
+
def open_path(path: str, reveal: bool = False) -> bool:
|
|
35
|
+
"""
|
|
36
|
+
Open file or directory in the system file manager or default application.
|
|
37
|
+
|
|
38
|
+
:param path: Path to file or directory
|
|
39
|
+
:param reveal: If True and path is a file, reveal it in the file manager
|
|
40
|
+
:return: True if successful, False otherwise
|
|
41
|
+
"""
|
|
42
|
+
p = os.path.abspath(path)
|
|
43
|
+
|
|
44
|
+
if IS_WINDOWS:
|
|
45
|
+
if reveal and os.path.isfile(p):
|
|
46
|
+
return Opener._reveal_windows(p)
|
|
47
|
+
if os.path.isdir(p):
|
|
48
|
+
return Opener._open_dir_windows(p)
|
|
49
|
+
return Opener._open_file_windows(p)
|
|
50
|
+
|
|
51
|
+
if IS_MAC:
|
|
52
|
+
if reveal and os.path.isfile(p):
|
|
53
|
+
return Opener._reveal_mac(p)
|
|
54
|
+
if os.path.isdir(p):
|
|
55
|
+
return Opener._open_dir_mac(p)
|
|
56
|
+
return Opener._open_file_mac(p)
|
|
57
|
+
|
|
58
|
+
# Linux
|
|
59
|
+
if reveal and os.path.isfile(p):
|
|
60
|
+
if Opener._reveal_linux(p):
|
|
61
|
+
return True
|
|
62
|
+
# fallback:
|
|
63
|
+
return Opener._open_dir_linux(os.path.dirname(p) or "/")
|
|
64
|
+
if os.path.isdir(p):
|
|
65
|
+
return Opener._open_dir_linux(p)
|
|
66
|
+
return Opener._open_file_linux(p)
|
|
67
|
+
|
|
68
|
+
# ===== Windows =====
|
|
69
|
+
@staticmethod
|
|
70
|
+
def _open_dir_windows(path: str) -> bool:
|
|
71
|
+
"""
|
|
72
|
+
Open directory in Windows Explorer
|
|
73
|
+
|
|
74
|
+
:param path: Path to directory
|
|
75
|
+
:return: True if successful, False otherwise
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
os.startfile(path)
|
|
79
|
+
return True
|
|
80
|
+
except Exception:
|
|
81
|
+
try:
|
|
82
|
+
subprocess.Popen(["explorer", path])
|
|
83
|
+
return True
|
|
84
|
+
except Exception:
|
|
85
|
+
return False
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def _reveal_windows(path: str) -> bool:
|
|
89
|
+
"""
|
|
90
|
+
Reveal file in Windows Explorer
|
|
91
|
+
|
|
92
|
+
:param path: Path to file
|
|
93
|
+
:return: True if successful, False otherwise
|
|
94
|
+
"""
|
|
95
|
+
try:
|
|
96
|
+
subprocess.Popen(["explorer", "/select,", os.path.normpath(path)])
|
|
97
|
+
return True
|
|
98
|
+
except Exception:
|
|
99
|
+
return Opener._open_dir_windows(os.path.dirname(path) or "C:\\")
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def _open_file_windows(path: str) -> bool:
|
|
103
|
+
"""
|
|
104
|
+
Open file with default application
|
|
105
|
+
|
|
106
|
+
:param path: Path to file
|
|
107
|
+
:return: True if successful, False otherwise
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
os.startfile(path)
|
|
111
|
+
return True
|
|
112
|
+
except Exception:
|
|
113
|
+
# Qt
|
|
114
|
+
return QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
|
115
|
+
|
|
116
|
+
# ===== macOS =====
|
|
117
|
+
@staticmethod
|
|
118
|
+
def _open_dir_mac(path: str) -> bool:
|
|
119
|
+
"""
|
|
120
|
+
Open directory in Finder
|
|
121
|
+
|
|
122
|
+
:param path: Path to directory
|
|
123
|
+
:return: True if successful, False otherwise
|
|
124
|
+
"""
|
|
125
|
+
try:
|
|
126
|
+
subprocess.Popen(["open", path])
|
|
127
|
+
return True
|
|
128
|
+
except Exception:
|
|
129
|
+
return QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
|
130
|
+
|
|
131
|
+
@staticmethod
|
|
132
|
+
def _reveal_mac(path: str) -> bool:
|
|
133
|
+
"""
|
|
134
|
+
Reveal file in Finder
|
|
135
|
+
|
|
136
|
+
:param path: Path to file
|
|
137
|
+
:return: True if successful, False otherwise
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
subprocess.Popen(["open", "-R", path])
|
|
141
|
+
return True
|
|
142
|
+
except Exception:
|
|
143
|
+
return Opener._open_dir_mac(os.path.dirname(path) or "/")
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def _open_file_mac(path: str) -> bool:
|
|
147
|
+
"""
|
|
148
|
+
Open file with default application
|
|
149
|
+
|
|
150
|
+
:param path: Path to file
|
|
151
|
+
:return: True if successful, False otherwise
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
subprocess.Popen(["open", path])
|
|
155
|
+
return True
|
|
156
|
+
except Exception:
|
|
157
|
+
return QDesktopServices.openUrl(QUrl.fromLocalFile(path))
|
|
158
|
+
|
|
159
|
+
# ===== Linux =====
|
|
160
|
+
@staticmethod
|
|
161
|
+
def _fm_iface():
|
|
162
|
+
"""
|
|
163
|
+
Get FileManager1 D-Bus interface if available
|
|
164
|
+
|
|
165
|
+
:return: QDBusInterface or None
|
|
166
|
+
"""
|
|
167
|
+
if not (IS_LINUX and HAS_QT_DBUS):
|
|
168
|
+
return None
|
|
169
|
+
bus = QDBusConnection.sessionBus()
|
|
170
|
+
iface = QDBusInterface(
|
|
171
|
+
"org.freedesktop.FileManager1",
|
|
172
|
+
"/org/freedesktop/FileManager1",
|
|
173
|
+
"org.freedesktop.FileManager1",
|
|
174
|
+
bus,
|
|
175
|
+
)
|
|
176
|
+
return iface if iface.isValid() else None
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def _show_folders_dbus(paths):
|
|
180
|
+
"""
|
|
181
|
+
Show folders using D-Bus FileManager1 interface
|
|
182
|
+
|
|
183
|
+
:param paths: List of folder paths
|
|
184
|
+
:return: True if successful, False otherwise
|
|
185
|
+
"""
|
|
186
|
+
iface = Opener._fm_iface()
|
|
187
|
+
if not iface:
|
|
188
|
+
return False
|
|
189
|
+
uris = [QUrl.fromLocalFile(p).toString() for p in paths]
|
|
190
|
+
reply = iface.call("ShowFolders", uris, "")
|
|
191
|
+
return reply.type() != QDBusMessage.ErrorMessage
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def _show_items_dbus(paths):
|
|
195
|
+
"""
|
|
196
|
+
Show items using D-Bus FileManager1 interface
|
|
197
|
+
|
|
198
|
+
:param paths: List of item paths
|
|
199
|
+
:return: True if successful, False otherwise
|
|
200
|
+
"""
|
|
201
|
+
iface = Opener._fm_iface()
|
|
202
|
+
if not iface:
|
|
203
|
+
return False
|
|
204
|
+
uris = [QUrl.fromLocalFile(p).toString() for p in paths]
|
|
205
|
+
reply = iface.call("ShowItems", uris, "")
|
|
206
|
+
return reply.type() != QDBusMessage.ErrorMessage
|
|
207
|
+
|
|
208
|
+
@staticmethod
|
|
209
|
+
def _open_with_cli_linux(path):
|
|
210
|
+
"""
|
|
211
|
+
Open path using common CLI tools (xdg-open, gio)
|
|
212
|
+
|
|
213
|
+
:param path: Path to file or directory
|
|
214
|
+
:return: True if successful, False otherwise
|
|
215
|
+
"""
|
|
216
|
+
for cmd in (["xdg-open", path], ["gio", "open", path]):
|
|
217
|
+
if shutil.which(cmd[0]):
|
|
218
|
+
try:
|
|
219
|
+
subprocess.Popen(cmd)
|
|
220
|
+
return True
|
|
221
|
+
except Exception:
|
|
222
|
+
pass
|
|
223
|
+
return False
|
|
224
|
+
|
|
225
|
+
@staticmethod
|
|
226
|
+
def _open_dir_linux(path: str) -> bool:
|
|
227
|
+
"""
|
|
228
|
+
Open directory in default file manager
|
|
229
|
+
|
|
230
|
+
:param path: Path to directory
|
|
231
|
+
:return: True if successful, False otherwise
|
|
232
|
+
"""
|
|
233
|
+
if Opener._show_folders_dbus([path]):
|
|
234
|
+
return True
|
|
235
|
+
if QDesktopServices.openUrl(QUrl.fromLocalFile(path)):
|
|
236
|
+
return True
|
|
237
|
+
return Opener._open_with_cli_linux(path)
|
|
238
|
+
|
|
239
|
+
@staticmethod
|
|
240
|
+
def _reveal_linux(path: str) -> bool:
|
|
241
|
+
"""
|
|
242
|
+
Reveal file in default file manager
|
|
243
|
+
|
|
244
|
+
:param path: Path to file
|
|
245
|
+
:return: True if successful, False otherwise
|
|
246
|
+
"""
|
|
247
|
+
if Opener._show_items_dbus([path]):
|
|
248
|
+
return True
|
|
249
|
+
return False
|
|
250
|
+
|
|
251
|
+
@staticmethod
|
|
252
|
+
def _open_file_linux(path: str) -> bool:
|
|
253
|
+
"""
|
|
254
|
+
Open file with default application
|
|
255
|
+
|
|
256
|
+
:param path: Path to file
|
|
257
|
+
:return: True if successful, False otherwise
|
|
258
|
+
"""
|
|
259
|
+
if QDesktopServices.openUrl(QUrl.fromLocalFile(path)):
|
|
260
|
+
return True
|
|
261
|
+
return Opener._open_with_cli_linux(path)
|
pygpt_net/core/filesystem/url.py
CHANGED
|
@@ -23,19 +23,12 @@ class Url:
|
|
|
23
23
|
|
|
24
24
|
def handle(self, url: QUrl):
|
|
25
25
|
"""
|
|
26
|
-
Handle URL
|
|
26
|
+
Handle URL, bridge action or local file
|
|
27
27
|
|
|
28
28
|
:param url: url
|
|
29
29
|
"""
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
'extra-code-copy',
|
|
33
|
-
'extra-copy',
|
|
34
|
-
'extra-delete',
|
|
35
|
-
'extra-edit',
|
|
36
|
-
'extra-join',
|
|
37
|
-
'extra-replay',
|
|
38
|
-
]
|
|
30
|
+
if not url.toString().strip():
|
|
31
|
+
return
|
|
39
32
|
|
|
40
33
|
# JS bridge
|
|
41
34
|
if url.toString().startswith('bridge://open_find'):
|
|
@@ -50,8 +43,18 @@ class Url:
|
|
|
50
43
|
pid = self.window.controller.ui.tabs.get_current_pid()
|
|
51
44
|
if pid in self.window.ui.nodes['output']:
|
|
52
45
|
self.window.ui.nodes['output'][pid].on_focus_js()
|
|
46
|
+
return
|
|
53
47
|
|
|
54
48
|
# -------------
|
|
49
|
+
extra_schemes = (
|
|
50
|
+
'extra-audio-read',
|
|
51
|
+
'extra-code-copy',
|
|
52
|
+
'extra-copy',
|
|
53
|
+
'extra-delete',
|
|
54
|
+
'extra-edit',
|
|
55
|
+
'extra-join',
|
|
56
|
+
'extra-replay'
|
|
57
|
+
)
|
|
55
58
|
|
|
56
59
|
# local file
|
|
57
60
|
if not url.scheme().startswith('http') and url.scheme() not in extra_schemes:
|
|
@@ -6,11 +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.
|
|
9
|
+
# Updated Date: 2025.08.25 18:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import platform
|
|
13
13
|
import os
|
|
14
|
+
import sys
|
|
14
15
|
|
|
15
16
|
from PySide6 import QtCore, QtWidgets
|
|
16
17
|
from PySide6.QtSvg import QSvgRenderer
|
|
@@ -87,7 +88,7 @@ class Platforms:
|
|
|
87
88
|
|
|
88
89
|
:return: True if OS is Linux
|
|
89
90
|
"""
|
|
90
|
-
return self.get_os() == 'Linux'
|
|
91
|
+
return self.get_os() == 'Linux' or sys.platform.startswith("linux")
|
|
91
92
|
|
|
92
93
|
def is_mac(self) -> bool:
|
|
93
94
|
"""
|
|
@@ -95,7 +96,7 @@ class Platforms:
|
|
|
95
96
|
|
|
96
97
|
:return: True if OS is MacOS
|
|
97
98
|
"""
|
|
98
|
-
return self.get_os() == 'Darwin'
|
|
99
|
+
return self.get_os() == 'Darwin' or sys.platform == "darwin"
|
|
99
100
|
|
|
100
101
|
def is_windows(self) -> bool:
|
|
101
102
|
"""
|
|
@@ -103,7 +104,7 @@ class Platforms:
|
|
|
103
104
|
|
|
104
105
|
:return: True if OS is Windows
|
|
105
106
|
"""
|
|
106
|
-
return self.get_os() == 'Windows'
|
|
107
|
+
return self.get_os() == 'Windows' or sys.platform.startswith("win")
|
|
107
108
|
|
|
108
109
|
def is_snap(self) -> bool:
|
|
109
110
|
"""
|