// ── Toolbar button: open settings page ──
browser.browserAction.onClicked.addListener(() => {
browser.runtime.openOptionsPage();
});
// ── "Erledigt" button in message display ──
async function executeErledigtAction(tab, actionConfig) {
const message = await messenger.messageDisplay.getDisplayedMessage(tab.id);
if (!message) {
browser.notifications.create({ type: 'basic', iconUrl: browser.runtime.getURL('icons/icon.png'), title: 'Fehler', message: 'Keine Nachricht ausgewählt.' });
return;
}
const storage = await browser.storage.local.get(['gitea_config', 'schlagwoerter_cache']);
const config = storage.gitea_config || {};
const schlagwoerter = storage.schlagwoerter_cache;
// Apply user's tag
let tagKey = null;
if (Array.isArray(schlagwoerter) && config.authorName) {
const match = schlagwoerter.find(u => u.name.toLowerCase() === config.authorName.toLowerCase());
if (match) {
tagKey = `$hps_${match.name.toLowerCase().replace(/\s+/g, '_')}`;
}
}
if (tagKey) {
const currentTags = message.tags || [];
if (!currentTags.includes(tagKey)) {
await messenger.messages.update(message.id, { tags: [...currentTags, tagKey] });
}
}
// Move to target folder
if (actionConfig.targetFolder) {
const folderInfo = JSON.parse(actionConfig.targetFolder);
await messenger.messages.move([message.id], folderInfo);
}
// Feedback
const parts = [];
if (tagKey) parts.push('markiert');
if (actionConfig.targetFolder) parts.push('verschoben');
const title = actionConfig.name || 'Erledigt';
browser.notifications.create({
type: 'basic',
iconUrl: browser.runtime.getURL('icons/icon.png'),
title,
message: parts.length ? `Nachricht ${parts.join(' & ')}.` : 'Kein Schlagwort oder Zielordner konfiguriert.'
});
}
// Single action: direct click without popup
messenger.messageDisplayAction.onClicked.addListener(async (tab) => {
try {
const result = await browser.storage.local.get('erledigt_config');
const actions = (result.erledigt_config || {}).actions || [];
await executeErledigtAction(tab, actions[0] || {});
} catch (e) {
console.error('Erledigt-Button Fehler:', e);
browser.notifications.create({ type: 'basic', iconUrl: browser.runtime.getURL('icons/icon.png'), title: 'Fehler', message: e.message });
}
});
// Toggle popup vs direct click based on action count
async function updateErledigtPopup() {
const result = await browser.storage.local.get('erledigt_config');
const actions = (result.erledigt_config || {}).actions || [];
if (actions.length > 1) {
await messenger.messageDisplayAction.setPopup({ popup: 'message_popup.html' });
await messenger.messageDisplayAction.setTitle({ title: 'Aktion wählen' });
} else {
await messenger.messageDisplayAction.setPopup({ popup: '' });
await messenger.messageDisplayAction.setTitle({ title: actions[0]?.name || 'Erledigt' });
}
}
// Update on config change
browser.storage.onChanged.addListener((changes, area) => {
if (area === 'local' && changes.erledigt_config) updateErledigtPopup();
});
updateErledigtPopup();
// ── Template insertion ──
browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.action === 'erledigtAction') {
(async () => {
try {
const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
const result = await browser.storage.local.get('erledigt_config');
const actions = (result.erledigt_config || {}).actions || [];
const action = actions[msg.index] || {};
await executeErledigtAction(tab, action);
sendResponse({ success: true });
} catch (e) {
console.error('Erledigt-Action Fehler:', e);
browser.notifications.create({ type: 'basic', iconUrl: browser.runtime.getURL('icons/icon.png'), title: 'Fehler', message: e.message });
sendResponse({ success: false, error: e.message });
}
})();
return true;
}
if (msg.action !== 'insertTemplate') return;
handleInsertTemplate(msg).then(() => sendResponse())
.catch(err => sendResponse({ error: err.message }));
return true; // keep channel open for async response
});
async function handleInsertTemplate(msg) {
try {
const [tab] = await browser.tabs.query({
active: true,
currentWindow: true,
windowType: 'messageCompose',
});
if (!tab) throw new Error('Kein Compose-Fenster gefunden');
const details = await browser.compose.getComposeDetails(tab.id);
const isHtmlTemplate = msg.text.includes('<');
// If compose is plain text but template is HTML, switch to HTML mode first
if (details.isPlainText && isHtmlTemplate) {
await browser.compose.setComposeDetails(tab.id, { isPlainText: false });
}
if (details.isPlainText && !isHtmlTemplate) {
// Plain text template in plain text mode - use old method
const old = details.plainTextBody || '';
const newBody = msg.text + (old ? '\n' + old : '');
await browser.compose.setComposeDetails(tab.id, { plainTextBody: newBody });
} else {
// HTML mode: use insertText API to go through the editor's rendering pipeline
const htmlContent = isHtmlTemplate ? msg.text : msg.text.replace(/\n/g, '
');
// Move cursor to beginning of body first via script injection
await browser.tabs.executeScript(tab.id, {
code: `
const editor = document.getElementById('messageEditor');
const editorDoc = editor ? editor.contentDocument : document;
const body = editorDoc.body;
if (body) {
const range = editorDoc.createRange();
range.setStart(body, 0);
range.collapse(true);
const sel = editorDoc.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
`
}).catch(() => {
// Fallback: some TB versions structure the editor differently
});
// Insert HTML at cursor position - this goes through the editor's render pipeline
await browser.compose.insertText(tab.id, htmlContent + '
', { insertAsText: false });
}
} catch (e) {
console.error('background.js error:', e);
// Fallback: if insertText fails, try setComposeDetails
try {
const [tab] = await browser.tabs.query({
active: true, currentWindow: true, windowType: 'messageCompose',
});
if (tab) {
const details = await browser.compose.getComposeDetails(tab.id);
const old = details.body || '';
const htmlTpl = msg.text.includes('<') ? msg.text : msg.text.replace(/\n/g, '
');
const bodyIdx = old.indexOf('