From e44c89ca7abe0d4fa9b94e316ad3d2c8912f1856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCnch?= Date: Fri, 22 Aug 2025 18:01:11 +0200 Subject: [PATCH 1/3] feat: new style with dark/light theme and emojis --- _locales/de/messages.json | 28 ++++++ _locales/en/messages.json | 29 ++++++ options/options.css | 45 ++++++++- options/options.html | 22 +++++ options/options.js | 44 +++++++++ popup/popup.css | 200 ++++++++++++++++++++++++++------------ popup/popup.html | 4 +- popup/popup.js | 14 +++ 8 files changed, 318 insertions(+), 68 deletions(-) diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 4b56a84..9482269 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -252,5 +252,33 @@ "optionsTestError": { "message": "Fehler beim Senden des Test-Webhooks: ", "description": "Fehlermeldung für den Test-Webhook." + }, + "optionsAppearanceHeader": { + "message": "Erscheinungsbild", + "description": "Überschrift für den Bereich Erscheinungsbild." + }, + "optionsThemeLabel": { + "message": "Theme", + "description": "Beschriftung für die Theme-Auswahl." + }, + "optionsThemeSystem": { + "message": "Systemstandard", + "description": "Option für das systemweite Standard-Theme." + }, + "optionsThemeLight": { + "message": "Hell", + "description": "Option für das helle Theme." + }, + "optionsThemeDark": { + "message": "Dunkel", + "description": "Option für das dunkle Theme." + }, + "optionsManageAppearanceButton": { + "message": "Erscheinungsbild", + "description": "Schaltfläche zum Öffnen des Erscheinungsbild-Dialogs." + }, + "optionsCloseAppearanceButton": { + "message": "Schließen", + "description": "Schaltfläche zum Schließen des Erscheinungsbild-Dialogs." } } diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 5a48c53..99e116b 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -253,4 +253,33 @@ "message": "Error sending test webhook: ", "description": "Error message for the test webhook." } + , + "optionsAppearanceHeader": { + "message": "Appearance", + "description": "Header for the appearance section." + }, + "optionsThemeLabel": { + "message": "Theme", + "description": "Label for the theme selector." + }, + "optionsThemeSystem": { + "message": "System default", + "description": "System default theme option." + }, + "optionsThemeLight": { + "message": "Light", + "description": "Light theme option." + }, + "optionsThemeDark": { + "message": "Dark", + "description": "Dark theme option." + }, + "optionsManageAppearanceButton": { + "message": "Appearance", + "description": "Button to open the appearance modal." + }, + "optionsCloseAppearanceButton": { + "message": "Close", + "description": "Button to close the appearance modal." + } } diff --git a/options/options.css b/options/options.css index 9f50b9d..3bbc2ba 100644 --- a/options/options.css +++ b/options/options.css @@ -410,7 +410,7 @@ button:hover { position: relative; } -.close, .close-manage-groups { +.close, .close-manage-groups, .close-manage-appearance { position: absolute; right: 15px; top: 15px; @@ -420,7 +420,7 @@ button:hover { color: #6b7280; } -.close:hover, .close-manage-groups:hover { +.close:hover, .close-manage-groups:hover, .close-manage-appearance:hover { color: #1f2937; } @@ -520,3 +520,44 @@ button:hover { .delete-group:hover { background-color: #dc2626; } + +/* Emoji icons for options buttons (non-intrusive: does not change textContent) */ +#add-new-webhook-btn::before, +#manage-groups-btn::before, +#manage-appearance-btn::before, +#cancel-edit-btn::before, +#close-manage-groups-btn::before, +#close-manage-appearance-btn::before, +#add-webhook-form button[type="submit"]::before, +#add-header-btn::before, +#add-group-btn::before, +#export-webhooks-btn::before, +#import-webhooks-btn::before, +.edit-btn::before, +.duplicate-btn::before, +.delete-btn::before, +.remove-header-btn::before, +.save-group-name::before, +.delete-group::before, +.edit-group-btn::before { + display: inline-block; + margin-right: 6px; +} + +#add-new-webhook-btn::before { content: "➕"; } +#manage-groups-btn::before { content: "🗂️"; } +#add-header-btn::before { content: "➕"; } +#add-group-btn::before { content: "➕"; } +#export-webhooks-btn::before { content: "📤"; } +#import-webhooks-btn::before { content: "📥"; } +#manage-appearance-btn::before { content: "🌓"; } +#add-webhook-form button[type="submit"]::before { content: "💾"; } +#cancel-edit-btn::before { content: "↩️"; } +#close-manage-groups-btn::before, #close-manage-appearance-btn::before { content: "✖️"; } +.edit-btn::before { content: "✏️"; } +.duplicate-btn::before { content: "📄"; } +.delete-btn::before { content: "🗑️"; } +.remove-header-btn::before { content: "❌"; } +.save-group-name::before { content: "💾"; } +.delete-group::before { content: "🗑️"; } +.edit-group-btn::before { content: "✏️"; } diff --git a/options/options.html b/options/options.html index 0a739ed..d7c5e46 100644 --- a/options/options.html +++ b/options/options.html @@ -2,6 +2,7 @@ + __MSG_optionsTitle__ @@ -10,6 +11,8 @@

__MSG_optionsPageHeader__

__MSG_optionsPageDescription__

+ + @@ -171,6 +174,25 @@

__MSG_optionsExistingGroupsHeader__

+ + + diff --git a/options/options.js b/options/options.js index f161d0d..b5f776b 100644 --- a/options/options.js +++ b/options/options.js @@ -383,6 +383,7 @@ const showAddWebhookBtn = document.getElementById("add-new-webhook-btn"); const testWebhookBtn = document.getElementById("test-webhook-btn"); const formStatusMessage = document.getElementById("form-status-message"); const manageGroupsBtn = document.getElementById("manage-groups-btn"); +const manageAppearanceBtn = document.getElementById("manage-appearance-btn"); const customPayloadInput = document.getElementById("webhook-custom-payload"); const variablesAutocomplete = document.getElementById("variables-autocomplete"); const toggleCustomPayloadBtn = document.getElementById("toggle-custom-payload"); @@ -395,11 +396,15 @@ const importWebhooksBtn = document.getElementById("import-webhooks-btn"); const importWebhooksInput = document.getElementById("import-webhooks-input"); const groupSelect = document.getElementById("webhook-group"); const manageGroupsModal = document.getElementById("manage-groups-modal"); +const manageAppearanceModal = document.getElementById("manage-appearance-modal"); const newGroupNameInput = document.getElementById("new-group-name"); const addGroupBtn = document.getElementById("add-group-btn"); const groupsList = document.getElementById("groups-list"); const closeManageGroupsBtn = document.getElementById("close-manage-groups-btn"); const closeManageGroups = document.querySelector("#manage-groups-modal .close-manage-groups"); +const closeManageAppearanceBtn = document.getElementById("close-manage-appearance-btn"); +const closeManageAppearance = document.querySelector("#manage-appearance-modal .close-manage-appearance"); +const themeSelect = document.getElementById("theme-select"); let headers = []; async function exportWebhooks() { @@ -528,6 +533,16 @@ manageGroupsBtn.addEventListener('click', async () => { await renderGroups(); }); +// Manage appearance button +if (manageAppearanceBtn) { + manageAppearanceBtn.addEventListener('click', async () => { + manageAppearanceModal.classList.remove('hidden'); + // Initialize theme select when opening + const { theme } = await browser.storage.sync.get('theme'); + themeSelect.value = theme || 'system'; + }); +} + // Close modal functions const closeManageGroupsModalFunc = () => { manageGroupsModal.classList.add('hidden'); @@ -537,6 +552,13 @@ const closeManageGroupsModalFunc = () => { closeManageGroupsBtn.addEventListener('click', closeManageGroupsModalFunc); closeManageGroups.addEventListener('click', closeManageGroupsModalFunc); +// Close appearance modal +const closeManageAppearanceModalFunc = () => { + manageAppearanceModal.classList.add('hidden'); +}; +if (closeManageAppearanceBtn) closeManageAppearanceBtn.addEventListener('click', closeManageAppearanceModalFunc); +if (closeManageAppearance) closeManageAppearance.addEventListener('click', closeManageAppearanceModalFunc); + // Close modals when clicking outside manageGroupsModal.addEventListener('click', (e) => { if (e.target === manageGroupsModal) { @@ -544,6 +566,15 @@ manageGroupsModal.addEventListener('click', (e) => { } }); +// Close appearance modal when clicking outside +if (manageAppearanceModal) { + manageAppearanceModal.addEventListener('click', (e) => { + if (e.target === manageAppearanceModal) { + closeManageAppearanceModalFunc(); + } + }); +} + // Add group addGroupBtn.addEventListener('click', async () => { const groupName = newGroupNameInput.value.trim(); @@ -1072,6 +1103,19 @@ document.addEventListener("DOMContentLoaded", () => { // Load webhooks loadWebhooks(); + + // Initialize theme select + if (themeSelect) { + browser.storage.sync.get("theme").then((res) => { + const value = res && res.theme ? res.theme : "system"; + themeSelect.value = value; + }); + + themeSelect.addEventListener("change", () => { + const value = themeSelect.value; + browser.storage.sync.set({ theme: value }); + }); + } }); // Export functions for testing in Node environment diff --git a/popup/popup.css b/popup/popup.css index b73b68e..a3bf0d5 100644 --- a/popup/popup.css +++ b/popup/popup.css @@ -1,107 +1,181 @@ +/* Theme variables */ +:root { + --bg: #f7f8fa; + --card-bg: #ffffff; + --text: #1f2937; + --muted: #6b7280; + --primary: #2563eb; + --primary-contrast: #ffffff; + --border: #e5e7eb; + --header-grad: linear-gradient(135deg, #6366f1, #2563eb); + --btn-bg: linear-gradient(135deg, #4f46e5, #2563eb); + --btn-bg-hover: linear-gradient(135deg, #4338ca, #1d4ed8); + --success-bg: #dcfce7; + --success-text: #166534; + --error-bg: #fee2e2; + --error-text: #991b1b; + --shadow: 0 6px 18px rgba(0, 0, 0, 0.08); +} + +@media (prefers-color-scheme: dark) { + :root { + --bg: #0b0f14; + --card-bg: #0f172a; + --text: #e5e7eb; + --muted: #9ca3af; + --primary: #60a5fa; + --primary-contrast: #0b0f14; + --border: #223045; + --header-grad: linear-gradient(135deg, #0ea5e9, #6366f1); + --btn-bg: linear-gradient(135deg, #0284c7, #4f46e5); + --btn-bg-hover: linear-gradient(135deg, #0369a1, #4338ca); + --success-bg: #064e3b; + --success-text: #a7f3d0; + --error-bg: #7f1d1d; + --error-text: #fecaca; + --shadow: 0 8px 20px rgba(0, 0, 0, 0.35); + } +} + +/* Explicit overrides */ +[data-theme="light"] { + --bg: #f7f8fa; + --card-bg: #ffffff; + --text: #1f2937; + --muted: #6b7280; + --primary: #2563eb; + --primary-contrast: #ffffff; + --border: #e5e7eb; + --header-grad: linear-gradient(135deg, #6366f1, #2563eb); + --btn-bg: linear-gradient(135deg, #4f46e5, #2563eb); + --btn-bg-hover: linear-gradient(135deg, #4338ca, #1d4ed8); + --success-bg: #dcfce7; + --success-text: #166534; + --error-bg: #fee2e2; + --error-text: #991b1b; + --shadow: 0 6px 18px rgba(0, 0, 0, 0.08); +} + +[data-theme="dark"] { + --bg: #0b0f14; + --card-bg: #0f172a; + --text: #e5e7eb; + --muted: #9ca3af; + --primary: #60a5fa; + --primary-contrast: #0b0f14; + --border: #223045; + --header-grad: linear-gradient(135deg, #0ea5e9, #6366f1); + --btn-bg: linear-gradient(135deg, #0284c7, #4f46e5); + --btn-bg-hover: linear-gradient(135deg, #0369a1, #4338ca); + --success-bg: #064e3b; + --success-text: #a7f3d0; + --error-bg: #7f1d1d; + --error-text: #fecaca; + --shadow: 0 8px 20px rgba(0, 0, 0, 0.35); +} + +/* Base layout */ body { - font-family: - -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, - sans-serif; - width: 300px; - padding: 0; - margin: 0; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, + sans-serif; + width: 320px; + padding: 0; + margin: 0; + background: var(--bg); + color: var(--text); } .container { - padding: 15px; -} - -.header { - text-align: center; - border-bottom: 1px solid #ccc; - padding-bottom: 10px; - margin-bottom: 15px; -} - -.header h3 { - margin: 0; - color: #333; + padding: 14px 14px 12px 14px; + background: transparent; + box-shadow: none; } #buttons-container { - display: flex; - flex-direction: column; - gap: 10px; + display: flex; + flex-direction: column; + gap: 8px; } .group-header { - font-weight: bold; - color: #555; - margin-top: 10px; - margin-bottom: 5px; - padding-bottom: 5px; - border-bottom: 1px solid #eee; + display: block; + font-weight: 700; + color: var(--text); + margin: 16px 0 6px 0; + padding-left: 10px; + border-left: 3px solid var(--primary); + font-size: 13px; + letter-spacing: 0.2px; } .webhook-btn { - width: 100%; - padding: 12px; - font-size: 1em; - color: #fff; - background-color: #007bff; - border: none; - border-radius: 5px; - cursor: pointer; - transition: - background-color 0.2s ease-in-out, - transform 0.1s ease; + width: 100%; + padding: 12px 12px; + font-size: 14px; + color: var(--primary-contrast); + background: var(--btn-bg); + border: 1px solid transparent; + border-radius: 10px; + cursor: pointer; + transition: transform 0.06s ease, box-shadow 0.2s ease, background 0.2s ease; + box-shadow: 0 1px 2px rgba(0,0,0,0.1); } + .webhook-btn:hover { - background-color: #0056b3; + background: var(--btn-bg-hover); + box-shadow: 0 4px 10px rgba(0,0,0,0.15); } .webhook-btn:active { - transform: scale(0.98); + transform: translateY(1px); } .webhook-btn:disabled { - background-color: #6c757d; - cursor: not-allowed; + opacity: 0.6; + cursor: not-allowed; } .no-hooks-msg { - color: #666; - text-align: center; - padding: 10px; + color: var(--muted); + text-align: center; + padding: 10px; } #status-message { - margin-top: 15px; - padding: 8px; - text-align: center; - border-radius: 4px; - font-size: 0.9em; + margin-top: 12px; + padding: 8px 10px; + text-align: center; + border-radius: 8px; + font-size: 12px; + border: 1px solid var(--border); } #status-message.success { - background-color: #d4edda; - color: #155724; + background-color: var(--success-bg); + color: var(--success-text); + border-color: transparent; } #status-message.error { - background-color: #f8d7da; - color: #721c24; + background-color: var(--error-bg); + color: var(--error-text); + border-color: transparent; } .footer { - margin-top: 15px; - padding-top: 10px; - border-top: 1px solid #ccc; - text-align: center; + margin-top: 12px; + padding-top: 2px; + text-align: center; } .footer a { - color: #007bff; - text-decoration: none; - font-size: 0.9em; + color: var(--primary); + text-decoration: none; + font-size: 12px; } .footer a:hover { - text-decoration: underline; + text-decoration: underline; } diff --git a/popup/popup.html b/popup/popup.html index d2325c3..e030844 100644 --- a/popup/popup.html +++ b/popup/popup.html @@ -2,14 +2,12 @@ + __MSG_popupTitle__
-
-

__MSG_popupHeader__

-
diff --git a/popup/popup.js b/popup/popup.js index e6c0e53..aec2f6f 100644 --- a/popup/popup.js +++ b/popup/popup.js @@ -7,6 +7,20 @@ document.addEventListener("DOMContentLoaded", async () => { const buttonsContainer = document.getElementById("buttons-container"); + // Apply theme preference (system | light | dark) + try { + const themeResult = await browserAPI.storage.sync.get("theme"); + const theme = themeResult && themeResult.theme ? themeResult.theme : "system"; + const root = document.documentElement; + if (theme === "light" || theme === "dark") { + root.setAttribute("data-theme", theme); + } else { + root.removeAttribute("data-theme"); + } + } catch (_) { + // ignore theme errors, fallback to system + } + // Load webhooks and groups from storage const { webhooks = [], groups = [] } = await browserAPI.storage.sync.get(["webhooks", "groups"]); const tabs = await browserAPI.tabs.query({ active: true, currentWindow: true }); From 2cb430161233f9d4863a39f579d7e044aa01c9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCnch?= Date: Fri, 22 Aug 2025 17:45:49 +0200 Subject: [PATCH 2/3] fix: remove not working emoji search --- _locales/de/messages.json | 20 +++++++++++ _locales/en/messages.json | 20 +++++++++++ options/options.css | 40 +++++++++++++++++++++ options/options.html | 17 +++++++++ options/options.js | 73 ++++++++++++++++++++++++++++++++++---- popup/popup.js | 10 +++--- tests/exportImport.test.js | 2 +- tests/options.test.js | 3 +- utils/utils.js | 48 ++++++++++++------------- 9 files changed, 196 insertions(+), 37 deletions(-) diff --git a/_locales/de/messages.json b/_locales/de/messages.json index 9482269..133fdcb 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -81,6 +81,26 @@ "message": "Label:", "description": "Label für das Eingabefeld des Webhook-Labels." }, + "optionsEmojiLabel": { + "message": "Emoji (optional):", + "description": "Beschriftung für das Emoji-Eingabefeld." + }, + "optionsEmojiPlaceholder": { + "message": "Emoji wählen, z. B. 🔔", + "description": "Platzhalter für das Emoji-Eingabefeld." + }, + "optionsEmojiChooseButton": { + "message": "Emoji auswählen", + "description": "Schaltflächentext zum Öffnen des Emoji-Pickers." + }, + "optionsEmojiClearButton": { + "message": "Leeren", + "description": "Schaltflächentext zum Leeren des Emoji-Feldes." + }, + "optionsEmojiSearchPlaceholder": { + "message": "Emoji suchen...", + "description": "Platzhalter für das Emoji-Suchfeld." + }, "optionsLabelInputPlaceholder": { "message": "z.B. 'An Slack senden'", "description": "Platzhalter für die Eingabe des Webhook-Labels." diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 99e116b..48da6d0 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -81,6 +81,26 @@ "message": "Label:", "description": "Label for the webhook label input field." }, + "optionsEmojiLabel": { + "message": "Emoji (optional):", + "description": "Label for the webhook emoji input field." + }, + "optionsEmojiPlaceholder": { + "message": "Pick an emoji, e.g. 🔔", + "description": "Placeholder for the webhook emoji input." + }, + "optionsEmojiChooseButton": { + "message": "Choose Emoji", + "description": "Button text to open the emoji picker." + }, + "optionsEmojiClearButton": { + "message": "Clear", + "description": "Button text to clear the emoji." + }, + "optionsEmojiSearchPlaceholder": { + "message": "Search emoji...", + "description": "Placeholder for the emoji search input." + }, "optionsLabelInputPlaceholder": { "message": "e.g. 'Send to Slack'", "description": "Placeholder for the webhook label input." diff --git a/options/options.css b/options/options.css index 3bbc2ba..64538fc 100644 --- a/options/options.css +++ b/options/options.css @@ -277,6 +277,46 @@ button:hover { display: none !important; } +/* Emoji picker */ +.emoji-input-row { + display: flex; + align-items: center; + gap: 8px; +} + +.emoji-picker { + margin-top: 8px; + border: 1px solid #d1d5db; + border-radius: 8px; + padding: 12px; + background: #fff; +} + +.emoji-picker input[type="text"] { + margin-bottom: 8px; +} + +.emoji-grid { + display: grid; + grid-template-columns: repeat(8, 1fr); + gap: 6px; +} + +.emoji-item { + font-size: 20px; + line-height: 1; + padding: 6px; + border: 1px solid transparent; + border-radius: 6px; + background: #f9fafb; + cursor: pointer; +} + +.emoji-item:hover { + background: #eef2ff; + border-color: #c7d2fe; +} + #no-webhooks-message { color: #6b7280; padding: 20px; diff --git a/options/options.html b/options/options.html index d7c5e46..8a55e52 100644 --- a/options/options.html +++ b/options/options.html @@ -31,6 +31,23 @@

__MSG_optionsAddWebhookHeader__

required />
+
+ +
+ + + +
+ +