pygpt-net 2.6.43__py3-none-any.whl → 2.6.44__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/debug/debug.py +7 -19
- pygpt_net/controller/debug/fixtures.py +103 -0
- pygpt_net/core/debug/debug.py +2 -2
- pygpt_net/core/fixtures/stream/__init__.py +0 -0
- pygpt_net/{provider/api/fake → core/fixtures/stream}/generator.py +1 -1
- pygpt_net/core/render/web/body.py +5 -1
- pygpt_net/data/config/config.json +9 -5
- pygpt_net/data/config/models.json +2 -2
- pygpt_net/data/config/settings.json +59 -19
- pygpt_net/data/js/app.js +727 -361
- pygpt_net/data/locale/locale.en.ini +8 -1
- pygpt_net/js_rc.py +11506 -10502
- pygpt_net/provider/api/openai/__init__.py +4 -12
- pygpt_net/provider/core/config/patch.py +14 -1
- pygpt_net/ui/base/context_menu.py +3 -2
- pygpt_net/ui/layout/ctx/ctx_list.py +3 -3
- pygpt_net/ui/menu/debug.py +36 -23
- pygpt_net/ui/widget/lists/context.py +233 -51
- {pygpt_net-2.6.43.dist-info → pygpt_net-2.6.44.dist-info}/METADATA +10 -2
- {pygpt_net-2.6.43.dist-info → pygpt_net-2.6.44.dist-info}/RECORD +26 -24
- /pygpt_net/{provider/api/fake/__init__.py → core/fixtures/__init__} +0 -0
- {pygpt_net-2.6.43.dist-info → pygpt_net-2.6.44.dist-info}/LICENSE +0 -0
- {pygpt_net-2.6.43.dist-info → pygpt_net-2.6.44.dist-info}/WHEEL +0 -0
- {pygpt_net-2.6.43.dist-info → pygpt_net-2.6.44.dist-info}/entry_points.txt +0 -0
|
@@ -6,9 +6,8 @@
|
|
|
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.09.
|
|
9
|
+
# Updated Date: 2025.09.12 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
|
-
import os
|
|
12
11
|
|
|
13
12
|
from openai import OpenAI
|
|
14
13
|
|
|
@@ -24,7 +23,6 @@ from pygpt_net.core.types import (
|
|
|
24
23
|
)
|
|
25
24
|
from pygpt_net.core.bridge.context import BridgeContext
|
|
26
25
|
from pygpt_net.item.model import ModelItem
|
|
27
|
-
from pygpt_net.provider.api.fake.generator import FakeOpenAIStream
|
|
28
26
|
|
|
29
27
|
from .audio import Audio
|
|
30
28
|
from .assistants import Assistants
|
|
@@ -119,7 +117,7 @@ class ApiOpenAI:
|
|
|
119
117
|
use_responses_api = self.responses.is_enabled(model, mode, parent_mode, is_expert_call, preset)
|
|
120
118
|
ctx.use_responses_api = use_responses_api # set in context
|
|
121
119
|
|
|
122
|
-
|
|
120
|
+
fixtures = self.window.controller.debug.fixtures
|
|
123
121
|
|
|
124
122
|
# get model id
|
|
125
123
|
model_id = None
|
|
@@ -159,15 +157,9 @@ class ApiOpenAI:
|
|
|
159
157
|
if is_realtime:
|
|
160
158
|
return True
|
|
161
159
|
|
|
162
|
-
if
|
|
163
|
-
# fake stream for testing
|
|
160
|
+
if fixtures.is_enabled("stream"): # fake stream for testing
|
|
164
161
|
use_responses_api = False
|
|
165
|
-
|
|
166
|
-
test_code_path = os.path.join(self.window.core.config.get_app_path(), "data", "js", "app.js")
|
|
167
|
-
response = FakeOpenAIStream(code_path=test_code_path).stream(
|
|
168
|
-
api="raw",
|
|
169
|
-
chunk="code",
|
|
170
|
-
)
|
|
162
|
+
response = fixtures.get_stream_generator(ctx)
|
|
171
163
|
else:
|
|
172
164
|
# responses API
|
|
173
165
|
if use_responses_api:
|
|
@@ -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.09.12
|
|
9
|
+
# Updated Date: 2025.09.12 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import copy
|
|
@@ -62,6 +62,19 @@ class Patch:
|
|
|
62
62
|
patch_css('web-blocks.css', True)
|
|
63
63
|
updated = True
|
|
64
64
|
|
|
65
|
+
# < 2.6.44
|
|
66
|
+
if old < parse_version("2.6.44"):
|
|
67
|
+
print("Migrating config from < 2.6.44...")
|
|
68
|
+
if "render.code_syntax.stream_n_line" not in data:
|
|
69
|
+
data["render.code_syntax.stream_n_line"] = 25
|
|
70
|
+
if "render.code_syntax.stream_n_chars" not in data:
|
|
71
|
+
data["render.code_syntax.stream_n_chars"] = 5000
|
|
72
|
+
if "render.code_syntax.disabled" not in data:
|
|
73
|
+
data["render.code_syntax.disabled"] = False
|
|
74
|
+
if "render.msg.user.collapse.px" not in data:
|
|
75
|
+
data["render.msg.user.collapse.px"] = 1500
|
|
76
|
+
updated = True
|
|
77
|
+
|
|
65
78
|
# update file
|
|
66
79
|
migrated = False
|
|
67
80
|
if updated:
|
|
@@ -23,6 +23,7 @@ class ContextMenu:
|
|
|
23
23
|
_ICON_PASTE = QIcon(":/icons/paste.svg")
|
|
24
24
|
_ICON_CODE = QIcon(":/icons/code.svg")
|
|
25
25
|
_ICON_TEXT = QIcon(":/icons/text.svg")
|
|
26
|
+
_ICON_TRANSLATOR = QIcon(":/icons/translate.svg")
|
|
26
27
|
|
|
27
28
|
def __init__(self, window=None):
|
|
28
29
|
"""
|
|
@@ -94,11 +95,11 @@ class ContextMenu:
|
|
|
94
95
|
menu.addSeparator()
|
|
95
96
|
translator = tools.get("translator")
|
|
96
97
|
if add_left:
|
|
97
|
-
action = QAction(self.
|
|
98
|
+
action = QAction(self._ICON_TRANSLATOR, trans('text.context_menu.copy_to.translator_left'), menu)
|
|
98
99
|
action.triggered.connect(lambda checked=False: translator.append_content("left", selected_text))
|
|
99
100
|
menu.addAction(action)
|
|
100
101
|
if add_right:
|
|
101
|
-
action = QAction(self.
|
|
102
|
+
action = QAction(self._ICON_TRANSLATOR, trans('text.context_menu.copy_to.translator_right'), menu)
|
|
102
103
|
action.triggered.connect(lambda checked=False: translator.append_content("right", selected_text))
|
|
103
104
|
menu.addAction(action)
|
|
104
105
|
|
|
@@ -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.09.
|
|
9
|
+
# Updated Date: 2025.09.12 23:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6 import QtCore
|
|
@@ -264,7 +264,7 @@ class CtxList:
|
|
|
264
264
|
is_important = data.important
|
|
265
265
|
is_attachment = data.has_additional_ctx()
|
|
266
266
|
in_group = bool(data.group)
|
|
267
|
-
append_dt = False if (is_group and self.
|
|
267
|
+
append_dt = False if (is_group and self._group_separators) or ((not is_group) and self._group_separators) else append_dt
|
|
268
268
|
|
|
269
269
|
dt = self.convert_date(data.updated)
|
|
270
270
|
date_time_str = datetime.fromtimestamp(data.updated).strftime("%Y-%m-%d %H:%M")
|
|
@@ -337,4 +337,4 @@ class CtxList:
|
|
|
337
337
|
elif 30 <= days_ago < 32:
|
|
338
338
|
return trans('dt.month')
|
|
339
339
|
else:
|
|
340
|
-
return date.strftime("%Y-%m-%d")
|
|
340
|
+
return date.strftime("%Y-%m-%d")
|
pygpt_net/ui/menu/debug.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: 2025.
|
|
9
|
+
# Updated Date: 2025.09.12 20:00:00 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
from PySide6.QtGui import QAction
|
|
@@ -46,7 +46,7 @@ class Debug:
|
|
|
46
46
|
'db',
|
|
47
47
|
'logger',
|
|
48
48
|
'app.log',
|
|
49
|
-
'
|
|
49
|
+
'fixtures.stream',
|
|
50
50
|
'kernel',
|
|
51
51
|
'render'
|
|
52
52
|
)
|
|
@@ -72,32 +72,45 @@ class Debug:
|
|
|
72
72
|
m['debug.logger'].triggered.connect(dbg.toggle_logger)
|
|
73
73
|
m['debug.app.log'].triggered.connect(dbg.toggle_app_log)
|
|
74
74
|
m['debug.render'].triggered.connect(dbg.toggle_render)
|
|
75
|
-
m['debug.
|
|
75
|
+
m['debug.fixtures.stream'].triggered.connect(
|
|
76
|
+
lambda _=False: dbg.fixtures.toggle_from_menu("stream")
|
|
77
|
+
)
|
|
76
78
|
|
|
77
79
|
m['menu.debug'] = win.menuBar().addMenu(trans("menu.debug"))
|
|
78
80
|
menu = m['menu.debug']
|
|
79
81
|
menu.addActions(
|
|
80
|
-
[
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
[
|
|
83
|
+
m['debug.logger'],
|
|
84
|
+
m['debug.render'],
|
|
85
|
+
m['debug.db'],
|
|
86
|
+
m['debug.app.log']
|
|
87
|
+
]
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
menu.addSeparator()
|
|
91
|
+
menu.addActions(
|
|
92
|
+
[
|
|
93
|
+
m['debug.fixtures.stream']
|
|
94
|
+
]
|
|
85
95
|
)
|
|
96
|
+
|
|
86
97
|
menu.addSeparator()
|
|
87
|
-
menu.addActions(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
menu.addActions(
|
|
99
|
+
[
|
|
100
|
+
m['debug.agent'],
|
|
101
|
+
m['debug.assistants'],
|
|
102
|
+
m['debug.attachments'],
|
|
103
|
+
m['debug.config'],
|
|
104
|
+
m['debug.context'],
|
|
105
|
+
m['debug.events'],
|
|
106
|
+
m['debug.indexes'],
|
|
107
|
+
m['debug.kernel'],
|
|
108
|
+
m['debug.models'],
|
|
109
|
+
m['debug.plugins'],
|
|
110
|
+
m['debug.presets'],
|
|
111
|
+
m['debug.tabs'],
|
|
112
|
+
m['debug.ui'],
|
|
113
|
+
]
|
|
114
|
+
)
|
|
102
115
|
|
|
103
116
|
m['debug.render'].setChecked(bool(win.core.config.get('debug.render')))
|
|
@@ -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.09.12 23:47:47 #
|
|
10
10
|
# ================================================== #
|
|
11
11
|
|
|
12
12
|
import datetime
|
|
@@ -47,12 +47,26 @@ class ContextList(BaseList):
|
|
|
47
47
|
'attachment': QIcon(":/icons/attachment.svg"),
|
|
48
48
|
}
|
|
49
49
|
self._color_icon_cache = {}
|
|
50
|
+
|
|
51
|
+
# Use a custom delegate for labels/pinned/attachment indicators and group border indicator
|
|
50
52
|
self.setItemDelegate(ImportantItemDelegate(self, self._icons['attachment']))
|
|
53
|
+
|
|
54
|
+
# Ensure context menu works as before
|
|
51
55
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
52
56
|
self.customContextMenuRequested.connect(self.show_context_menu)
|
|
53
57
|
self._backup_selection = None
|
|
54
58
|
self.restore_after_ctx_menu = True
|
|
55
59
|
|
|
60
|
+
# Make group rows visually stick to the left edge (if this is a tree view).
|
|
61
|
+
# Children remain indented by delegate's manual shift (+15 px), preserving structure.
|
|
62
|
+
try:
|
|
63
|
+
if hasattr(self, 'setIndentation'):
|
|
64
|
+
# Set tree indentation to 0 so group/folder rows do not look like children
|
|
65
|
+
self.setIndentation(0)
|
|
66
|
+
except Exception:
|
|
67
|
+
# Safe no-op if the underlying view does not support setIndentation
|
|
68
|
+
pass
|
|
69
|
+
|
|
56
70
|
@property
|
|
57
71
|
def _model(self):
|
|
58
72
|
return self.window.ui.models['ctx.list']
|
|
@@ -62,6 +76,9 @@ class ContextList(BaseList):
|
|
|
62
76
|
return self.window.ui.nodes['ctx.list']
|
|
63
77
|
|
|
64
78
|
def _color_icon(self, color: QColor) -> QIcon:
|
|
79
|
+
"""
|
|
80
|
+
Returns (and caches) a solid color icon pixmap for menu items.
|
|
81
|
+
"""
|
|
65
82
|
key = color.rgba()
|
|
66
83
|
icon = self._color_icon_cache.get(key)
|
|
67
84
|
if icon is None:
|
|
@@ -405,11 +422,19 @@ class ContextList(BaseList):
|
|
|
405
422
|
|
|
406
423
|
class ImportantItemDelegate(QtWidgets.QStyledItemDelegate):
|
|
407
424
|
"""
|
|
408
|
-
|
|
425
|
+
Item delegate that paints:
|
|
426
|
+
- Attachment icon on the right side (centered vertically),
|
|
427
|
+
- Pinned indicator (small circle) in the top-right corner (overlays if needed),
|
|
428
|
+
- Label color as a full-height vertical bar on the left for labeled items,
|
|
429
|
+
- Group enclosure indicator for expanded groups:
|
|
430
|
+
- thin vertical bar (default 2 px) on the left side of child rows area,
|
|
431
|
+
- thin horizontal bar (default 2 px) at the bottom of the last child row.
|
|
409
432
|
"""
|
|
410
433
|
def __init__(self, parent=None, attachment_icon: QIcon = None):
|
|
411
434
|
super().__init__(parent)
|
|
412
435
|
self._attachment_icon = attachment_icon or QIcon(":/icons/attachment.svg")
|
|
436
|
+
|
|
437
|
+
# Predefined label colors (status -> QColor)
|
|
413
438
|
self._status_colors = {
|
|
414
439
|
0: QColor(100, 100, 100),
|
|
415
440
|
1: QColor(255, 0, 0),
|
|
@@ -420,64 +445,219 @@ class ImportantItemDelegate(QtWidgets.QStyledItemDelegate):
|
|
|
420
445
|
6: QColor(75, 0, 130),
|
|
421
446
|
7: QColor(238, 130, 238),
|
|
422
447
|
}
|
|
423
|
-
self._pin_pen = QtGui.QPen(QtCore.Qt.black, 0.5, QtCore.Qt.SolidLine)
|
|
424
448
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
449
|
+
# Visual tuning constants
|
|
450
|
+
self._pin_pen = QtGui.QPen(QtCore.Qt.black, 0.5, QtCore.Qt.SolidLine)
|
|
451
|
+
self._pin_diameter = 4 # Small pinned circle diameter
|
|
452
|
+
self._pin_margin = 3 # Margin from top and right edges
|
|
453
|
+
self._attach_spacing = 4 # Kept for potential future layout tweaks
|
|
454
|
+
self._label_bar_width = 4 # Full-height label bar width (left side)
|
|
455
|
+
self._label_v_margin = 3 # 3px top/bottom margin for the label bar
|
|
456
|
+
|
|
457
|
+
# Manual child indent to keep hierarchy visible when view indentation is 0
|
|
458
|
+
self._child_indent = 15
|
|
459
|
+
|
|
460
|
+
# Group indicator defaults (can be overridden by config)
|
|
461
|
+
self._group_indicator_enabled = True
|
|
462
|
+
self._group_indicator_width = 2
|
|
463
|
+
self._group_indicator_color = QColor(67, 75, 78) # soft gray
|
|
464
|
+
self._group_indicator_gap = 6 # gap between child content left and the vertical bar
|
|
465
|
+
self._group_indicator_bottom_offset = 6
|
|
466
|
+
|
|
467
|
+
# Try to load customization from application config (safe if missing)
|
|
468
|
+
self._init_group_indicator_from_config()
|
|
469
|
+
|
|
470
|
+
def _init_group_indicator_from_config(self):
|
|
471
|
+
"""
|
|
472
|
+
Initialize group indicator settings from config if available.
|
|
473
|
+
Accepts:
|
|
474
|
+
- color: list/tuple [r,g,b], dict {'r','g','b'}, "#RRGGBB", or "r,g,b"
|
|
475
|
+
- width: int
|
|
476
|
+
- enabled: bool
|
|
477
|
+
- gap: int
|
|
478
|
+
"""
|
|
479
|
+
try:
|
|
480
|
+
view = self.parent()
|
|
481
|
+
window = getattr(view, 'window', None)
|
|
482
|
+
cfg = getattr(getattr(window, 'core', None), 'config', None)
|
|
483
|
+
if not cfg:
|
|
484
|
+
return
|
|
430
485
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
486
|
+
enabled = cfg.get('ctx.records.groups.indicator.enabled')
|
|
487
|
+
if enabled is not None:
|
|
488
|
+
self._group_indicator_enabled = bool(enabled)
|
|
489
|
+
|
|
490
|
+
width = cfg.get('ctx.records.groups.indicator.width')
|
|
491
|
+
if isinstance(width, int) and width >= 0:
|
|
492
|
+
self._group_indicator_width = int(width)
|
|
493
|
+
|
|
494
|
+
gap = cfg.get('ctx.records.groups.indicator.gap')
|
|
495
|
+
if isinstance(gap, int) and gap >= 0:
|
|
496
|
+
self._group_indicator_gap = int(gap)
|
|
497
|
+
|
|
498
|
+
color = cfg.get('ctx.records.groups.indicator.color')
|
|
499
|
+
qcolor = self._parse_qcolor(color)
|
|
500
|
+
if qcolor is not None:
|
|
501
|
+
self._group_indicator_color = qcolor
|
|
502
|
+
except Exception:
|
|
503
|
+
# Fail-safe: keep defaults if anything goes wrong
|
|
504
|
+
pass
|
|
505
|
+
|
|
506
|
+
def _parse_qcolor(self, value):
|
|
507
|
+
"""
|
|
508
|
+
Parses various color formats into QColor.
|
|
509
|
+
Supports:
|
|
510
|
+
- QColor
|
|
511
|
+
- list/tuple [r, g, b]
|
|
512
|
+
- dict {'r':..,'g':..,'b':..} or {'red':..,'green':..,'blue':..}
|
|
513
|
+
- "#RRGGBB"
|
|
514
|
+
- "r,g,b" (also "r;g;b")
|
|
515
|
+
"""
|
|
516
|
+
if value is None:
|
|
517
|
+
return None
|
|
518
|
+
if isinstance(value, QColor):
|
|
519
|
+
return value
|
|
520
|
+
if isinstance(value, (list, tuple)) and len(value) >= 3:
|
|
521
|
+
try:
|
|
522
|
+
r, g, b = int(value[0]), int(value[1]), int(value[2])
|
|
523
|
+
return QColor(r, g, b)
|
|
524
|
+
except Exception:
|
|
525
|
+
return None
|
|
526
|
+
if isinstance(value, dict):
|
|
527
|
+
keys = value.keys()
|
|
528
|
+
try:
|
|
529
|
+
if all(k in keys for k in ('r', 'g', 'b')):
|
|
530
|
+
return QColor(int(value['r']), int(value['g']), int(value['b']))
|
|
531
|
+
if all(k in keys for k in ('red', 'green', 'blue')):
|
|
532
|
+
return QColor(int(value['red']), int(value['green']), int(value['blue']))
|
|
533
|
+
except Exception:
|
|
534
|
+
return None
|
|
535
|
+
if isinstance(value, str):
|
|
536
|
+
s = value.strip()
|
|
537
|
+
if s.startswith('#'):
|
|
538
|
+
qc = QColor(s)
|
|
539
|
+
return qc if qc.isValid() else None
|
|
540
|
+
s = s.replace(';', ',')
|
|
541
|
+
parts = [p.strip() for p in s.split(',') if p.strip()]
|
|
542
|
+
if len(parts) >= 3:
|
|
543
|
+
try:
|
|
544
|
+
r, g, b = int(parts[0]), int(parts[1]), int(parts[2])
|
|
545
|
+
return QColor(r, g, b)
|
|
546
|
+
except Exception:
|
|
547
|
+
return None
|
|
548
|
+
return None
|
|
436
549
|
|
|
550
|
+
def paint(self, painter, option, index):
|
|
551
|
+
# Shift children by +15 px to keep them visually nested.
|
|
552
|
+
is_child = index.parent().isValid()
|
|
553
|
+
if is_child:
|
|
554
|
+
option.rect.adjust(self._child_indent, 0, 0, 0)
|
|
555
|
+
|
|
556
|
+
# Detect if this row is a group/folder (top-level section).
|
|
557
|
+
is_group = False
|
|
558
|
+
try:
|
|
559
|
+
model = index.model()
|
|
560
|
+
item = model.itemFromIndex(index) if hasattr(model, "itemFromIndex") else None
|
|
561
|
+
is_group = bool(item is not None and getattr(item, 'isFolder', False))
|
|
562
|
+
except Exception:
|
|
563
|
+
is_group = False
|
|
564
|
+
|
|
565
|
+
# Default painting:
|
|
566
|
+
# - For groups: translate painter -8 px to push folder/icon closer to the left edge.
|
|
567
|
+
# - For others: paint normally.
|
|
568
|
+
if is_group:
|
|
437
569
|
painter.save()
|
|
570
|
+
painter.translate(-2, 0)
|
|
571
|
+
super(ImportantItemDelegate, self).paint(painter, option, index)
|
|
572
|
+
painter.restore()
|
|
573
|
+
else:
|
|
574
|
+
super(ImportantItemDelegate, self).paint(painter, option, index)
|
|
575
|
+
|
|
576
|
+
# Group enclosure indicator (left bar + bottom bar on last child)
|
|
577
|
+
# This applies only to child rows (i.e., when a group is expanded).
|
|
578
|
+
if self._group_indicator_enabled and not is_group and is_child and self._group_indicator_width > 0:
|
|
579
|
+
try:
|
|
580
|
+
painter.save()
|
|
581
|
+
# Use solid fill for crisp 2px bars (no anti-alias blur)
|
|
582
|
+
painter.setRenderHint(QtGui.QPainter.Antialiasing, False)
|
|
583
|
+
color = self._group_indicator_color
|
|
584
|
+
painter.setPen(QtCore.Qt.NoPen)
|
|
585
|
+
painter.setBrush(color)
|
|
438
586
|
|
|
439
|
-
|
|
587
|
+
# Compute vertical bar geometry:
|
|
588
|
+
# Place the bar to the LEFT of the child content area, leaving a small gap.
|
|
589
|
+
child_left = option.rect.x()
|
|
590
|
+
bar_w = self._group_indicator_width
|
|
591
|
+
# Left edge of the vertical bar (never below 0)
|
|
592
|
+
vbar_left = max(0, child_left - (self._group_indicator_gap + bar_w))
|
|
593
|
+
vbar_rect = QtCore.QRect(vbar_left, option.rect.y(), bar_w, option.rect.height())
|
|
594
|
+
painter.drawRect(vbar_rect)
|
|
595
|
+
|
|
596
|
+
painter.restore()
|
|
597
|
+
except Exception:
|
|
598
|
+
# Fail-safe: do not block painting if anything goes wrong
|
|
599
|
+
pass
|
|
600
|
+
|
|
601
|
+
# Custom data painting for non-group items only (labels, pinned, attachments).
|
|
602
|
+
if not is_group:
|
|
603
|
+
data = index.data(QtCore.Qt.ItemDataRole.UserRole)
|
|
604
|
+
if data:
|
|
605
|
+
label = data.get("label", 0)
|
|
606
|
+
is_important = data.get("is_important", False)
|
|
607
|
+
is_attachment = data.get("is_attachment", False)
|
|
608
|
+
|
|
609
|
+
painter.save()
|
|
610
|
+
|
|
611
|
+
# Draw attachment icon on the right (centered vertically).
|
|
612
|
+
# This is painted first, so the pin can overlay it when needed.
|
|
440
613
|
icon_size = option.decorationSize or QtCore.QSize(16, 16)
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
if is_important:
|
|
452
|
-
color = self.get_color_for_status(3)
|
|
453
|
-
square_size = 3
|
|
454
|
-
square_rect = QtCore.QRect(
|
|
455
|
-
option.rect.left(),
|
|
456
|
-
option.rect.top() + 2,
|
|
457
|
-
square_size,
|
|
458
|
-
square_size,
|
|
459
|
-
)
|
|
460
|
-
painter.setBrush(color)
|
|
461
|
-
painter.setPen(self._pin_pen)
|
|
462
|
-
painter.drawRect(square_rect)
|
|
463
|
-
|
|
464
|
-
if label > 0:
|
|
465
|
-
color = self.get_color_for_status(label)
|
|
466
|
-
square_size = 5
|
|
467
|
-
y = option.rect.center().y() - (square_size // 2) + 2
|
|
468
|
-
square_rect = QtCore.QRect(
|
|
469
|
-
option.rect.left(),
|
|
470
|
-
y,
|
|
471
|
-
square_size,
|
|
472
|
-
square_size,
|
|
473
|
-
)
|
|
474
|
-
painter.setBrush(color)
|
|
475
|
-
painter.setPen(QtCore.Qt.NoPen)
|
|
476
|
-
painter.drawRect(square_rect)
|
|
614
|
+
if is_attachment:
|
|
615
|
+
icon_pos_x = option.rect.right() - icon_size.width()
|
|
616
|
+
icon_pos_y = option.rect.top() + (option.rect.height() - icon_size.height()) // 2
|
|
617
|
+
icon_rect = QtCore.QRect(
|
|
618
|
+
icon_pos_x,
|
|
619
|
+
icon_pos_y,
|
|
620
|
+
icon_size.width(),
|
|
621
|
+
icon_size.height()
|
|
622
|
+
)
|
|
623
|
+
self._attachment_icon.paint(painter, icon_rect, QtCore.Qt.AlignCenter)
|
|
477
624
|
|
|
478
|
-
|
|
625
|
+
# Pinned indicator (small circle) kept at a fixed top-right position.
|
|
626
|
+
# It does not shift left when the attachment is present; it overlays above it.
|
|
627
|
+
if is_important:
|
|
628
|
+
painter.setRenderHint(QtGui.QPainter.Antialiasing, True)
|
|
629
|
+
painter.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver)
|
|
630
|
+
color = self.get_color_for_status(3)
|
|
631
|
+
|
|
632
|
+
x = option.rect.x() + option.rect.width() - self._pin_margin - self._pin_diameter
|
|
633
|
+
y = option.rect.y() + self._pin_margin
|
|
634
|
+
pin_rect = QtCore.QRect(x, y, self._pin_diameter, self._pin_diameter)
|
|
635
|
+
|
|
636
|
+
painter.setBrush(color)
|
|
637
|
+
painter.setPen(self._pin_pen)
|
|
638
|
+
painter.drawEllipse(pin_rect)
|
|
639
|
+
|
|
640
|
+
# Label bar on the left with 3px vertical margins
|
|
641
|
+
if label > 0:
|
|
642
|
+
color = self.get_color_for_status(label)
|
|
643
|
+
bar_y = option.rect.y() + self._label_v_margin
|
|
644
|
+
bar_h = max(1, option.rect.height() - 2 * self._label_v_margin)
|
|
645
|
+
bar_rect = QtCore.QRect(
|
|
646
|
+
option.rect.x(),
|
|
647
|
+
bar_y,
|
|
648
|
+
self._label_bar_width,
|
|
649
|
+
bar_h,
|
|
650
|
+
)
|
|
651
|
+
painter.setBrush(color)
|
|
652
|
+
painter.setPen(QtCore.Qt.NoPen)
|
|
653
|
+
painter.drawRect(bar_rect)
|
|
654
|
+
|
|
655
|
+
painter.restore()
|
|
479
656
|
|
|
480
657
|
def get_color_for_status(self, status: int) -> QColor:
|
|
658
|
+
"""
|
|
659
|
+
Returns color mapped for given status value.
|
|
660
|
+
"""
|
|
481
661
|
return self._status_colors.get(status, self._status_colors[0])
|
|
482
662
|
|
|
483
663
|
|
|
@@ -485,6 +665,7 @@ class GroupItem(QStandardItem):
|
|
|
485
665
|
def __init__(self, icon, name, id):
|
|
486
666
|
super().__init__(icon, name)
|
|
487
667
|
self.id = id
|
|
668
|
+
# Keep name as provided; display text is handled by the model/view
|
|
488
669
|
self.name = name
|
|
489
670
|
self.isFolder = True
|
|
490
671
|
self.isPinned = False
|
|
@@ -496,6 +677,7 @@ class Item(QStandardItem):
|
|
|
496
677
|
def __init__(self, name, id):
|
|
497
678
|
super().__init__(name)
|
|
498
679
|
self.id = id
|
|
680
|
+
# Keep name as provided; display text is handled by the model/view
|
|
499
681
|
self.name = name
|
|
500
682
|
self.isFolder = False
|
|
501
683
|
self.isPinned = False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pygpt-net
|
|
3
|
-
Version: 2.6.
|
|
3
|
+
Version: 2.6.44
|
|
4
4
|
Summary: Desktop AI Assistant powered by: OpenAI GPT-5, GPT-4, o1, o3, Gemini, Claude, Grok, DeepSeek, and other models supported by Llama Index, and Ollama. Chatbot, agents, completion, image generation, vision analysis, speech-to-text, plugins, internet access, file handling, command execution and more.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: ai,api,api key,app,assistant,bielik,chat,chatbot,chatgpt,claude,dall-e,deepseek,desktop,gemini,gpt,gpt-3.5,gpt-4,gpt-4-vision,gpt-4o,gpt-5,gpt-oss,gpt3.5,gpt4,grok,langchain,llama-index,llama3,mistral,o1,o3,ollama,openai,presets,py-gpt,py_gpt,pygpt,pyside,qt,text completion,tts,ui,vision,whisper
|
|
@@ -118,7 +118,7 @@ Description-Content-Type: text/markdown
|
|
|
118
118
|
|
|
119
119
|
[](https://snapcraft.io/pygpt)
|
|
120
120
|
|
|
121
|
-
Release: **2.6.
|
|
121
|
+
Release: **2.6.44** | build: **2025-09-12** | Python: **>=3.10, <3.14**
|
|
122
122
|
|
|
123
123
|
> Official website: https://pygpt.net | Documentation: https://pygpt.readthedocs.io
|
|
124
124
|
>
|
|
@@ -3567,6 +3567,14 @@ may consume additional tokens that are not displayed in the main window.
|
|
|
3567
3567
|
|
|
3568
3568
|
## Recent changes:
|
|
3569
3569
|
|
|
3570
|
+
**2.6.44 (2025-09-12)**
|
|
3571
|
+
|
|
3572
|
+
- Added: Auto-collapse for large user input blocks.
|
|
3573
|
+
- Added: Configuration for syntax highlighting intervals.
|
|
3574
|
+
- Improved: Visibility of label icons.
|
|
3575
|
+
- Improved: Scrolling of code blocks.
|
|
3576
|
+
- Fixed: Parsing of quotes in custom markdown blocks.
|
|
3577
|
+
|
|
3570
3578
|
**2.6.43 (2025-09-12)**
|
|
3571
3579
|
|
|
3572
3580
|
- Fixed: preset restoration when switching profiles.
|