dialoghelper 0.0.35__py3-none-any.whl → 0.0.37__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.
- dialoghelper/__init__.py +1 -1
- dialoghelper/_modidx.py +6 -9
- dialoghelper/core.py +91 -55
- dialoghelper/experimental.py +16 -71
- dialoghelper/screenshot.js +25 -121
- {dialoghelper-0.0.35.dist-info → dialoghelper-0.0.37.dist-info}/METADATA +3 -1
- dialoghelper-0.0.37.dist-info/RECORD +12 -0
- dialoghelper-0.0.35.dist-info/RECORD +0 -12
- {dialoghelper-0.0.35.dist-info → dialoghelper-0.0.37.dist-info}/WHEEL +0 -0
- {dialoghelper-0.0.35.dist-info → dialoghelper-0.0.37.dist-info}/entry_points.txt +0 -0
- {dialoghelper-0.0.35.dist-info → dialoghelper-0.0.37.dist-info}/licenses/LICENSE +0 -0
- {dialoghelper-0.0.35.dist-info → dialoghelper-0.0.37.dist-info}/top_level.txt +0 -0
dialoghelper/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.0.
|
|
1
|
+
__version__ = "0.0.37"
|
|
2
2
|
from .core import *
|
dialoghelper/_modidx.py
CHANGED
|
@@ -6,9 +6,11 @@ d = { 'settings': { 'branch': 'main',
|
|
|
6
6
|
'git_url': 'https://github.com/AnswerDotAI/dialoghelper',
|
|
7
7
|
'lib_path': 'dialoghelper'},
|
|
8
8
|
'syms': { 'dialoghelper.core': { 'dialoghelper.core._add_msg_unsafe': ('core.html#_add_msg_unsafe', 'dialoghelper/core.py'),
|
|
9
|
+
'dialoghelper.core._find_frame_dict': ('core.html#_find_frame_dict', 'dialoghelper/core.py'),
|
|
9
10
|
'dialoghelper.core._umsg': ('core.html#_umsg', 'dialoghelper/core.py'),
|
|
10
11
|
'dialoghelper.core.add_html': ('core.html#add_html', 'dialoghelper/core.py'),
|
|
11
12
|
'dialoghelper.core.add_msg': ('core.html#add_msg', 'dialoghelper/core.py'),
|
|
13
|
+
'dialoghelper.core.add_styles': ('core.html#add_styles', 'dialoghelper/core.py'),
|
|
12
14
|
'dialoghelper.core.ast_grep': ('core.html#ast_grep', 'dialoghelper/core.py'),
|
|
13
15
|
'dialoghelper.core.ast_py': ('core.html#ast_py', 'dialoghelper/core.py'),
|
|
14
16
|
'dialoghelper.core.call_endp': ('core.html#call_endp', 'dialoghelper/core.py'),
|
|
@@ -32,18 +34,13 @@ d = { 'settings': { 'branch': 'main',
|
|
|
32
34
|
'dialoghelper.core.msg_strs_replace': ('core.html#msg_strs_replace', 'dialoghelper/core.py'),
|
|
33
35
|
'dialoghelper.core.read_msg': ('core.html#read_msg', 'dialoghelper/core.py'),
|
|
34
36
|
'dialoghelper.core.run_msg': ('core.html#run_msg', 'dialoghelper/core.py'),
|
|
37
|
+
'dialoghelper.core.set_var': ('core.html#set_var', 'dialoghelper/core.py'),
|
|
35
38
|
'dialoghelper.core.tool_info': ('core.html#tool_info', 'dialoghelper/core.py'),
|
|
36
39
|
'dialoghelper.core.update_msg': ('core.html#update_msg', 'dialoghelper/core.py'),
|
|
37
40
|
'dialoghelper.core.url2note': ('core.html#url2note', 'dialoghelper/core.py')},
|
|
38
41
|
'dialoghelper.db_dc': {},
|
|
39
|
-
'dialoghelper.experimental': { 'dialoghelper.experimental.
|
|
40
|
-
'dialoghelper/experimental.py'),
|
|
41
|
-
'dialoghelper.experimental.capture_screen': ( 'experimental.html#capture_screen',
|
|
42
|
+
'dialoghelper.experimental': { 'dialoghelper.experimental.capture_screen': ( 'experimental.html#capture_screen',
|
|
42
43
|
'dialoghelper/experimental.py'),
|
|
43
44
|
'dialoghelper.experimental.iife': ('experimental.html#iife', 'dialoghelper/experimental.py'),
|
|
44
|
-
'dialoghelper.experimental.
|
|
45
|
-
|
|
46
|
-
'dialoghelper.experimental.start_screen_share': ( 'experimental.html#start_screen_share',
|
|
47
|
-
'dialoghelper/experimental.py'),
|
|
48
|
-
'dialoghelper.experimental.stop_screen_share': ( 'experimental.html#stop_screen_share',
|
|
49
|
-
'dialoghelper/experimental.py')}}}
|
|
45
|
+
'dialoghelper.experimental.start_share': ( 'experimental.html#start_share',
|
|
46
|
+
'dialoghelper/experimental.py')}}}
|
dialoghelper/core.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
|
|
2
2
|
|
|
3
3
|
# %% auto 0
|
|
4
|
-
__all__ = ['
|
|
5
|
-
'
|
|
6
|
-
'
|
|
7
|
-
'
|
|
4
|
+
__all__ = ['md_cls_d', 'dh_settings', 'Placements', 'empty', 'add_styles', 'find_var', 'set_var', 'call_endp', 'find_dname',
|
|
5
|
+
'find_msg_id', 'curr_dialog', 'msg_idx', 'find_msgs', 'add_html', 'read_msg', 'add_msg', 'del_msg',
|
|
6
|
+
'update_msg', 'run_msg', 'url2note', 'ast_py', 'ast_grep', 'msg_insert_line', 'msg_str_replace',
|
|
7
|
+
'msg_strs_replace', 'msg_replace_lines', 'load_gist', 'gist_file', 'import_string', 'is_usable_tool',
|
|
8
|
+
'mk_toollist', 'import_gist', 'tool_info', 'fc_tool_info']
|
|
8
9
|
|
|
9
10
|
# %% ../nbs/00_core.ipynb
|
|
10
|
-
import json,
|
|
11
|
+
import json,importlib,linecache,re,inspect
|
|
11
12
|
from typing import Dict
|
|
12
13
|
from tempfile import TemporaryDirectory
|
|
13
14
|
from ipykernel_helper import *
|
|
@@ -21,27 +22,53 @@ from fastlite import *
|
|
|
21
22
|
from fastcore.xtras import asdict
|
|
22
23
|
from inspect import currentframe,Parameter,signature
|
|
23
24
|
from httpx import get as xget, post as xpost
|
|
24
|
-
from .core import __all__ as _all
|
|
25
25
|
from IPython.display import display,Markdown
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
from monsterui.all import franken_class_map,apply_classes
|
|
27
|
+
from fasthtml.common import Safe
|
|
28
|
+
|
|
29
|
+
# %% ../nbs/00_core.ipynb
|
|
30
|
+
md_cls_d = {
|
|
31
|
+
**{f'h{i}': f'uk-h{i}' for i in range(1,5)},
|
|
32
|
+
'a': "uk-link",
|
|
33
|
+
'blockquote': "uk-blockquote",
|
|
34
|
+
'hr':'uk-divider-icon',
|
|
35
|
+
'table':'uk-table uk-table-sm uk-table-middle uk-table-divider border [&_tr]:divide-x w-[80%] mx-auto',
|
|
36
|
+
'ol': 'uk-list uk-list-decimal space-y-0',
|
|
37
|
+
'ul': 'uk-list uk-list-bullet space-y-0',
|
|
38
|
+
'p': 'leading-tight',
|
|
39
|
+
'li': 'leading-tight',
|
|
40
|
+
'pre': '', 'pre code': '',
|
|
41
|
+
'code': 'tracking-tight'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def add_styles(s:str, cls_map:dict=None):
|
|
45
|
+
"Add solveit styles to `s`"
|
|
46
|
+
return Safe(apply_classes(s, class_map=cls_map or md_cls_d))
|
|
47
|
+
|
|
48
|
+
# %% ../nbs/00_core.ipynb
|
|
49
|
+
def _find_frame_dict(var:str):
|
|
50
|
+
"Find the dict (globals or locals) containing var"
|
|
51
|
+
frame = currentframe().f_back.f_back
|
|
34
52
|
while frame:
|
|
35
|
-
|
|
36
|
-
if dv: return dv
|
|
53
|
+
if var in frame.f_globals: return frame.f_globals
|
|
37
54
|
frame = frame.f_back
|
|
38
55
|
raise ValueError(f"Could not find {var} in any scope")
|
|
39
56
|
|
|
57
|
+
def find_var(var:str):
|
|
58
|
+
"Search for var in all frames of the call stack"
|
|
59
|
+
return _find_frame_dict(var)[var]
|
|
60
|
+
|
|
61
|
+
def set_var(var:str, val):
|
|
62
|
+
"Set var to val after finding it in all frames of the call stack"
|
|
63
|
+
_find_frame_dict(var)[var] = val
|
|
64
|
+
|
|
40
65
|
# %% ../nbs/00_core.ipynb
|
|
66
|
+
dh_settings = {'port':5001}
|
|
67
|
+
|
|
41
68
|
def call_endp(path, dname='', json=False, raiseex=False, **data):
|
|
42
69
|
if not dname: dname = find_dname()
|
|
43
70
|
data['dlg_name'] = dname
|
|
44
|
-
res = xpost(f'http://localhost:
|
|
71
|
+
res = xpost(f'http://localhost:{dh_settings["port"]}/{path}', data=data)
|
|
45
72
|
if raiseex: res.raise_for_status()
|
|
46
73
|
try: return res.json() if json else res.text
|
|
47
74
|
except Exception as e: return str(e)
|
|
@@ -65,6 +92,15 @@ def curr_dialog(
|
|
|
65
92
|
res = call_endp('curr_dialog_', dname, json=True, with_messages=with_messages)
|
|
66
93
|
if res: return {'name': res['name'], 'mode': res['mode']}
|
|
67
94
|
|
|
95
|
+
# %% ../nbs/00_core.ipynb
|
|
96
|
+
def msg_idx(
|
|
97
|
+
msgid=None, # Message id to find (defaults to current message)
|
|
98
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
99
|
+
):
|
|
100
|
+
"Get absolute index of message in dialog."
|
|
101
|
+
if not msgid: msgid = find_msg_id()
|
|
102
|
+
return call_endp('msg_idx_', dname, json=True, msgid=msgid)['msgid']
|
|
103
|
+
|
|
68
104
|
# %% ../nbs/00_core.ipynb
|
|
69
105
|
def find_msgs(
|
|
70
106
|
re_pattern:str='', # Optional regex to search for (re.DOTALL+re.MULTILINE is used)
|
|
@@ -79,15 +115,6 @@ def find_msgs(
|
|
|
79
115
|
for o in res: o.pop('output', None)
|
|
80
116
|
return res
|
|
81
117
|
|
|
82
|
-
# %% ../nbs/00_core.ipynb
|
|
83
|
-
def msg_idx(
|
|
84
|
-
msgid=None, # Message id to find (defaults to current message)
|
|
85
|
-
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
86
|
-
):
|
|
87
|
-
"Get absolute index of message in dialog."
|
|
88
|
-
if not msgid: msgid = find_msg_id()
|
|
89
|
-
return call_endp('msg_idx_', dname, json=True, msgid=msgid)['msgid']
|
|
90
|
-
|
|
91
118
|
# %% ../nbs/00_core.ipynb
|
|
92
119
|
def add_html(
|
|
93
120
|
content:str, # The HTML to send to the client (generally should include hx-swap-oob)
|
|
@@ -99,6 +126,25 @@ def add_html(
|
|
|
99
126
|
# %% ../nbs/00_core.ipynb
|
|
100
127
|
Placements = str_enum('Placements', 'add_after', 'add_before', 'at_start', 'at_end')
|
|
101
128
|
|
|
129
|
+
# %% ../nbs/00_core.ipynb
|
|
130
|
+
def read_msg(
|
|
131
|
+
n:int=-1, # Message index (if relative, +ve is downwards)
|
|
132
|
+
relative:bool=True, # Is `n` relative to current message (True) or absolute (False)?
|
|
133
|
+
msgid:str=None, # Message id to find (defaults to current message)
|
|
134
|
+
view_range:list[int,int]=None, # Optional 1-indexed (start, end) line range for files, end=-1 for EOF
|
|
135
|
+
nums:bool=False, # Whether to show line numbers
|
|
136
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
137
|
+
):
|
|
138
|
+
"""Get the message indexed in the current dialog.
|
|
139
|
+
- To get the exact message use `n=0` and `relative=True` together with `msgid`.
|
|
140
|
+
- To get a relative message use `n` (relative position index).
|
|
141
|
+
- To get the nth message use `n` with `relative=False`, e.g `n=0` first message, `n=-1` last message."""
|
|
142
|
+
if not msgid: msgid = find_msg_id()
|
|
143
|
+
data = dict(n=n, relative=relative, msgid=msgid)
|
|
144
|
+
if view_range: data['view_range'] = view_range # None gets converted to '' so we avoid passing it to use the p.default
|
|
145
|
+
if nums: data['nums'] = nums
|
|
146
|
+
return call_endp('read_msg_', dname, json=True, **data)
|
|
147
|
+
|
|
102
148
|
# %% ../nbs/00_core.ipynb
|
|
103
149
|
def add_msg(
|
|
104
150
|
content:str, # Content of the message (i.e the message prompt, code, or note text)
|
|
@@ -117,10 +163,12 @@ def add_msg(
|
|
|
117
163
|
):
|
|
118
164
|
"Add/update a message to the queue to show after code execution completes."
|
|
119
165
|
if placement not in ('at_start','at_end') and not msgid: msgid = find_msg_id()
|
|
120
|
-
|
|
166
|
+
res = call_endp(
|
|
121
167
|
'add_relative_', dname, content=content, placement=placement, msgid=msgid, msg_type=msg_type, output=output,
|
|
122
168
|
time_run=time_run, is_exported=is_exported, skipped=skipped, pinned=pinned,
|
|
123
169
|
i_collapsed=i_collapsed, o_collapsed=o_collapsed, heading_collapsed=heading_collapsed)
|
|
170
|
+
set_var('__msg_id', res)
|
|
171
|
+
return res
|
|
124
172
|
|
|
125
173
|
# %% ../nbs/00_core.ipynb
|
|
126
174
|
def del_msg(
|
|
@@ -143,8 +191,10 @@ def _add_msg_unsafe(
|
|
|
143
191
|
"""Add/update a message to the queue to show after code execution completes, and optionally run it. Be sure to pass a `sid` (stable id) not a `mid` (which is used only for sorting, and can change).
|
|
144
192
|
*WARNING*--This can execute arbitrary code, so check carefully what you run!--*WARNING"""
|
|
145
193
|
if placement not in ('at_start','at_end') and not msgid: msgid = find_msg_id()
|
|
146
|
-
|
|
194
|
+
res = call_endp(
|
|
147
195
|
'add_relative_', dname, content=content, placement=placement, msgid=msgid, run=run, **kwargs)
|
|
196
|
+
set_var('__msg_id', res)
|
|
197
|
+
return res
|
|
148
198
|
|
|
149
199
|
# %% ../nbs/00_core.ipynb
|
|
150
200
|
def _umsg(
|
|
@@ -168,10 +218,20 @@ def update_msg(
|
|
|
168
218
|
dname:str='', # Running dialog to get info for; defaults to current dialog
|
|
169
219
|
**kwargs):
|
|
170
220
|
"""Update an existing message. Provide either `msg` OR field key/values to update.
|
|
171
|
-
Use `content` param to update contents.
|
|
172
|
-
Only include parameters to update--missing ones will be left unchanged."""
|
|
221
|
+
- Use `content` param to update contents.
|
|
222
|
+
- Only include parameters to update--missing ones will be left unchanged."""
|
|
173
223
|
if not msgid and not msg: raise TypeError("update_msg needs either a dict message or `msgid=`")
|
|
174
|
-
|
|
224
|
+
res = call_endp('add_relative_', dname, placement='update', msgid=msgid, **kwargs)
|
|
225
|
+
set_var('__msg_id', res)
|
|
226
|
+
return res
|
|
227
|
+
|
|
228
|
+
# %% ../nbs/00_core.ipynb
|
|
229
|
+
def run_msg(
|
|
230
|
+
msgid:str=None, # id of message to execute
|
|
231
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
232
|
+
):
|
|
233
|
+
"Adds a message to the run queue. Use read_msg to see the output once it runs."
|
|
234
|
+
return call_endp('add_runq_', dname, msgid=msgid, api=True)
|
|
175
235
|
|
|
176
236
|
# %% ../nbs/00_core.ipynb
|
|
177
237
|
def url2note(
|
|
@@ -202,30 +262,6 @@ def ast_grep(
|
|
|
202
262
|
res = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
|
203
263
|
return json.loads(res.stdout) if res.stdout else res.stderr
|
|
204
264
|
|
|
205
|
-
# %% ../nbs/00_core.ipynb
|
|
206
|
-
def read_msg(
|
|
207
|
-
n:int=-1, # Message index (if relative, +ve is downwards)
|
|
208
|
-
relative:bool=True, # Is `n` relative to current message (True) or absolute (False)?
|
|
209
|
-
msgid:str=None, # Message id to find (defaults to current message)
|
|
210
|
-
view_range:list[int,int]=None, # Optional 1-indexed (start, end) line range for files, end=-1 for EOF
|
|
211
|
-
nums:bool=False, # Whether to show line numbers
|
|
212
|
-
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
213
|
-
):
|
|
214
|
-
"Get the `Message` object indexed in the current dialog."
|
|
215
|
-
if not msgid: msgid = find_msg_id()
|
|
216
|
-
data = dict(n=n, relative=relative, msgid=msgid)
|
|
217
|
-
if view_range: data['view_range'] = view_range # None gets converted to '' so we avoid passing it to use the p.default
|
|
218
|
-
if nums: data['nums'] = nums
|
|
219
|
-
return call_endp('read_msg_', dname, json=True, **data)
|
|
220
|
-
|
|
221
|
-
# %% ../nbs/00_core.ipynb
|
|
222
|
-
def run_msg(
|
|
223
|
-
msgid:str=None, # id of message to execute
|
|
224
|
-
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
225
|
-
):
|
|
226
|
-
"Adds a message to the run queue. Use read_msg to see the output once it runs."
|
|
227
|
-
return call_endp('add_runq_', dname, msgid=msgid, api=True)
|
|
228
|
-
|
|
229
265
|
# %% ../nbs/00_core.ipynb
|
|
230
266
|
def msg_insert_line(
|
|
231
267
|
msgid:str, # Message id to edit
|
dialoghelper/experimental.py
CHANGED
|
@@ -3,25 +3,17 @@
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_experimental.ipynb.
|
|
4
4
|
|
|
5
5
|
# %% auto 0
|
|
6
|
-
__all__ = ['iife', '
|
|
6
|
+
__all__ = ['iife', 'start_share', 'capture_screen']
|
|
7
7
|
|
|
8
8
|
# %% ../nbs/01_experimental.ipynb
|
|
9
|
-
import uuid,json
|
|
10
|
-
from importlib import resources
|
|
11
|
-
from fasthtml.common import Div,Script
|
|
12
|
-
from claudette import ToolResult
|
|
13
9
|
from .core import *
|
|
10
|
+
from fastcore.all import *
|
|
11
|
+
from fasthtml.common import Div,Script
|
|
14
12
|
from httpx import post as xpost
|
|
13
|
+
from importlib import resources
|
|
14
|
+
from lisette.core import *
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
def _pop_data(data_id, timeout=20):
|
|
18
|
-
params = {'data_id': data_id, 'timeout':timeout}
|
|
19
|
-
result = xpost('http://localhost:5001/pop_data_blocking_', data=params, timeout=timeout+1)
|
|
20
|
-
if result.status_code==200 and result.text.strip():
|
|
21
|
-
if (data := result.json()): return data
|
|
22
|
-
# except json.JSONDecodeError as e: print(f"JSON decode error: {e}")
|
|
23
|
-
raise RuntimeError("No data received")
|
|
24
|
-
raise RuntimeError(result.status_code)
|
|
16
|
+
import base64,json,uuid,time
|
|
25
17
|
|
|
26
18
|
# %% ../nbs/01_experimental.ipynb
|
|
27
19
|
def iife(code: str) -> str:
|
|
@@ -34,63 +26,16 @@ def iife(code: str) -> str:
|
|
|
34
26
|
add_html(Div(Script(trigger_script), hx_swap_oob=f'beforeend:#js-script'))
|
|
35
27
|
|
|
36
28
|
# %% ../nbs/01_experimental.ipynb
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"Load screenshot capability and wait for confirmation it's ready."
|
|
41
|
-
global _js_loaded
|
|
42
|
-
if _js_loaded and not force: return
|
|
43
|
-
# print("Loading screenshot.js ...")
|
|
44
|
-
status_id = str(uuid.uuid4())
|
|
45
|
-
js_content = (resources.files('dialoghelper')/'screenshot.js').read_text()
|
|
46
|
-
iife(js_content + f'sendDataToServer("{status_id}", {{"js_status": "ready"}});')
|
|
47
|
-
data = _pop_data(status_id, timeout)
|
|
48
|
-
if (stat:=data.get('js_status'))!='ready': raise RuntimeError(f"Failed to load screenshot.js: {stat}")
|
|
49
|
-
_js_loaded = True
|
|
50
|
-
# print("Screenshot.js loaded and ready")
|
|
51
|
-
|
|
52
|
-
# %% ../nbs/01_experimental.ipynb
|
|
53
|
-
_screen_share_active = False
|
|
54
|
-
|
|
55
|
-
def start_screen_share(timeout=45):
|
|
56
|
-
"Start persistent screen sharing session, waiting for confirmation."
|
|
57
|
-
global _screen_share_active
|
|
58
|
-
load_screenshot_js()
|
|
59
|
-
status_id = str(uuid.uuid4())
|
|
60
|
-
iife(f'startPersistentScreenShare("{status_id}");')
|
|
61
|
-
# print("Requesting screen share permission ...")
|
|
62
|
-
data = _pop_data(status_id, timeout)
|
|
63
|
-
js_status = data.get('js_status')
|
|
64
|
-
if js_status=='ready':
|
|
65
|
-
_screen_share_active = True
|
|
66
|
-
# print("Screen share started successfully.")
|
|
67
|
-
elif js_status=='error': raise RuntimeError(f"Screen share failed: {data.get('error', 'Unknown error')}")
|
|
68
|
-
elif js_status=='connecting': raise RuntimeError("Screen share timed out after {timeout} seconds.")
|
|
69
|
-
|
|
70
|
-
# %% ../nbs/01_experimental.ipynb
|
|
71
|
-
def stop_screen_share():
|
|
72
|
-
"Stop persistent screen sharing session."
|
|
73
|
-
global _screen_share_active
|
|
74
|
-
load_screenshot_js()
|
|
75
|
-
iife('stopPersistentScreenShare();')
|
|
76
|
-
_screen_share_active = False
|
|
77
|
-
# print("Screen share stopped.")
|
|
29
|
+
def start_share():
|
|
30
|
+
iife((resources.files('dialoghelper')/'screenshot.js').read_text())
|
|
31
|
+
iife('await setupVideoStream();')
|
|
78
32
|
|
|
79
33
|
# %% ../nbs/01_experimental.ipynb
|
|
80
34
|
def capture_screen():
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
data_id = str(uuid.uuid4())
|
|
89
|
-
screenshot_code = f'captureScreenFromStream("{data_id}");'
|
|
90
|
-
# print("📸 Capturing from persistent stream...")
|
|
91
|
-
iife(screenshot_code)
|
|
92
|
-
data = _pop_data(data_id, timeout=45)
|
|
93
|
-
if 'error' in data: raise RuntimeError(f"Screenshot failed: {data['error']}")
|
|
94
|
-
if not ('img_data' in data and 'img_type' in data):
|
|
95
|
-
raise RuntimeError("Screenshot capture failed, failed to retrieve data.")
|
|
96
|
-
return ToolResult(data=data['img_data'], result_type=data['img_type'])
|
|
35
|
+
'Capture screenshot, automatically starting screen share if needed.'
|
|
36
|
+
idx = uuid.uuid4()
|
|
37
|
+
iife(f"pushData('{idx}', {{img_data: await getScreenshot()}});")
|
|
38
|
+
time.sleep(0.5)
|
|
39
|
+
d = dict2obj(xpost('http://localhost:5001/pop_data_blocking_', data={'data_id': idx}).json())
|
|
40
|
+
if 'img_data' in d: return ToolResponse([{'type': 'image_url', 'image_url': d.img_data}])
|
|
41
|
+
else: return f'Capture failed: {d.error}'
|
dialoghelper/screenshot.js
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
async function setupVideoStream() {
|
|
2
|
+
vstream = await navigator.mediaDevices.getDisplayMedia();
|
|
3
|
+
window.vtrack = vstream.getVideoTracks()[0];
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async function getScreenshot(maxWidth = 1280, maxHeight = 1024) {
|
|
7
|
+
if (window?.vtrack) {
|
|
8
|
+
const cap = new ImageCapture(window.vtrack);
|
|
9
|
+
const img = await cap.grabFrame();
|
|
10
|
+
// rescale and convert to b64
|
|
11
|
+
const scaleX = maxWidth / img.width;
|
|
12
|
+
const scaleY = maxHeight / img.height;
|
|
13
|
+
const scale = Math.min(scaleX, scaleY, 1); // don't upscale
|
|
14
|
+
const newWidth = Math.floor(img.width * scale);
|
|
15
|
+
const newHeight = Math.floor(img.height * scale);
|
|
16
|
+
const c = document.createElement('canvas');
|
|
17
|
+
c.width = newWidth; c.height = newHeight;
|
|
18
|
+
c.getContext('2d').drawImage(img, 0, 0, newWidth, newHeight);
|
|
19
|
+
return c.toDataURL();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
8
22
|
|
|
9
|
-
function
|
|
23
|
+
function pushData(dataId, data) {
|
|
10
24
|
return fetch('/push_data_blocking_', {
|
|
11
25
|
method: 'POST',
|
|
12
26
|
headers: {'Content-Type': 'application/json'},
|
|
@@ -14,116 +28,6 @@ function sendDataToServer(dataId, data) {
|
|
|
14
28
|
});
|
|
15
29
|
}
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
video.srcObject = stream;
|
|
21
|
-
video.muted = true;
|
|
22
|
-
video.playsInline = true;
|
|
23
|
-
video.addEventListener("loadedmetadata", () => {
|
|
24
|
-
// Downscale to maxWidth x maxHeight using canvas
|
|
25
|
-
const videoWidth = video.videoWidth;
|
|
26
|
-
const videoHeight = video.videoHeight;
|
|
27
|
-
const scaleX = maxWidth / videoWidth;
|
|
28
|
-
const scaleY = maxHeight / videoHeight;
|
|
29
|
-
const scale = Math.min(scaleX, scaleY, 1); // don't upscale
|
|
30
|
-
const newWidth = Math.floor(videoWidth * scale);
|
|
31
|
-
const newHeight = Math.floor(videoHeight * scale);
|
|
32
|
-
const canvas = document.createElement("canvas");
|
|
33
|
-
canvas.width = newWidth;
|
|
34
|
-
canvas.height = newHeight;
|
|
35
|
-
const ctx = canvas.getContext("2d");
|
|
36
|
-
ctx.drawImage(video, 0, 0, newWidth, newHeight);
|
|
37
|
-
canvas.toBlob(resolve, "image/png");
|
|
38
|
-
});
|
|
39
|
-
video.addEventListener("error", reject);
|
|
40
|
-
video.play().catch(reject);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function waitForGetDisplayMedia(timeout = 30000) {
|
|
45
|
-
const start = Date.now();
|
|
46
|
-
while (Date.now() - start < timeout) {
|
|
47
|
-
if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) { return true; }
|
|
48
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
49
|
-
}
|
|
50
|
-
throw new Error("getDisplayMedia not available after timeout");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function startPersistentScreenShare(statusId = null) {
|
|
54
|
-
try {
|
|
55
|
-
await waitForGetDisplayMedia();
|
|
56
|
-
persistentStream = await navigator.mediaDevices.getDisplayMedia({
|
|
57
|
-
video: { mediaSource: "screen", displaySurface: "monitor" }, audio: false,
|
|
58
|
-
});
|
|
59
|
-
persistentStream.getVideoTracks()[0].addEventListener("ended", () => {
|
|
60
|
-
console.log("Screen share ended by user");
|
|
61
|
-
stopPersistentScreenShare();
|
|
62
|
-
});
|
|
63
|
-
console.log("✅ Persistent screen share started");
|
|
64
|
-
if (statusId) { sendDataToServer(statusId, { js_status: "ready" }); }
|
|
65
|
-
streamStatus = "connected";
|
|
66
|
-
return { status: "success", message: "Screen share started" };
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.error("Failed to start persistent screen share:", error);
|
|
69
|
-
if (statusId) { sendDataToServer(statusId, { js_status: "error", error: error.message }); }
|
|
70
|
-
return { status: "error", message: error.message };
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function stopPersistentScreenShare() {
|
|
75
|
-
if (persistentStream) {
|
|
76
|
-
persistentStream.getTracks().forEach((track) => track.stop());
|
|
77
|
-
persistentStream = null;
|
|
78
|
-
}
|
|
79
|
-
streamStatus = "disconnected";
|
|
80
|
-
console.log("🛑 Persistent screen share stopped");
|
|
81
|
-
return { status: "success", message: "Screen share stopped" };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function blobToBase64(blob) {
|
|
85
|
-
return new Promise((resolve, reject) => {
|
|
86
|
-
const reader = new FileReader();
|
|
87
|
-
reader.onload = () => resolve(reader.result);
|
|
88
|
-
reader.onerror = reject;
|
|
89
|
-
reader.readAsDataURL(blob);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function processScreenshotBlob(blob, dataId) {
|
|
94
|
-
const base64String = await blobToBase64(blob);
|
|
95
|
-
const blob_type = blob.type || "image/png";
|
|
96
|
-
const b64data = base64String.split(",")[1]; // Remove the data URL prefix
|
|
97
|
-
return {
|
|
98
|
-
data_id: dataId,
|
|
99
|
-
size: blob.size,
|
|
100
|
-
img_type: blob_type,
|
|
101
|
-
img_data: b64data,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async function captureScreenFromStream(dataId) {
|
|
106
|
-
console.log("Executing screenshot from persistent stream");
|
|
107
|
-
try {
|
|
108
|
-
if (!persistentStream || streamStatus !== "connected") {
|
|
109
|
-
console.log("Stream status:", streamStatus);
|
|
110
|
-
console.log("Persistent stream:", persistentStream);
|
|
111
|
-
throw new Error("No active screen share. Call startPersistentScreenShare() first.");
|
|
112
|
-
}
|
|
113
|
-
const blob = await streamToBlob(persistentStream);
|
|
114
|
-
const result = await processScreenshotBlob(blob, dataId);
|
|
115
|
-
console.log("Screenshot result:", result);
|
|
116
|
-
const pushResponse = await sendDataToServer(dataId, result)
|
|
117
|
-
if (pushResponse.ok) { console.log("✅ Screenshot data pushed to server"); }
|
|
118
|
-
else { console.log("❌ Failed to push screenshot data"); }
|
|
119
|
-
} catch (error) {
|
|
120
|
-
console.error("Screenshot error:", error);
|
|
121
|
-
sendDataToServer(dataId, { error: error.message });
|
|
122
|
-
}
|
|
123
|
-
console.log("Finished executing screenshot");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
window.startPersistentScreenShare = startPersistentScreenShare;
|
|
127
|
-
window.stopPersistentScreenShare = stopPersistentScreenShare;
|
|
128
|
-
window.captureScreenFromStream = captureScreenFromStream;
|
|
129
|
-
window.getStreamStatus = () => streamStatus;
|
|
31
|
+
window.setupVideoStream = setupVideoStream;
|
|
32
|
+
window.getScreenshot = getScreenshot;
|
|
33
|
+
window.pushData = pushData;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dialoghelper
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.37
|
|
4
4
|
Summary: Helper functions for solveit dialogs
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/dialoghelper
|
|
6
6
|
Author: Jeremy Howard
|
|
@@ -25,6 +25,8 @@ Requires-Dist: ipykernel-helper
|
|
|
25
25
|
Requires-Dist: claudette
|
|
26
26
|
Requires-Dist: ast-grep-cli
|
|
27
27
|
Requires-Dist: ast-grep-py
|
|
28
|
+
Requires-Dist: MonsterUI
|
|
29
|
+
Requires-Dist: lisette>=0.0.13
|
|
28
30
|
Provides-Extra: dev
|
|
29
31
|
Requires-Dist: python-fasthtml; extra == "dev"
|
|
30
32
|
Dynamic: author
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
dialoghelper/__init__.py,sha256=38YNAJyeXf7hteiqxTtdpbcKxmqYNHn5TJE6Yfg5Ks4,43
|
|
2
|
+
dialoghelper/_modidx.py,sha256=a_9jKSOoEZsMp0piUBSKFNxtbGt9XMWnRoPhUaK4jls,4832
|
|
3
|
+
dialoghelper/core.py,sha256=Kc5T6tpaGimEYt3dqi9lY2XiAVCMVRrqdGrAvGsNsxc,18867
|
|
4
|
+
dialoghelper/db_dc.py,sha256=mi2Q2am_SoAPGpNQg7KPFS5pR9WEapRXT8ypkNmMfw0,2330
|
|
5
|
+
dialoghelper/experimental.py,sha256=k-vCuRzzWYrKCg-zi_EJmUPp40s_O3ji3eF1Bn27YGE,1351
|
|
6
|
+
dialoghelper/screenshot.js,sha256=nzmnO3bon1d_6pvgGOUY-EQb_IlqCA8_X5vx7JT_M5M,1194
|
|
7
|
+
dialoghelper-0.0.37.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
8
|
+
dialoghelper-0.0.37.dist-info/METADATA,sha256=QaYuRY5KHLblAlH47AFW7j6BIRbBl69PxdqH3Hixf_A,2749
|
|
9
|
+
dialoghelper-0.0.37.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
dialoghelper-0.0.37.dist-info/entry_points.txt,sha256=wvDeX-XTS_XVjWiiPQe6yVfmyNwy9eCr36ewp9baFIg,46
|
|
11
|
+
dialoghelper-0.0.37.dist-info/top_level.txt,sha256=VXLlkgltFs_q-XB9imt2G64I_-MPm1RnxlpvUWPuLKM,13
|
|
12
|
+
dialoghelper-0.0.37.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
dialoghelper/__init__.py,sha256=nXRRWJpKFa9Fjsny-pNH8T_A-gGKLYZSnNT-NX70XbI,43
|
|
2
|
-
dialoghelper/_modidx.py,sha256=ivpKN2mT49JlnSFMiyGaNwIKo8AUckJ_gf78haqTJr0,5243
|
|
3
|
-
dialoghelper/core.py,sha256=_-kEJFjnSnS24l0KQ5-NxnHAeAqjHtCujjBxYVm2wu8,17492
|
|
4
|
-
dialoghelper/db_dc.py,sha256=mi2Q2am_SoAPGpNQg7KPFS5pR9WEapRXT8ypkNmMfw0,2330
|
|
5
|
-
dialoghelper/experimental.py,sha256=yTXzyY37MPW-9mqzbem-q6CCPXElN0GwAyOK-iJofMY,3910
|
|
6
|
-
dialoghelper/screenshot.js,sha256=DDqlRhemwj8I0AloxMJBnBqDDIqu5knlT1lpG_0rphI,4851
|
|
7
|
-
dialoghelper-0.0.35.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
8
|
-
dialoghelper-0.0.35.dist-info/METADATA,sha256=L6Wmq1Qm1ZqccjP3da6LmwcwlJxRRP6ctPG1jQSEV5Q,2693
|
|
9
|
-
dialoghelper-0.0.35.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
-
dialoghelper-0.0.35.dist-info/entry_points.txt,sha256=wvDeX-XTS_XVjWiiPQe6yVfmyNwy9eCr36ewp9baFIg,46
|
|
11
|
-
dialoghelper-0.0.35.dist-info/top_level.txt,sha256=VXLlkgltFs_q-XB9imt2G64I_-MPm1RnxlpvUWPuLKM,13
|
|
12
|
-
dialoghelper-0.0.35.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|