Bots now announce every click and typing step before it runs, and the web app draws a cursor in the bot's colour that glides to that point over the live computer screen, ripples on clicks and shows typing. - contracts: ComputerPointerPayload on the existing thread.computer event - executor: computer_act and browser_act announce pointer steps (best effort); the page-browser helper reports element screen boxes, which are stripped before results reach the model - web: agentPointerFeed + AgentCursor over the side-panel preview and the full-window screen, mapped through the noVNC letterbox - in-app browser: native WebContentsView on desktop (own session, http(s) only, clamped bounds, back/forward, reload, DevTools, load errors) and a sandboxed iframe on the web; replaces the dead panel, the fake 50%/40% cursors and the unimplemented devServer bridge Claude-Session: https://claude.ai/code/session_01WiGooXtsKrZQmdszepRrNi Co-authored-by: Claude <noreply@anthropic.com>
51 lines
2.3 KiB
JavaScript
51 lines
2.3 KiB
JavaScript
const { contextBridge, ipcRenderer } = require("electron");
|
|
|
|
contextBridge.exposeInMainWorld("rakazoDesktop", {
|
|
platform: process.platform,
|
|
files: {
|
|
pickDirectory: () => ipcRenderer.invoke("desktop.files.pickDirectory"),
|
|
},
|
|
localSettings: {
|
|
request: (pathname, body) =>
|
|
ipcRenderer.invoke("desktop.localSettings.request", pathname, body),
|
|
},
|
|
window: {
|
|
close: () => ipcRenderer.invoke("desktop.window.close"),
|
|
minimize: () => ipcRenderer.invoke("desktop.window.minimize"),
|
|
toggleMaximize: () => ipcRenderer.invoke("desktop.window.toggleMaximize"),
|
|
state: () => ipcRenderer.invoke("desktop.window.state"),
|
|
},
|
|
update: {
|
|
state: () => ipcRenderer.invoke("desktop.update.state"),
|
|
check: () => ipcRenderer.invoke("desktop.update.check"),
|
|
download: () => ipcRenderer.invoke("desktop.update.download"),
|
|
install: () => ipcRenderer.invoke("desktop.update.install"),
|
|
},
|
|
browserPreview: {
|
|
state: () => ipcRenderer.invoke("desktop.browserPreview.state"),
|
|
show: (bounds) => ipcRenderer.invoke("desktop.browserPreview.show", bounds),
|
|
hide: () => ipcRenderer.invoke("desktop.browserPreview.hide"),
|
|
navigate: (url) => ipcRenderer.invoke("desktop.browserPreview.navigate", url),
|
|
back: () => ipcRenderer.invoke("desktop.browserPreview.back"),
|
|
forward: () => ipcRenderer.invoke("desktop.browserPreview.forward"),
|
|
reload: () => ipcRenderer.invoke("desktop.browserPreview.reload"),
|
|
stop: () => ipcRenderer.invoke("desktop.browserPreview.stop"),
|
|
openDevTools: () => ipcRenderer.invoke("desktop.browserPreview.openDevTools"),
|
|
onState: (listener) => {
|
|
const handler = (_event, state) => listener(state);
|
|
ipcRenderer.on("desktop.browserPreview.state", handler);
|
|
return () => ipcRenderer.off("desktop.browserPreview.state", handler);
|
|
},
|
|
},
|
|
oauth: {
|
|
open: (url) => ipcRenderer.invoke("desktop.oauth.open", url),
|
|
cancel: (url) => ipcRenderer.invoke("desktop.oauth.cancel", url),
|
|
onCallback: (listener) => {
|
|
// The IpcRendererEvent stays in the preload: the renderer only sees the code.
|
|
const handler = (_event, callback) => listener(callback);
|
|
ipcRenderer.on("desktop.oauth.callback", handler);
|
|
return () => ipcRenderer.off("desktop.oauth.callback", handler);
|
|
},
|
|
},
|
|
});
|