refactor: migrate CC dark theme feature to the new architecture

Bug: twpowertools:176
Change-Id: Iaea1015a788038a8a31acbdd33b267b551e0b6a1
diff --git a/src/common/architecture/dependenciesProvider/DependenciesProvider.ts b/src/common/architecture/dependenciesProvider/DependenciesProvider.ts
index 68f77fd..794a681 100644
--- a/src/common/architecture/dependenciesProvider/DependenciesProvider.ts
+++ b/src/common/architecture/dependenciesProvider/DependenciesProvider.ts
@@ -4,10 +4,13 @@
 import WorkflowsImport from '../../../features/workflows/core/communityConsole/import';
 import Workflows from '../../../features/workflows/core/communityConsole/workflows';
 import StartupDataStorage from '../../../contentScripts/communityConsole/utils/StartupDataStorage';
+import ReportDialogColorThemeFix from '../../../features/ccDarkTheme/core/logic/reportDialog';
 
 export const AutoRefreshDependency = 'autoRefresh';
 export const ExtraInfoDependency = 'extraInfo';
 export const OptionsProviderDependency = 'optionsProvider';
+export const ReportDialogColorThemeFixDependency =
+  'report-dialog-color-theme-fix';
 export const StartupDataStorageDependency = 'startupDataStorage';
 export const WorkflowsDependency = 'workflows';
 export const WorkflowsImportDependency = 'workflowsImport';
@@ -15,6 +18,7 @@
   [AutoRefreshDependency]: AutoRefresh,
   [ExtraInfoDependency]: ExtraInfo,
   [OptionsProviderDependency]: OptionsProvider,
+  [ReportDialogColorThemeFixDependency]: ReportDialogColorThemeFix,
   [StartupDataStorageDependency]: StartupDataStorage,
   [WorkflowsDependency]: Workflows,
   [WorkflowsImportDependency]: WorkflowsImport,
diff --git a/src/contentScripts/communityConsole/main.js b/src/contentScripts/communityConsole/main.js
index 22e35f7..890f3b0 100644
--- a/src/contentScripts/communityConsole/main.js
+++ b/src/contentScripts/communityConsole/main.js
@@ -5,9 +5,6 @@
 
 import AvatarsHandler from './avatars.js';
 import {batchLock} from './batchLock.js';
-import {injectDarkThemeButton, isDarkThemeOn} from './darkTheme/darkTheme.js';
-import ReportDialogColorThemeFix from './darkTheme/reportDialog.js';
-import {unifiedProfilesFix} from './darkTheme/unifiedProfiles.js';
 // #!if ['chromium', 'chromium_mv3'].includes(browser_target)
 import {applyDragAndDropFixIfEnabled} from './dragAndDropFix.js';
 // #!endif
@@ -15,14 +12,9 @@
 import {kRepliesSectionSelector} from './threadToolbar/constants.js';
 import ThreadToolbar from './threadToolbar/threadToolbar.js';
 
-var mutationObserver, options, avatars, threadToolbar, flattenThreads,
-    reportDialogColorThemeFix;
+var mutationObserver, options, avatars, threadToolbar, flattenThreads;
 
 const watchedNodesSelectors = [
-  // App container (used to set up the intersection observer and inject the dark
-  // mode button)
-  'ec-app',
-
   // Scrollable content (used for the intersection observer)
   '.scrollable-content',
 
@@ -51,9 +43,6 @@
   // Thread list (used for the autorefresh feature)
   'ec-thread-list',
 
-  // Unified profile iframe and report dialog iframe
-  'iframe',
-
   // Thread page reply section (for the thread page toolbar)
   kRepliesSectionSelector,
 
@@ -63,17 +52,6 @@
 
 function handleCandidateNode(node) {
   if (typeof node.classList !== 'undefined') {
-    if (('tagName' in node) && node.tagName == 'EC-APP') {
-      // Inject the dark mode button
-      // TODO(avm99963): make this feature dynamic.
-      if (options.ccdarktheme && options.ccdarktheme_mode == 'switch') {
-        var rightControl = node.querySelector('header .right-control');
-        if (rightControl !== null)
-          injectDarkThemeButton(
-              rightControl, options.ccdarktheme_switch_status);
-      }
-    }
-
     // Show the "previous posts" links if the option is currently enabled.
     //   Here we're selecting the 'ec-user > div' element (unique child)
     if (node.matches(
@@ -112,16 +90,6 @@
       avatars.injectIfEnabled(node);
     }
 
-    if (node.tagName == 'IFRAME') {
-      // Redirect unified profile iframe to dark version if applicable
-      if (isDarkThemeOn(options) && unifiedProfilesFix.checkIframe(node)) {
-        unifiedProfilesFix.fixIframe(node);
-      }
-
-      // Set report dialog iframe's theme to the appropriate theme
-      reportDialogColorThemeFix.fixThemeIfReportDialogIframeAndApplicable(node);
-    }
-
     // Inject thread toolbar
     if (threadToolbar.shouldInject(node)) {
       threadToolbar.injectIfApplicable(node);
@@ -180,7 +148,6 @@
   avatars = new AvatarsHandler();
   threadToolbar = new ThreadToolbar();
   flattenThreads = new FlattenThreads();
-  reportDialogColorThemeFix = new ReportDialogColorThemeFix(options);
 
   // Before starting the mutation Observer, check whether we missed any
   // mutations by manually checking whether some watched nodes already
diff --git a/src/contentScripts/communityConsole/start.js b/src/contentScripts/communityConsole/start.js
index 2f8ba2a..b9c7f58 100644
--- a/src/contentScripts/communityConsole/start.js
+++ b/src/contentScripts/communityConsole/start.js
@@ -19,21 +19,6 @@
         'data-startup', JSON.stringify(startup));
   }
 
-  if (options.ccdarktheme) {
-    switch (options.ccdarktheme_mode) {
-      case 'switch':
-        if (options.ccdarktheme_switch_status == true)
-          injectStylesheet(chrome.runtime.getURL('ccDarkTheme.bundle.css'));
-        break;
-
-      case 'system':
-        injectStylesheet(chrome.runtime.getURL('ccDarkTheme.bundle.css'), {
-          'media': '(prefers-color-scheme: dark)',
-        });
-        break;
-    }
-  }
-
   if (options.uispacing) {
     injectStylesheet(chrome.runtime.getURL('css/ui_spacing/shared.css'));
     injectStylesheet(chrome.runtime.getURL('css/ui_spacing/console.css'));
diff --git a/src/features/Features.ts b/src/features/Features.ts
index a93f8a3..67c93aa 100644
--- a/src/features/Features.ts
+++ b/src/features/Features.ts
@@ -4,12 +4,14 @@
 import ScriptFilterListProvider from '../common/architecture/scripts/ScriptFilterListProvider';
 import ExtraInfoFeature from './extraInfo/extraInfo.feature';
 import WorkflowsFeature from './workflows/workflows.feature';
+import CCDarkThemeFeature from './ccDarkTheme/ccDarkTheme.feature';
 
 export type ConcreteFeatureClass = { new (): Feature };
 
 export default class Features extends ScriptFilterListProvider {
   private features: ConcreteFeatureClass[] = [
     AutoRefreshFeature,
+    CCDarkThemeFeature,
     ExtraInfoFeature,
     InfiniteScrollFeature,
     WorkflowsFeature,
diff --git a/src/features/ccDarkTheme/ccDarkTheme.feature.ts b/src/features/ccDarkTheme/ccDarkTheme.feature.ts
new file mode 100644
index 0000000..74efbf2
--- /dev/null
+++ b/src/features/ccDarkTheme/ccDarkTheme.feature.ts
@@ -0,0 +1,21 @@
+import Feature from '../../common/architecture/features/Feature';
+import { ConcreteScript } from '../../common/architecture/scripts/Script';
+import { OptionCodename } from '../../common/options/optionsPrototype';
+import InjectAutoDarkTheme from './scripts/injectAutoDarkTheme.script';
+import InjectForcedDarkTheme from './scripts/injectForcedDarkTheme.script';
+import CCDarkThemeNodeWatcherScript from './scripts/nodeWatcher.script';
+
+export default class CCDarkThemeFeature extends Feature {
+  public readonly scripts: ConcreteScript[] = [
+    InjectAutoDarkTheme,
+    InjectForcedDarkTheme,
+    CCDarkThemeNodeWatcherScript,
+  ];
+
+  readonly codename = 'darkTheme';
+  readonly relatedOptions: OptionCodename[] = [
+    'ccdarktheme',
+    'ccdarktheme_mode',
+    'ccdarktheme_switch_status',
+  ];
+}
diff --git a/src/contentScripts/communityConsole/darkTheme/darkTheme.js b/src/features/ccDarkTheme/core/logic/darkTheme.js
similarity index 64%
rename from src/contentScripts/communityConsole/darkTheme/darkTheme.js
rename to src/features/ccDarkTheme/core/logic/darkTheme.js
index c1888ea..d3d7e5e 100644
--- a/src/contentScripts/communityConsole/darkTheme/darkTheme.js
+++ b/src/features/ccDarkTheme/core/logic/darkTheme.js
@@ -1,7 +1,7 @@
-import {MDCTooltip} from '@material/tooltip';
+import { MDCTooltip } from '@material/tooltip';
 
-import {createPlainTooltip} from '../../../common/tooltip.js';
-import {createExtBadge} from '../utils/common.js';
+import { createPlainTooltip } from '../../../../common/tooltip.js';
+import { createExtBadge } from '../../../../contentScripts/communityConsole/utils/common.js';
 
 export const kColorThemeMode = Object.freeze({
   Auto: Symbol('auto'),
@@ -9,17 +9,16 @@
   Dark: Symbol('dark'),
 });
 
-export function injectDarkThemeButton(rightControl, previousDarkThemeOption) {
+export function injectDarkThemeButton(rightControl) {
   var darkThemeSwitch = document.createElement('material-button');
   darkThemeSwitch.classList.add('TWPT-dark-theme', 'TWPT-btn--with-badge');
   darkThemeSwitch.setAttribute('button', '');
 
   darkThemeSwitch.addEventListener('click', () => {
-    chrome.storage.sync.get(null, currentOptions => {
-      currentOptions.ccdarktheme_switch_status = !previousDarkThemeOption;
-      chrome.storage.sync.set(currentOptions, () => {
-        location.reload();
-      });
+    chrome.storage.sync.get(null, (currentOptions) => {
+      currentOptions.ccdarktheme_switch_status =
+        !currentOptions.ccdarktheme_switch_status;
+      chrome.storage.sync.set(currentOptions);
     });
   });
 
@@ -42,11 +41,13 @@
   darkThemeSwitch.appendChild(badgeContent);
 
   rightControl.style.width =
-      (parseInt(window.getComputedStyle(rightControl).width) + 58) + 'px';
+    parseInt(window.getComputedStyle(rightControl).width) + 58 + 'px';
   rightControl.insertAdjacentElement('afterbegin', darkThemeSwitch);
 
   createPlainTooltip(
-      switchContent, chrome.i18n.getMessage('inject_ccdarktheme_helper'));
+    switchContent,
+    chrome.i18n.getMessage('inject_ccdarktheme_helper'),
+  );
   new MDCTooltip(badgeTooltip);
 }
 
@@ -55,8 +56,9 @@
     return kColorThemeMode.Light;
   } else {
     if (options.ccdarktheme_mode == 'switch') {
-      return options.ccdarktheme_switch_status ? kColorThemeMode.Dark :
-                                                 kColorThemeMode.Light;
+      return options.ccdarktheme_switch_status
+        ? kColorThemeMode.Dark
+        : kColorThemeMode.Light;
     } else {
       return kColorThemeMode.Auto;
     }
@@ -68,8 +70,10 @@
 
   switch (activeColorTheme) {
     case kColorThemeMode.Auto:
-      return window.matchMedia &&
-          window.matchMedia('(prefers-color-scheme: dark)').matches;
+      return (
+        window.matchMedia &&
+        window.matchMedia('(prefers-color-scheme: dark)').matches
+      );
 
     case kColorThemeMode.Light:
       return false;
diff --git a/src/contentScripts/communityConsole/darkTheme/reportDialog.js b/src/features/ccDarkTheme/core/logic/reportDialog.js
similarity index 77%
rename from src/contentScripts/communityConsole/darkTheme/reportDialog.js
rename to src/features/ccDarkTheme/core/logic/reportDialog.js
index d7c84a8..4e79f4a 100644
--- a/src/contentScripts/communityConsole/darkTheme/reportDialog.js
+++ b/src/features/ccDarkTheme/core/logic/reportDialog.js
@@ -7,14 +7,13 @@
 };
 
 export default class ReportDialogColorThemeFix {
-  constructor(options) {
-    this.optionsAtPageLoad = options;
-  }
+  constructor() {}
 
-  fixThemeIfReportDialogIframeAndApplicable(iframe) {
+  async fixThemeIfReportDialogIframeAndApplicable(iframe, optionsProvider) {
     if (!this.isReportDialogIframe(iframe)) return;
 
-    const currentColorTheme = getCurrentColorTheme(this.optionsAtPageLoad);
+    const options = await optionsProvider.getOptionsValues();
+    const currentColorTheme = getCurrentColorTheme(options);
 
     // By default the report dialog is added with the light theme
     if (currentColorTheme === kColorThemeMode.Light) return;
diff --git a/src/contentScripts/communityConsole/darkTheme/unifiedProfiles.js b/src/features/ccDarkTheme/core/logic/unifiedProfiles.js
similarity index 100%
rename from src/contentScripts/communityConsole/darkTheme/unifiedProfiles.js
rename to src/features/ccDarkTheme/core/logic/unifiedProfiles.js
diff --git a/src/ccDarkTheme/abstracts/_variables.scss b/src/features/ccDarkTheme/core/styles/abstracts/_variables.scss
similarity index 97%
rename from src/ccDarkTheme/abstracts/_variables.scss
rename to src/features/ccDarkTheme/core/styles/abstracts/_variables.scss
index 9deae11..bcb922d 100644
--- a/src/ccDarkTheme/abstracts/_variables.scss
+++ b/src/features/ccDarkTheme/core/styles/abstracts/_variables.scss
@@ -1,4 +1,4 @@
-@use '../../md3/theme' as md3-theme;
+@use '../../../../../md3/theme' as md3-theme;
 
 :root {
   --TWPT-primary-text: #e8eaed;
diff --git a/src/ccDarkTheme/base/_base.scss b/src/features/ccDarkTheme/core/styles/base/_base.scss
similarity index 100%
rename from src/ccDarkTheme/base/_base.scss
rename to src/features/ccDarkTheme/core/styles/base/_base.scss
diff --git a/src/ccDarkTheme/components/_action-bar.scss b/src/features/ccDarkTheme/core/styles/components/_action-bar.scss
similarity index 100%
rename from src/ccDarkTheme/components/_action-bar.scss
rename to src/features/ccDarkTheme/core/styles/components/_action-bar.scss
diff --git a/src/ccDarkTheme/components/_activity-panel.scss b/src/features/ccDarkTheme/core/styles/components/_activity-panel.scss
similarity index 100%
rename from src/ccDarkTheme/components/_activity-panel.scss
rename to src/features/ccDarkTheme/core/styles/components/_activity-panel.scss
diff --git a/src/ccDarkTheme/components/_canned-responses.scss b/src/features/ccDarkTheme/core/styles/components/_canned-responses.scss
similarity index 100%
rename from src/ccDarkTheme/components/_canned-responses.scss
rename to src/features/ccDarkTheme/core/styles/components/_canned-responses.scss
diff --git a/src/ccDarkTheme/components/_drawer.scss b/src/features/ccDarkTheme/core/styles/components/_drawer.scss
similarity index 100%
rename from src/ccDarkTheme/components/_drawer.scss
rename to src/features/ccDarkTheme/core/styles/components/_drawer.scss
diff --git a/src/ccDarkTheme/components/_duplicate-thread.scss b/src/features/ccDarkTheme/core/styles/components/_duplicate-thread.scss
similarity index 100%
rename from src/ccDarkTheme/components/_duplicate-thread.scss
rename to src/features/ccDarkTheme/core/styles/components/_duplicate-thread.scss
diff --git a/src/ccDarkTheme/components/_icon.scss b/src/features/ccDarkTheme/core/styles/components/_icon.scss
similarity index 100%
rename from src/ccDarkTheme/components/_icon.scss
rename to src/features/ccDarkTheme/core/styles/components/_icon.scss
diff --git a/src/ccDarkTheme/components/_index.scss b/src/features/ccDarkTheme/core/styles/components/_index.scss
similarity index 100%
rename from src/ccDarkTheme/components/_index.scss
rename to src/features/ccDarkTheme/core/styles/components/_index.scss
diff --git a/src/ccDarkTheme/components/_loading-spinner.scss b/src/features/ccDarkTheme/core/styles/components/_loading-spinner.scss
similarity index 100%
rename from src/ccDarkTheme/components/_loading-spinner.scss
rename to src/features/ccDarkTheme/core/styles/components/_loading-spinner.scss
diff --git a/src/ccDarkTheme/components/_menu.scss b/src/features/ccDarkTheme/core/styles/components/_menu.scss
similarity index 100%
rename from src/ccDarkTheme/components/_menu.scss
rename to src/features/ccDarkTheme/core/styles/components/_menu.scss
diff --git a/src/ccDarkTheme/components/_profile-chart.scss b/src/features/ccDarkTheme/core/styles/components/_profile-chart.scss
similarity index 100%
rename from src/ccDarkTheme/components/_profile-chart.scss
rename to src/features/ccDarkTheme/core/styles/components/_profile-chart.scss
diff --git a/src/ccDarkTheme/components/_query-builder.scss b/src/features/ccDarkTheme/core/styles/components/_query-builder.scss
similarity index 100%
rename from src/ccDarkTheme/components/_query-builder.scss
rename to src/features/ccDarkTheme/core/styles/components/_query-builder.scss
diff --git a/src/ccDarkTheme/components/_review-bar.scss b/src/features/ccDarkTheme/core/styles/components/_review-bar.scss
similarity index 100%
rename from src/ccDarkTheme/components/_review-bar.scss
rename to src/features/ccDarkTheme/core/styles/components/_review-bar.scss
diff --git a/src/ccDarkTheme/components/_rich-text-editor.scss b/src/features/ccDarkTheme/core/styles/components/_rich-text-editor.scss
similarity index 100%
rename from src/ccDarkTheme/components/_rich-text-editor.scss
rename to src/features/ccDarkTheme/core/styles/components/_rich-text-editor.scss
diff --git a/src/ccDarkTheme/components/_settings.scss b/src/features/ccDarkTheme/core/styles/components/_settings.scss
similarity index 100%
rename from src/ccDarkTheme/components/_settings.scss
rename to src/features/ccDarkTheme/core/styles/components/_settings.scss
diff --git a/src/ccDarkTheme/components/_stepper.scss b/src/features/ccDarkTheme/core/styles/components/_stepper.scss
similarity index 100%
rename from src/ccDarkTheme/components/_stepper.scss
rename to src/features/ccDarkTheme/core/styles/components/_stepper.scss
diff --git a/src/ccDarkTheme/components/_tabs.scss b/src/features/ccDarkTheme/core/styles/components/_tabs.scss
similarity index 100%
rename from src/ccDarkTheme/components/_tabs.scss
rename to src/features/ccDarkTheme/core/styles/components/_tabs.scss
diff --git a/src/ccDarkTheme/components/_thread-composer.scss b/src/features/ccDarkTheme/core/styles/components/_thread-composer.scss
similarity index 100%
rename from src/ccDarkTheme/components/_thread-composer.scss
rename to src/features/ccDarkTheme/core/styles/components/_thread-composer.scss
diff --git a/src/ccDarkTheme/components/_thread-insert.scss b/src/features/ccDarkTheme/core/styles/components/_thread-insert.scss
similarity index 100%
rename from src/ccDarkTheme/components/_thread-insert.scss
rename to src/features/ccDarkTheme/core/styles/components/_thread-insert.scss
diff --git a/src/ccDarkTheme/components/_thread-list.scss b/src/features/ccDarkTheme/core/styles/components/_thread-list.scss
similarity index 100%
rename from src/ccDarkTheme/components/_thread-list.scss
rename to src/features/ccDarkTheme/core/styles/components/_thread-list.scss
diff --git a/src/ccDarkTheme/components/_thread-view.scss b/src/features/ccDarkTheme/core/styles/components/_thread-view.scss
similarity index 100%
rename from src/ccDarkTheme/components/_thread-view.scss
rename to src/features/ccDarkTheme/core/styles/components/_thread-view.scss
diff --git a/src/ccDarkTheme/components/_user-badge.scss b/src/features/ccDarkTheme/core/styles/components/_user-badge.scss
similarity index 100%
rename from src/ccDarkTheme/components/_user-badge.scss
rename to src/features/ccDarkTheme/core/styles/components/_user-badge.scss
diff --git a/src/ccDarkTheme/components/buttons/_button.scss b/src/features/ccDarkTheme/core/styles/components/buttons/_button.scss
similarity index 100%
rename from src/ccDarkTheme/components/buttons/_button.scss
rename to src/features/ccDarkTheme/core/styles/components/buttons/_button.scss
diff --git a/src/ccDarkTheme/components/buttons/_index.scss b/src/features/ccDarkTheme/core/styles/components/buttons/_index.scss
similarity index 100%
rename from src/ccDarkTheme/components/buttons/_index.scss
rename to src/features/ccDarkTheme/core/styles/components/buttons/_index.scss
diff --git a/src/ccDarkTheme/components/buttons/_reply.scss b/src/features/ccDarkTheme/core/styles/components/buttons/_reply.scss
similarity index 100%
rename from src/ccDarkTheme/components/buttons/_reply.scss
rename to src/features/ccDarkTheme/core/styles/components/buttons/_reply.scss
diff --git a/src/ccDarkTheme/components/buttons/_skip-to-main.scss b/src/features/ccDarkTheme/core/styles/components/buttons/_skip-to-main.scss
similarity index 100%
rename from src/ccDarkTheme/components/buttons/_skip-to-main.scss
rename to src/features/ccDarkTheme/core/styles/components/buttons/_skip-to-main.scss
diff --git a/src/ccDarkTheme/components/buttons/_yes-no.scss b/src/features/ccDarkTheme/core/styles/components/buttons/_yes-no.scss
similarity index 100%
rename from src/ccDarkTheme/components/buttons/_yes-no.scss
rename to src/features/ccDarkTheme/core/styles/components/buttons/_yes-no.scss
diff --git a/src/ccDarkTheme/components/custom/_chip.scss b/src/features/ccDarkTheme/core/styles/components/custom/_chip.scss
similarity index 100%
rename from src/ccDarkTheme/components/custom/_chip.scss
rename to src/features/ccDarkTheme/core/styles/components/custom/_chip.scss
diff --git a/src/ccDarkTheme/components/custom/_index.scss b/src/features/ccDarkTheme/core/styles/components/custom/_index.scss
similarity index 100%
rename from src/ccDarkTheme/components/custom/_index.scss
rename to src/features/ccDarkTheme/core/styles/components/custom/_index.scss
diff --git a/src/ccDarkTheme/components/custom/_log.scss b/src/features/ccDarkTheme/core/styles/components/custom/_log.scss
similarity index 100%
rename from src/ccDarkTheme/components/custom/_log.scss
rename to src/features/ccDarkTheme/core/styles/components/custom/_log.scss
diff --git a/src/ccDarkTheme/components/custom/_warning.scss b/src/features/ccDarkTheme/core/styles/components/custom/_warning.scss
similarity index 100%
rename from src/ccDarkTheme/components/custom/_warning.scss
rename to src/features/ccDarkTheme/core/styles/components/custom/_warning.scss
diff --git a/src/ccDarkTheme/components/dialogs/_bug-case-links.scss b/src/features/ccDarkTheme/core/styles/components/dialogs/_bug-case-links.scss
similarity index 100%
rename from src/ccDarkTheme/components/dialogs/_bug-case-links.scss
rename to src/features/ccDarkTheme/core/styles/components/dialogs/_bug-case-links.scss
diff --git a/src/ccDarkTheme/components/dialogs/_dialog.scss b/src/features/ccDarkTheme/core/styles/components/dialogs/_dialog.scss
similarity index 100%
rename from src/ccDarkTheme/components/dialogs/_dialog.scss
rename to src/features/ccDarkTheme/core/styles/components/dialogs/_dialog.scss
diff --git a/src/ccDarkTheme/components/dialogs/_index.scss b/src/features/ccDarkTheme/core/styles/components/dialogs/_index.scss
similarity index 100%
rename from src/ccDarkTheme/components/dialogs/_index.scss
rename to src/features/ccDarkTheme/core/styles/components/dialogs/_index.scss
diff --git a/src/ccDarkTheme/components/dialogs/_keyboard-shortcuts.scss b/src/features/ccDarkTheme/core/styles/components/dialogs/_keyboard-shortcuts.scss
similarity index 100%
rename from src/ccDarkTheme/components/dialogs/_keyboard-shortcuts.scss
rename to src/features/ccDarkTheme/core/styles/components/dialogs/_keyboard-shortcuts.scss
diff --git a/src/ccDarkTheme/components/dialogs/_private-message.scss b/src/features/ccDarkTheme/core/styles/components/dialogs/_private-message.scss
similarity index 100%
rename from src/ccDarkTheme/components/dialogs/_private-message.scss
rename to src/features/ccDarkTheme/core/styles/components/dialogs/_private-message.scss
diff --git a/src/ccDarkTheme/components/dialogs/_report.scss b/src/features/ccDarkTheme/core/styles/components/dialogs/_report.scss
similarity index 100%
rename from src/ccDarkTheme/components/dialogs/_report.scss
rename to src/features/ccDarkTheme/core/styles/components/dialogs/_report.scss
diff --git a/src/ccDarkTheme/components/inputs/_checkbox.scss b/src/features/ccDarkTheme/core/styles/components/inputs/_checkbox.scss
similarity index 100%
rename from src/ccDarkTheme/components/inputs/_checkbox.scss
rename to src/features/ccDarkTheme/core/styles/components/inputs/_checkbox.scss
diff --git a/src/ccDarkTheme/components/inputs/_dropdown-select.scss b/src/features/ccDarkTheme/core/styles/components/inputs/_dropdown-select.scss
similarity index 100%
rename from src/ccDarkTheme/components/inputs/_dropdown-select.scss
rename to src/features/ccDarkTheme/core/styles/components/inputs/_dropdown-select.scss
diff --git a/src/ccDarkTheme/components/inputs/_index.scss b/src/features/ccDarkTheme/core/styles/components/inputs/_index.scss
similarity index 100%
rename from src/ccDarkTheme/components/inputs/_index.scss
rename to src/features/ccDarkTheme/core/styles/components/inputs/_index.scss
diff --git a/src/ccDarkTheme/components/inputs/_pickers.scss b/src/features/ccDarkTheme/core/styles/components/inputs/_pickers.scss
similarity index 100%
rename from src/ccDarkTheme/components/inputs/_pickers.scss
rename to src/features/ccDarkTheme/core/styles/components/inputs/_pickers.scss
diff --git a/src/ccDarkTheme/components/inputs/_radio.scss b/src/features/ccDarkTheme/core/styles/components/inputs/_radio.scss
similarity index 100%
rename from src/ccDarkTheme/components/inputs/_radio.scss
rename to src/features/ccDarkTheme/core/styles/components/inputs/_radio.scss
diff --git a/src/ccDarkTheme/components/inputs/_selector.scss b/src/features/ccDarkTheme/core/styles/components/inputs/_selector.scss
similarity index 100%
rename from src/ccDarkTheme/components/inputs/_selector.scss
rename to src/features/ccDarkTheme/core/styles/components/inputs/_selector.scss
diff --git a/src/ccDarkTheme/components/inputs/_switch.scss b/src/features/ccDarkTheme/core/styles/components/inputs/_switch.scss
similarity index 100%
rename from src/ccDarkTheme/components/inputs/_switch.scss
rename to src/features/ccDarkTheme/core/styles/components/inputs/_switch.scss
diff --git a/src/ccDarkTheme/components/inputs/_text.scss b/src/features/ccDarkTheme/core/styles/components/inputs/_text.scss
similarity index 100%
rename from src/ccDarkTheme/components/inputs/_text.scss
rename to src/features/ccDarkTheme/core/styles/components/inputs/_text.scss
diff --git a/src/ccDarkTheme/components/notifications/_bell.scss b/src/features/ccDarkTheme/core/styles/components/notifications/_bell.scss
similarity index 100%
rename from src/ccDarkTheme/components/notifications/_bell.scss
rename to src/features/ccDarkTheme/core/styles/components/notifications/_bell.scss
diff --git a/src/ccDarkTheme/components/notifications/_index.scss b/src/features/ccDarkTheme/core/styles/components/notifications/_index.scss
similarity index 100%
rename from src/ccDarkTheme/components/notifications/_index.scss
rename to src/features/ccDarkTheme/core/styles/components/notifications/_index.scss
diff --git a/src/ccDarkTheme/components/notifications/_panel.scss b/src/features/ccDarkTheme/core/styles/components/notifications/_panel.scss
similarity index 100%
rename from src/ccDarkTheme/components/notifications/_panel.scss
rename to src/features/ccDarkTheme/core/styles/components/notifications/_panel.scss
diff --git a/src/ccDarkTheme/components/popups/_account-selector.scss b/src/features/ccDarkTheme/core/styles/components/popups/_account-selector.scss
similarity index 100%
rename from src/ccDarkTheme/components/popups/_account-selector.scss
rename to src/features/ccDarkTheme/core/styles/components/popups/_account-selector.scss
diff --git a/src/ccDarkTheme/components/popups/_announcements.scss b/src/features/ccDarkTheme/core/styles/components/popups/_announcements.scss
similarity index 100%
rename from src/ccDarkTheme/components/popups/_announcements.scss
rename to src/features/ccDarkTheme/core/styles/components/popups/_announcements.scss
diff --git a/src/ccDarkTheme/components/popups/_index.scss b/src/features/ccDarkTheme/core/styles/components/popups/_index.scss
similarity index 100%
rename from src/ccDarkTheme/components/popups/_index.scss
rename to src/features/ccDarkTheme/core/styles/components/popups/_index.scss
diff --git a/src/ccDarkTheme/components/popups/_popup.scss b/src/features/ccDarkTheme/core/styles/components/popups/_popup.scss
similarity index 100%
rename from src/ccDarkTheme/components/popups/_popup.scss
rename to src/features/ccDarkTheme/core/styles/components/popups/_popup.scss
diff --git a/src/ccDarkTheme/layout/_header.scss b/src/features/ccDarkTheme/core/styles/layout/_header.scss
similarity index 100%
rename from src/ccDarkTheme/layout/_header.scss
rename to src/features/ccDarkTheme/core/styles/layout/_header.scss
diff --git a/src/ccDarkTheme/layout/_index.scss b/src/features/ccDarkTheme/core/styles/layout/_index.scss
similarity index 100%
rename from src/ccDarkTheme/layout/_index.scss
rename to src/features/ccDarkTheme/core/styles/layout/_index.scss
diff --git a/src/ccDarkTheme/layout/_main.scss b/src/features/ccDarkTheme/core/styles/layout/_main.scss
similarity index 100%
rename from src/ccDarkTheme/layout/_main.scss
rename to src/features/ccDarkTheme/core/styles/layout/_main.scss
diff --git a/src/ccDarkTheme/main.scss b/src/features/ccDarkTheme/core/styles/main.scss
similarity index 100%
rename from src/ccDarkTheme/main.scss
rename to src/features/ccDarkTheme/core/styles/main.scss
diff --git a/src/ccDarkTheme/vendors/_index.scss b/src/features/ccDarkTheme/core/styles/vendors/_index.scss
similarity index 100%
rename from src/ccDarkTheme/vendors/_index.scss
rename to src/features/ccDarkTheme/core/styles/vendors/_index.scss
diff --git a/src/ccDarkTheme/vendors/google/IMPORTANT_NOTICE.txt b/src/features/ccDarkTheme/core/styles/vendors/google/IMPORTANT_NOTICE.txt
similarity index 100%
rename from src/ccDarkTheme/vendors/google/IMPORTANT_NOTICE.txt
rename to src/features/ccDarkTheme/core/styles/vendors/google/IMPORTANT_NOTICE.txt
diff --git a/src/ccDarkTheme/vendors/google/_common-interop-components.scss b/src/features/ccDarkTheme/core/styles/vendors/google/_common-interop-components.scss
similarity index 100%
rename from src/ccDarkTheme/vendors/google/_common-interop-components.scss
rename to src/features/ccDarkTheme/core/styles/vendors/google/_common-interop-components.scss
diff --git a/src/ccDarkTheme/vendors/google/_index.scss b/src/features/ccDarkTheme/core/styles/vendors/google/_index.scss
similarity index 100%
rename from src/ccDarkTheme/vendors/google/_index.scss
rename to src/features/ccDarkTheme/core/styles/vendors/google/_index.scss
diff --git a/src/ccDarkTheme/vendors/google/_profile.scss b/src/features/ccDarkTheme/core/styles/vendors/google/_profile.scss
similarity index 100%
rename from src/ccDarkTheme/vendors/google/_profile.scss
rename to src/features/ccDarkTheme/core/styles/vendors/google/_profile.scss
diff --git a/src/ccDarkTheme/vendors/google/_thread.scss b/src/features/ccDarkTheme/core/styles/vendors/google/_thread.scss
similarity index 100%
rename from src/ccDarkTheme/vendors/google/_thread.scss
rename to src/features/ccDarkTheme/core/styles/vendors/google/_thread.scss
diff --git a/src/features/ccDarkTheme/nodeWatcherHandlers/ecApp.handler.ts b/src/features/ccDarkTheme/nodeWatcherHandlers/ecApp.handler.ts
new file mode 100644
index 0000000..12d50bb
--- /dev/null
+++ b/src/features/ccDarkTheme/nodeWatcherHandlers/ecApp.handler.ts
@@ -0,0 +1,26 @@
+import CssSelectorNodeWatcherScriptHandler from '../../../common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler';
+import { NodeMutation } from '../../../common/nodeWatcher/NodeWatcherHandler';
+import { injectDarkThemeButton } from '../core/logic/darkTheme';
+import { CCDarkThemeNodeWatcherDependencies } from '../scripts/nodeWatcher.script';
+
+/**
+ * Injects the dark theme button.
+ */
+export default class CCDarkThemeEcAppHandler extends CssSelectorNodeWatcherScriptHandler<CCDarkThemeNodeWatcherDependencies> {
+  cssSelector = 'ec-app';
+
+  async onMutatedNode(mutation: NodeMutation) {
+    if (!(mutation.node instanceof Element)) return;
+
+    const optionsProvider = this.options.optionsProvider;
+    const isEnabled = await optionsProvider.isEnabled('ccdarktheme');
+    const mode = await optionsProvider.getOptionValue('ccdarktheme_mode');
+
+    // TODO(avm99963): make this feature dynamic.
+    if (isEnabled && mode === 'switch') {
+      const rightControl = mutation.node.querySelector('header .right-control');
+      if (rightControl === null) return;
+      injectDarkThemeButton(rightControl);
+    }
+  }
+}
diff --git a/src/features/ccDarkTheme/nodeWatcherHandlers/reportDialog.handler.ts b/src/features/ccDarkTheme/nodeWatcherHandlers/reportDialog.handler.ts
new file mode 100644
index 0000000..83942e5
--- /dev/null
+++ b/src/features/ccDarkTheme/nodeWatcherHandlers/reportDialog.handler.ts
@@ -0,0 +1,17 @@
+import CssSelectorNodeWatcherScriptHandler from '../../../common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler';
+import { NodeMutation } from '../../../common/nodeWatcher/NodeWatcherHandler';
+import { CCDarkThemeNodeWatcherDependencies } from '../scripts/nodeWatcher.script';
+
+/**
+ * Sets the report dialog iframe's theme to the appropriate theme.
+ */
+export default class CCDarkThemeReportDialogHandler extends CssSelectorNodeWatcherScriptHandler<CCDarkThemeNodeWatcherDependencies> {
+  cssSelector = 'iframe';
+
+  onMutatedNode(mutation: NodeMutation) {
+    this.options.reportDialogColorThemeFix.fixThemeIfReportDialogIframeAndApplicable(
+      mutation.node,
+      this.options.optionsProvider,
+    );
+  }
+}
diff --git a/src/features/ccDarkTheme/nodeWatcherHandlers/unifiedProfilesIframe.handler.ts b/src/features/ccDarkTheme/nodeWatcherHandlers/unifiedProfilesIframe.handler.ts
new file mode 100644
index 0000000..b71b9a7
--- /dev/null
+++ b/src/features/ccDarkTheme/nodeWatcherHandlers/unifiedProfilesIframe.handler.ts
@@ -0,0 +1,23 @@
+import CssSelectorNodeWatcherScriptHandler from '../../../common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler';
+import { NodeMutation } from '../../../common/nodeWatcher/NodeWatcherHandler';
+import { isDarkThemeOn } from '../core/logic/darkTheme';
+import { unifiedProfilesFix } from '../core/logic/unifiedProfiles';
+import { CCDarkThemeNodeWatcherDependencies } from '../scripts/nodeWatcher.script';
+
+/**
+ * Redirect unified profile iframe to dark version if applicable
+ */
+export default class CCDarkThemeUnifiedProfilesIframeHandler extends CssSelectorNodeWatcherScriptHandler<CCDarkThemeNodeWatcherDependencies> {
+  cssSelector = 'iframe';
+
+  async onMutatedNode(mutation: NodeMutation) {
+    const optionsValues = await this.options.optionsProvider.getOptionsValues();
+
+    if (
+      isDarkThemeOn(optionsValues) &&
+      unifiedProfilesFix.checkIframe(mutation.node)
+    ) {
+      unifiedProfilesFix.fixIframe(mutation.node);
+    }
+  }
+}
diff --git a/src/features/ccDarkTheme/scripts/injectAutoDarkTheme.script.ts b/src/features/ccDarkTheme/scripts/injectAutoDarkTheme.script.ts
new file mode 100644
index 0000000..c462a03
--- /dev/null
+++ b/src/features/ccDarkTheme/scripts/injectAutoDarkTheme.script.ts
@@ -0,0 +1,17 @@
+import { ScriptPage } from '../../../common/architecture/scripts/Script';
+import StylesheetScript from '../../../common/architecture/scripts/stylesheet/StylesheetScript';
+
+export default class InjectAutoDarkTheme extends StylesheetScript {
+  stylesheet = 'ccDarkTheme.bundle.css';
+  attributes = { media: '(prefers-color-scheme: dark)' };
+  page = ScriptPage.CommunityConsole;
+
+  async shouldBeInjected(): Promise<boolean> {
+    const ccDarkThemeEnabled =
+      await this.optionsProvider.isEnabled('ccdarktheme');
+    const ccDarkThemeMode =
+      await this.optionsProvider.getOptionValue('ccdarktheme_mode');
+
+    return ccDarkThemeEnabled && ccDarkThemeMode === 'system';
+  }
+}
diff --git a/src/features/ccDarkTheme/scripts/injectForcedDarkTheme.script.ts b/src/features/ccDarkTheme/scripts/injectForcedDarkTheme.script.ts
new file mode 100644
index 0000000..ebd221b
--- /dev/null
+++ b/src/features/ccDarkTheme/scripts/injectForcedDarkTheme.script.ts
@@ -0,0 +1,23 @@
+import { ScriptPage } from '../../../common/architecture/scripts/Script';
+import StylesheetScript from '../../../common/architecture/scripts/stylesheet/StylesheetScript';
+
+export default class InjectForcedDarkTheme extends StylesheetScript {
+  stylesheet = 'ccDarkTheme.bundle.css';
+  page = ScriptPage.CommunityConsole;
+
+  async shouldBeInjected(): Promise<boolean> {
+    const ccDarkThemeEnabled =
+      await this.optionsProvider.isEnabled('ccdarktheme');
+    const ccDarkThemeMode =
+      await this.optionsProvider.getOptionValue('ccdarktheme_mode');
+    const ccDarkThemeSwitchEnabled = await this.optionsProvider.isEnabled(
+      'ccdarktheme_switch_status',
+    );
+
+    return (
+      ccDarkThemeEnabled &&
+      ccDarkThemeMode === 'switch' &&
+      ccDarkThemeSwitchEnabled
+    );
+  }
+}
diff --git a/src/features/ccDarkTheme/scripts/nodeWatcher.script.ts b/src/features/ccDarkTheme/scripts/nodeWatcher.script.ts
new file mode 100644
index 0000000..7a78798
--- /dev/null
+++ b/src/features/ccDarkTheme/scripts/nodeWatcher.script.ts
@@ -0,0 +1,46 @@
+import DependenciesProviderSingleton, {
+  OptionsProviderDependency,
+  ReportDialogColorThemeFixDependency,
+} from '../../../common/architecture/dependenciesProvider/DependenciesProvider';
+import {
+  ScriptEnvironment,
+  ScriptPage,
+  ScriptRunPhase,
+} from '../../../common/architecture/scripts/Script';
+import NodeWatcherScript from '../../../common/architecture/scripts/nodeWatcher/NodeWatcherScript';
+import OptionsProvider from '../../../common/options/OptionsProvider';
+import ReportDialogColorThemeFix from '../core/logic/reportDialog';
+import CCDarkThemeEcAppHandler from '../nodeWatcherHandlers/ecApp.handler';
+import CCDarkThemeReportDialogHandler from '../nodeWatcherHandlers/reportDialog.handler';
+import CCDarkThemeUnifiedProfilesIframeHandler from '../nodeWatcherHandlers/unifiedProfilesIframe.handler';
+
+export interface CCDarkThemeNodeWatcherDependencies {
+  reportDialogColorThemeFix: ReportDialogColorThemeFix;
+  optionsProvider: OptionsProvider;
+}
+
+export default class CCDarkThemeNodeWatcherScript extends NodeWatcherScript<CCDarkThemeNodeWatcherDependencies> {
+  public page = ScriptPage.CommunityConsole;
+  public environment = ScriptEnvironment.ContentScript;
+  public runPhase = ScriptRunPhase.Main;
+  public handlers = new Map([
+    ['cc-dark-theme-ec-app', CCDarkThemeEcAppHandler],
+    ['cc-dark-theme-report-dialog', CCDarkThemeReportDialogHandler],
+    [
+      'cc-dark-theme-unified-profiles-iframe',
+      CCDarkThemeUnifiedProfilesIframeHandler,
+    ],
+  ]);
+
+  protected optionsFactory(): CCDarkThemeNodeWatcherDependencies {
+    const dependenciesProvider = DependenciesProviderSingleton.getInstance();
+    return {
+      reportDialogColorThemeFix: dependenciesProvider.getDependency(
+        ReportDialogColorThemeFixDependency,
+      ),
+      optionsProvider: dependenciesProvider.getDependency(
+        OptionsProviderDependency,
+      ),
+    };
+  }
+}
diff --git a/webpack.config.js b/webpack.config.js
index dbd9ccc..31bbfae 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -74,7 +74,7 @@
     mdcStyles: './src/mdc/index.js',
 
     // Compiled Sass
-    ccDarkTheme: './src/ccDarkTheme/main.scss?asCSSFile',
+    ccDarkTheme: './src/features/ccDarkTheme/core/styles/main.scss?asCSSFile',
   };
 
   // Background script (or service worker for MV3)