1297 lines
52 KiB
JavaScript
1297 lines
52 KiB
JavaScript
document.addEventListener('DOMContentLoaded', function() {
|
|
const closeSettingsBtn = document.getElementById('closeSettings');
|
|
const advancedModeToggle = document.getElementById('advancedModeToggle');
|
|
const webhookUrlInput = document.getElementById('webhookUrl');
|
|
const webhookPayloadInput = document.getElementById('webhookPayload');
|
|
const debugModeToggle = document.getElementById('debugModeToggle');
|
|
const webhookStatus = document.getElementById('webhookStatus');
|
|
const testWebhookBtn = document.getElementById('testWebhook');
|
|
const mcpServerUrlInput = document.getElementById('mcpServerUrl');
|
|
const mcpApiKeyInput = document.getElementById('mcpApiKey');
|
|
const toggleMcpKeyBtn = document.getElementById('toggleMcpKey');
|
|
const mcpStatus = document.getElementById('mcpStatus');
|
|
const testMcpBtn = document.getElementById('testMcp');
|
|
const cloudProviderInput = document.getElementById('cloudProvider');
|
|
const cloudEndpointInput = document.getElementById('cloudEndpoint');
|
|
const cloudStatus = document.getElementById('cloudStatus');
|
|
const testCloudBtn = document.getElementById('testCloud');
|
|
const gcalToolNameInput = document.getElementById('gcalToolName');
|
|
const gcalCalendarIdInput = document.getElementById('gcalCalendarId');
|
|
const gcalMaxResultsInput = document.getElementById('gcalMaxResults');
|
|
const gcalStatus = document.getElementById('gcalStatus');
|
|
const fetchCalendarBtn = document.getElementById('fetchCalendar');
|
|
const gcalOutput = document.getElementById('gcalOutput');
|
|
const refreshMcpToolsBtn = document.getElementById('refreshMcpTools');
|
|
const mcpToolsStatus = document.getElementById('mcpToolsStatus');
|
|
const mcpToolSelect = document.getElementById('mcpToolSelect');
|
|
const mcpToolArgs = document.getElementById('mcpToolArgs');
|
|
const runMcpToolBtn = document.getElementById('runMcpTool');
|
|
const mcpToolOutput = document.getElementById('mcpToolOutput');
|
|
const presetNameInput = document.getElementById('presetName');
|
|
const presetDescriptionInput = document.getElementById('presetDescription');
|
|
const presetTagsInput = document.getElementById('presetTags');
|
|
const presetToolNameInput = document.getElementById('presetToolName');
|
|
const presetArgsInput = document.getElementById('presetArgs');
|
|
const savePresetBtn = document.getElementById('savePreset');
|
|
const clearPresetBtn = document.getElementById('clearPreset');
|
|
const presetStatus = document.getElementById('presetStatus');
|
|
const presetSelect = document.getElementById('presetSelect');
|
|
const runPresetBtn = document.getElementById('runPreset');
|
|
const deletePresetBtn = document.getElementById('deletePreset');
|
|
const presetOutput = document.getElementById('presetOutput');
|
|
|
|
const automationList = document.getElementById('automationList');
|
|
const addAutomationBtn = document.getElementById('addAutomation');
|
|
const automationEditor = document.getElementById('automationEditor');
|
|
const automationEditorStatus = document.getElementById('automationEditorStatus');
|
|
const automationNameInput = document.getElementById('automationName');
|
|
const automationTypeSelect = document.getElementById('automationType');
|
|
const automationEnabledToggle = document.getElementById('automationEnabled');
|
|
const automationTriggerStart = document.getElementById('automationTriggerStart');
|
|
const automationTriggerEnd = document.getElementById('automationTriggerEnd');
|
|
const automationTriggerManual = document.getElementById('automationTriggerManual');
|
|
const automationRequireApproval = document.getElementById('automationRequireApproval');
|
|
const automationActionsSection = document.getElementById('automationActionsSection');
|
|
const automationStandupSection = document.getElementById('automationStandupSection');
|
|
const automationActionLabel = document.getElementById('automationActionLabel');
|
|
const automationActionType = document.getElementById('automationActionType');
|
|
const automationActionMcpFields = document.getElementById('automationActionMcpFields');
|
|
const automationActionWebhookFields = document.getElementById('automationActionWebhookFields');
|
|
const automationActionTool = document.getElementById('automationActionTool');
|
|
const automationActionArgs = document.getElementById('automationActionArgs');
|
|
const automationActionWebhookUrl = document.getElementById('automationActionWebhookUrl');
|
|
const automationActionWebhookMethod = document.getElementById('automationActionWebhookMethod');
|
|
const automationActionWebhookHeaders = document.getElementById('automationActionWebhookHeaders');
|
|
const automationActionWebhookBody = document.getElementById('automationActionWebhookBody');
|
|
const automationActionWebhookRetryCount = document.getElementById('automationActionWebhookRetryCount');
|
|
const addAutomationActionBtn = document.getElementById('addAutomationAction');
|
|
const automationActionSelect = document.getElementById('automationActionSelect');
|
|
const removeAutomationActionBtn = document.getElementById('removeAutomationAction');
|
|
const standupDiscordTool = document.getElementById('standupDiscordTool');
|
|
const standupDiscordArgs = document.getElementById('standupDiscordArgs');
|
|
const standupNextcloudTool = document.getElementById('standupNextcloudTool');
|
|
const standupNextcloudArgs = document.getElementById('standupNextcloudArgs');
|
|
const saveAutomationBtn = document.getElementById('saveAutomation');
|
|
const testAutomationBtn = document.getElementById('testAutomation');
|
|
const runAutomationNowBtn = document.getElementById('runAutomationNow');
|
|
const deleteAutomationBtn = document.getElementById('deleteAutomation');
|
|
const automationRunStatus = document.getElementById('automationRunStatus');
|
|
const automationOutput = document.getElementById('automationOutput');
|
|
const placeholderStatus = document.getElementById('placeholderStatus');
|
|
|
|
const DEFAULT_SETTINGS = {
|
|
advancedMode: false,
|
|
debugMode: false,
|
|
webhookUrl: '',
|
|
webhookPayload: '',
|
|
mcpServerUrl: '',
|
|
mcpApiKey: '',
|
|
cloudProvider: '',
|
|
cloudEndpoint: '',
|
|
gcalToolName: '',
|
|
gcalCalendarId: 'primary',
|
|
gcalMaxResults: 5,
|
|
customToolPresets: [],
|
|
automations: []
|
|
};
|
|
|
|
let activeAutomationId = null;
|
|
let activeAutomationActionId = '';
|
|
let lastFocusedInput = null;
|
|
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const settings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}) };
|
|
if (advancedModeToggle) advancedModeToggle.checked = Boolean(settings.advancedMode);
|
|
if (debugModeToggle) debugModeToggle.checked = Boolean(settings.debugMode);
|
|
if (webhookUrlInput) webhookUrlInput.value = settings.webhookUrl || '';
|
|
if (webhookPayloadInput) webhookPayloadInput.value = settings.webhookPayload || '';
|
|
if (mcpServerUrlInput) mcpServerUrlInput.value = settings.mcpServerUrl || '';
|
|
if (mcpApiKeyInput) mcpApiKeyInput.value = settings.mcpApiKey || '';
|
|
if (cloudProviderInput) cloudProviderInput.value = settings.cloudProvider || '';
|
|
if (cloudEndpointInput) cloudEndpointInput.value = settings.cloudEndpoint || '';
|
|
if (gcalToolNameInput) gcalToolNameInput.value = settings.gcalToolName || '';
|
|
if (gcalCalendarIdInput) gcalCalendarIdInput.value = settings.gcalCalendarId || 'primary';
|
|
if (gcalMaxResultsInput) gcalMaxResultsInput.value = settings.gcalMaxResults || 5;
|
|
updatePresetSelect(settings.customToolPresets || []);
|
|
hydrateAutomations(settings.automations || []);
|
|
});
|
|
|
|
function saveSettings(partial) {
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const nextSettings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}), ...partial };
|
|
chrome.storage.sync.set({ advancedSettings: nextSettings });
|
|
});
|
|
}
|
|
|
|
function isValidUrl(value) {
|
|
try {
|
|
const url = new URL(value);
|
|
return Boolean(url.protocol && url.hostname);
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function setStatus(el, text, type) {
|
|
if (!el) return;
|
|
el.textContent = text;
|
|
el.className = 'status-message';
|
|
if (type) el.classList.add(type);
|
|
}
|
|
|
|
function setOutput(el, text) {
|
|
if (!el) return;
|
|
el.textContent = text || '';
|
|
}
|
|
|
|
function setPlaceholderStatus(text, type) {
|
|
if (!placeholderStatus) return;
|
|
placeholderStatus.textContent = text;
|
|
placeholderStatus.className = 'status-message';
|
|
if (type) placeholderStatus.classList.add(type);
|
|
}
|
|
|
|
function updatePresetSelect(presets) {
|
|
if (!presetSelect) return;
|
|
presetSelect.innerHTML = '';
|
|
if (!presets || presets.length === 0) {
|
|
const option = document.createElement('option');
|
|
option.value = '';
|
|
option.textContent = 'No presets saved';
|
|
presetSelect.appendChild(option);
|
|
return;
|
|
}
|
|
presets.forEach((preset) => {
|
|
const option = document.createElement('option');
|
|
option.value = preset.id;
|
|
option.textContent = preset.name;
|
|
presetSelect.appendChild(option);
|
|
});
|
|
}
|
|
|
|
function hydrateAutomations(automations) {
|
|
renderAutomationList(automations);
|
|
if (!activeAutomationId && automations.length > 0) {
|
|
activeAutomationId = automations[0].id;
|
|
}
|
|
renderAutomationEditor(automations);
|
|
}
|
|
|
|
function renderAutomationList(automations) {
|
|
if (!automationList) return;
|
|
automationList.innerHTML = '';
|
|
if (!automations.length) {
|
|
const empty = document.createElement('div');
|
|
empty.className = 'status-message';
|
|
empty.textContent = 'No automations yet.';
|
|
automationList.appendChild(empty);
|
|
return;
|
|
}
|
|
automations.forEach((automation) => {
|
|
const item = document.createElement('button');
|
|
item.type = 'button';
|
|
item.className = `automation-item${automation.id === activeAutomationId ? ' active' : ''}`;
|
|
item.textContent = automation.name || 'Untitled Automation';
|
|
item.addEventListener('click', () => {
|
|
activeAutomationId = automation.id;
|
|
renderAutomationList(automations);
|
|
renderAutomationEditor(automations);
|
|
});
|
|
automationList.appendChild(item);
|
|
});
|
|
}
|
|
|
|
function renderAutomationEditor(automations) {
|
|
if (!automationEditor || !automationEditorStatus) return;
|
|
const automation = automations.find((item) => item.id === activeAutomationId);
|
|
if (!automation) {
|
|
automationEditor.style.display = 'none';
|
|
automationEditorStatus.textContent = 'Select an automation to edit.';
|
|
return;
|
|
}
|
|
automationEditor.style.display = 'block';
|
|
automationEditorStatus.textContent = '';
|
|
if (automationNameInput) automationNameInput.value = automation.name || '';
|
|
if (automationTypeSelect) automationTypeSelect.value = automation.kind || 'actions';
|
|
if (automationEnabledToggle) automationEnabledToggle.checked = Boolean(automation.enabled);
|
|
if (automationTriggerStart) automationTriggerStart.checked = Boolean(automation.triggers?.sessionStart);
|
|
if (automationTriggerEnd) automationTriggerEnd.checked = Boolean(automation.triggers?.sessionEnd);
|
|
if (automationTriggerManual) automationTriggerManual.checked = Boolean(automation.triggers?.manual);
|
|
if (automationRequireApproval) automationRequireApproval.checked = Boolean(automation.requireApproval);
|
|
|
|
const isStandup = automation.kind === 'standup';
|
|
if (automationActionsSection) automationActionsSection.style.display = isStandup ? 'none' : 'block';
|
|
if (automationStandupSection) automationStandupSection.style.display = isStandup ? 'block' : 'none';
|
|
|
|
if (isStandup) {
|
|
if (standupDiscordTool) standupDiscordTool.value = automation.standup?.discordToolName || '';
|
|
if (standupDiscordArgs) standupDiscordArgs.value = automation.standup?.discordArgsTemplate || '';
|
|
if (standupNextcloudTool) standupNextcloudTool.value = automation.standup?.nextcloudToolName || '';
|
|
if (standupNextcloudArgs) standupNextcloudArgs.value = automation.standup?.nextcloudArgsTemplate || '';
|
|
activeAutomationActionId = '';
|
|
clearAutomationActionForm();
|
|
} else {
|
|
updateAutomationActionSelect(automation.actions || []);
|
|
populateAutomationActionEditor(automation.actions || []);
|
|
}
|
|
}
|
|
|
|
function updateAutomationActionSelect(actions) {
|
|
if (!automationActionSelect) return;
|
|
automationActionSelect.innerHTML = '';
|
|
if (!actions || actions.length === 0) {
|
|
activeAutomationActionId = '';
|
|
const option = document.createElement('option');
|
|
option.value = '';
|
|
option.textContent = 'No actions configured';
|
|
automationActionSelect.appendChild(option);
|
|
return;
|
|
}
|
|
actions.forEach((action) => {
|
|
const option = document.createElement('option');
|
|
option.value = action.id;
|
|
const typeLabel = action.type === 'webhook' ? 'Webhook' : 'MCP';
|
|
option.textContent = `${action.label} (${typeLabel})`;
|
|
automationActionSelect.appendChild(option);
|
|
});
|
|
const selectedStillExists = actions.some((action) => action.id === activeAutomationActionId);
|
|
activeAutomationActionId = selectedStillExists ? activeAutomationActionId : actions[0].id;
|
|
automationActionSelect.value = activeAutomationActionId;
|
|
}
|
|
|
|
function populateAutomationActionEditor(actions) {
|
|
if (!actions || !actions.length || !automationActionSelect) {
|
|
clearAutomationActionForm();
|
|
return;
|
|
}
|
|
|
|
const selectedId = automationActionSelect.value || activeAutomationActionId;
|
|
const action = actions.find((item) => item.id === selectedId) || actions[0];
|
|
if (!action) {
|
|
clearAutomationActionForm();
|
|
return;
|
|
}
|
|
|
|
activeAutomationActionId = action.id;
|
|
if (automationActionSelect.value !== action.id) {
|
|
automationActionSelect.value = action.id;
|
|
}
|
|
|
|
if (automationActionLabel) automationActionLabel.value = action.label || '';
|
|
if (automationActionType) automationActionType.value = action.type || 'mcp';
|
|
if (action.type === 'webhook') {
|
|
if (automationActionWebhookUrl) automationActionWebhookUrl.value = action.webhookUrl || '';
|
|
if (automationActionWebhookMethod) automationActionWebhookMethod.value = action.method || 'POST';
|
|
if (automationActionWebhookHeaders) automationActionWebhookHeaders.value = JSON.stringify(action.headers || {}, null, 2);
|
|
if (automationActionWebhookBody) automationActionWebhookBody.value = action.bodyTemplate || '';
|
|
if (automationActionWebhookRetryCount) automationActionWebhookRetryCount.value = String(action.retryCount ?? 0);
|
|
if (automationActionTool) automationActionTool.value = '';
|
|
if (automationActionArgs) automationActionArgs.value = '';
|
|
} else {
|
|
if (automationActionTool) automationActionTool.value = action.toolName || '';
|
|
if (automationActionArgs) automationActionArgs.value = JSON.stringify(action.args || {}, null, 2);
|
|
if (automationActionWebhookUrl) automationActionWebhookUrl.value = '';
|
|
if (automationActionWebhookMethod) automationActionWebhookMethod.value = 'POST';
|
|
if (automationActionWebhookHeaders) automationActionWebhookHeaders.value = '';
|
|
if (automationActionWebhookBody) automationActionWebhookBody.value = '';
|
|
if (automationActionWebhookRetryCount) automationActionWebhookRetryCount.value = '0';
|
|
}
|
|
updateActionTypeUI();
|
|
}
|
|
|
|
function updateAutomation(automationId, updater) {
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const settings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}) };
|
|
const automations = Array.isArray(settings.automations) ? settings.automations : [];
|
|
const index = automations.findIndex((item) => item.id === automationId);
|
|
if (index === -1) return;
|
|
const updated = updater(automations[index]);
|
|
automations[index] = updated;
|
|
settings.automations = automations;
|
|
chrome.storage.sync.set({ advancedSettings: settings }, () => {
|
|
hydrateAutomations(automations);
|
|
});
|
|
});
|
|
}
|
|
|
|
function persistCurrentAutomationFromForm() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => {
|
|
const next = { ...automation };
|
|
if (automationNameInput) next.name = automationNameInput.value.trim();
|
|
if (automationTypeSelect) next.kind = automationTypeSelect.value;
|
|
if (automationEnabledToggle) next.enabled = automationEnabledToggle.checked;
|
|
next.triggers = {
|
|
sessionStart: Boolean(automationTriggerStart?.checked),
|
|
sessionEnd: Boolean(automationTriggerEnd?.checked),
|
|
manual: Boolean(automationTriggerManual?.checked)
|
|
};
|
|
next.requireApproval = Boolean(automationRequireApproval?.checked);
|
|
if (next.kind === 'standup') {
|
|
next.standup = {
|
|
discordToolName: standupDiscordTool?.value.trim() || '',
|
|
discordArgsTemplate: standupDiscordArgs?.value || '',
|
|
nextcloudToolName: standupNextcloudTool?.value.trim() || '',
|
|
nextcloudArgsTemplate: standupNextcloudArgs?.value || ''
|
|
};
|
|
}
|
|
return next;
|
|
});
|
|
}
|
|
|
|
function persistSelectedAutomationActionFromForm() {
|
|
if (!activeAutomationId || !activeAutomationActionId) return;
|
|
updateAutomation(activeAutomationId, (automation) => {
|
|
const actions = Array.isArray(automation.actions) ? [...automation.actions] : [];
|
|
const index = actions.findIndex((action) => action.id === activeAutomationActionId);
|
|
if (index === -1) return automation;
|
|
|
|
const existing = actions[index];
|
|
const type = automationActionType ? automationActionType.value : existing.type || 'mcp';
|
|
const label = automationActionLabel ? automationActionLabel.value.trim() : existing.label || '';
|
|
|
|
if (type === 'webhook') {
|
|
let headers = existing.headers || {};
|
|
if (automationActionWebhookHeaders) {
|
|
const parsedHeaders = readJsonArgs(
|
|
automationActionWebhookHeaders.value || '',
|
|
automationRunStatus,
|
|
'Webhook headers must be valid JSON.'
|
|
);
|
|
if (parsedHeaders !== null) {
|
|
headers = parsedHeaders;
|
|
}
|
|
}
|
|
|
|
const retryRaw = Number(automationActionWebhookRetryCount ? automationActionWebhookRetryCount.value : existing.retryCount || 0);
|
|
const retryCount = Number.isFinite(retryRaw) ? Math.max(0, Math.min(5, Math.floor(retryRaw))) : 0;
|
|
|
|
actions[index] = {
|
|
...existing,
|
|
type: 'webhook',
|
|
label,
|
|
webhookUrl: automationActionWebhookUrl ? automationActionWebhookUrl.value.trim() : existing.webhookUrl || '',
|
|
method: automationActionWebhookMethod ? automationActionWebhookMethod.value : existing.method || 'POST',
|
|
headers,
|
|
bodyTemplate: automationActionWebhookBody ? automationActionWebhookBody.value : existing.bodyTemplate || '',
|
|
retryCount
|
|
};
|
|
} else {
|
|
let args = existing.args || {};
|
|
if (automationActionArgs) {
|
|
const parsedArgs = readJsonArgs(
|
|
automationActionArgs.value || '',
|
|
automationRunStatus,
|
|
'Action args must be valid JSON.'
|
|
);
|
|
if (parsedArgs !== null) {
|
|
args = parsedArgs;
|
|
}
|
|
}
|
|
|
|
actions[index] = {
|
|
...existing,
|
|
type: 'mcp',
|
|
label,
|
|
toolName: automationActionTool ? automationActionTool.value.trim() : existing.toolName || '',
|
|
args
|
|
};
|
|
}
|
|
|
|
return { ...automation, actions };
|
|
});
|
|
}
|
|
|
|
function readJsonArgs(text, statusEl, errorMessage) {
|
|
if (!text.trim()) return {};
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch (error) {
|
|
setStatus(statusEl, errorMessage, 'error');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function validateJsonField(inputEl, statusEl, label) {
|
|
if (!inputEl) return;
|
|
const value = inputEl.value.trim();
|
|
if (!value) {
|
|
setStatus(statusEl, '', '');
|
|
return;
|
|
}
|
|
try {
|
|
JSON.parse(value);
|
|
setStatus(statusEl, `${label} JSON is valid.`, 'success');
|
|
} catch (error) {
|
|
setStatus(statusEl, `${label} JSON is invalid.`, 'error');
|
|
}
|
|
}
|
|
|
|
function updateActionTypeUI() {
|
|
const type = automationActionType ? automationActionType.value : 'mcp';
|
|
if (automationActionMcpFields) automationActionMcpFields.style.display = type === 'mcp' ? 'block' : 'none';
|
|
if (automationActionWebhookFields) automationActionWebhookFields.style.display = type === 'webhook' ? 'block' : 'none';
|
|
}
|
|
|
|
function clearAutomationActionForm() {
|
|
if (automationActionLabel) automationActionLabel.value = '';
|
|
if (automationActionType) automationActionType.value = 'mcp';
|
|
if (automationActionTool) automationActionTool.value = '';
|
|
if (automationActionArgs) automationActionArgs.value = '';
|
|
if (automationActionWebhookUrl) automationActionWebhookUrl.value = '';
|
|
if (automationActionWebhookMethod) automationActionWebhookMethod.value = 'POST';
|
|
if (automationActionWebhookHeaders) automationActionWebhookHeaders.value = '';
|
|
if (automationActionWebhookBody) automationActionWebhookBody.value = '';
|
|
if (automationActionWebhookRetryCount) automationActionWebhookRetryCount.value = '0';
|
|
updateActionTypeUI();
|
|
}
|
|
|
|
if (advancedModeToggle) {
|
|
advancedModeToggle.addEventListener('change', function() {
|
|
saveSettings({ advancedMode: this.checked });
|
|
});
|
|
}
|
|
|
|
if (debugModeToggle) {
|
|
debugModeToggle.addEventListener('change', function() {
|
|
saveSettings({ debugMode: this.checked });
|
|
});
|
|
}
|
|
|
|
if (webhookUrlInput) {
|
|
webhookUrlInput.addEventListener('input', function() {
|
|
saveSettings({ webhookUrl: this.value.trim() });
|
|
if (this.value.trim() && !isValidUrl(this.value.trim())) {
|
|
setStatus(webhookStatus, 'Invalid URL format.', 'error');
|
|
} else {
|
|
setStatus(webhookStatus, '', '');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (webhookPayloadInput) {
|
|
webhookPayloadInput.addEventListener('input', function() {
|
|
saveSettings({ webhookPayload: this.value });
|
|
validateJsonField(webhookPayloadInput, webhookStatus, 'Webhook payload');
|
|
});
|
|
}
|
|
|
|
if (mcpServerUrlInput) {
|
|
mcpServerUrlInput.addEventListener('input', function() {
|
|
saveSettings({ mcpServerUrl: this.value.trim() });
|
|
if (this.value.trim() && !isValidUrl(this.value.trim())) {
|
|
setStatus(mcpStatus, 'Invalid URL format.', 'error');
|
|
} else {
|
|
setStatus(mcpStatus, '', '');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (mcpApiKeyInput) {
|
|
mcpApiKeyInput.addEventListener('input', function() {
|
|
saveSettings({ mcpApiKey: this.value.trim() });
|
|
});
|
|
}
|
|
|
|
if (toggleMcpKeyBtn && mcpApiKeyInput) {
|
|
toggleMcpKeyBtn.addEventListener('click', function() {
|
|
const isHidden = mcpApiKeyInput.type === 'password';
|
|
mcpApiKeyInput.type = isHidden ? 'text' : 'password';
|
|
toggleMcpKeyBtn.textContent = isHidden ? 'Hide' : 'Show';
|
|
});
|
|
}
|
|
|
|
if (cloudProviderInput) {
|
|
cloudProviderInput.addEventListener('input', function() {
|
|
saveSettings({ cloudProvider: this.value.trim() });
|
|
});
|
|
}
|
|
|
|
if (cloudEndpointInput) {
|
|
cloudEndpointInput.addEventListener('input', function() {
|
|
saveSettings({ cloudEndpoint: this.value.trim() });
|
|
if (this.value.trim() && !isValidUrl(this.value.trim())) {
|
|
setStatus(cloudStatus, 'Invalid URL format.', 'error');
|
|
} else {
|
|
setStatus(cloudStatus, '', '');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (gcalToolNameInput) {
|
|
gcalToolNameInput.addEventListener('input', function() {
|
|
saveSettings({ gcalToolName: this.value.trim() });
|
|
});
|
|
}
|
|
|
|
if (gcalCalendarIdInput) {
|
|
gcalCalendarIdInput.addEventListener('input', function() {
|
|
saveSettings({ gcalCalendarId: this.value.trim() || 'primary' });
|
|
});
|
|
}
|
|
|
|
if (gcalMaxResultsInput) {
|
|
gcalMaxResultsInput.addEventListener('input', function() {
|
|
const parsed = Number(this.value);
|
|
saveSettings({ gcalMaxResults: Number.isFinite(parsed) && parsed > 0 ? parsed : 5 });
|
|
});
|
|
}
|
|
|
|
if (testWebhookBtn) {
|
|
testWebhookBtn.addEventListener('click', async function() {
|
|
const url = webhookUrlInput ? webhookUrlInput.value.trim() : '';
|
|
if (!isValidUrl(url)) {
|
|
setStatus(webhookStatus, 'Enter a valid webhook URL first.', 'error');
|
|
return;
|
|
}
|
|
setStatus(webhookStatus, 'Sending test payload...', '');
|
|
try {
|
|
let payload = {
|
|
type: 'test',
|
|
source: 'ai-assistant',
|
|
timestamp: new Date().toISOString()
|
|
};
|
|
if (webhookPayloadInput && webhookPayloadInput.value.trim()) {
|
|
payload = JSON.parse(webhookPayloadInput.value);
|
|
}
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
const debugEnabled = debugModeToggle ? debugModeToggle.checked : false;
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
const message = debugEnabled && errorText
|
|
? `Webhook responded with ${response.status}: ${errorText}`
|
|
: `Webhook responded with ${response.status}.`;
|
|
setStatus(webhookStatus, message, 'error');
|
|
return;
|
|
}
|
|
if (debugEnabled) {
|
|
const responseText = await response.text();
|
|
if (responseText) {
|
|
setStatus(webhookStatus, `Webhook test successful: ${responseText}`, 'success');
|
|
return;
|
|
}
|
|
}
|
|
setStatus(webhookStatus, 'Webhook test successful.', 'success');
|
|
} catch (error) {
|
|
const debugEnabled = debugModeToggle ? debugModeToggle.checked : false;
|
|
const message = debugEnabled && error.stack
|
|
? `${error.message}\n${error.stack}`
|
|
: error.message || 'Webhook test failed.';
|
|
setStatus(webhookStatus, message, 'error');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (testMcpBtn) {
|
|
testMcpBtn.addEventListener('click', async function() {
|
|
const url = mcpServerUrlInput ? mcpServerUrlInput.value.trim() : '';
|
|
if (!isValidUrl(url)) {
|
|
setStatus(mcpStatus, 'Enter a valid MCP server URL first.', 'error');
|
|
return;
|
|
}
|
|
setStatus(mcpStatus, 'Checking MCP server...', '');
|
|
try {
|
|
let healthUrl = url.replace(/\/$/, '');
|
|
if (healthUrl.endsWith('/mcp/health')) {
|
|
// leave as is
|
|
} else if (healthUrl.endsWith('/mcp')) {
|
|
healthUrl = `${healthUrl}/health`;
|
|
} else if (!healthUrl.endsWith('/health')) {
|
|
healthUrl = `${healthUrl}/mcp/health`;
|
|
}
|
|
const headers = {};
|
|
const apiKey = mcpApiKeyInput ? mcpApiKeyInput.value.trim() : '';
|
|
if (apiKey) {
|
|
headers['x-mcp-api-key'] = apiKey;
|
|
headers.Authorization = `Bearer ${apiKey}`;
|
|
}
|
|
const response = await fetch(healthUrl, { method: 'GET', headers });
|
|
if (!response.ok) {
|
|
setStatus(mcpStatus, `MCP server responded with ${response.status}.`, 'error');
|
|
return;
|
|
}
|
|
setStatus(mcpStatus, 'MCP server is reachable.', 'success');
|
|
} catch (error) {
|
|
setStatus(mcpStatus, error.message || 'MCP test failed.', 'error');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (testCloudBtn) {
|
|
testCloudBtn.addEventListener('click', async function() {
|
|
const endpoint = cloudEndpointInput ? cloudEndpointInput.value.trim() : '';
|
|
if (!endpoint || !isValidUrl(endpoint)) {
|
|
setStatus(cloudStatus, 'Enter a valid cloud endpoint first.', 'error');
|
|
return;
|
|
}
|
|
setStatus(cloudStatus, 'Validating cloud endpoint...', '');
|
|
try {
|
|
const response = await fetch(endpoint, { method: 'GET' });
|
|
if (!response.ok) {
|
|
setStatus(cloudStatus, `Cloud endpoint responded with ${response.status}.`, 'error');
|
|
return;
|
|
}
|
|
setStatus(cloudStatus, 'Cloud endpoint is reachable.', 'success');
|
|
} catch (error) {
|
|
setStatus(cloudStatus, error.message || 'Cloud test failed.', 'error');
|
|
}
|
|
});
|
|
}
|
|
|
|
if (fetchCalendarBtn) {
|
|
fetchCalendarBtn.addEventListener('click', function() {
|
|
const toolName = gcalToolNameInput ? gcalToolNameInput.value.trim() : '';
|
|
if (!toolName) {
|
|
setStatus(gcalStatus, 'Set the Google Calendar tool name first.', 'error');
|
|
return;
|
|
}
|
|
const calendarId = gcalCalendarIdInput ? gcalCalendarIdInput.value.trim() || 'primary' : 'primary';
|
|
const maxResultsRaw = gcalMaxResultsInput ? Number(gcalMaxResultsInput.value) : 5;
|
|
const maxResults = Number.isFinite(maxResultsRaw) && maxResultsRaw > 0 ? maxResultsRaw : 5;
|
|
setStatus(gcalStatus, 'Fetching events...', '');
|
|
setOutput(gcalOutput, '');
|
|
chrome.runtime.sendMessage(
|
|
{
|
|
action: 'mcp:callTool',
|
|
toolName,
|
|
args: {
|
|
calendarId,
|
|
maxResults,
|
|
timeMin: new Date().toISOString()
|
|
}
|
|
},
|
|
(response) => {
|
|
if (chrome.runtime.lastError || !response) {
|
|
setStatus(gcalStatus, 'Failed to fetch events.', 'error');
|
|
return;
|
|
}
|
|
if (!response.success) {
|
|
setStatus(gcalStatus, response.error || 'Failed to fetch events.', 'error');
|
|
return;
|
|
}
|
|
setStatus(gcalStatus, 'Events loaded.', 'success');
|
|
setOutput(gcalOutput, JSON.stringify(response.result, null, 2));
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
if (refreshMcpToolsBtn) {
|
|
refreshMcpToolsBtn.addEventListener('click', function() {
|
|
setStatus(mcpToolsStatus, 'Fetching tools...', '');
|
|
chrome.runtime.sendMessage({ action: 'mcp:listTools' }, (response) => {
|
|
if (chrome.runtime.lastError || !response) {
|
|
setStatus(mcpToolsStatus, 'Failed to fetch tools.', 'error');
|
|
return;
|
|
}
|
|
if (!response.success) {
|
|
setStatus(mcpToolsStatus, response.error || 'Failed to fetch tools.', 'error');
|
|
return;
|
|
}
|
|
if (mcpToolSelect) {
|
|
mcpToolSelect.innerHTML = '';
|
|
const tools = response.tools || [];
|
|
if (!tools.length) {
|
|
const option = document.createElement('option');
|
|
option.value = '';
|
|
option.textContent = 'No tools available';
|
|
mcpToolSelect.appendChild(option);
|
|
} else {
|
|
tools.forEach((tool) => {
|
|
const option = document.createElement('option');
|
|
option.value = tool.name || '';
|
|
option.textContent = tool.name || 'Unnamed tool';
|
|
mcpToolSelect.appendChild(option);
|
|
});
|
|
}
|
|
}
|
|
setStatus(mcpToolsStatus, `Loaded ${response.tools?.length || 0} tools.`, 'success');
|
|
});
|
|
});
|
|
}
|
|
|
|
if (runMcpToolBtn) {
|
|
runMcpToolBtn.addEventListener('click', function() {
|
|
const toolName = mcpToolSelect ? mcpToolSelect.value : '';
|
|
if (!toolName) {
|
|
setStatus(mcpToolsStatus, 'Select a tool first.', 'error');
|
|
return;
|
|
}
|
|
let args = {};
|
|
if (mcpToolArgs && mcpToolArgs.value.trim()) {
|
|
try {
|
|
args = JSON.parse(mcpToolArgs.value);
|
|
} catch (error) {
|
|
setStatus(mcpToolsStatus, 'Arguments must be valid JSON.', 'error');
|
|
return;
|
|
}
|
|
}
|
|
setStatus(mcpToolsStatus, 'Running tool...', '');
|
|
setOutput(mcpToolOutput, '');
|
|
chrome.runtime.sendMessage({ action: 'mcp:callTool', toolName, args }, (response) => {
|
|
if (chrome.runtime.lastError || !response) {
|
|
setStatus(mcpToolsStatus, 'Tool call failed.', 'error');
|
|
return;
|
|
}
|
|
if (!response.success) {
|
|
setStatus(mcpToolsStatus, response.error || 'Tool call failed.', 'error');
|
|
return;
|
|
}
|
|
setStatus(mcpToolsStatus, 'Tool executed.', 'success');
|
|
setOutput(mcpToolOutput, JSON.stringify(response.result, null, 2));
|
|
});
|
|
});
|
|
}
|
|
|
|
if (mcpToolArgs) {
|
|
mcpToolArgs.addEventListener('input', function() {
|
|
validateJsonField(mcpToolArgs, mcpToolsStatus, 'Tool args');
|
|
});
|
|
}
|
|
|
|
if (savePresetBtn) {
|
|
savePresetBtn.addEventListener('click', function() {
|
|
const name = presetNameInput ? presetNameInput.value.trim() : '';
|
|
const toolName = presetToolNameInput ? presetToolNameInput.value.trim() : '';
|
|
if (!name || !toolName) {
|
|
setStatus(presetStatus, 'Preset name and tool name are required.', 'error');
|
|
return;
|
|
}
|
|
let args = {};
|
|
if (presetArgsInput && presetArgsInput.value.trim()) {
|
|
const parsed = readJsonArgs(presetArgsInput.value, presetStatus, 'Preset arguments must be valid JSON.');
|
|
if (parsed === null) return;
|
|
args = parsed;
|
|
}
|
|
const tags = presetTagsInput
|
|
? presetTagsInput.value.split(',').map((tag) => tag.trim()).filter(Boolean)
|
|
: [];
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const settings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}) };
|
|
const presets = Array.isArray(settings.customToolPresets) ? settings.customToolPresets : [];
|
|
const existingIndex = presets.findIndex((item) => item.name.toLowerCase() === name.toLowerCase());
|
|
const nextPreset = {
|
|
id: existingIndex >= 0 ? presets[existingIndex].id : `${Date.now()}_${Math.random().toString(16).slice(2)}`,
|
|
name,
|
|
description: presetDescriptionInput ? presetDescriptionInput.value.trim() : '',
|
|
tags,
|
|
toolName,
|
|
args
|
|
};
|
|
if (existingIndex >= 0) {
|
|
presets[existingIndex] = nextPreset;
|
|
} else {
|
|
presets.push(nextPreset);
|
|
}
|
|
settings.customToolPresets = presets;
|
|
chrome.storage.sync.set({ advancedSettings: settings }, () => {
|
|
updatePresetSelect(presets);
|
|
setStatus(presetStatus, existingIndex >= 0 ? 'Preset updated.' : 'Preset saved.', 'success');
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
if (presetArgsInput) {
|
|
presetArgsInput.addEventListener('input', function() {
|
|
validateJsonField(presetArgsInput, presetStatus, 'Preset args');
|
|
});
|
|
}
|
|
|
|
if (clearPresetBtn) {
|
|
clearPresetBtn.addEventListener('click', function() {
|
|
if (presetNameInput) presetNameInput.value = '';
|
|
if (presetDescriptionInput) presetDescriptionInput.value = '';
|
|
if (presetTagsInput) presetTagsInput.value = '';
|
|
if (presetToolNameInput) presetToolNameInput.value = '';
|
|
if (presetArgsInput) presetArgsInput.value = '';
|
|
setStatus(presetStatus, '', '');
|
|
});
|
|
}
|
|
|
|
if (presetSelect) {
|
|
presetSelect.addEventListener('change', function() {
|
|
const id = presetSelect.value;
|
|
if (!id) return;
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const settings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}) };
|
|
const presets = Array.isArray(settings.customToolPresets) ? settings.customToolPresets : [];
|
|
const preset = presets.find((item) => item.id === id);
|
|
if (!preset) return;
|
|
if (presetNameInput) presetNameInput.value = preset.name;
|
|
if (presetDescriptionInput) presetDescriptionInput.value = preset.description || '';
|
|
if (presetTagsInput) presetTagsInput.value = Array.isArray(preset.tags) ? preset.tags.join(', ') : '';
|
|
if (presetToolNameInput) presetToolNameInput.value = preset.toolName;
|
|
if (presetArgsInput) presetArgsInput.value = JSON.stringify(preset.args || {}, null, 2);
|
|
});
|
|
});
|
|
}
|
|
|
|
if (runPresetBtn) {
|
|
runPresetBtn.addEventListener('click', function() {
|
|
const id = presetSelect ? presetSelect.value : '';
|
|
if (!id) {
|
|
setStatus(presetStatus, 'Select a preset first.', 'error');
|
|
return;
|
|
}
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const settings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}) };
|
|
const presets = Array.isArray(settings.customToolPresets) ? settings.customToolPresets : [];
|
|
const preset = presets.find((item) => item.id === id);
|
|
if (!preset) {
|
|
setStatus(presetStatus, 'Preset not found.', 'error');
|
|
return;
|
|
}
|
|
setStatus(presetStatus, 'Running preset...', '');
|
|
setOutput(presetOutput, '');
|
|
chrome.runtime.sendMessage(
|
|
{ action: 'mcp:callTool', toolName: preset.toolName, args: preset.args || {} },
|
|
(response) => {
|
|
if (chrome.runtime.lastError || !response) {
|
|
setStatus(presetStatus, 'Preset failed.', 'error');
|
|
return;
|
|
}
|
|
if (!response.success) {
|
|
setStatus(presetStatus, response.error || 'Preset failed.', 'error');
|
|
return;
|
|
}
|
|
setStatus(presetStatus, 'Preset executed.', 'success');
|
|
setOutput(presetOutput, JSON.stringify(response.result, null, 2));
|
|
}
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
if (deletePresetBtn) {
|
|
deletePresetBtn.addEventListener('click', function() {
|
|
const id = presetSelect ? presetSelect.value : '';
|
|
if (!id) {
|
|
setStatus(presetStatus, 'Select a preset first.', 'error');
|
|
return;
|
|
}
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const settings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}) };
|
|
const presets = Array.isArray(settings.customToolPresets) ? settings.customToolPresets : [];
|
|
const nextPresets = presets.filter((item) => item.id !== id);
|
|
settings.customToolPresets = nextPresets;
|
|
chrome.storage.sync.set({ advancedSettings: settings }, () => {
|
|
updatePresetSelect(nextPresets);
|
|
if (presetNameInput) presetNameInput.value = '';
|
|
if (presetDescriptionInput) presetDescriptionInput.value = '';
|
|
if (presetTagsInput) presetTagsInput.value = '';
|
|
if (presetToolNameInput) presetToolNameInput.value = '';
|
|
if (presetArgsInput) presetArgsInput.value = '';
|
|
setOutput(presetOutput, '');
|
|
setStatus(presetStatus, 'Preset deleted.', 'success');
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
if (addAutomationBtn) {
|
|
addAutomationBtn.addEventListener('click', function() {
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const settings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}) };
|
|
const automations = Array.isArray(settings.automations) ? settings.automations : [];
|
|
const newAutomation = {
|
|
id: `${Date.now()}_${Math.random().toString(16).slice(2)}`,
|
|
name: 'New automation',
|
|
kind: 'actions',
|
|
enabled: true,
|
|
triggers: { sessionStart: false, sessionEnd: true, manual: true },
|
|
requireApproval: true,
|
|
actions: [],
|
|
standup: {
|
|
discordToolName: '',
|
|
discordArgsTemplate: '',
|
|
nextcloudToolName: '',
|
|
nextcloudArgsTemplate: '{\n \"path\": \"notes/daily/standup-{{date}}.txt\",\n \"content\": \"Daily Standup - {{date_human}}\\\\n\\\\nSummary\\\\n{{summary_full}}\\\\n\\\\nAction Items\\\\n{{action_items}}\\\\n\\\\nBlockers\\\\n{{blockers}}\\\\n\\\\nDecisions\\\\n{{decisions}}\"\n}'
|
|
}
|
|
};
|
|
automations.push(newAutomation);
|
|
settings.automations = automations;
|
|
chrome.storage.sync.set({ advancedSettings: settings }, () => {
|
|
activeAutomationId = newAutomation.id;
|
|
hydrateAutomations(automations);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
if (automationNameInput) {
|
|
automationNameInput.addEventListener('input', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({ ...automation, name: this.value.trim() }));
|
|
});
|
|
}
|
|
|
|
if (automationTypeSelect) {
|
|
automationTypeSelect.addEventListener('change', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({ ...automation, kind: this.value }));
|
|
});
|
|
}
|
|
|
|
if (automationEnabledToggle) {
|
|
automationEnabledToggle.addEventListener('change', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({ ...automation, enabled: this.checked }));
|
|
});
|
|
}
|
|
|
|
if (automationTriggerStart) {
|
|
automationTriggerStart.addEventListener('change', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({
|
|
...automation,
|
|
triggers: { ...automation.triggers, sessionStart: this.checked }
|
|
}));
|
|
});
|
|
}
|
|
|
|
if (automationTriggerEnd) {
|
|
automationTriggerEnd.addEventListener('change', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({
|
|
...automation,
|
|
triggers: { ...automation.triggers, sessionEnd: this.checked }
|
|
}));
|
|
});
|
|
}
|
|
|
|
if (automationTriggerManual) {
|
|
automationTriggerManual.addEventListener('change', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({
|
|
...automation,
|
|
triggers: { ...automation.triggers, manual: this.checked }
|
|
}));
|
|
});
|
|
}
|
|
|
|
if (automationRequireApproval) {
|
|
automationRequireApproval.addEventListener('change', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({ ...automation, requireApproval: this.checked }));
|
|
});
|
|
}
|
|
|
|
if (addAutomationActionBtn) {
|
|
addAutomationActionBtn.addEventListener('click', function() {
|
|
if (!activeAutomationId) return;
|
|
const label = automationActionLabel ? automationActionLabel.value.trim() : '';
|
|
const type = automationActionType ? automationActionType.value : 'mcp';
|
|
if (!label) {
|
|
setStatus(automationRunStatus, 'Action label is required.', 'error');
|
|
return;
|
|
}
|
|
|
|
let nextAction = null;
|
|
if (type === 'mcp') {
|
|
const toolName = automationActionTool ? automationActionTool.value.trim() : '';
|
|
if (!toolName) {
|
|
setStatus(automationRunStatus, 'MCP tool name is required.', 'error');
|
|
return;
|
|
}
|
|
const args = readJsonArgs(automationActionArgs?.value || '', automationRunStatus, 'Action args must be valid JSON.');
|
|
if (args === null) return;
|
|
nextAction = {
|
|
id: `${Date.now()}_${Math.random().toString(16).slice(2)}`,
|
|
type: 'mcp',
|
|
label,
|
|
toolName,
|
|
args
|
|
};
|
|
} else {
|
|
const url = automationActionWebhookUrl ? automationActionWebhookUrl.value.trim() : '';
|
|
const method = automationActionWebhookMethod ? automationActionWebhookMethod.value : 'POST';
|
|
const headers = readJsonArgs(
|
|
automationActionWebhookHeaders?.value || '',
|
|
automationRunStatus,
|
|
'Webhook headers must be valid JSON.'
|
|
);
|
|
if (headers === null) return;
|
|
const bodyTemplate = automationActionWebhookBody ? automationActionWebhookBody.value.trim() : '';
|
|
const retryRaw = Number(automationActionWebhookRetryCount ? automationActionWebhookRetryCount.value : 0);
|
|
const retryCount = Number.isFinite(retryRaw) ? Math.max(0, Math.min(5, Math.floor(retryRaw))) : 0;
|
|
nextAction = {
|
|
id: `${Date.now()}_${Math.random().toString(16).slice(2)}`,
|
|
type: 'webhook',
|
|
label,
|
|
webhookUrl: url,
|
|
method,
|
|
headers,
|
|
bodyTemplate,
|
|
retryCount
|
|
};
|
|
}
|
|
|
|
updateAutomation(activeAutomationId, (automation) => {
|
|
const actions = Array.isArray(automation.actions) ? automation.actions : [];
|
|
actions.push(nextAction);
|
|
return { ...automation, actions };
|
|
});
|
|
activeAutomationActionId = nextAction.id;
|
|
setStatus(automationRunStatus, 'Action added.', 'success');
|
|
});
|
|
}
|
|
|
|
if (automationActionArgs) {
|
|
automationActionArgs.addEventListener('input', function() {
|
|
validateJsonField(automationActionArgs, automationRunStatus, 'Action args');
|
|
});
|
|
automationActionArgs.addEventListener('change', function() {
|
|
persistSelectedAutomationActionFromForm();
|
|
});
|
|
}
|
|
|
|
if (automationActionWebhookHeaders) {
|
|
automationActionWebhookHeaders.addEventListener('input', function() {
|
|
validateJsonField(automationActionWebhookHeaders, automationRunStatus, 'Webhook headers');
|
|
});
|
|
automationActionWebhookHeaders.addEventListener('change', function() {
|
|
persistSelectedAutomationActionFromForm();
|
|
});
|
|
}
|
|
|
|
if (automationActionType) {
|
|
automationActionType.addEventListener('change', function() {
|
|
updateActionTypeUI();
|
|
persistSelectedAutomationActionFromForm();
|
|
});
|
|
}
|
|
|
|
if (automationActionLabel) {
|
|
automationActionLabel.addEventListener('change', function() {
|
|
persistSelectedAutomationActionFromForm();
|
|
});
|
|
}
|
|
|
|
if (automationActionTool) {
|
|
automationActionTool.addEventListener('change', function() {
|
|
persistSelectedAutomationActionFromForm();
|
|
});
|
|
}
|
|
|
|
if (automationActionWebhookUrl) {
|
|
automationActionWebhookUrl.addEventListener('change', function() {
|
|
persistSelectedAutomationActionFromForm();
|
|
});
|
|
}
|
|
|
|
if (automationActionWebhookMethod) {
|
|
automationActionWebhookMethod.addEventListener('change', function() {
|
|
persistSelectedAutomationActionFromForm();
|
|
});
|
|
}
|
|
|
|
if (automationActionWebhookBody) {
|
|
automationActionWebhookBody.addEventListener('change', function() {
|
|
persistSelectedAutomationActionFromForm();
|
|
});
|
|
}
|
|
|
|
if (automationActionWebhookRetryCount) {
|
|
automationActionWebhookRetryCount.addEventListener('change', function() {
|
|
persistSelectedAutomationActionFromForm();
|
|
});
|
|
}
|
|
|
|
if (automationActionSelect) {
|
|
automationActionSelect.addEventListener('change', function() {
|
|
activeAutomationActionId = this.value || '';
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const settings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}) };
|
|
const automations = Array.isArray(settings.automations) ? settings.automations : [];
|
|
const automation = automations.find((item) => item.id === activeAutomationId);
|
|
if (!automation) return;
|
|
populateAutomationActionEditor(automation.actions || []);
|
|
});
|
|
});
|
|
}
|
|
|
|
if (removeAutomationActionBtn) {
|
|
removeAutomationActionBtn.addEventListener('click', function() {
|
|
if (!activeAutomationId) return;
|
|
const id = automationActionSelect ? automationActionSelect.value : '';
|
|
if (!id) {
|
|
setStatus(automationRunStatus, 'Select an action first.', 'error');
|
|
return;
|
|
}
|
|
updateAutomation(activeAutomationId, (automation) => {
|
|
const actions = Array.isArray(automation.actions) ? automation.actions : [];
|
|
return { ...automation, actions: actions.filter((action) => action.id !== id) };
|
|
});
|
|
activeAutomationActionId = '';
|
|
});
|
|
}
|
|
|
|
if (standupDiscordTool) {
|
|
standupDiscordTool.addEventListener('input', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({
|
|
...automation,
|
|
standup: { ...automation.standup, discordToolName: this.value.trim() }
|
|
}));
|
|
});
|
|
}
|
|
|
|
if (standupDiscordArgs) {
|
|
standupDiscordArgs.addEventListener('input', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({
|
|
...automation,
|
|
standup: { ...automation.standup, discordArgsTemplate: this.value }
|
|
}));
|
|
validateJsonField(standupDiscordArgs, automationRunStatus, 'Standup Discord args');
|
|
});
|
|
}
|
|
|
|
if (standupNextcloudTool) {
|
|
standupNextcloudTool.addEventListener('input', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({
|
|
...automation,
|
|
standup: { ...automation.standup, nextcloudToolName: this.value.trim() }
|
|
}));
|
|
});
|
|
}
|
|
|
|
if (standupNextcloudArgs) {
|
|
standupNextcloudArgs.addEventListener('input', function() {
|
|
if (!activeAutomationId) return;
|
|
updateAutomation(activeAutomationId, (automation) => ({
|
|
...automation,
|
|
standup: { ...automation.standup, nextcloudArgsTemplate: this.value }
|
|
}));
|
|
validateJsonField(standupNextcloudArgs, automationRunStatus, 'Standup Nextcloud args');
|
|
});
|
|
}
|
|
|
|
if (runAutomationNowBtn) {
|
|
runAutomationNowBtn.addEventListener('click', function() {
|
|
if (!activeAutomationId) return;
|
|
persistCurrentAutomationFromForm();
|
|
persistSelectedAutomationActionFromForm();
|
|
setStatus(automationRunStatus, 'Running automation...', '');
|
|
setOutput(automationOutput, '');
|
|
chrome.runtime.sendMessage({ action: 'automation:run', trigger: 'manual', automationId: activeAutomationId }, (response) => {
|
|
if (chrome.runtime.lastError || !response) {
|
|
setStatus(automationRunStatus, 'Automation failed.', 'error');
|
|
return;
|
|
}
|
|
if (!response.success) {
|
|
setStatus(automationRunStatus, response.error || 'Automation failed.', 'error');
|
|
return;
|
|
}
|
|
setStatus(automationRunStatus, 'Automation completed.', 'success');
|
|
setOutput(automationOutput, JSON.stringify(response.results || response.result || {}, null, 2));
|
|
});
|
|
});
|
|
}
|
|
|
|
if (saveAutomationBtn) {
|
|
saveAutomationBtn.addEventListener('click', function() {
|
|
if (!activeAutomationId) return;
|
|
persistCurrentAutomationFromForm();
|
|
persistSelectedAutomationActionFromForm();
|
|
setStatus(automationRunStatus, 'Automation saved.', 'success');
|
|
});
|
|
}
|
|
|
|
if (testAutomationBtn) {
|
|
testAutomationBtn.addEventListener('click', function() {
|
|
if (!activeAutomationId) return;
|
|
persistCurrentAutomationFromForm();
|
|
persistSelectedAutomationActionFromForm();
|
|
setStatus(automationRunStatus, 'Running test...', '');
|
|
setOutput(automationOutput, '');
|
|
chrome.runtime.sendMessage(
|
|
{ action: 'automation:run', trigger: 'manual', automationId: activeAutomationId, testMode: true },
|
|
(response) => {
|
|
if (chrome.runtime.lastError || !response) {
|
|
setStatus(automationRunStatus, 'Test failed.', 'error');
|
|
return;
|
|
}
|
|
if (!response.success) {
|
|
setStatus(automationRunStatus, response.error || 'Test failed.', 'error');
|
|
return;
|
|
}
|
|
setStatus(automationRunStatus, 'Test completed.', 'success');
|
|
setOutput(automationOutput, JSON.stringify(response.results || response.result || {}, null, 2));
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
if (deleteAutomationBtn) {
|
|
deleteAutomationBtn.addEventListener('click', function() {
|
|
if (!activeAutomationId) return;
|
|
chrome.storage.sync.get(['advancedSettings'], (result) => {
|
|
const settings = { ...DEFAULT_SETTINGS, ...(result.advancedSettings || {}) };
|
|
const automations = Array.isArray(settings.automations) ? settings.automations : [];
|
|
const nextAutomations = automations.filter((item) => item.id !== activeAutomationId);
|
|
settings.automations = nextAutomations;
|
|
chrome.storage.sync.set({ advancedSettings: settings }, () => {
|
|
activeAutomationId = nextAutomations[0]?.id || null;
|
|
hydrateAutomations(nextAutomations);
|
|
setStatus(automationRunStatus, 'Automation deleted.', 'success');
|
|
setOutput(automationOutput, '');
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
document.querySelectorAll('input[type=\"text\"], textarea').forEach((field) => {
|
|
field.addEventListener('focus', () => {
|
|
lastFocusedInput = field;
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll('.placeholder-chip').forEach((chip) => {
|
|
chip.addEventListener('click', async function() {
|
|
const placeholder = chip.getAttribute('data-placeholder');
|
|
if (!placeholder) return;
|
|
if (!lastFocusedInput) {
|
|
if (standupDiscordArgs) {
|
|
standupDiscordArgs.focus();
|
|
lastFocusedInput = standupDiscordArgs;
|
|
} else if (standupNextcloudArgs) {
|
|
standupNextcloudArgs.focus();
|
|
lastFocusedInput = standupNextcloudArgs;
|
|
}
|
|
}
|
|
|
|
if (lastFocusedInput && typeof lastFocusedInput.selectionStart === 'number') {
|
|
const start = lastFocusedInput.selectionStart;
|
|
const end = lastFocusedInput.selectionEnd;
|
|
const value = lastFocusedInput.value || '';
|
|
lastFocusedInput.value = value.slice(0, start) + placeholder + value.slice(end);
|
|
lastFocusedInput.selectionStart = lastFocusedInput.selectionEnd = start + placeholder.length;
|
|
lastFocusedInput.dispatchEvent(new Event('input', { bubbles: true }));
|
|
setPlaceholderStatus(`Inserted ${placeholder}.`, 'success');
|
|
return;
|
|
}
|
|
try {
|
|
await navigator.clipboard.writeText(placeholder);
|
|
setPlaceholderStatus(`Copied ${placeholder} to clipboard.`, 'success');
|
|
} catch (error) {
|
|
setPlaceholderStatus('Failed to copy placeholder.', 'error');
|
|
}
|
|
});
|
|
});
|
|
|
|
clearAutomationActionForm();
|
|
|
|
if (closeSettingsBtn) {
|
|
closeSettingsBtn.addEventListener('click', function() {
|
|
window.close();
|
|
});
|
|
}
|
|
});
|