Some checks are pending
CI / build-check-test (push) Waiting to run
- Fix empty state overlay covering chat input (bottom:130px offset) - Fix suggestion chips click-through with corrected z-layering - Fix handleSuggestion to use chatPanel.agentInterface.setInput() - Add JaeTerminalPanel: xterm.js + WebSocket bash shell (port 7701) - Add JaeBrowserPanel: Playwright chromium screenshots (port 7702) - Add terminal/browser toggle buttons in header toolbar - Add collapsible right panel with Terminal/Browser tabs - Add server/terminal-server.mjs and server/browser-server.mjs - Add npm run dev:all script (Vite + terminal + browser servers)
65 lines
2.4 KiB
JavaScript
65 lines
2.4 KiB
JavaScript
import { WebSocketServer } from 'ws';
|
|
import { chromium } from 'playwright';
|
|
|
|
const PORT = 7702;
|
|
const wss = new WebSocketServer({ port: PORT });
|
|
console.log(`Browser WS server on ws://localhost:${PORT}`);
|
|
|
|
let browser = null;
|
|
|
|
async function getBrowser() {
|
|
if (!browser) browser = await chromium.launch({ headless: true, args: ['--no-sandbox','--disable-setuid-sandbox'] });
|
|
return browser;
|
|
}
|
|
|
|
wss.on('connection', async (ws) => {
|
|
let context = null;
|
|
let page = null;
|
|
|
|
async function screenshot() {
|
|
if (!page) return;
|
|
try {
|
|
const buf = await page.screenshot({ type: 'jpeg', quality: 70, fullPage: false });
|
|
ws.send(JSON.stringify({ type: 'screenshot', data: buf.toString('base64'), url: page.url() }));
|
|
} catch(e) { ws.send(JSON.stringify({ type: 'error', msg: String(e) })); }
|
|
}
|
|
|
|
async function navigate(url) {
|
|
try {
|
|
if (!context) {
|
|
const b = await getBrowser();
|
|
context = await b.newContext({ viewport: { width: 1280, height: 800 } });
|
|
page = await context.newPage();
|
|
}
|
|
if (!url.startsWith('http')) url = 'https://' + url;
|
|
ws.send(JSON.stringify({ type: 'loading' }));
|
|
await page.goto(url, { timeout: 30000, waitUntil: 'domcontentloaded' });
|
|
await screenshot();
|
|
} catch(e) { ws.send(JSON.stringify({ type: 'error', msg: String(e) })); }
|
|
}
|
|
|
|
ws.on('message', async (msg) => {
|
|
try {
|
|
const m = JSON.parse(msg.toString());
|
|
if (m.type === 'navigate') await navigate(m.url);
|
|
if (m.type === 'screenshot') await screenshot();
|
|
if (m.type === 'click') {
|
|
if (page) { await page.mouse.click(m.x, m.y); await screenshot(); }
|
|
}
|
|
if (m.type === 'scroll') {
|
|
if (page) { await page.mouse.wheel(0, m.dy); await screenshot(); }
|
|
}
|
|
if (m.type === 'type') {
|
|
if (page) { await page.keyboard.type(m.text); await screenshot(); }
|
|
}
|
|
if (m.type === 'back') { if (page) { await page.goBack(); await screenshot(); } }
|
|
if (m.type === 'fwd') { if (page) { await page.goForward(); await screenshot(); } }
|
|
if (m.type === 'reload'){ if (page) { await page.reload(); await screenshot(); } }
|
|
} catch(e) { ws.send(JSON.stringify({ type: 'error', msg: String(e) })); }
|
|
});
|
|
|
|
ws.on('close', async () => { if (context) await context.close().catch(()=>{}); context = null; page = null; });
|
|
|
|
// Send welcome screenshot placeholder
|
|
ws.send(JSON.stringify({ type: 'ready' }));
|
|
});
|