flow-extension/popup.js
type-two fc3e668504 feat: Prompt Mode / DL Mode split in the popup
- DL Mode button: one click harvests everything on screen (click ⋮ -> hover
  Download -> click 1K, verified per tile), click again to stop after current
  tile; no queue involved, no server reporting for manual runs
- Prompt Mode = the existing queue runner, relabelled
- harvestActive synced to storage so the popup button reflects state

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:22:06 +10:00

181 lines
6.1 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 Prompt Mode';
toggleBtn.className = 'btn btn-primary stop-state';
} else {
toggleBtn.textContent = '▶ Prompt Mode';
toggleBtn.className = 'btn btn-primary start-state';
}
}
// DL Mode — one-shot harvest of everything on screen (1K originals), click again to stop.
const dlBtn = document.getElementById('dl-btn');
function updateDlUI(active) {
dlBtn.textContent = active ? '⏹ Stop DL' : '⬇ DL Mode';
dlBtn.className = 'btn btn-primary ' + (active ? 'stop-state' : 'start-state');
}
dlBtn.addEventListener('click', () => {
chrome.storage.local.get({ harvestActive: false }, (r) => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (!tabs[0]) return;
chrome.tabs.sendMessage(tabs[0].id, { type: r.harvestActive ? 'stopHarvest' : 'startHarvest' }, () => {
if (chrome.runtime.lastError) addSystemLog('DL mode: no Flow tab responding — open the Flow project tab first.');
});
});
});
});
chrome.storage.local.get({ harvestActive: false }, (r) => updateDlUI(r.harvestActive));
// 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.harvestActive) {
updateDlUI(changes.harvestActive.newValue);
}
if (changes.logs) {
// Re-render logs if they changed (e.g. from background content script)
renderLogs(changes.logs.newValue);
}
}
});