diff --git a/page-agent.user.js b/page-agent.user.js index 637bfc7..3875444 100644 --- a/page-agent.user.js +++ b/page-agent.user.js @@ -1,8 +1,8 @@ // ==UserScript== -// @name Page Agent — LLM in the page +// @name Page Agent - LLM in the page // @namespace https://github.com/example/page-agent // @version 0.1.0 -// @description Turn any web page into an LLM agent that can run JS in the page to inspect, extract, fetch and act — then bake what it learned into a standalone, LLM-free userscript. +// @description Turn any web page into an LLM agent that can run JS in the page to inspect, extract, fetch and act - then bake what it learned into a standalone, LLM-free userscript. // @match *://*/* // @run-at document-idle // @grant GM.xmlHttpRequest @@ -41,12 +41,12 @@ const maxSteps = 8; // upper bound on the agent's tool-use loop // The panel starts fully hidden. Reveal it with: - // Desktop — Ctrl+Shift+Space (see HOTKEY) - // Mobile — three-finger tap anywhere on the page + // Desktop - Ctrl+Shift+Space (see HOTKEY) + // Mobile - three-finger tap anywhere on the page const HOTKEY = { ctrl: true, shift: true, alt: false, code: 'Space', label: 'Ctrl+Shift+Space' }; // ============================================================ - // safeStringify — serialize arbitrary values without blowing up + // safeStringify - serialize arbitrary values without blowing up // ============================================================ function safeStringify(value, maxLen = 20000) { const seen = new WeakSet(); @@ -70,7 +70,7 @@ if (out === undefined) out = String(value); // undefined coercion if (out.length > maxLen) { const total = out.length; - out = out.slice(0, maxLen) + '…[truncated, ' + total + ' chars]'; + out = out.slice(0, maxLen) + '...[truncated, ' + total + ' chars]'; } return out; } @@ -86,7 +86,7 @@ 'Execute JavaScript in the context of the current web page. Your code runs ' + 'as the BODY of an async function, so you can use `await` and you MUST `return` ' + 'a value to see any result. The returned value is JSON-stringified and sent back ' + - 'to you, so keep it SMALL and RELEVANT — return only the specific fields/text you ' + + 'to you, so keep it SMALL and RELEVANT - return only the specific fields/text you ' + 'need, never whole DOM trees or huge arrays. Use it to query selectors, extract ' + 'content, call fetch(), read/modify the page, click, fill and navigate. Take ' + 'several small targeted steps rather than one giant snippet.', @@ -190,9 +190,9 @@ args = { code: '' }; } const code = args.code || ''; - log('» run_js:\n' + code); + log('>> run_js:\n' + code); const result = await runJs(code); - log('« result: ' + result.slice(0, 500)); + log('<< result: ' + result.slice(0, 500)); messages.push({ role: 'tool', tool_call_id: tc.id, content: result }); } } @@ -201,7 +201,7 @@ } // ============================================================ - // UI — floating panel (bottom-right, dark), hidden by default + // UI - floating panel (bottom-right, dark), hidden by default // ============================================================ const PANEL_ID = 'llm-page-agent-panel'; if (document.getElementById(PANEL_ID)) return; // avoid double-inject @@ -254,15 +254,15 @@ panel.className = 'pa-hidden'; // hidden until revealed panel.innerHTML = '
' + - '🤖 Page Agent' + - '' + - '' + - '' + + '[AI] Page Agent' + + '' + + '' + + '' + '
' + '
' + '
' + '
' + - '' + + '' + '' + '
' + '
' + @@ -290,7 +290,7 @@ } // ============================================================ - // Visibility — hidden by default; toggle via hotkey / gesture + // Visibility - hidden by default; toggle via hotkey / gesture // ============================================================ let hintShown = false; function isHidden() { return panel.classList.contains('pa-hidden'); } @@ -335,7 +335,7 @@ sendBtn.dataset.label = sendBtn.textContent; sendBtn.innerHTML = ''; statusEl = document.createElement('div'); - statusEl.innerHTML = ' ' + (label || 'thinking…'); + statusEl.innerHTML = ' ' + (label || 'thinking...'); logEl.appendChild(statusEl); logEl.scrollTop = logEl.scrollHeight; } else { @@ -353,13 +353,13 @@ async function run(request) { if (busy) return; - if (!CONFIG.apiKey) { append('⚠ No API key set. Click ⚙ to configure.'); return; } - append('› ' + request); - setBusy(true, 'thinking…'); + if (!CONFIG.apiKey) { append('! No API key set. Click [cfg] to configure.'); return; } + append('> ' + request); + setBusy(true, 'thinking...'); try { const { answer, messages } = await agent(request, append, conversation); conversation = messages; // persist for later turns - append('🤖 ' + answer); + append('[AI] ' + answer); } catch (e) { append('ERROR: ' + (e && e.message ? e.message : String(e))); } finally { @@ -375,7 +375,7 @@ } // ============================================================ - // Convert / Download — bake the session into a standalone userscript + // Convert / Download - bake the session into a standalone userscript // ============================================================ const CONVERT_PROMPT = 'Now convert EVERYTHING you learned and did in this session into a single, ' + @@ -385,7 +385,7 @@ 'Include a complete ==UserScript== metadata header (@name, @namespace, @version, ' + '@description, an appropriate @match, @run-at and any @grant/@connect you need). ' + 'The script must be self-contained and idempotent. Output ONLY the finished ' + - 'userscript inside a single ' + BT3 + 'javascript code block — no explanation ' + + 'userscript inside a single ' + BT3 + 'javascript code block - no explanation ' + 'before or after.'; function extractCode(text) { @@ -436,12 +436,12 @@ async function convert() { if (busy) return; - if (!conversation) { append('⚠ Nothing to convert yet — run the agent first.'); return; } - setBusy(true, 'converting…'); + if (!conversation) { append('! Nothing to convert yet - run the agent first.'); return; } + setBusy(true, 'converting...'); try { const code = await generateUserscript(); await copyToClipboard(code); - append('✅ Userscript generated & copied to clipboard (' + code.length + ' chars). Use “Download .user.js” to save it.'); + append('OK Userscript generated & copied to clipboard (' + code.length + ' chars). Use "Download .user.js" to save it.'); } catch (e) { append('ERROR: ' + (e && e.message ? e.message : String(e))); } finally { @@ -451,12 +451,12 @@ async function download() { if (busy) return; - if (!conversation) { append('⚠ Nothing to convert yet — run the agent first.'); return; } - setBusy(true, 'preparing download…'); + if (!conversation) { append('! Nothing to convert yet - run the agent first.'); return; } + setBusy(true, 'preparing download...'); try { const code = lastCode || await generateUserscript(); saveFile(code, 'page-agent-generated.user.js'); - append('⬇ Saved page-agent-generated.user.js (' + code.length + ' chars).'); + append('v Saved page-agent-generated.user.js (' + code.length + ' chars).'); } catch (e) { append('ERROR: ' + (e && e.message ? e.message : String(e))); } finally { @@ -474,7 +474,7 @@ if (key !== null) { CONFIG.apiKey = key.trim(); await GM.setValue('apiKey', CONFIG.apiKey); } const mdl = prompt('Model:', CONFIG.model); if (mdl !== null) { CONFIG.model = mdl.trim() || DEFAULTS.model; await GM.setValue('model', CONFIG.model); } - append('⚙ Settings saved — endpoint=' + CONFIG.endpoint + ' model=' + CONFIG.model + ' key=' + (CONFIG.apiKey ? '(set)' : '(empty)')); + append('[cfg] Settings saved - endpoint=' + CONFIG.endpoint + ' model=' + CONFIG.model + ' key=' + (CONFIG.apiKey ? '(set)' : '(empty)')); } // ============================================================ @@ -488,11 +488,11 @@ hideBtn.addEventListener('click', hidePanel); collapseBtn.addEventListener('click', () => { const collapsed = panel.classList.toggle('pa-collapsed'); - collapseBtn.textContent = collapsed ? '▢' : '—'; + collapseBtn.textContent = collapsed ? '[ ]' : '-'; collapseBtn.title = collapsed ? 'Expand' : 'Collapse'; }); - // Desktop reveal/hide — global hotkey (Ctrl+Shift+Space by default). + // Desktop reveal/hide - global hotkey (Ctrl+Shift+Space by default). window.addEventListener('keydown', (e) => { if (!!e.ctrlKey === HOTKEY.ctrl && !!e.shiftKey === HOTKEY.shift && !!e.altKey === HOTKEY.alt && e.code === HOTKEY.code) { @@ -503,7 +503,7 @@ } }, true); - // Mobile reveal/hide — three-finger tap anywhere on the page. + // Mobile reveal/hide - three-finger tap anywhere on the page. window.addEventListener('touchstart', (e) => { if (e.touches && e.touches.length === 3) { e.preventDefault(); @@ -511,6 +511,6 @@ } }, { capture: true, passive: false }); - append('Ready. ' + (CONFIG.apiKey ? '' : 'Set your API key via ⚙ first. ') + 'Ask me to inspect or act on this page.'); - showToast('🤖 Page Agent loaded — ' + HOTKEY.label + ' (desktop) or three-finger tap (mobile) to open.'); + append('Ready. ' + (CONFIG.apiKey ? '' : 'Set your API key via [cfg] first. ') + 'Ask me to inspect or act on this page.'); + showToast('[AI] Page Agent loaded - ' + HOTKEY.label + ' (desktop) or three-finger tap (mobile) to open.'); })();