Files
Outcut/content/observer.js
Joel Brock 3de2db7d89 rename: Outlook Relook → Outcut, add keyboard presets, remove design UI from popup
- Rename extension to "Outcut" throughout all source files and console logs
- Add KEY_PRESETS object (gmail/outlook) and matchesAction() helper to keyboard.js
- Rewrite handleKeydown to use preset-based dispatch instead of hardcoded switch
- Update updateSettings to re-init when keyboardPreset changes
- Add keyboardPreset: 'gmail' default to settings-defaults.js
- Replace popup Design Tweaks UI with preset dropdown + dynamic help text
- Keep all design tweak content script logic intact (activatable via storage)
- Update manifest: version 1.0.0, remove activeTab, add homepage_url
2026-04-27 14:11:45 -07:00

127 lines
3.7 KiB
JavaScript

// Outlook Relook — MutationObserver
// Watches OWA's DOM and removes dynamically injected elements
// based on active settings.
window.OutlookRelook = window.OutlookRelook || {};
window.OutlookRelook.Observer = (function () {
'use strict';
let observer = null;
let currentSettings = {};
// Map setting keys to selector registry names
// Each setting can suppress one or more logical elements
const SETTING_TO_SELECTORS = {
hideCopilot: ['copilot-button', 'copilot-pane', 'copilot-compose-suggestions'],
hideSuggestedReplies: ['suggested-replies'],
hidePromoBanners: ['promo-banners'],
hideFocusedOtherTabs: ['focused-other-tabs'],
hideSidebarAppIcons: ['sidebar-app-icons'],
hideGroupsSection: ['groups-section'],
hideMyDayButtons: ['my-day-buttons'],
hideSenderAvatars: ['sender-avatars'],
hideFeatureDiscovery: ['feature-discovery'],
hideVivaInsights: ['viva-insights'],
hideUnreadOtherBanner: ['unread-other-banner'],
hideActivityFeed: ['activity-feed'],
};
// Text content patterns to match elements by their inner text.
// Used when aria/data selectors don't catch dynamically injected content.
const TEXT_PATTERNS = {
hidePromoBanners: [
/try the new outlook/i,
/upgrade to premium/i,
/get the outlook app/i,
/switch to the new/i,
],
hideFeatureDiscovery: [
/what's new/i,
/new feature/i,
/did you know/i,
],
};
function suppressElements() {
for (const [settingKey, selectorNames] of Object.entries(SETTING_TO_SELECTORS)) {
if (!currentSettings[settingKey]) continue;
for (const name of selectorNames) {
const elements = window.OutlookRelook.resolveSelector(name);
for (const el of elements) {
if (el.style.display !== 'none') {
el.style.display = 'none';
console.log('[Outcut] Suppressed: ' + name, el);
}
}
}
}
// Text-based suppression for elements missed by selectors
for (const [settingKey, patterns] of Object.entries(TEXT_PATTERNS)) {
if (!currentSettings[settingKey]) continue;
for (const pattern of patterns) {
// Check buttons, banners, and generic containers
const candidates = document.querySelectorAll(
'[role="alert"], [role="banner"], [role="dialog"], [role="status"], button, a'
);
for (const el of candidates) {
if (el.style.display === 'none') continue;
var text = el.textContent || '';
if (pattern.test(text) && text.length < 200) {
el.style.display = 'none';
console.log('[Outcut] Suppressed by text: "' + text.trim().substring(0, 50) + '"', el);
}
}
}
}
}
function start(settings) {
currentSettings = settings;
// Initial pass
suppressElements();
// Watch for DOM mutations
observer = new MutationObserver(function (mutations) {
// Debounce: only process if nodes were actually added
var hasAddedNodes = false;
for (var i = 0; i < mutations.length; i++) {
if (mutations[i].addedNodes.length > 0) {
hasAddedNodes = true;
break;
}
}
if (hasAddedNodes) {
suppressElements();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
console.log('[Outcut] Observer started');
}
function updateSettings(settings) {
currentSettings = settings;
// Re-run suppression with new settings
suppressElements();
}
function stop() {
if (observer) {
observer.disconnect();
observer = null;
console.log('[Outcut] Observer stopped');
}
}
return { start: start, updateSettings: updateSettings, stop: stop };
})();