pygpt-net 2.4.38__py3-none-any.whl → 2.4.40__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. CHANGELOG.md +19 -0
  2. README.md +25 -2
  3. pygpt_net/CHANGELOG.txt +19 -0
  4. pygpt_net/__init__.py +3 -3
  5. pygpt_net/controller/__init__.py +7 -3
  6. pygpt_net/controller/audio/__init__.py +9 -1
  7. pygpt_net/controller/calendar/__init__.py +3 -1
  8. pygpt_net/controller/chat/input.py +2 -1
  9. pygpt_net/controller/chat/render.py +8 -5
  10. pygpt_net/controller/ctx/__init__.py +33 -25
  11. pygpt_net/controller/ctx/common.py +3 -2
  12. pygpt_net/controller/debug/__init__.py +13 -2
  13. pygpt_net/controller/dialogs/confirm.py +2 -2
  14. pygpt_net/controller/kernel/__init__.py +2 -1
  15. pygpt_net/controller/lang/custom.py +2 -7
  16. pygpt_net/controller/lang/mapping.py +2 -2
  17. pygpt_net/controller/layout.py +2 -2
  18. pygpt_net/controller/notepad.py +14 -10
  19. pygpt_net/controller/theme/nodes.py +2 -5
  20. pygpt_net/controller/tools/__init__.py +37 -1
  21. pygpt_net/controller/ui/__init__.py +1 -5
  22. pygpt_net/controller/ui/tabs.py +295 -60
  23. pygpt_net/core/command.py +3 -1
  24. pygpt_net/core/ctx/__init__.py +16 -2
  25. pygpt_net/core/ctx/container.py +18 -11
  26. pygpt_net/core/ctx/output.py +86 -67
  27. pygpt_net/core/debug/tabs.py +5 -2
  28. pygpt_net/core/filesystem/url.py +7 -3
  29. pygpt_net/core/render/base.py +14 -3
  30. pygpt_net/core/render/markdown/renderer.py +3 -1
  31. pygpt_net/core/render/plain/renderer.py +3 -3
  32. pygpt_net/core/render/web/body.py +10 -4
  33. pygpt_net/core/render/web/renderer.py +213 -41
  34. pygpt_net/core/tabs/__init__.py +268 -98
  35. pygpt_net/core/tabs/tab.py +16 -4
  36. pygpt_net/core/web.py +127 -1
  37. pygpt_net/data/config/config.json +12 -5
  38. pygpt_net/data/config/models.json +3 -3
  39. pygpt_net/data/config/modes.json +3 -3
  40. pygpt_net/data/css/web-blocks.css +18 -0
  41. pygpt_net/data/css/web-blocks.light.css +7 -0
  42. pygpt_net/data/css/web-chatgpt.css +8 -0
  43. pygpt_net/data/css/web-chatgpt_wide.css +8 -0
  44. pygpt_net/data/icons/split_screen.svg +1 -0
  45. pygpt_net/data/locale/locale.de.ini +3 -0
  46. pygpt_net/data/locale/locale.en.ini +6 -1
  47. pygpt_net/data/locale/locale.es.ini +3 -0
  48. pygpt_net/data/locale/locale.fr.ini +3 -0
  49. pygpt_net/data/locale/locale.it.ini +3 -0
  50. pygpt_net/data/locale/locale.pl.ini +4 -1
  51. pygpt_net/data/locale/locale.uk.ini +3 -0
  52. pygpt_net/data/locale/locale.zh.ini +3 -0
  53. pygpt_net/data/locale/plugin.cmd_web.de.ini +2 -0
  54. pygpt_net/data/locale/plugin.cmd_web.en.ini +20 -10
  55. pygpt_net/data/locale/plugin.cmd_web.es.ini +2 -0
  56. pygpt_net/data/locale/plugin.cmd_web.fr.ini +2 -0
  57. pygpt_net/data/locale/plugin.cmd_web.it.ini +2 -0
  58. pygpt_net/data/locale/plugin.cmd_web.pl.ini +2 -0
  59. pygpt_net/data/locale/plugin.cmd_web.uk.ini +2 -0
  60. pygpt_net/data/locale/plugin.cmd_web.zh.ini +2 -0
  61. pygpt_net/icons.qrc +1 -0
  62. pygpt_net/icons_rc.py +165 -136
  63. pygpt_net/item/ctx.py +46 -24
  64. pygpt_net/plugin/audio_input/simple.py +4 -2
  65. pygpt_net/plugin/audio_output/__init__.py +4 -1
  66. pygpt_net/plugin/base/plugin.py +18 -4
  67. pygpt_net/plugin/cmd_code_interpreter/__init__.py +39 -37
  68. pygpt_net/plugin/cmd_code_interpreter/runner.py +25 -12
  69. pygpt_net/plugin/cmd_web/__init__.py +46 -6
  70. pygpt_net/plugin/cmd_web/config.py +74 -48
  71. pygpt_net/plugin/cmd_web/websearch.py +61 -28
  72. pygpt_net/plugin/cmd_web/worker.py +79 -13
  73. pygpt_net/provider/core/config/patch.py +29 -1
  74. pygpt_net/provider/core/ctx/base.py +4 -1
  75. pygpt_net/provider/core/ctx/db_sqlite/__init__.py +10 -1
  76. pygpt_net/provider/core/ctx/db_sqlite/storage.py +22 -1
  77. pygpt_net/tools/__init__.py +9 -1
  78. pygpt_net/tools/base.py +15 -1
  79. pygpt_net/tools/code_interpreter/__init__.py +174 -75
  80. pygpt_net/tools/code_interpreter/ui/dialogs.py +21 -103
  81. pygpt_net/tools/code_interpreter/ui/widgets.py +284 -9
  82. pygpt_net/tools/html_canvas/__init__.py +78 -23
  83. pygpt_net/tools/html_canvas/ui/dialogs.py +46 -62
  84. pygpt_net/tools/html_canvas/ui/widgets.py +96 -3
  85. pygpt_net/ui/base/context_menu.py +2 -2
  86. pygpt_net/ui/layout/chat/input.py +10 -18
  87. pygpt_net/ui/layout/chat/output.py +26 -44
  88. pygpt_net/ui/layout/ctx/ctx_list.py +13 -4
  89. pygpt_net/ui/layout/toolbox/footer.py +18 -2
  90. pygpt_net/ui/main.py +2 -2
  91. pygpt_net/ui/menu/debug.py +11 -1
  92. pygpt_net/ui/widget/filesystem/explorer.py +2 -2
  93. pygpt_net/ui/widget/lists/context.py +26 -5
  94. pygpt_net/ui/widget/tabs/Input.py +2 -2
  95. pygpt_net/ui/widget/tabs/body.py +2 -1
  96. pygpt_net/ui/widget/tabs/layout.py +195 -0
  97. pygpt_net/ui/widget/tabs/output.py +209 -55
  98. pygpt_net/ui/widget/textarea/html.py +11 -1
  99. pygpt_net/ui/widget/textarea/output.py +10 -1
  100. pygpt_net/ui/widget/textarea/web.py +49 -9
  101. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/METADATA +26 -3
  102. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/RECORD +105 -103
  103. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/LICENSE +0 -0
  104. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/WHEEL +0 -0
  105. {pygpt_net-2.4.38.dist-info → pygpt_net-2.4.40.dist-info}/entry_points.txt +0 -0
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.26 19:00:00 #
9
+ # Updated Date: 2024.12.13 08:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import copy
@@ -490,6 +490,16 @@ class Ctx:
490
490
  for item in self.get_items():
491
491
  if item.id == id:
492
492
  return item
493
+ return self.fetch_item_by_id(id) # if no item found, try to fetch from DB
494
+
495
+ def fetch_item_by_id(self, id: int) -> CtxItem:
496
+ """
497
+ Fetch ctx item by id
498
+
499
+ :param id: item id
500
+ :return: context item
501
+ """
502
+ return self.provider.get_item_by_id(id)
493
503
 
494
504
  def get_meta(self, reload: bool = False) -> dict:
495
505
  """
@@ -1361,10 +1371,13 @@ class Ctx:
1361
1371
  """
1362
1372
  prev_ctx = CtxItem()
1363
1373
  prev_ctx.urls = copy.deepcopy(ctx.urls)
1374
+ prev_ctx.urls_before = copy.deepcopy(ctx.urls_before)
1364
1375
  prev_ctx.images = copy.deepcopy(ctx.images)
1365
1376
  prev_ctx.images_before = copy.deepcopy(ctx.images_before)
1366
1377
  prev_ctx.files = copy.deepcopy(ctx.files)
1378
+ prev_ctx.files_before = copy.deepcopy(ctx.files_before)
1367
1379
  prev_ctx.attachments = copy.deepcopy(ctx.attachments)
1380
+ prev_ctx.attachments_before = copy.deepcopy(ctx.attachments_before)
1368
1381
  prev_ctx.results = copy.deepcopy(ctx.results)
1369
1382
  prev_ctx.index_meta = copy.deepcopy(ctx.index_meta)
1370
1383
  prev_ctx.doc_ids = copy.deepcopy(ctx.doc_ids)
@@ -1372,7 +1385,8 @@ class Ctx:
1372
1385
  prev_ctx.output_name = copy.deepcopy(ctx.output_name)
1373
1386
 
1374
1387
  ctx.clear_reply() # clear current reply result
1375
- ctx.from_previous() # get result from previous if exists
1388
+ if len(ctx.cmds) == 0:
1389
+ ctx.from_previous() # get result from previous if exists
1376
1390
  return prev_ctx
1377
1391
 
1378
1392
  def dump(self, ctx: CtxItem) -> str:
@@ -6,39 +6,44 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.07 23:00:00 #
9
+ # Updated Date: 2024.12.12 04:00:00 #
10
10
  # ================================================== #
11
11
 
12
- from PySide6.QtWidgets import QVBoxLayout
12
+ from PySide6.QtWidgets import QVBoxLayout, QWidget
13
13
 
14
- from .bag import Bag
14
+ from pygpt_net.core.tabs.tab import Tab
15
15
  from pygpt_net.ui.widget.textarea.output import ChatOutput
16
16
 
17
+ from .bag import Bag
17
18
 
18
19
  class Container:
19
20
  def __init__(self, window=None):
20
21
  """
21
- Context container
22
+ Context output container
23
+
24
+ :param window: Window
22
25
  """
23
26
  self.window = window
24
27
  self.bags = {}
25
28
  self.bags[0] = Bag(window) # always create initial bag
26
29
 
27
- def register_output(self, id: int = 0):
30
+ def get(self, tab: Tab) -> QWidget:
28
31
  """
29
- Register output
32
+ Register and return output
30
33
 
31
- :param id: ID
34
+ :param tab: Tab
32
35
  :return: Widget
33
36
  """
34
37
  # plain output
35
38
  output_plain = ChatOutput(self.window)
39
+ output_plain.set_tab(tab)
36
40
 
37
41
  # web
38
42
  if self.window.core.config.get("render.engine") == "web":
39
43
  from pygpt_net.ui.widget.textarea.web import ChatWebOutput, CustomWebEnginePage
40
44
  # build output
41
45
  output_html = ChatWebOutput(self.window)
46
+ output_html.set_tab(tab)
42
47
  output_html.setPage(
43
48
  CustomWebEnginePage(self.window, output_html)
44
49
  )
@@ -48,13 +53,15 @@ class Container:
48
53
  else:
49
54
  # legacy
50
55
  output_html = ChatOutput(self.window)
56
+ output_html.set_tab(tab)
51
57
 
52
58
  if 'output_plain' not in self.window.ui.nodes:
53
59
  self.window.ui.nodes['output_plain'] = {}
54
60
  if 'output' not in self.window.ui.nodes:
55
61
  self.window.ui.nodes['output'] = {}
56
- self.window.ui.nodes['output_plain'][id] = output_plain
57
- self.window.ui.nodes['output'][id] = output_html
62
+
63
+ self.window.ui.nodes['output_plain'][tab.pid] = output_plain
64
+ self.window.ui.nodes['output'][tab.pid] = output_html
58
65
 
59
66
  # show/hide plain/html
60
67
  if self.window.core.config.get("render.plain") is True:
@@ -66,8 +73,8 @@ class Container:
66
73
 
67
74
  # build layout
68
75
  layout = QVBoxLayout()
69
- layout.addWidget(self.window.ui.nodes['output_plain'][id])
70
- layout.addWidget(self.window.ui.nodes['output'][id])
76
+ layout.addWidget(self.window.ui.nodes['output_plain'][tab.pid])
77
+ layout.addWidget(self.window.ui.nodes['output'][tab.pid])
71
78
  layout.setContentsMargins(0, 0, 0, 0)
72
79
  widget = self.window.core.tabs.from_layout(layout)
73
80
  return widget
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.05 23:00:00 #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from pygpt_net.item.ctx import CtxItem, CtxMeta
@@ -16,11 +16,28 @@ class Output:
16
16
  def __init__(self, window=None):
17
17
  """
18
18
  Context output mapping
19
+
20
+ :param window: Window
19
21
  """
20
22
  self.window = window
21
- self.mapping = {} # pid => meta.id
23
+ self.mapping = {} # [column_idx => [pid => meta.id]]
22
24
  self.last_pids = {} # meta.id => pid
23
25
  self.last_pid = 0 # last used PID
26
+ self.initialized = False
27
+
28
+ def init(self, force: bool = False):
29
+ """
30
+ Initialize mappings
31
+
32
+ :param force: Force reinitialize
33
+ """
34
+ if self.initialized and not force:
35
+ return
36
+ self.clear()
37
+ for n in range(0, self.window.core.tabs.NUM_COLS):
38
+ self.mapping[n] = {}
39
+ self.last_pids[n] = {}
40
+ self.initialized = True
24
41
 
25
42
  def store(self, meta: CtxMeta) -> int:
26
43
  """
@@ -29,31 +46,20 @@ class Output:
29
46
  :param meta: Meta
30
47
  :return: PID
31
48
  """
49
+ self.init()
32
50
  pid = self.window.core.tabs.get_active_pid()
33
- # check if allowed tab
34
51
  tab = self.window.core.tabs.get_tab_by_pid(pid)
35
52
  if tab is None or tab.type != Tab.TAB_CHAT:
36
53
  return 0
37
- self.mapping[pid] = meta.id
38
- self.last_pids[meta.id] = pid
54
+ col_idx = tab.column_idx
55
+ self.mapping[col_idx][pid] = meta.id
56
+ self.last_pids[col_idx][meta.id] = pid
39
57
  self.last_pid = pid
40
58
  tab = self.window.core.tabs.get_tab_by_pid(pid)
41
59
  if tab is not None:
42
60
  tab.data_id = meta.id # store meta id in tab data
43
61
  return pid
44
62
 
45
- def meta_id_in_tab(self, meta_id: int) -> bool:
46
- """
47
- Check if meta id is in tab
48
-
49
- :param meta_id: Meta ID
50
- :return: bool
51
- """
52
- for pid, mid in self.mapping.items():
53
- if mid == meta_id:
54
- return True
55
- return False
56
-
57
63
  def is_mapped(self, meta: CtxMeta) -> bool:
58
64
  """
59
65
  Check if meta is mapped
@@ -61,19 +67,46 @@ class Output:
61
67
  :param meta: Meta
62
68
  :return: bool
63
69
  """
64
- return meta.id in self.mapping.values()
70
+ self.init()
71
+ for col_idx in self.mapping:
72
+ if meta.id in self.mapping[col_idx].values():
73
+ return True
74
+ return False
65
75
 
66
- def get_meta(self, pid: int) -> CtxMeta or None:
76
+ def get_meta(self, pid: int) -> int or None:
67
77
  """
68
78
  Get meta by PID
69
79
 
70
80
  :param pid: PID
71
- :return: Meta or None
81
+ :return: meta ID or None
72
82
  """
73
- if pid in self.mapping:
74
- return self.mapping[pid]
83
+ self.init()
84
+ for col_idx in self.mapping:
85
+ if pid in self.mapping[col_idx]:
86
+ return self.mapping[col_idx][pid]
75
87
  return
76
88
 
89
+ def prepare_meta(self, tab: Tab) -> int or None:
90
+ """
91
+ Get meta ID by PID
92
+
93
+ :param tab: Tab
94
+ :return: Meta ID or None
95
+ """
96
+ self.init()
97
+ pid = tab.pid
98
+ col_idx = tab.column_idx
99
+ meta_id = None
100
+ if pid in self.mapping[col_idx]:
101
+ return self.mapping[col_idx][pid]
102
+ if meta_id is None:
103
+ meta_id = tab.data_id
104
+ if meta_id is not None:
105
+ self.mapping[col_idx][pid] = meta_id
106
+ self.last_pids[col_idx][meta_id] = pid
107
+ self.last_pid = pid
108
+ return meta_id
109
+
77
110
  def get_mapped(self, meta: CtxMeta) -> int or None:
78
111
  """
79
112
  Get PID by meta
@@ -81,75 +114,59 @@ class Output:
81
114
  :param meta: Meta
82
115
  :return: PID or None
83
116
  """
117
+ self.init()
84
118
  all = []
85
119
  pid = None
86
120
  active_pid = self.window.core.tabs.get_active_pid()
87
- for pid, meta_id in self.mapping.items():
121
+ tab = self.window.core.tabs.get_tab_by_pid(active_pid)
122
+ if tab is None:
123
+ return None
124
+ col_idx = tab.column_idx
125
+ for pid, meta_id in self.mapping[col_idx].items():
88
126
  if meta_id == meta.id:
89
127
  all.append(pid)
90
128
  for pid in all:
91
- if pid == active_pid: # at first check if active
129
+ if pid == active_pid: # at first check for current tab
92
130
  return pid
93
131
  return pid
94
132
 
95
- def get_mapped_last(self, meta: CtxMeta) -> int or None:
96
- """
97
- Get last PID by meta
98
-
99
- :param meta: Meta
100
- :return: PID or None
101
- """
102
- active_pid = self.window.core.tabs.get_active_pid()
103
- if self.mapping[active_pid] == meta.id:
104
- return active_pid # return active PID at first
105
- if meta.id in self.last_pids:
106
- return self.last_pids[meta.id]
107
- return
108
-
109
- def prepare(self, meta: CtxMeta):
110
- """
111
- Clear mapping
112
-
113
- :param meta: meta to prepare
114
- """
115
- active_pid = self.window.core.tabs.get_active_pid()
116
- if active_pid in self.mapping:
117
- del self.mapping[active_pid] # remove from mapping
118
-
119
- def clear(self):
120
- """
121
- Clear mapping
122
- """
123
- self.mapping = {}
124
- self.last_pids = {}
125
- self.last_pid = 0
126
-
127
133
  def is_empty(self) -> bool:
128
134
  """
129
135
  Check if mapping is empty
130
136
 
131
137
  :return: bool
132
138
  """
139
+ self.init()
133
140
  active_pid = self.window.core.tabs.get_active_pid()
134
- if active_pid in self.mapping:
135
- return False
141
+ for col_idx in list(self.mapping.keys()):
142
+ if active_pid in self.mapping[col_idx]:
143
+ return False
136
144
  return True
137
145
 
138
- def get_pid(self, meta: CtxMeta = None) -> int:
146
+ def get_pid(self, meta: CtxMeta = None) -> int or None:
139
147
  """
140
148
  Get PID by meta
141
149
 
142
150
  :param meta: Meta
143
151
  :return: PID
144
152
  """
153
+ self.init()
154
+ active_pid = self.window.core.tabs.get_active_pid()
145
155
  if meta is None:
146
- return self.window.core.tabs.get_active_pid()
156
+ return None
147
157
  if self.is_mapped(meta):
148
- if self.window.core.tabs.get_active_pid() != self.get_mapped(meta): # if loaded in another tab
149
- return self.store(meta) # update to current
150
- return self.get_mapped_last(meta)
151
- else:
152
- return self.store(meta)
158
+ if active_pid == self.get_mapped(meta): # if loaded in current tab
159
+ return active_pid # return current
160
+ return self.store(meta)
161
+
162
+ def clear(self):
163
+ """
164
+ Clear mapping
165
+ """
166
+ self.mapping = {}
167
+ self.last_pids = {}
168
+ self.last_pid = 0
169
+ self.initialized = False
153
170
 
154
171
  def get_current(self, meta: CtxMeta = None):
155
172
  """
@@ -176,7 +193,8 @@ class Output:
176
193
  if pid in self.window.ui.nodes['output_plain']:
177
194
  return self.window.ui.nodes['output_plain'][pid]
178
195
  else:
179
- return self.window.ui.nodes['output_plain'][0]
196
+ for pid in self.window.ui.nodes['output_plain']:
197
+ return self.window.ui.nodes['output_plain'][pid]
180
198
 
181
199
  def get_by_pid(self, pid = None):
182
200
  """
@@ -201,7 +219,8 @@ class Output:
201
219
  if pid in self.window.ui.nodes['output_plain']:
202
220
  return self.window.ui.nodes['output_plain'][pid]
203
221
  else:
204
- return self.window.ui.nodes['output_plain'][0]
222
+ for pid in self.window.ui.nodes['output_plain']:
223
+ return self.window.ui.nodes['output_plain'][pid]
205
224
 
206
225
  def get_all(self) -> list:
207
226
  """
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.07 23:00:00 #
9
+ # Updated Date: 2024.12.12 01:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  class TabsDebug:
@@ -25,7 +25,10 @@ class TabsDebug:
25
25
  self.window.core.debug.add(self.id, 'current PID', str(self.window.controller.ui.tabs.get_current_pid()))
26
26
  self.window.core.debug.add(self.id, 'current IDX', str(self.window.controller.ui.tabs.get_current_idx()))
27
27
  self.window.core.debug.add(self.id, 'current Type', str(self.window.controller.ui.tabs.get_current_type()))
28
+ self.window.core.debug.add(self.id, 'current Column', str(self.window.controller.ui.tabs.get_current_column_idx()))
28
29
  self.window.core.debug.add(self.id, 'last_pid', str(self.window.core.tabs.last_pid))
30
+ self.window.core.debug.add(self.id, 'locked', str(self.window.controller.ui.tabs.locked))
31
+ self.window.core.debug.add(self.id, 'col', str(self.window.controller.ui.tabs.col))
29
32
  self.window.core.debug.add(self.id, 'count(pids)', str(len(self.window.core.tabs.pids)))
30
33
  self.window.core.debug.add(self.id, 'count(ctx bags)', str(len(self.window.core.ctx.container.bags)))
31
34
  self.window.core.debug.add(self.id, '----', '')
@@ -38,6 +41,6 @@ class TabsDebug:
38
41
 
39
42
  # mapping PID => meta.id
40
43
  self.window.core.debug.add(self.id, 'PID => meta.id', str(self.window.core.ctx.output.mapping))
41
- self.window.core.debug.add(self.id, '(last) PID => meta.id', str(self.window.core.ctx.output.last_pids))
44
+ self.window.core.debug.add(self.id, '(last) meta.id => PID', str(self.window.core.ctx.output.last_pids))
42
45
  self.window.core.debug.add(self.id, '(last) PID', str(self.window.core.ctx.output.last_pid))
43
46
  self.window.core.debug.end(self.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.11.08 06:00:00 #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from PySide6.QtCore import QUrl
@@ -38,14 +38,18 @@ class Url:
38
38
  ]
39
39
 
40
40
  # JS bridge
41
- if url.toString() == 'bridge://open_find':
42
- pid = self.window.controller.ui.tabs.get_current_pid()
41
+ if url.toString().startswith('bridge://open_find'):
42
+ pid = int(url.toString().split(':')[2])
43
43
  if pid in self.window.ui.nodes['output']:
44
44
  self.window.ui.nodes['output'][pid].find_open()
45
45
  return
46
46
  elif url.toString() == 'bridge://escape':
47
47
  self.window.controller.access.on_escape()
48
48
  return
49
+ elif url.toString() == 'bridge://focus':
50
+ pid = self.window.controller.ui.tabs.get_current_pid()
51
+ if pid in self.window.ui.nodes['output']:
52
+ self.window.ui.nodes['output'][pid].on_focus_js()
49
53
 
50
54
  # -------------
51
55
 
@@ -6,9 +6,10 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.07 23:00:00 #
9
+ # Updated Date: 2024.12.12 04:00:00 #
10
10
  # ================================================== #
11
11
 
12
+ from pygpt_net.core.tabs.tab import Tab
12
13
  from pygpt_net.item.ctx import CtxItem, CtxMeta
13
14
 
14
15
 
@@ -20,6 +21,15 @@ class BaseRenderer:
20
21
  :param window: Window instance
21
22
  """
22
23
  self.window = window
24
+ self.tab = None
25
+
26
+ def set_tab(self, tab: Tab):
27
+ """
28
+ Append tab
29
+
30
+ :param tab: Tab
31
+ """
32
+ self.tab = tab
23
33
 
24
34
  def prepare(self):
25
35
  """
@@ -218,13 +228,14 @@ class BaseRenderer:
218
228
  """
219
229
  pass
220
230
 
221
- def on_page_loaded(self, meta: CtxMeta = None):
231
+ def on_page_loaded(self, meta: CtxMeta = None, tab: Tab = None):
222
232
  """
223
233
  On page loaded callback
224
234
 
225
235
  :param meta: context meta
236
+ :param tab: Tab
226
237
  """
227
- pass
238
+ self.tab = tab
228
239
 
229
240
  def on_enable_edit(self, live: bool = True):
230
241
  """
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.05 23:00:00 #
9
+ # Updated Date: 2024.12.09 00:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import re
@@ -50,6 +50,8 @@ class Renderer(BaseRenderer):
50
50
 
51
51
  :param meta: context PID
52
52
  """
53
+ if self.tab is not None:
54
+ return self.tab.pid # get PID from tab if exists
53
55
  return self.window.core.ctx.output.get_pid(meta)
54
56
 
55
57
  def get_or_create_pid(self, meta: CtxMeta):
@@ -6,7 +6,7 @@
6
6
  # GitHub: https://github.com/szczyglis-dev/py-gpt #
7
7
  # MIT License #
8
8
  # Created By : Marcin Szczygliński #
9
- # Updated Date: 2024.11.05 23:00:00 #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  from datetime import datetime
@@ -128,7 +128,7 @@ class Renderer(BaseRenderer):
128
128
  :param clear: True if clear all output before append
129
129
  """
130
130
  if clear:
131
- self.clear_output()
131
+ self.clear_output(meta)
132
132
 
133
133
  i = 0
134
134
  for item in items:
@@ -399,7 +399,7 @@ class Renderer(BaseRenderer):
399
399
  :param meta: context meta
400
400
  """
401
401
  self.reset()
402
- self.get_output_node().clear()
402
+ self.get_output_node(meta).clear()
403
403
 
404
404
  def clear_input(self):
405
405
  """Clear input"""
@@ -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.07 21:00:00 #
9
+ # Updated Date: 2024.12.09 03:00:00 #
10
10
  # ================================================== #
11
11
 
12
12
  import os
@@ -207,7 +207,7 @@ class Body:
207
207
  num_str = ""
208
208
  if num is not None and num_all is not None and num_all > 1:
209
209
  num_str = " [{}]".format(num)
210
- return """{icon}<b>{num}</b> <a href="{url}" title="{url}">{url}</a>""". \
210
+ return """{icon}<a href="{url}" title="{url}">{url}</a> <small>{num}</small>""". \
211
211
  format(url=url,
212
212
  num=num_str,
213
213
  icon=icon,
@@ -325,7 +325,7 @@ class Body:
325
325
  html += "</div>"
326
326
  return html
327
327
 
328
- def get_html(self) -> str:
328
+ def get_html(self, pid: int) -> str:
329
329
  """
330
330
  Build webview HTML code
331
331
 
@@ -370,13 +370,14 @@ class Body:
370
370
  let scrollTimeout = null;
371
371
  let prevScroll = 0;
372
372
  let bridge;
373
+ let pid = """ + str(pid) + """
373
374
  new QWebChannel(qt.webChannelTransport, function (channel) {
374
375
  bridge = channel.objects.bridge;
375
376
  });
376
377
  history.scrollRestoration = "manual";
377
378
  document.addEventListener('keydown', function(event) {
378
379
  if (event.ctrlKey && event.key === 'f') {
379
- window.location.href = 'bridge://open_find'; // send to bridge
380
+ window.location.href = 'bridge://open_find:' + pid; // send to bridge
380
381
  event.preventDefault();
381
382
  }
382
383
  if (event.key === 'Escape') {
@@ -384,6 +385,11 @@ class Body:
384
385
  event.preventDefault();
385
386
  }
386
387
  });
388
+ document.addEventListener('click', function(event) {
389
+ if (event.target.tagName !== 'A' && !event.target.closest('a')) {
390
+ window.location.href = 'bridge://focus';
391
+ }
392
+ });
387
393
  function highlightCode() {
388
394
  document.querySelectorAll('pre code').forEach(el => {
389
395
  if (!el.classList.contains('hljs')) hljs.highlightElement(el);