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 CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.0.35"
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._pop_data': ( 'experimental.html#_pop_data',
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.load_screenshot_js': ( 'experimental.html#load_screenshot_js',
45
- 'dialoghelper/experimental.py'),
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__ = ['Placements', 'empty', 'find_var', 'call_endp', 'find_dname', 'find_msg_id', 'curr_dialog', 'find_msgs', 'msg_idx',
5
- 'add_html', 'add_msg', 'del_msg', 'update_msg', 'url2note', 'ast_py', 'ast_grep', 'read_msg', 'run_msg',
6
- 'msg_insert_line', 'msg_str_replace', 'msg_strs_replace', 'msg_replace_lines', 'load_gist', 'gist_file',
7
- 'import_string', 'is_usable_tool', 'mk_toollist', 'import_gist', 'tool_info', 'fc_tool_info', 'asdict']
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, importlib, linecache
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
- # %% ../nbs/00_core.ipynb
28
- _all_ = ["asdict"]
29
-
30
- # %% ../nbs/00_core.ipynb
31
- def find_var(var:str):
32
- "Search for var in all frames of the call stack"
33
- frame = currentframe()
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
- dv = frame.f_globals.get(var, frame.f_locals.get(var, None))
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:5001/{path}', data=data)
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
- return call_endp(
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
- return call_endp(
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
- return call_endp('add_relative_', dname, placement='update', msgid=msgid, **kwargs)
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
@@ -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', 'load_screenshot_js', 'start_screen_share', 'stop_screen_share', 'capture_screen']
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
- # %% ../nbs/01_experimental.ipynb
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
- _js_loaded = False
38
-
39
- def load_screenshot_js(force=False, timeout=5):
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
- "Capture screenshot, automatically starting screen share if needed."
82
- global _screen_share_active
83
- load_screenshot_js()
84
- if not _screen_share_active:
85
- # print("🔄 No active screen share, starting one...")
86
- result = start_screen_share()
87
- if not _screen_share_active: raise RuntimeError(f"Failed to start screen share: {result}")
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}'
@@ -1,12 +1,26 @@
1
- /**
2
- * Screenshot capture functionality for dialoghelper
3
- * Provides persistent screen sharing with HTTP polling for screenshots
4
- */
5
-
6
- let persistentStream = null;
7
- let streamStatus = "disconnected"; // 'disconnected', 'connecting', 'connected', 'error'
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 sendDataToServer(dataId, data) {
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
- async function streamToBlob(stream, maxWidth = 1280, maxHeight = 1024) {
18
- return new Promise((resolve, reject) => {
19
- const video = document.createElement("video");
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.35
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,,