dialoghelper 0.0.27__py3-none-any.whl → 0.0.29__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.
Potentially problematic release.
This version of dialoghelper might be problematic. Click here for more details.
- dialoghelper/__init__.py +1 -1
- dialoghelper/experimental.py +32 -29
- dialoghelper/screenshot.js +129 -0
- {dialoghelper-0.0.27.dist-info → dialoghelper-0.0.29.dist-info}/METADATA +2 -1
- dialoghelper-0.0.29.dist-info/RECORD +12 -0
- dialoghelper-0.0.27.dist-info/RECORD +0 -11
- {dialoghelper-0.0.27.dist-info → dialoghelper-0.0.29.dist-info}/WHEEL +0 -0
- {dialoghelper-0.0.27.dist-info → dialoghelper-0.0.29.dist-info}/entry_points.txt +0 -0
- {dialoghelper-0.0.27.dist-info → dialoghelper-0.0.29.dist-info}/licenses/LICENSE +0 -0
- {dialoghelper-0.0.27.dist-info → dialoghelper-0.0.29.dist-info}/top_level.txt +0 -0
dialoghelper/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
__version__ = "0.0.
|
|
1
|
+
__version__ = "0.0.29"
|
|
2
2
|
from .core import *
|
dialoghelper/experimental.py
CHANGED
|
@@ -6,22 +6,22 @@
|
|
|
6
6
|
__all__ = ['iife', 'load_screenshot_js', 'start_screen_share', 'stop_screen_share', 'capture_screen']
|
|
7
7
|
|
|
8
8
|
# %% ../nbs/01_experimental.ipynb
|
|
9
|
+
import uuid,json
|
|
9
10
|
from importlib import resources
|
|
10
|
-
import
|
|
11
|
-
from fasthtml.common import Div, Script
|
|
12
|
-
import json
|
|
11
|
+
from fasthtml.common import Div,Script
|
|
13
12
|
from claudette import ToolResult
|
|
14
13
|
from .core import *
|
|
15
14
|
from httpx import post as xpost
|
|
16
15
|
|
|
17
16
|
# %% ../nbs/01_experimental.ipynb
|
|
18
|
-
def _pop_data(data_id, timeout=20
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (data := result.json())
|
|
23
|
-
|
|
24
|
-
|
|
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)
|
|
25
25
|
|
|
26
26
|
# %% ../nbs/01_experimental.ipynb
|
|
27
27
|
def iife(code: str) -> str:
|
|
@@ -40,13 +40,14 @@ def load_screenshot_js(force=False, timeout=5):
|
|
|
40
40
|
"Load screenshot capability and wait for confirmation it's ready."
|
|
41
41
|
global _js_loaded
|
|
42
42
|
if _js_loaded and not force: return
|
|
43
|
-
|
|
43
|
+
# print("Loading screenshot.js ...")
|
|
44
44
|
status_id = str(uuid.uuid4())
|
|
45
|
-
js_content = (resources.files('dialoghelper')
|
|
45
|
+
js_content = (resources.files('dialoghelper')/'screenshot.js').read_text()
|
|
46
46
|
iife(js_content + f'sendDataToServer("{status_id}", {{"js_status": "ready"}});')
|
|
47
|
-
data = _pop_data(status_id, timeout
|
|
47
|
+
data = _pop_data(status_id, timeout)
|
|
48
|
+
if (stat:=data.get('js_status'))!='ready': raise RuntimeError(f"Failed to load screenshot.js: {stat}")
|
|
48
49
|
_js_loaded = True
|
|
49
|
-
|
|
50
|
+
# print("Screenshot.js loaded and ready")
|
|
50
51
|
|
|
51
52
|
# %% ../nbs/01_experimental.ipynb
|
|
52
53
|
_screen_share_active = False
|
|
@@ -57,23 +58,23 @@ def start_screen_share(timeout=45):
|
|
|
57
58
|
load_screenshot_js()
|
|
58
59
|
status_id = str(uuid.uuid4())
|
|
59
60
|
iife(f'startPersistentScreenShare("{status_id}");')
|
|
60
|
-
|
|
61
|
-
data = _pop_data(status_id, timeout
|
|
61
|
+
# print("Requesting screen share permission ...")
|
|
62
|
+
data = _pop_data(status_id, timeout)
|
|
62
63
|
js_status = data.get('js_status')
|
|
63
|
-
if js_status
|
|
64
|
+
if js_status=='ready':
|
|
64
65
|
_screen_share_active = True
|
|
65
|
-
|
|
66
|
-
elif js_status
|
|
67
|
-
elif js_status
|
|
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.")
|
|
68
69
|
|
|
69
70
|
# %% ../nbs/01_experimental.ipynb
|
|
70
71
|
def stop_screen_share():
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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.")
|
|
77
78
|
|
|
78
79
|
# %% ../nbs/01_experimental.ipynb
|
|
79
80
|
def capture_screen():
|
|
@@ -81,13 +82,15 @@ def capture_screen():
|
|
|
81
82
|
global _screen_share_active
|
|
82
83
|
load_screenshot_js()
|
|
83
84
|
if not _screen_share_active:
|
|
84
|
-
|
|
85
|
+
# print("🔄 No active screen share, starting one...")
|
|
85
86
|
result = start_screen_share()
|
|
86
87
|
if not _screen_share_active: raise RuntimeError(f"Failed to start screen share: {result}")
|
|
87
88
|
data_id = str(uuid.uuid4())
|
|
88
89
|
screenshot_code = f'captureScreenFromStream("{data_id}");'
|
|
89
|
-
|
|
90
|
+
# print("📸 Capturing from persistent stream...")
|
|
90
91
|
iife(screenshot_code)
|
|
91
|
-
data = _pop_data(data_id, timeout=
|
|
92
|
+
data = _pop_data(data_id, timeout=45)
|
|
92
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.")
|
|
93
96
|
return ToolResult(data=data['img_data'], result_type=data['img_type'])
|
|
@@ -0,0 +1,129 @@
|
|
|
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'
|
|
8
|
+
|
|
9
|
+
function sendDataToServer(dataId, data) {
|
|
10
|
+
return fetch('/push_data_blocking_', {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: {'Content-Type': 'application/json'},
|
|
13
|
+
body: JSON.stringify({data_id: dataId, ...data})
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
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;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dialoghelper
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.29
|
|
4
4
|
Summary: Helper functions for solveit dialogs
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/dialoghelper
|
|
6
6
|
Author: Jeremy Howard
|
|
@@ -24,6 +24,7 @@ Requires-Dist: ghapi
|
|
|
24
24
|
Requires-Dist: ipykernel-helper
|
|
25
25
|
Requires-Dist: claudette
|
|
26
26
|
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: python-fasthtml; extra == "dev"
|
|
27
28
|
Dynamic: author
|
|
28
29
|
Dynamic: author-email
|
|
29
30
|
Dynamic: classifier
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
dialoghelper/__init__.py,sha256=WbA03-_Zk9_bs1UWTpY7lBis1mVY0z7IJ3-qkKikrHE,43
|
|
2
|
+
dialoghelper/_modidx.py,sha256=haFiPtlNF3j0tsHG-ODfyiy85H1OTbbdjR3ojhP6K4U,4156
|
|
3
|
+
dialoghelper/core.py,sha256=U9FvC8ZR6-v7X_PoP-D2etYrZX_Agzed5xpyNFL6a-8,11160
|
|
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.29.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
8
|
+
dialoghelper-0.0.29.dist-info/METADATA,sha256=koS3q01TrcrDbKaWXZJXFyCryirDDUvvKDIq3ck-buM,2637
|
|
9
|
+
dialoghelper-0.0.29.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
dialoghelper-0.0.29.dist-info/entry_points.txt,sha256=wvDeX-XTS_XVjWiiPQe6yVfmyNwy9eCr36ewp9baFIg,46
|
|
11
|
+
dialoghelper-0.0.29.dist-info/top_level.txt,sha256=VXLlkgltFs_q-XB9imt2G64I_-MPm1RnxlpvUWPuLKM,13
|
|
12
|
+
dialoghelper-0.0.29.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
dialoghelper/__init__.py,sha256=RR5qXprLU41FEqcFMBsvo0Vdtv-eF_LS0-htzpt3GGI,43
|
|
2
|
-
dialoghelper/_modidx.py,sha256=haFiPtlNF3j0tsHG-ODfyiy85H1OTbbdjR3ojhP6K4U,4156
|
|
3
|
-
dialoghelper/core.py,sha256=U9FvC8ZR6-v7X_PoP-D2etYrZX_Agzed5xpyNFL6a-8,11160
|
|
4
|
-
dialoghelper/db_dc.py,sha256=mi2Q2am_SoAPGpNQg7KPFS5pR9WEapRXT8ypkNmMfw0,2330
|
|
5
|
-
dialoghelper/experimental.py,sha256=7kUcsYRMd30A1QSE1chyGgnREFrvCmmT5ZJM9Pg5oQY,3964
|
|
6
|
-
dialoghelper-0.0.27.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
7
|
-
dialoghelper-0.0.27.dist-info/METADATA,sha256=huwfNYq5GGZW30fuZuc1A488HVoQ-6zVZb7zH8uuRgg,2590
|
|
8
|
-
dialoghelper-0.0.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
-
dialoghelper-0.0.27.dist-info/entry_points.txt,sha256=wvDeX-XTS_XVjWiiPQe6yVfmyNwy9eCr36ewp9baFIg,46
|
|
10
|
-
dialoghelper-0.0.27.dist-info/top_level.txt,sha256=VXLlkgltFs_q-XB9imt2G64I_-MPm1RnxlpvUWPuLKM,13
|
|
11
|
-
dialoghelper-0.0.27.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|