Update src/ai-repo-commander.user.js

1. Two Helper Methods (added to DebugConsole class):

flashBtn(btn, label, ms) - Temporarily disables button, shows checkmark feedback
toast(msg, ms) - Shows temporary toast notification in bottom-right of panel

2. Enhanced All Button Click Handlers:

Copy buttons - Flash "Copied ✓" and show toast
Pause/Resume - Flash "Paused ✓" or "Resumed ✓" with toast
STOP API - Flash "Stopped ✓" with toast
Clear History - Flash "Cleared ✓" with toast
Toggles & Number inputs - Show inline toast feedback
Save/Reset Config - Flash button and show toast
Bridge Key buttons - Flash and show toast feedback
Run Again buttons - Flash "Running ✓" or "Dismissed ✓"

3. Version Bump:

Updated from v1.5.1 to v1.5.2
This commit is contained in:
rob 2025-10-09 18:34:45 +00:00
parent f341ffb39e
commit 3981fc0528
1 changed files with 78 additions and 25 deletions

View File

@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name AI Repo Commander // @name AI Repo Commander
// @namespace http://tampermonkey.net/ // @namespace http://tampermonkey.net/
// @version 1.5.1 // @version 1.5.2
// @description Execute @bridge@ YAML commands from AI assistants (safe & robust): complete-block detection, streaming-settle, persistent dedupe, paste+autosubmit, debug console with Tools/Settings, draggable/collapsible panel // @description Execute @bridge@ YAML commands from AI assistants (safe & robust): complete-block detection, streaming-settle, persistent dedupe, paste+autosubmit, debug console with Tools/Settings, draggable/collapsible panel
// @author Your Name // @author Your Name
// @match https://chat.openai.com/* // @match https://chat.openai.com/*
@ -36,7 +36,7 @@
// Timing & API // Timing & API
DEBOUNCE_DELAY: 3000, DEBOUNCE_DELAY: 3000,
MAX_RETRIES: 2, MAX_RETRIES: 2,
VERSION: '1.5.1', VERSION: '1.5.2',
API_TIMEOUT_MS: 60000, API_TIMEOUT_MS: 60000,
PROCESS_EXISTING: false, PROCESS_EXISTING: false,
@ -227,6 +227,43 @@
if (this.panel) this._renderRow(entry); if (this.panel) this._renderRow(entry);
} }
_renderRow(e) {
if (!this.bodyLogs) return;
const row = document.createElement('div');
row.style.cssText = 'padding:4px 0;border-bottom:1px dashed #2a2a34;white-space:pre-wrap;word-break:break-word;';
row.textContent = `${e.ts} ${e.level.padEnd(5)} ${e.msg}${e.data? ' ' + JSON.stringify(e.data): ''}`;
this.bodyLogs.appendChild(row);
while (this.bodyLogs.children.length > this.cfg.DEBUG_MAX_LINES) this.bodyLogs.firstChild.remove();
this.bodyLogs.scrollTop = this.bodyLogs.scrollHeight;
}
flashBtn(btn, label = 'Done', ms = 900) {
if (!btn) return;
const old = btn.textContent;
btn.disabled = true;
btn.textContent = `${label}`;
btn.style.opacity = '0.7';
setTimeout(() => {
btn.disabled = false;
btn.textContent = old;
btn.style.opacity = '';
}, ms);
}
toast(msg, ms = 1200) {
if (!this.panel) return;
const t = document.createElement('div');
t.textContent = msg;
t.style.cssText = `
position:absolute; right:12px; bottom:12px; padding:6px 10px;
background:#111827; color:#e5e7eb; border:1px solid #374151;
border-radius:6px; font:12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
opacity:.98; pointer-events:none; box-shadow:0 6px 20px rgba(0,0,0,.35)
`;
this.panel.appendChild(t);
setTimeout(() => t.remove(), ms);
}
mount() { mount() {
if (!document.body) { setTimeout(() => this.mount(), 100); return; } if (!document.body) { setTimeout(() => this.mount(), 100); return; }
@ -341,8 +378,17 @@
sel.value = String(this.cfg.DEBUG_LEVEL); sel.value = String(this.cfg.DEBUG_LEVEL);
sel.addEventListener('change', () => this.setLevel(parseInt(sel.value,10))); sel.addEventListener('change', () => this.setLevel(parseInt(sel.value,10)));
root.querySelector('.rc-copy').addEventListener('click', () => this.copyLast(50)); root.querySelector('.rc-copy').addEventListener('click', (e) => {
root.querySelector('.rc-copy-200').addEventListener('click', () => this.copyLast(200)); this.copyLast(50);
this.flashBtn(e.currentTarget, 'Copied');
this.toast('Copied last 50 logs');
});
root.querySelector('.rc-copy-200').addEventListener('click', (e) => {
this.copyLast(200);
this.flashBtn(e.currentTarget, 'Copied');
this.toast('Copied last 200 logs');
});
const pauseBtn = root.querySelector('.rc-pause'); const pauseBtn = root.querySelector('.rc-pause');
pauseBtn.addEventListener('click', () => { pauseBtn.addEventListener('click', () => {
@ -351,11 +397,15 @@
pauseBtn.textContent = this.cfg.RUNTIME.PAUSED ? 'Resume' : 'Pause'; pauseBtn.textContent = this.cfg.RUNTIME.PAUSED ? 'Resume' : 'Pause';
pauseBtn.style.background = this.cfg.RUNTIME.PAUSED ? '#f59e0b' : ''; pauseBtn.style.background = this.cfg.RUNTIME.PAUSED ? '#f59e0b' : '';
pauseBtn.style.color = this.cfg.RUNTIME.PAUSED ? '#111827' : ''; pauseBtn.style.color = this.cfg.RUNTIME.PAUSED ? '#111827' : '';
this.flashBtn(pauseBtn, this.cfg.RUNTIME.PAUSED ? 'Paused' : 'Resumed');
this.toast(this.cfg.RUNTIME.PAUSED ? 'Paused scanning' : 'Resumed scanning');
this.info(`Runtime ${this.cfg.RUNTIME.PAUSED ? 'paused' : 'resumed'}`); this.info(`Runtime ${this.cfg.RUNTIME.PAUSED ? 'paused' : 'resumed'}`);
}); });
root.querySelector('.rc-stop').addEventListener('click', () => { root.querySelector('.rc-stop').addEventListener('click', (e) => {
window.AI_REPO_STOP?.(); window.AI_REPO_STOP?.();
this.flashBtn(e.currentTarget, 'Stopped');
this.toast('Emergency STOP activated');
this.warn('Emergency STOP activated'); this.warn('Emergency STOP activated');
}); });
@ -428,15 +478,17 @@
}; };
// Tools: Clear History // Tools: Clear History
root.querySelector('.rc-clear-history').addEventListener('click', () => { root.querySelector('.rc-clear-history').addEventListener('click', (e) => {
try { try {
commandMonitor?.history?.resetAll?.(); commandMonitor?.history?.resetAll?.();
RC_DEBUG?.info('Conversation history cleared'); RC_DEBUG?.info('Conversation history cleared');
GM_notification({ title: 'AI Repo Commander', text: 'This conversation\'s execution marks cleared', timeout: 2500 }); GM_notification({ title: 'AI Repo Commander', text: 'Execution marks cleared', timeout: 2500 });
} catch { } catch {
localStorage.removeItem(STORAGE_KEYS.history); localStorage.removeItem(STORAGE_KEYS.history);
RC_DEBUG?.info('Legacy history key cleared'); RC_DEBUG?.info('Legacy history key cleared');
} }
this.flashBtn(e.currentTarget, 'Cleared');
this.toast('Conversation marks cleared');
}); });
// Tools: toggles & numbers // Tools: toggles & numbers
@ -446,6 +498,7 @@
inp.addEventListener('change', () => { inp.addEventListener('change', () => {
this.cfg[key] = !!inp.checked; this.cfg[key] = !!inp.checked;
saveConfig(this.cfg); saveConfig(this.cfg);
this.toast(`${key} = ${this.cfg[key] ? 'on' : 'off'}`);
this.info(`Config ${key} => ${this.cfg[key]}`); this.info(`Config ${key} => ${this.cfg[key]}`);
}); });
}); });
@ -456,13 +509,14 @@
if (!Number.isNaN(v)) { if (!Number.isNaN(v)) {
this.cfg[inp.dataset.key] = v; this.cfg[inp.dataset.key] = v;
saveConfig(this.cfg); saveConfig(this.cfg);
this.toast(`${inp.dataset.key} = ${v}`);
this.info(`Config ${inp.dataset.key} => ${v}`); this.info(`Config ${inp.dataset.key} => ${v}`);
} }
}); });
}); });
// Tools: JSON input // Tools: JSON input
root.querySelector('.rc-save-json').addEventListener('click', () => { root.querySelector('.rc-save-json').addEventListener('click', (e) => {
try { try {
const raw = root.querySelector('.rc-json').value; const raw = root.querySelector('.rc-json').value;
const parsed = JSON.parse(raw); const parsed = JSON.parse(raw);
@ -483,18 +537,23 @@
if (dump.BRIDGE_KEY) dump.BRIDGE_KEY = '•'.repeat(8); if (dump.BRIDGE_KEY) dump.BRIDGE_KEY = '•'.repeat(8);
root.querySelector('.rc-json').value = JSON.stringify(dump, null, 2); root.querySelector('.rc-json').value = JSON.stringify(dump, null, 2);
this.flashBtn(e.currentTarget, 'Saved');
this.toast('Config saved');
this.info('Config JSON saved'); this.info('Config JSON saved');
} catch (e) { } catch (err) {
this.warn('Invalid JSON in config textarea', { error: String(e) }); this.toast('Invalid JSON', 1500);
this.warn('Invalid JSON in config textarea', { error: String(err) });
} }
}); });
root.querySelector('.rc-reset-defaults').addEventListener('click', () => { root.querySelector('.rc-reset-defaults').addEventListener('click', (e) => {
Object.assign(this.cfg, structuredClone(DEFAULT_CONFIG)); Object.assign(this.cfg, structuredClone(DEFAULT_CONFIG));
saveConfig(this.cfg); saveConfig(this.cfg);
BRIDGE_KEY = null; BRIDGE_KEY = null;
const bridgeKeyInput = root.querySelector('.rc-bridge-key'); const bridgeKeyInput = root.querySelector('.rc-bridge-key');
if (bridgeKeyInput) bridgeKeyInput.value = ''; if (bridgeKeyInput) bridgeKeyInput.value = '';
this.flashBtn(e.currentTarget, 'Reset');
this.toast('Defaults restored');
this.info('Config reset to defaults'); this.info('Config reset to defaults');
}); });
@ -509,7 +568,7 @@
const bridgeKeyInput = root.querySelector('.rc-bridge-key'); const bridgeKeyInput = root.querySelector('.rc-bridge-key');
bridgeKeyInput.value = this.cfg.BRIDGE_KEY ? '•'.repeat(8) : ''; bridgeKeyInput.value = this.cfg.BRIDGE_KEY ? '•'.repeat(8) : '';
root.querySelector('.rc-save-bridge-key').addEventListener('click', () => { root.querySelector('.rc-save-bridge-key').addEventListener('click', (e) => {
const raw = (bridgeKeyInput.value || '').trim(); const raw = (bridgeKeyInput.value || '').trim();
if (/^•+$/.test(raw)) { if (/^•+$/.test(raw)) {
this.info('Bridge key unchanged'); this.info('Bridge key unchanged');
@ -520,30 +579,24 @@
saveConfig(this.cfg); saveConfig(this.cfg);
BRIDGE_KEY = raw || null; BRIDGE_KEY = raw || null;
bridgeKeyInput.value = this.cfg.BRIDGE_KEY ? '•'.repeat(8) : ''; bridgeKeyInput.value = this.cfg.BRIDGE_KEY ? '•'.repeat(8) : '';
this.flashBtn(e.currentTarget, 'Saved');
this.toast('Bridge key saved');
this.info('Bridge key saved (masked)'); this.info('Bridge key saved (masked)');
GM_notification({ title: 'AI Repo Commander', text: 'Bridge key saved', timeout: 2500 }); GM_notification({ title: 'AI Repo Commander', text: 'Bridge key saved', timeout: 2500 });
}); });
root.querySelector('.rc-clear-bridge-key').addEventListener('click', () => { root.querySelector('.rc-clear-bridge-key').addEventListener('click', (e) => {
this.cfg.BRIDGE_KEY = ''; this.cfg.BRIDGE_KEY = '';
bridgeKeyInput.value = ''; bridgeKeyInput.value = '';
saveConfig(this.cfg); saveConfig(this.cfg);
BRIDGE_KEY = null; BRIDGE_KEY = null;
this.flashBtn(e.currentTarget, 'Cleared');
this.toast('Bridge key cleared');
this.info('Bridge key cleared'); this.info('Bridge key cleared');
GM_notification({ title: 'AI Repo Commander', text: 'Bridge key cleared', timeout: 2500 }); GM_notification({ title: 'AI Repo Commander', text: 'Bridge key cleared', timeout: 2500 });
}); });
} }
_renderRow(e) {
if (!this.bodyLogs) return;
const row = document.createElement('div');
row.style.cssText = 'padding:4px 0;border-bottom:1px dashed #2a2a34;white-space:pre-wrap;word-break:break-word;';
row.textContent = `${e.ts} ${e.level.padEnd(5)} ${e.msg}${e.data? ' ' + JSON.stringify(e.data): ''}`;
this.bodyLogs.appendChild(row);
while (this.bodyLogs.children.length > this.cfg.DEBUG_MAX_LINES) this.bodyLogs.firstChild.remove();
this.bodyLogs.scrollTop = this.bodyLogs.scrollHeight;
}
destroy() { destroy() {
try { clearInterval(this.loopCleanupInterval); } catch {} try { clearInterval(this.loopCleanupInterval); } catch {}
if (this.panel && this.panel.parentNode) this.panel.parentNode.removeChild(this.panel); if (this.panel && this.panel.parentNode) this.panel.parentNode.removeChild(this.panel);
@ -912,8 +965,8 @@
const dismiss = document.createElement('button'); const dismiss = document.createElement('button');
dismiss.textContent = 'Dismiss'; dismiss.textContent = 'Dismiss';
dismiss.style.cssText = 'padding:4px 10px; border:1px solid #374151; border-radius:4px; background:#111827; color:#9ca3af;'; dismiss.style.cssText = 'padding:4px 10px; border:1px solid #374151; border-radius:4px; background:#111827; color:#9ca3af;';
run.onclick = onRun; run.onclick = (ev) => { RC_DEBUG?.flashBtn?.(ev.currentTarget, 'Running'); onRun(); };
dismiss.onclick = () => bar.remove(); dismiss.onclick = (ev) => { RC_DEBUG?.flashBtn?.(ev.currentTarget, 'Dismissed'); bar.remove(); };
bar.append(msg, run, dismiss); bar.append(msg, run, dismiss);
containerEl.appendChild(bar); containerEl.appendChild(bar);
} }