- background.js: chrome.downloads via page session (no CSP/base64) - content.js: fast-fail on Flow error banner, image vs video timeouts, autoStart - local_server_example.py: /enqueue + /save, downloads/ output, resume-skip - launch.sh: idempotent server-up + caffeinate + single-tab focus - install.sh + plist: self-healing agent every 10 min (TCC-safe App Support copy) - flowclient.py: enqueue/wait_for helper for game projects - README: full setup + macOS TCC gotcha Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
160 lines
5.2 KiB
JavaScript
160 lines
5.2 KiB
JavaScript
// Default Settings
|
|
const DEFAULT_SETTINGS = {
|
|
serverUrl: 'http://localhost:8017',
|
|
isRunning: false,
|
|
promptSel: '',
|
|
submitSel: '',
|
|
resultSel: '',
|
|
autoStart: false,
|
|
pollInterval: 4000,
|
|
logs: []
|
|
};
|
|
|
|
// UI Elements
|
|
const serverUrlInput = document.getElementById('server-url');
|
|
const toggleBtn = document.getElementById('toggle-btn');
|
|
const globalStatus = document.getElementById('global-status');
|
|
const statusLabel = globalStatus.querySelector('.status-label');
|
|
const logConsole = document.getElementById('log-console');
|
|
const clearLogsBtn = document.getElementById('clear-logs');
|
|
const autoStartInput = document.getElementById('auto-start');
|
|
|
|
// Advanced inputs
|
|
const selPromptInput = document.getElementById('sel-prompt');
|
|
const selGenerateInput = document.getElementById('sel-generate');
|
|
const selModelInput = document.getElementById('sel-model');
|
|
const pollIntervalInput = document.getElementById('poll-interval');
|
|
|
|
// Load settings on startup
|
|
chrome.storage.local.get(DEFAULT_SETTINGS, (settings) => {
|
|
serverUrlInput.value = settings.serverUrl;
|
|
autoStartInput.checked = !!settings.autoStart;
|
|
selPromptInput.value = settings.promptSel;
|
|
selGenerateInput.value = settings.submitSel;
|
|
selModelInput.value = settings.resultSel;
|
|
pollIntervalInput.value = settings.pollInterval;
|
|
|
|
updateToggleUI(settings.isRunning);
|
|
renderLogs(settings.logs);
|
|
updateStatusUI(settings.isRunning ? 'running' : 'idle');
|
|
});
|
|
|
|
// Helper: Append log to UI console
|
|
function appendLogToUI(log) {
|
|
const entry = document.createElement('div');
|
|
entry.className = `log-entry ${log.level}-log`;
|
|
|
|
const time = new Date(log.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
entry.innerText = `[${time}] ${log.text}`;
|
|
|
|
logConsole.appendChild(entry);
|
|
logConsole.scrollTop = logConsole.scrollHeight;
|
|
}
|
|
|
|
// Render entire log list
|
|
function renderLogs(logs) {
|
|
logConsole.innerHTML = '';
|
|
if (!logs || logs.length === 0) {
|
|
const entry = document.createElement('div');
|
|
entry.className = 'log-entry system-log';
|
|
entry.innerText = '[System] No logs yet. Click Start to run.';
|
|
logConsole.appendChild(entry);
|
|
return;
|
|
}
|
|
logs.forEach(appendLogToUI);
|
|
}
|
|
|
|
// Update Toggle Button Style
|
|
function updateToggleUI(isRunning) {
|
|
if (isRunning) {
|
|
toggleBtn.textContent = 'Stop Automation';
|
|
toggleBtn.className = 'btn btn-primary stop-state';
|
|
} else {
|
|
toggleBtn.textContent = 'Start Automation';
|
|
toggleBtn.className = 'btn btn-primary start-state';
|
|
}
|
|
}
|
|
|
|
// Update Global Status indicator
|
|
function updateStatusUI(status) {
|
|
globalStatus.className = 'status-indicator';
|
|
if (status === 'running') {
|
|
globalStatus.classList.add('active');
|
|
statusLabel.textContent = 'Running';
|
|
} else if (status === 'idle') {
|
|
statusLabel.textContent = 'Idle';
|
|
} else if (status === 'error') {
|
|
globalStatus.classList.add('error');
|
|
statusLabel.textContent = 'Error';
|
|
} else if (status === 'working') {
|
|
globalStatus.classList.add('active');
|
|
statusLabel.textContent = 'Generating...';
|
|
}
|
|
}
|
|
|
|
// Input Change Handlers
|
|
function saveSetting(key, value) {
|
|
chrome.storage.local.set({ [key]: value });
|
|
}
|
|
|
|
serverUrlInput.addEventListener('change', (e) => saveSetting('serverUrl', e.target.value));
|
|
autoStartInput.addEventListener('change', (e) => saveSetting('autoStart', e.target.checked));
|
|
selPromptInput.addEventListener('change', (e) => saveSetting('promptSel', e.target.value));
|
|
selGenerateInput.addEventListener('change', (e) => saveSetting('submitSel', e.target.value));
|
|
selModelInput.addEventListener('change', (e) => saveSetting('resultSel', e.target.value));
|
|
pollIntervalInput.addEventListener('change', (e) => saveSetting('pollInterval', parseInt(e.target.value, 10) || 4000));
|
|
|
|
// Clear Logs
|
|
clearLogsBtn.addEventListener('click', () => {
|
|
chrome.storage.local.set({ logs: [] }, () => {
|
|
logConsole.innerHTML = '';
|
|
});
|
|
});
|
|
|
|
// Toggle Run Loop
|
|
toggleBtn.addEventListener('click', () => {
|
|
chrome.storage.local.get('isRunning', (res) => {
|
|
const nextState = !res.isRunning;
|
|
chrome.storage.local.set({ isRunning: nextState }, () => {
|
|
updateToggleUI(nextState);
|
|
updateStatusUI(nextState ? 'running' : 'idle');
|
|
|
|
// Log event
|
|
const logMsg = nextState ? 'Automation started.' : 'Automation stopped.';
|
|
addSystemLog(logMsg);
|
|
});
|
|
});
|
|
});
|
|
|
|
// Add System Log Helper
|
|
function addSystemLog(text) {
|
|
chrome.storage.local.get({ logs: [] }, (res) => {
|
|
const newLog = {
|
|
level: 'system',
|
|
text: text,
|
|
timestamp: Date.now()
|
|
};
|
|
const updated = [...res.logs, newLog].slice(-100); // limit to 100 logs
|
|
chrome.storage.local.set({ logs: updated }, () => {
|
|
appendLogToUI(newLog);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Listen for storage changes to sync UI across multiple popup openings
|
|
chrome.storage.onChanged.addListener((changes, area) => {
|
|
if (area === 'local') {
|
|
if (changes.isRunning) {
|
|
updateToggleUI(changes.isRunning.newValue);
|
|
updateStatusUI(changes.isRunning.newValue ? 'running' : 'idle');
|
|
}
|
|
if (changes.status) {
|
|
updateStatusUI(changes.status.newValue);
|
|
}
|
|
if (changes.logs) {
|
|
// Re-render logs if they changed (e.g. from background content script)
|
|
renderLogs(changes.logs.newValue);
|
|
}
|
|
}
|
|
});
|