mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-14 00:12:12 +02:00
feat: cleanup + screenshot buttons in chat toolbar (not just inspector)
Quick actions toolbar (🧹 Cleanup, 📸 Screenshot) now appears above the chat input, always visible. Both inspector and chat buttons share runCleanup() and runScreenshot() helper functions. Clicking either set shows loading state on both simultaneously. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -590,6 +590,59 @@ body::after {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Command Bar ─────────────────────────────────────── */
|
/* ─── Command Bar ─────────────────────────────────────── */
|
||||||
|
/* ─── Quick Actions Toolbar ─────────────────────────────── */
|
||||||
|
|
||||||
|
.quick-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
background: var(--bg-surface);
|
||||||
|
border-top: 1px solid var(--border-subtle);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-action-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
height: 26px;
|
||||||
|
padding: 0 10px;
|
||||||
|
background: none;
|
||||||
|
border: 1px solid var(--zinc-600);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
color: var(--text-label);
|
||||||
|
font-family: var(--font-system);
|
||||||
|
font-size: 11px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 150ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-action-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
color: var(--text-body);
|
||||||
|
border-color: var(--zinc-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-action-btn:active {
|
||||||
|
transform: scale(0.96);
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-action-btn.loading {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quick-action-btn.loading::after {
|
||||||
|
content: '';
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border: 2px solid var(--zinc-600);
|
||||||
|
border-top-color: var(--amber-400);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.6s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.command-bar {
|
.command-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -136,6 +136,12 @@
|
|||||||
Browser co-pilot — controls this browser, reports back to your workspace
|
Browser co-pilot — controls this browser, reports back to your workspace
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Quick Actions Toolbar -->
|
||||||
|
<div class="quick-actions" id="quick-actions">
|
||||||
|
<button id="chat-cleanup-btn" class="quick-action-btn" title="Remove ads, banners, popups">🧹 Cleanup</button>
|
||||||
|
<button id="chat-screenshot-btn" class="quick-action-btn" title="Take a screenshot">📸 Screenshot</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Command Bar -->
|
<!-- Command Bar -->
|
||||||
<div class="command-bar">
|
<div class="command-bar">
|
||||||
<button class="stop-btn" id="stop-agent-btn" title="Stop agent" style="display: none;">■</button>
|
<button class="stop-btn" id="stop-agent-btn" title="Stop agent" style="display: none;">■</button>
|
||||||
|
|||||||
+65
-64
@@ -1149,73 +1149,74 @@ inspectorSendBtn.addEventListener('click', () => {
|
|||||||
chrome.runtime.sendMessage({ type: 'sidebar-command', message });
|
chrome.runtime.sendMessage({ type: 'sidebar-command', message });
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─── Cleanup Button ─────────────────────────────────────────────
|
// ─── Quick Action Helpers (shared between chat toolbar + inspector) ──
|
||||||
|
|
||||||
|
async function runCleanup(...buttons) {
|
||||||
|
if (!serverUrl || !serverToken) {
|
||||||
|
addChatEntry({ type: 'notification', message: 'Not connected to browse server' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buttons.forEach(b => b?.classList.add('loading'));
|
||||||
|
try {
|
||||||
|
const resp = await fetch(`${serverUrl}/command`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { ...authHeaders(), 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ command: 'cleanup', args: ['--all'] }),
|
||||||
|
signal: AbortSignal.timeout(15000),
|
||||||
|
});
|
||||||
|
const text = await resp.text();
|
||||||
|
if (resp.ok) {
|
||||||
|
addChatEntry({ type: 'notification', message: text || 'Page cleaned up' });
|
||||||
|
if (typeof inspectorShowEmpty === 'function') inspectorShowEmpty();
|
||||||
|
} else {
|
||||||
|
const err = JSON.parse(text).error || 'Cleanup failed';
|
||||||
|
addChatEntry({ type: 'notification', message: 'Error: ' + err });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
addChatEntry({ type: 'notification', message: 'Cleanup failed: ' + err.message });
|
||||||
|
} finally {
|
||||||
|
buttons.forEach(b => b?.classList.remove('loading'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runScreenshot(...buttons) {
|
||||||
|
if (!serverUrl || !serverToken) {
|
||||||
|
addChatEntry({ type: 'notification', message: 'Not connected to browse server' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buttons.forEach(b => b?.classList.add('loading'));
|
||||||
|
try {
|
||||||
|
const resp = await fetch(`${serverUrl}/command`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { ...authHeaders(), 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ command: 'screenshot', args: [] }),
|
||||||
|
signal: AbortSignal.timeout(15000),
|
||||||
|
});
|
||||||
|
const text = await resp.text();
|
||||||
|
if (resp.ok) {
|
||||||
|
addChatEntry({ type: 'notification', message: text || 'Screenshot saved' });
|
||||||
|
} else {
|
||||||
|
const err = JSON.parse(text).error || 'Screenshot failed';
|
||||||
|
addChatEntry({ type: 'notification', message: 'Error: ' + err });
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
addChatEntry({ type: 'notification', message: 'Screenshot failed: ' + err.message });
|
||||||
|
} finally {
|
||||||
|
buttons.forEach(b => b?.classList.remove('loading'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Wire up all cleanup/screenshot buttons (inspector + chat toolbar) ──
|
||||||
|
|
||||||
const inspectorCleanupBtn = document.getElementById('inspector-cleanup-btn');
|
const inspectorCleanupBtn = document.getElementById('inspector-cleanup-btn');
|
||||||
if (inspectorCleanupBtn) {
|
|
||||||
inspectorCleanupBtn.addEventListener('click', async () => {
|
|
||||||
if (inspectorCleanupBtn.classList.contains('loading')) return;
|
|
||||||
if (!serverUrl || !serverToken) {
|
|
||||||
addChatEntry({ type: 'notification', message: 'Not connected to browse server' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inspectorCleanupBtn.classList.add('loading');
|
|
||||||
try {
|
|
||||||
const resp = await fetch(`${serverUrl}/command`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { ...authHeaders(), 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ command: 'cleanup', args: ['--all'] }),
|
|
||||||
signal: AbortSignal.timeout(15000),
|
|
||||||
});
|
|
||||||
const text = await resp.text();
|
|
||||||
if (resp.ok) {
|
|
||||||
addChatEntry({ type: 'notification', message: text || 'Page cleaned up' });
|
|
||||||
// Reset inspector — selected element may have been removed
|
|
||||||
inspectorShowEmpty();
|
|
||||||
} else {
|
|
||||||
const err = JSON.parse(text).error || 'Cleanup failed';
|
|
||||||
addChatEntry({ type: 'notification', message: 'Error: ' + err });
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
addChatEntry({ type: 'notification', message: 'Cleanup failed: ' + err.message });
|
|
||||||
} finally {
|
|
||||||
inspectorCleanupBtn.classList.remove('loading');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Screenshot Button ──────────────────────────────────────────
|
|
||||||
|
|
||||||
const inspectorScreenshotBtn = document.getElementById('inspector-screenshot-btn');
|
const inspectorScreenshotBtn = document.getElementById('inspector-screenshot-btn');
|
||||||
if (inspectorScreenshotBtn) {
|
const chatCleanupBtn = document.getElementById('chat-cleanup-btn');
|
||||||
inspectorScreenshotBtn.addEventListener('click', async () => {
|
const chatScreenshotBtn = document.getElementById('chat-screenshot-btn');
|
||||||
if (inspectorScreenshotBtn.classList.contains('loading')) return;
|
|
||||||
if (!serverUrl || !serverToken) {
|
if (inspectorCleanupBtn) inspectorCleanupBtn.addEventListener('click', () => runCleanup(inspectorCleanupBtn, chatCleanupBtn));
|
||||||
addChatEntry({ type: 'notification', message: 'Not connected to browse server' });
|
if (inspectorScreenshotBtn) inspectorScreenshotBtn.addEventListener('click', () => runScreenshot(inspectorScreenshotBtn, chatScreenshotBtn));
|
||||||
return;
|
if (chatCleanupBtn) chatCleanupBtn.addEventListener('click', () => runCleanup(chatCleanupBtn, inspectorCleanupBtn));
|
||||||
}
|
if (chatScreenshotBtn) chatScreenshotBtn.addEventListener('click', () => runScreenshot(chatScreenshotBtn, inspectorScreenshotBtn));
|
||||||
inspectorScreenshotBtn.classList.add('loading');
|
|
||||||
try {
|
|
||||||
const resp = await fetch(`${serverUrl}/command`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { ...authHeaders(), 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ command: 'screenshot', args: [] }),
|
|
||||||
signal: AbortSignal.timeout(15000),
|
|
||||||
});
|
|
||||||
const text = await resp.text();
|
|
||||||
if (resp.ok) {
|
|
||||||
addChatEntry({ type: 'notification', message: text || 'Screenshot saved' });
|
|
||||||
} else {
|
|
||||||
const err = JSON.parse(text).error || 'Screenshot failed';
|
|
||||||
addChatEntry({ type: 'notification', message: 'Error: ' + err });
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
addChatEntry({ type: 'notification', message: 'Screenshot failed: ' + err.message });
|
|
||||||
} finally {
|
|
||||||
inspectorScreenshotBtn.classList.remove('loading');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ─── Section Toggles ────────────────────────────────────────────
|
// ─── Section Toggles ────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user