@hypothesi/tauri-mcp-server 0.8.3 → 0.10.0
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.
- package/README.md +16 -2
- package/dist/api.d.ts +2 -0
- package/dist/api.js +2 -0
- package/dist/config.d.ts +35 -0
- package/dist/driver/app-discovery.d.ts +75 -0
- package/dist/driver/element-picker.d.ts +42 -0
- package/dist/driver/element-picker.js +272 -0
- package/dist/driver/plugin-client.d.ts +100 -0
- package/dist/driver/plugin-commands.d.ts +163 -0
- package/dist/driver/protocol.d.ts +128 -0
- package/dist/driver/script-manager.d.ts +91 -0
- package/dist/driver/scripts/aria-api-loader.d.ts +17 -0
- package/dist/driver/scripts/element-picker.js +395 -0
- package/dist/driver/scripts/html2canvas-loader.d.ts +25 -0
- package/dist/driver/scripts/index.d.ts +37 -0
- package/dist/driver/scripts/index.js +1 -0
- package/dist/driver/session-manager.d.ts +76 -0
- package/dist/driver/webview-executor.d.ts +122 -0
- package/dist/driver/webview-interactions.d.ts +349 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -119
- package/dist/logger.d.ts +16 -0
- package/dist/manager/mobile.d.ts +6 -0
- package/dist/monitor/logs.d.ts +32 -0
- package/dist/prompts-registry.d.ts +30 -0
- package/dist/prompts-registry.js +46 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +110 -0
- package/dist/tools-registry.d.ts +73 -0
- package/dist/tools-registry.js +51 -0
- package/dist/types/window.d.ts +30 -0
- package/dist/version.d.ts +15 -0
- package/package.json +9 -1
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Element picker overlay for MCP Server Tauri.
|
|
3
|
+
*
|
|
4
|
+
* Activated by the agent via webview_select_element. Displays a hover highlight,
|
|
5
|
+
* context tooltip, and cancel bar. On click (desktop) or two-tap (mobile) the
|
|
6
|
+
* selected element's metadata is emitted as a Tauri event so the MCP server can
|
|
7
|
+
* retrieve it asynchronously.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} params
|
|
10
|
+
* @param {string} params.mode - 'pick' (agent-initiated picker)
|
|
11
|
+
* @param {string} params.pickerId - Unique identifier for this picker session
|
|
12
|
+
*/
|
|
13
|
+
(function(params) {
|
|
14
|
+
var mode = params.mode;
|
|
15
|
+
var pickerId = params.pickerId;
|
|
16
|
+
|
|
17
|
+
// Duplicate-activation guard
|
|
18
|
+
if (window.__MCP_PICKER_ACTIVE__) {
|
|
19
|
+
// Cancel the previous picker
|
|
20
|
+
var prevId = window.__MCP_PICKER_ACTIVE__;
|
|
21
|
+
cleanup();
|
|
22
|
+
if (window.__TAURI__ && window.__TAURI__.event && window.__TAURI__.event.emit) {
|
|
23
|
+
window.__TAURI__.event.emit('__element_picked', { pickerId: prevId, cancelled: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
window.__MCP_PICKER_ACTIVE__ = pickerId;
|
|
27
|
+
|
|
28
|
+
var isTouch = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
|
|
29
|
+
var highlight = null;
|
|
30
|
+
var tooltip = null;
|
|
31
|
+
var cancelBar = null;
|
|
32
|
+
var trackedElement = null; // element currently being highlighted
|
|
33
|
+
var rafId = null; // requestAnimationFrame handle for cyclic repositioning
|
|
34
|
+
|
|
35
|
+
// ── Velocity-based hover throttling state ──────────────────────────────
|
|
36
|
+
var lastMouseX = 0;
|
|
37
|
+
var lastMouseY = 0;
|
|
38
|
+
var lastMouseTime = 0;
|
|
39
|
+
var mouseVelocity = 0;
|
|
40
|
+
var hoverUpdateTimer = null;
|
|
41
|
+
|
|
42
|
+
// ── Cancel bar ─────────────────────────────────────────────────────────
|
|
43
|
+
cancelBar = document.createElement('div');
|
|
44
|
+
cancelBar.setAttribute('data-mcp-picker', 'cancel-bar');
|
|
45
|
+
var cancelBarAtTop = true;
|
|
46
|
+
cancelBar.style.cssText =
|
|
47
|
+
'position:fixed;top:0;left:0;right:0;z-index:2147483647;height:40px;' +
|
|
48
|
+
'background:rgba(30,41,59,0.95);display:flex;align-items:center;' +
|
|
49
|
+
'justify-content:space-between;padding:0 12px;font:13px/1 system-ui,sans-serif;' +
|
|
50
|
+
'color:#E2E8F0;box-sizing:border-box;' +
|
|
51
|
+
'transition:top 0.2s ease,bottom 0.2s ease;';
|
|
52
|
+
|
|
53
|
+
var cancelText = document.createElement('span');
|
|
54
|
+
cancelText.textContent = 'MCP Element Picker \u2014 Click to select | ESC or tap X to cancel';
|
|
55
|
+
cancelText.style.cssText = 'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;';
|
|
56
|
+
|
|
57
|
+
// Move button — toggles bar between top and bottom
|
|
58
|
+
var moveBtn = document.createElement('button');
|
|
59
|
+
moveBtn.setAttribute('data-mcp-picker', 'move-btn');
|
|
60
|
+
moveBtn.textContent = '\u2193'; // ↓
|
|
61
|
+
moveBtn.title = 'Move bar to bottom';
|
|
62
|
+
moveBtn.style.cssText =
|
|
63
|
+
'background:none;border:none;color:#94A3B8;font-size:16px;cursor:pointer;' +
|
|
64
|
+
'width:32px;height:40px;display:flex;align-items:center;justify-content:center;' +
|
|
65
|
+
'flex-shrink:0;';
|
|
66
|
+
function toggleCancelBarPosition(e) {
|
|
67
|
+
e.stopPropagation();
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
cancelBarAtTop = !cancelBarAtTop;
|
|
70
|
+
cancelBar.style.top = cancelBarAtTop ? '0' : 'auto';
|
|
71
|
+
cancelBar.style.bottom = cancelBarAtTop ? 'auto' : '0';
|
|
72
|
+
moveBtn.textContent = cancelBarAtTop ? '\u2193' : '\u2191'; // ↓ or ↑
|
|
73
|
+
moveBtn.title = cancelBarAtTop ? 'Move bar to bottom' : 'Move bar to top';
|
|
74
|
+
}
|
|
75
|
+
moveBtn.addEventListener('click', toggleCancelBarPosition);
|
|
76
|
+
moveBtn.addEventListener('touchend', toggleCancelBarPosition);
|
|
77
|
+
|
|
78
|
+
var cancelBtn = document.createElement('button');
|
|
79
|
+
cancelBtn.setAttribute('data-mcp-picker', 'cancel-btn');
|
|
80
|
+
cancelBtn.textContent = '\u2715';
|
|
81
|
+
cancelBtn.style.cssText =
|
|
82
|
+
'background:none;border:none;color:#E2E8F0;font-size:20px;cursor:pointer;' +
|
|
83
|
+
'width:40px;height:40px;display:flex;align-items:center;justify-content:center;' +
|
|
84
|
+
'flex-shrink:0;';
|
|
85
|
+
cancelBtn.addEventListener('click', function(e) {
|
|
86
|
+
e.stopPropagation();
|
|
87
|
+
cancelPicker();
|
|
88
|
+
});
|
|
89
|
+
cancelBtn.addEventListener('touchend', function(e) {
|
|
90
|
+
e.stopPropagation();
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
cancelPicker();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
cancelBar.appendChild(cancelText);
|
|
96
|
+
cancelBar.appendChild(moveBtn);
|
|
97
|
+
cancelBar.appendChild(cancelBtn);
|
|
98
|
+
document.body.appendChild(cancelBar);
|
|
99
|
+
|
|
100
|
+
// ── Highlight helpers ──────────────────────────────────────────────────
|
|
101
|
+
function createHighlight() {
|
|
102
|
+
var el = document.createElement('div');
|
|
103
|
+
el.setAttribute('data-mcp-picker', 'highlight');
|
|
104
|
+
el.style.cssText =
|
|
105
|
+
'position:fixed;z-index:2147483645;pointer-events:none;' +
|
|
106
|
+
'background:rgba(59,130,246,0.15);border:2px solid #3B82F6;';
|
|
107
|
+
document.body.appendChild(el);
|
|
108
|
+
return el;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function positionHighlight(el, target) {
|
|
112
|
+
var rect = target.getBoundingClientRect();
|
|
113
|
+
el.style.top = rect.top + 'px';
|
|
114
|
+
el.style.left = rect.left + 'px';
|
|
115
|
+
el.style.width = rect.width + 'px';
|
|
116
|
+
el.style.height = rect.height + 'px';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Cyclic highlight repositioning via rAF ─────────────────────────────
|
|
120
|
+
// Keeps the highlight tracking animated or repositioned elements at ~60fps.
|
|
121
|
+
function startTracking() {
|
|
122
|
+
if (rafId) return;
|
|
123
|
+
function tick() {
|
|
124
|
+
if (trackedElement && highlight) {
|
|
125
|
+
positionHighlight(highlight, trackedElement);
|
|
126
|
+
}
|
|
127
|
+
rafId = requestAnimationFrame(tick);
|
|
128
|
+
}
|
|
129
|
+
rafId = requestAnimationFrame(tick);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function stopTracking() {
|
|
133
|
+
if (rafId) {
|
|
134
|
+
cancelAnimationFrame(rafId);
|
|
135
|
+
rafId = null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ── Tooltip helpers ────────────────────────────────────────────────────
|
|
140
|
+
function showTooltip(target) {
|
|
141
|
+
if (!tooltip) {
|
|
142
|
+
tooltip = document.createElement('div');
|
|
143
|
+
tooltip.setAttribute('data-mcp-picker', 'tooltip');
|
|
144
|
+
tooltip.style.cssText =
|
|
145
|
+
'position:fixed;z-index:2147483646;pointer-events:none;' +
|
|
146
|
+
'background:#1E293B;color:#E2E8F0;font:12px/1.4 monospace;' +
|
|
147
|
+
'padding:4px 8px;border-radius:4px;white-space:nowrap;max-width:300px;' +
|
|
148
|
+
'overflow:hidden;text-overflow:ellipsis;';
|
|
149
|
+
document.body.appendChild(tooltip);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
var rect = target.getBoundingClientRect();
|
|
153
|
+
var tag = target.tagName.toLowerCase();
|
|
154
|
+
var id = target.id ? '#' + target.id : '';
|
|
155
|
+
var cls = target.className && typeof target.className === 'string'
|
|
156
|
+
? '.' + target.className.trim().split(/\s+/).join('.')
|
|
157
|
+
: '';
|
|
158
|
+
var label = (tag + id + cls);
|
|
159
|
+
if (label.length > 60) label = label.substring(0, 57) + '...';
|
|
160
|
+
label += ' (' + Math.round(rect.width) + '\u00d7' + Math.round(rect.height) + ')';
|
|
161
|
+
tooltip.textContent = label;
|
|
162
|
+
|
|
163
|
+
// Position above or below the element
|
|
164
|
+
var tooltipTop = rect.top - 28;
|
|
165
|
+
if (tooltipTop < 44) { // below the cancel bar (40px + 4px padding)
|
|
166
|
+
tooltipTop = rect.bottom + 4;
|
|
167
|
+
}
|
|
168
|
+
tooltip.style.top = tooltipTop + 'px';
|
|
169
|
+
tooltip.style.left = Math.max(4, rect.left) + 'px';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function hideTooltip() {
|
|
173
|
+
if (tooltip && tooltip.parentNode) {
|
|
174
|
+
tooltip.parentNode.removeChild(tooltip);
|
|
175
|
+
tooltip = null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ── Element detection ──────────────────────────────────────────────────
|
|
180
|
+
// Uses the shared elementsFromPoint helper from bridge.js when available,
|
|
181
|
+
// falls back to document.elementFromPoint.
|
|
182
|
+
function findElementAt(x, y) {
|
|
183
|
+
if (window.__MCP_GET_ELEMENT_AT_POINT__) {
|
|
184
|
+
return window.__MCP_GET_ELEMENT_AT_POINT__(x, y);
|
|
185
|
+
}
|
|
186
|
+
// Fallback: hide highlight, use single elementFromPoint
|
|
187
|
+
if (highlight) highlight.style.display = 'none';
|
|
188
|
+
var el = document.elementFromPoint(x, y);
|
|
189
|
+
if (highlight) highlight.style.display = '';
|
|
190
|
+
return el;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Picker element detection guard (fallback for findElementAt) ────────
|
|
194
|
+
function isPickerUI(el) {
|
|
195
|
+
while (el) {
|
|
196
|
+
if (el.getAttribute && el.getAttribute('data-mcp-picker')) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
el = el.parentElement;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ── Selection ──────────────────────────────────────────────────────────
|
|
205
|
+
function selectElement(target) {
|
|
206
|
+
var metadata;
|
|
207
|
+
if (window.__MCP_COLLECT_ELEMENT_METADATA__) {
|
|
208
|
+
metadata = window.__MCP_COLLECT_ELEMENT_METADATA__(target);
|
|
209
|
+
} else {
|
|
210
|
+
metadata = { tag: target.tagName.toLowerCase(), cssSelector: '' };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Store in window for later retrieval
|
|
214
|
+
window.__MCP_PICKED_ELEMENT__ = metadata;
|
|
215
|
+
|
|
216
|
+
// Remove overlay + tooltip but keep highlight for screenshot
|
|
217
|
+
removeCancelBar();
|
|
218
|
+
hideTooltip();
|
|
219
|
+
stopTracking();
|
|
220
|
+
removeListeners();
|
|
221
|
+
|
|
222
|
+
// Emit Tauri event
|
|
223
|
+
if (window.__TAURI__ && window.__TAURI__.event && window.__TAURI__.event.emit) {
|
|
224
|
+
window.__TAURI__.event.emit('__element_picked', { pickerId: pickerId, element: metadata });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
window.__MCP_PICKER_ACTIVE__ = null;
|
|
228
|
+
return 'Element selected: ' + metadata.tag + (metadata.id ? '#' + metadata.id : '');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── Cancellation ──────────────────────────────────────────────────────
|
|
232
|
+
function cancelPicker() {
|
|
233
|
+
cleanup();
|
|
234
|
+
if (window.__TAURI__ && window.__TAURI__.event && window.__TAURI__.event.emit) {
|
|
235
|
+
window.__TAURI__.event.emit('__element_picked', { pickerId: pickerId, cancelled: true });
|
|
236
|
+
}
|
|
237
|
+
window.__MCP_PICKER_ACTIVE__ = null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ── Cleanup ────────────────────────────────────────────────────────────
|
|
241
|
+
function removeCancelBar() {
|
|
242
|
+
if (cancelBar && cancelBar.parentNode) {
|
|
243
|
+
cancelBar.parentNode.removeChild(cancelBar);
|
|
244
|
+
cancelBar = null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function removeHighlight() {
|
|
249
|
+
if (highlight && highlight.parentNode) {
|
|
250
|
+
highlight.parentNode.removeChild(highlight);
|
|
251
|
+
highlight = null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function cleanup() {
|
|
256
|
+
removeCancelBar();
|
|
257
|
+
removeHighlight();
|
|
258
|
+
hideTooltip();
|
|
259
|
+
stopTracking();
|
|
260
|
+
removeListeners();
|
|
261
|
+
trackedElement = null;
|
|
262
|
+
if (hoverUpdateTimer) {
|
|
263
|
+
clearTimeout(hoverUpdateTimer);
|
|
264
|
+
hoverUpdateTimer = null;
|
|
265
|
+
}
|
|
266
|
+
window.__MCP_PICKER_ACTIVE__ = null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// ── Core hover-update logic (shared by throttled and immediate paths) ──
|
|
270
|
+
function updateHoveredElement() {
|
|
271
|
+
var el = findElementAt(lastMouseX, lastMouseY);
|
|
272
|
+
|
|
273
|
+
if (!el || isPickerUI(el)) return;
|
|
274
|
+
|
|
275
|
+
if (el === trackedElement) return; // no change
|
|
276
|
+
trackedElement = el;
|
|
277
|
+
|
|
278
|
+
if (!highlight) {
|
|
279
|
+
highlight = createHighlight();
|
|
280
|
+
startTracking();
|
|
281
|
+
}
|
|
282
|
+
positionHighlight(highlight, el);
|
|
283
|
+
showTooltip(el);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ── Desktop: hover + click with velocity throttling ────────────────────
|
|
287
|
+
function onMouseMove(e) {
|
|
288
|
+
var now = performance.now();
|
|
289
|
+
var dx = e.clientX - lastMouseX;
|
|
290
|
+
var dy = e.clientY - lastMouseY;
|
|
291
|
+
var dt = now - lastMouseTime;
|
|
292
|
+
var distance = Math.sqrt(dx * dx + dy * dy);
|
|
293
|
+
|
|
294
|
+
lastMouseX = e.clientX;
|
|
295
|
+
lastMouseY = e.clientY;
|
|
296
|
+
|
|
297
|
+
// Calculate velocity in pixels per second
|
|
298
|
+
mouseVelocity = dt > 0 ? (distance / dt) * 1000 : 0;
|
|
299
|
+
lastMouseTime = now;
|
|
300
|
+
|
|
301
|
+
// If moving fast (>600 px/s), throttle updates to ~28fps
|
|
302
|
+
if (mouseVelocity > 600) {
|
|
303
|
+
if (hoverUpdateTimer) {
|
|
304
|
+
clearTimeout(hoverUpdateTimer);
|
|
305
|
+
}
|
|
306
|
+
hoverUpdateTimer = setTimeout(updateHoveredElement, 36); // ~28fps
|
|
307
|
+
} else {
|
|
308
|
+
if (hoverUpdateTimer) {
|
|
309
|
+
clearTimeout(hoverUpdateTimer);
|
|
310
|
+
hoverUpdateTimer = null;
|
|
311
|
+
}
|
|
312
|
+
updateHoveredElement();
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function onClick(e) {
|
|
317
|
+
// Let clicks on picker UI (cancel, move buttons) pass through to their handlers
|
|
318
|
+
if (isPickerUI(e.target)) return;
|
|
319
|
+
|
|
320
|
+
e.preventDefault();
|
|
321
|
+
e.stopPropagation();
|
|
322
|
+
|
|
323
|
+
var el = findElementAt(e.clientX, e.clientY);
|
|
324
|
+
|
|
325
|
+
if (!el || isPickerUI(el)) return;
|
|
326
|
+
|
|
327
|
+
selectElement(el);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ── Mobile: two-tap ────────────────────────────────────────────────────
|
|
331
|
+
var lastTapTarget = null;
|
|
332
|
+
|
|
333
|
+
function onTouchEnd(e) {
|
|
334
|
+
var touch = e.changedTouches[0];
|
|
335
|
+
if (!touch) return;
|
|
336
|
+
|
|
337
|
+
var el = findElementAt(touch.clientX, touch.clientY);
|
|
338
|
+
|
|
339
|
+
// Let taps on picker UI (cancel, move buttons) pass through to their handlers
|
|
340
|
+
if (!el || isPickerUI(el)) return;
|
|
341
|
+
|
|
342
|
+
e.preventDefault();
|
|
343
|
+
e.stopPropagation();
|
|
344
|
+
|
|
345
|
+
if (lastTapTarget === el) {
|
|
346
|
+
// Second tap on same element -> confirm
|
|
347
|
+
selectElement(el);
|
|
348
|
+
} else {
|
|
349
|
+
// First tap (or different element) -> highlight
|
|
350
|
+
lastTapTarget = el;
|
|
351
|
+
trackedElement = el;
|
|
352
|
+
|
|
353
|
+
if (!highlight) {
|
|
354
|
+
highlight = createHighlight();
|
|
355
|
+
startTracking();
|
|
356
|
+
}
|
|
357
|
+
positionHighlight(highlight, el);
|
|
358
|
+
showTooltip(el);
|
|
359
|
+
|
|
360
|
+
if (cancelText) {
|
|
361
|
+
cancelText.textContent = 'Tap element again to send | X Cancel';
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ── Keyboard escape ────────────────────────────────────────────────────
|
|
367
|
+
function onKeyDown(e) {
|
|
368
|
+
if (e.key === 'Escape') {
|
|
369
|
+
e.preventDefault();
|
|
370
|
+
e.stopPropagation();
|
|
371
|
+
cancelPicker();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ── Listener management ────────────────────────────────────────────────
|
|
376
|
+
function removeListeners() {
|
|
377
|
+
document.removeEventListener('mousemove', onMouseMove, true);
|
|
378
|
+
document.removeEventListener('click', onClick, true);
|
|
379
|
+
document.removeEventListener('touchend', onTouchEnd, true);
|
|
380
|
+
document.removeEventListener('keydown', onKeyDown, true);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Attach appropriate listeners based on device
|
|
384
|
+
document.addEventListener('keydown', onKeyDown, true);
|
|
385
|
+
|
|
386
|
+
if (isTouch) {
|
|
387
|
+
document.addEventListener('touchend', onTouchEnd, true);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Always attach mouse listeners (hybrid devices like touch laptops)
|
|
391
|
+
document.addEventListener('mousemove', onMouseMove, true);
|
|
392
|
+
document.addEventListener('click', onClick, true);
|
|
393
|
+
|
|
394
|
+
return 'Picker activated (id: ' + pickerId + ')';
|
|
395
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* html2canvas-pro library loader
|
|
3
|
+
*
|
|
4
|
+
* Loads the html2canvas-pro library from node_modules and provides it as a string
|
|
5
|
+
* that can be injected into the webview. html2canvas-pro is a fork of html2canvas
|
|
6
|
+
* that adds support for modern CSS color functions like oklch(), oklab(), lab(),
|
|
7
|
+
* lch(), and color().
|
|
8
|
+
*/
|
|
9
|
+
/** Script ID used for the html2canvas library in the script registry. */
|
|
10
|
+
export declare const HTML2CANVAS_SCRIPT_ID = "__mcp_html2canvas__";
|
|
11
|
+
/**
|
|
12
|
+
* Get the html2canvas-pro library source code.
|
|
13
|
+
* Loaded lazily and cached.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getHtml2CanvasSource(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Build a script that captures a screenshot using html2canvas.
|
|
18
|
+
* Assumes html2canvas is already loaded (either via script manager or inline).
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildScreenshotCaptureScript(format: 'png' | 'jpeg', quality: number): string;
|
|
21
|
+
/**
|
|
22
|
+
* Build a script that injects html2canvas and captures a screenshot.
|
|
23
|
+
* This is the legacy function that inlines the library - kept for fallback.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildScreenshotScript(format: 'png' | 'jpeg', quality: number): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Script loader for webview injection scripts
|
|
3
|
+
*
|
|
4
|
+
* These scripts are loaded at build time and injected into the webview at runtime.
|
|
5
|
+
* Each script is an IIFE that accepts a params object.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SCRIPTS: {
|
|
8
|
+
readonly resolveRef: string;
|
|
9
|
+
readonly interact: string;
|
|
10
|
+
readonly swipe: string;
|
|
11
|
+
readonly keyboard: string;
|
|
12
|
+
readonly waitFor: string;
|
|
13
|
+
readonly getStyles: string;
|
|
14
|
+
readonly focus: string;
|
|
15
|
+
readonly findElement: string;
|
|
16
|
+
readonly domSnapshot: string;
|
|
17
|
+
readonly elementPicker: string;
|
|
18
|
+
};
|
|
19
|
+
/** Script ID used for resolve-ref in the script registry. */
|
|
20
|
+
export declare const RESOLVE_REF_SCRIPT_ID = "__mcp_resolve_ref__";
|
|
21
|
+
/**
|
|
22
|
+
* Get the resolve-ref script source code.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getResolveRefSource(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Build a script invocation with parameters
|
|
27
|
+
* The script should be an IIFE that accepts a params object
|
|
28
|
+
*/
|
|
29
|
+
export declare function buildScript(script: string, params: Record<string, unknown>): string;
|
|
30
|
+
/**
|
|
31
|
+
* Build a script for typing text (uses the keyboard script's typeText function)
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildTypeScript(selector: string, text: string, strategy?: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Build a script for key events (press, down, up)
|
|
36
|
+
*/
|
|
37
|
+
export declare function buildKeyEventScript(action: string, key: string, modifiers?: string[]): string;
|
|
@@ -22,6 +22,7 @@ export const SCRIPTS = {
|
|
|
22
22
|
focus: loadScript('focus'),
|
|
23
23
|
findElement: loadScript('find-element'),
|
|
24
24
|
domSnapshot: loadScript('dom-snapshot'),
|
|
25
|
+
elementPicker: loadScript('element-picker'),
|
|
25
26
|
};
|
|
26
27
|
/** Script ID used for resolve-ref in the script registry. */
|
|
27
28
|
export const RESOLVE_REF_SCRIPT_ID = '__mcp_resolve_ref__';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PluginClient } from './plugin-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Session Manager - Native IPC-based session management
|
|
5
|
+
*
|
|
6
|
+
* This module provides lightweight native session management using Tauri IPC.
|
|
7
|
+
* The "session" concept is maintained for API compatibility.
|
|
8
|
+
*
|
|
9
|
+
* Connection Strategy:
|
|
10
|
+
* 1. Try localhost first (most reliable for simulators/emulators/desktop)
|
|
11
|
+
* 2. If localhost fails and a remote host is configured, try that host
|
|
12
|
+
* 3. Return error if all connection attempts fail
|
|
13
|
+
*/
|
|
14
|
+
export declare const ManageDriverSessionSchema: z.ZodObject<{
|
|
15
|
+
action: z.ZodEnum<["start", "stop", "status"]>;
|
|
16
|
+
host: z.ZodOptional<z.ZodString>;
|
|
17
|
+
port: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
appIdentifier: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
action: "status" | "start" | "stop";
|
|
21
|
+
host?: string | undefined;
|
|
22
|
+
port?: number | undefined;
|
|
23
|
+
appIdentifier?: string | number | undefined;
|
|
24
|
+
}, {
|
|
25
|
+
action: "status" | "start" | "stop";
|
|
26
|
+
host?: string | undefined;
|
|
27
|
+
port?: number | undefined;
|
|
28
|
+
appIdentifier?: string | number | undefined;
|
|
29
|
+
}>;
|
|
30
|
+
export interface SessionInfo {
|
|
31
|
+
name: string;
|
|
32
|
+
identifier: string | null;
|
|
33
|
+
host: string;
|
|
34
|
+
port: number;
|
|
35
|
+
client: PluginClient;
|
|
36
|
+
connected: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Check if any session is currently active.
|
|
40
|
+
* @returns true if at least one session exists
|
|
41
|
+
*/
|
|
42
|
+
export declare function hasActiveSession(): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Get a specific session by port.
|
|
45
|
+
*/
|
|
46
|
+
export declare function getSession(port: number): SessionInfo | null;
|
|
47
|
+
/**
|
|
48
|
+
* Get the default session (most recently connected).
|
|
49
|
+
*/
|
|
50
|
+
export declare function getDefaultSession(): SessionInfo | null;
|
|
51
|
+
/**
|
|
52
|
+
* Get all active sessions.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getAllSessions(): SessionInfo[];
|
|
55
|
+
/**
|
|
56
|
+
* Resolve target app from port or identifier.
|
|
57
|
+
* Returns the appropriate session based on the routing logic.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveTargetApp(portOrIdentifier?: string | number): SessionInfo;
|
|
60
|
+
/**
|
|
61
|
+
* Manage session lifecycle (start, stop, or status).
|
|
62
|
+
*
|
|
63
|
+
* Connection strategy for 'start':
|
|
64
|
+
* 1. Try localhost:{port} first (most reliable for simulators/emulators/desktop)
|
|
65
|
+
* 2. If localhost fails AND a different host is configured, try {host}:{port}
|
|
66
|
+
* 3. If both fail, try auto-discovery on localhost
|
|
67
|
+
* 4. Return error if all attempts fail
|
|
68
|
+
*
|
|
69
|
+
* @param action - 'start', 'stop', or 'status'
|
|
70
|
+
* @param host - Optional host address (defaults to env var or localhost)
|
|
71
|
+
* @param port - Optional port number (defaults to 9223)
|
|
72
|
+
* @param appIdentifier - Optional app identifier for 'stop' action (port or bundle ID)
|
|
73
|
+
* @returns For 'start'/'stop': A message string describing the result.
|
|
74
|
+
* For 'status': A JSON string with connection details
|
|
75
|
+
*/
|
|
76
|
+
export declare function manageDriverSession(action: 'start' | 'stop' | 'status', host?: string, port?: number, appIdentifier?: string | number): Promise<string>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Ensures the MCP server is fully initialized and ready to use.
|
|
4
|
+
* This is called automatically by all tool functions.
|
|
5
|
+
*
|
|
6
|
+
* Initialization includes:
|
|
7
|
+
* - Verifying an active session exists (via driver_session)
|
|
8
|
+
* - Connecting to the plugin WebSocket using session config
|
|
9
|
+
* - Console capture is already initialized by bridge.js in the Tauri app
|
|
10
|
+
*
|
|
11
|
+
* This function is idempotent - calling it multiple times is safe.
|
|
12
|
+
*
|
|
13
|
+
* @throws Error if no session is active (driver_session must be called first)
|
|
14
|
+
*/
|
|
15
|
+
export declare function ensureReady(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Reset initialization state (useful for testing or reconnecting).
|
|
18
|
+
*/
|
|
19
|
+
export declare function resetInitialization(): void;
|
|
20
|
+
export interface ExecuteInWebviewResult {
|
|
21
|
+
result: string;
|
|
22
|
+
windowLabel: string;
|
|
23
|
+
warning?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Execute JavaScript in the Tauri webview using native IPC via WebSocket.
|
|
27
|
+
*
|
|
28
|
+
* @param script - JavaScript code to execute in the webview context
|
|
29
|
+
* @param windowId - Optional window label to target (defaults to "main")
|
|
30
|
+
* @param appIdentifier - Optional app identifier to target specific app
|
|
31
|
+
* @returns Result of the script execution with window context
|
|
32
|
+
*/
|
|
33
|
+
export declare function executeInWebview(script: string, windowId?: string, appIdentifier?: string | number): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Execute JavaScript in the Tauri webview and return window context.
|
|
36
|
+
*
|
|
37
|
+
* @param script - JavaScript code to execute in the webview context
|
|
38
|
+
* @param windowId - Optional window label to target (defaults to "main")
|
|
39
|
+
* @param appIdentifier - Optional app identifier to target specific app
|
|
40
|
+
* @returns Result of the script execution with window context
|
|
41
|
+
*/
|
|
42
|
+
export declare function executeInWebviewWithContext(script: string, windowId?: string, appIdentifier?: string | number): Promise<ExecuteInWebviewResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Execute async JavaScript in the webview with timeout support.
|
|
45
|
+
*
|
|
46
|
+
* @param script - JavaScript code to execute (can use await)
|
|
47
|
+
* @param windowId - Optional window label to target (defaults to "main")
|
|
48
|
+
* @param timeout - Timeout in milliseconds (default: 5000)
|
|
49
|
+
* @returns Result of the script execution
|
|
50
|
+
*/
|
|
51
|
+
export declare function executeAsyncInWebview(script: string, windowId?: string, timeout?: number): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Initialize console log capture in the webview.
|
|
54
|
+
* This intercepts console methods and stores logs in memory.
|
|
55
|
+
*
|
|
56
|
+
* NOTE: Console capture is now automatically initialized by bridge.js when the
|
|
57
|
+
* Tauri app starts. This function is kept for backwards compatibility and will
|
|
58
|
+
* simply return early if capture is already initialized.
|
|
59
|
+
*/
|
|
60
|
+
export declare function initializeConsoleCapture(): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* Retrieve captured console logs with optional filtering.
|
|
63
|
+
*
|
|
64
|
+
* @param filter - Optional regex pattern to filter log messages
|
|
65
|
+
* @param since - Optional ISO timestamp to filter logs after this time
|
|
66
|
+
* @param windowId - Optional window label to target (defaults to "main")
|
|
67
|
+
* @param appIdentifier - Optional app identifier to target specific app
|
|
68
|
+
* @returns Formatted console logs as string
|
|
69
|
+
*/
|
|
70
|
+
export declare function getConsoleLogs(filter?: string, since?: string, windowId?: string, appIdentifier?: string | number): Promise<string>;
|
|
71
|
+
/**
|
|
72
|
+
* Clear all captured console logs.
|
|
73
|
+
*/
|
|
74
|
+
export declare function clearConsoleLogs(): Promise<string>;
|
|
75
|
+
import type { ToolContent } from '../tools-registry.js';
|
|
76
|
+
/**
|
|
77
|
+
* Result of a screenshot capture, containing both image data and optional context.
|
|
78
|
+
*/
|
|
79
|
+
export interface ScreenshotResult {
|
|
80
|
+
content: ToolContent[];
|
|
81
|
+
}
|
|
82
|
+
export interface CaptureScreenshotOptions {
|
|
83
|
+
format?: 'png' | 'jpeg';
|
|
84
|
+
quality?: number;
|
|
85
|
+
windowId?: string;
|
|
86
|
+
appIdentifier?: string | number;
|
|
87
|
+
maxWidth?: number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Capture a screenshot of the entire webview.
|
|
91
|
+
*
|
|
92
|
+
* @param options - Screenshot options (format, quality, windowId, appIdentifier, etc.)
|
|
93
|
+
* @returns Screenshot result with image content
|
|
94
|
+
*/
|
|
95
|
+
export declare function captureScreenshot(options?: CaptureScreenshotOptions): Promise<ScreenshotResult>;
|
|
96
|
+
export declare const ExecuteScriptSchema: z.ZodObject<{
|
|
97
|
+
script: z.ZodString;
|
|
98
|
+
}, "strip", z.ZodTypeAny, {
|
|
99
|
+
script: string;
|
|
100
|
+
}, {
|
|
101
|
+
script: string;
|
|
102
|
+
}>;
|
|
103
|
+
export declare const GetConsoleLogsSchema: z.ZodObject<{
|
|
104
|
+
filter: z.ZodOptional<z.ZodString>;
|
|
105
|
+
since: z.ZodOptional<z.ZodString>;
|
|
106
|
+
}, "strip", z.ZodTypeAny, {
|
|
107
|
+
filter?: string | undefined;
|
|
108
|
+
since?: string | undefined;
|
|
109
|
+
}, {
|
|
110
|
+
filter?: string | undefined;
|
|
111
|
+
since?: string | undefined;
|
|
112
|
+
}>;
|
|
113
|
+
export declare const CaptureScreenshotSchema: z.ZodObject<{
|
|
114
|
+
format: z.ZodDefault<z.ZodOptional<z.ZodEnum<["png", "jpeg"]>>>;
|
|
115
|
+
quality: z.ZodOptional<z.ZodNumber>;
|
|
116
|
+
}, "strip", z.ZodTypeAny, {
|
|
117
|
+
format: "png" | "jpeg";
|
|
118
|
+
quality?: number | undefined;
|
|
119
|
+
}, {
|
|
120
|
+
format?: "png" | "jpeg" | undefined;
|
|
121
|
+
quality?: number | undefined;
|
|
122
|
+
}>;
|