refactor: migrate workflows feature to the new architecture

Bug: twpowertools:176
Change-Id: Ib0af4cd828577f2a399be93264d45fdfddbad9b0
diff --git a/src/common/architecture/dependenciesProvider/DependenciesProvider.ts b/src/common/architecture/dependenciesProvider/DependenciesProvider.ts
index 105e6ac..5c7ec33 100644
--- a/src/common/architecture/dependenciesProvider/DependenciesProvider.ts
+++ b/src/common/architecture/dependenciesProvider/DependenciesProvider.ts
@@ -1,14 +1,20 @@
 import ExtraInfo from '../../../features/extraInfo/core';
 import AutoRefresh from '../../../features/autoRefresh/core/autoRefresh';
 import OptionsProvider from '../../OptionsProvider';
+import WorkflowsImport from '../../../features/workflows/core/communityConsole/import';
+import Workflows from '../../../features/workflows/core/communityConsole/workflows';
 
 export const AutoRefreshDependency = 'autoRefresh';
 export const ExtraInfoDependency = 'extraInfo';
 export const OptionsProviderDependency = 'optionsProvider';
+export const WorkflowsDependency = 'workflows';
+export const WorkflowsImportDependency = 'workflowsImport';
 export const DependenciesToClass = {
   [AutoRefreshDependency]: AutoRefresh,
   [ExtraInfoDependency]: ExtraInfo,
   [OptionsProviderDependency]: OptionsProvider,
+  [WorkflowsDependency]: Workflows,
+  [WorkflowsImportDependency]: WorkflowsImport,
 };
 
 interface OurWindow extends Window {
diff --git a/src/contentScripts/communityConsole/main.js b/src/contentScripts/communityConsole/main.js
index 309e41b..6cbfced 100644
--- a/src/contentScripts/communityConsole/main.js
+++ b/src/contentScripts/communityConsole/main.js
@@ -14,10 +14,9 @@
 import {default as FlattenThreads, kMatchingSelectors as kFlattenThreadMatchingSelectors} from './flattenThreads/flattenThreads.js';
 import {kRepliesSectionSelector} from './threadToolbar/constants.js';
 import ThreadToolbar from './threadToolbar/threadToolbar.js';
-import Workflows from './workflows/workflows.js';
 
-var mutationObserver, options, avatars, workflows,
-    threadToolbar, flattenThreads, reportDialogColorThemeFix;
+var mutationObserver, options, avatars, threadToolbar, flattenThreads,
+    reportDialogColorThemeFix;
 
 const watchedNodesSelectors = [
   // App container (used to set up the intersection observer and inject the dark
@@ -55,9 +54,6 @@
   // Unified profile iframe and report dialog iframe
   'iframe',
 
-  // Canned response tags (for the "import CR" popup for the workflows feature)
-  'ec-canned-response-row .tags',
-
   // Thread page reply section (for the thread page toolbar)
   kRepliesSectionSelector,
 
@@ -102,12 +98,6 @@
     }
     // #!endif
 
-    // Inject the worflows menu in the thread list if the option is currently
-    // enabled.
-    if (workflows.shouldAddThreadListBtn(node)) {
-      workflows.addThreadListBtnIfEnabled(node);
-    }
-
     // Inject the batch lock button in the thread list if the option is
     // currently enabled.
     if (batchLock.shouldAddButton(node)) {
@@ -132,12 +122,6 @@
       reportDialogColorThemeFix.fixThemeIfReportDialogIframeAndApplicable(node);
     }
 
-    // Add the "import" button in the canned responses view for the workflows
-    // feature if applicable.
-    if (node.matches('ec-canned-response-row .tags')) {
-      window.TWPTWorkflowsImport.addButtonIfEnabled(node);
-    }
-
     // Inject thread toolbar
     if (threadToolbar.shouldInject(node)) {
       threadToolbar.injectIfApplicable(node);
@@ -194,13 +178,10 @@
 
   // Initialize classes needed by the mutation observer
   avatars = new AvatarsHandler();
-  workflows = new Workflows();
   threadToolbar = new ThreadToolbar();
   flattenThreads = new FlattenThreads();
   reportDialogColorThemeFix = new ReportDialogColorThemeFix(options);
 
-  // workflowsImport is initialized in start.js
-
   // Before starting the mutation Observer, check whether we missed any
   // mutations by manually checking whether some watched nodes already
   // exist.
@@ -252,8 +233,6 @@
   injectStylesheet(chrome.runtime.getURL('css/batchlock_inject.css'));
   // Thread list avatars
   injectStylesheet(chrome.runtime.getURL('css/thread_list_avatars.css'));
-  // Workflows, Thread toolbar
-  injectScript(chrome.runtime.getURL('litComponentsInject.bundle.js'));
   // Thread toolbar
   injectStylesheet(chrome.runtime.getURL('css/thread_toolbar.css'));
   // Flatten threads
diff --git a/src/contentScripts/communityConsole/start.js b/src/contentScripts/communityConsole/start.js
index cc54540..cf5a7a5 100644
--- a/src/contentScripts/communityConsole/start.js
+++ b/src/contentScripts/communityConsole/start.js
@@ -2,7 +2,6 @@
 import {getOptions} from '../../common/optionsUtils.js';
 
 import FlattenThreadsReplyActionHandler from './flattenThreads/replyActionHandler.js';
-import WorkflowsImport from './workflows/import.js';
 
 getOptions(null).then(options => {
   /* IMPORTANT NOTE: Remember to change this when changing the "ifs" below!! */
@@ -20,10 +19,6 @@
         'data-startup', JSON.stringify(startup));
   }
 
-  // Initialized here instead of in main.js so the first event is received if it
-  // happens when the page loads.
-  window.TWPTWorkflowsImport = new WorkflowsImport();
-
   if (options.ccdarktheme) {
     switch (options.ccdarktheme_mode) {
       case 'switch':
diff --git a/src/features/Features.ts b/src/features/Features.ts
index 41e6b5d..a93f8a3 100644
--- a/src/features/Features.ts
+++ b/src/features/Features.ts
@@ -3,6 +3,7 @@
 import InfiniteScrollFeature from './infiniteScroll/infiniteScroll.feature';
 import ScriptFilterListProvider from '../common/architecture/scripts/ScriptFilterListProvider';
 import ExtraInfoFeature from './extraInfo/extraInfo.feature';
+import WorkflowsFeature from './workflows/workflows.feature';
 
 export type ConcreteFeatureClass = { new (): Feature };
 
@@ -11,6 +12,7 @@
     AutoRefreshFeature,
     ExtraInfoFeature,
     InfiniteScrollFeature,
+    WorkflowsFeature,
   ];
   private initializedFeatures: Feature[];
 
diff --git a/src/workflows/common.js b/src/features/workflows/core/common.js
similarity index 100%
rename from src/workflows/common.js
rename to src/features/workflows/core/common.js
diff --git a/src/contentScripts/communityConsole/workflows/actionRunners/attribute.js b/src/features/workflows/core/communityConsole/actionRunners/attribute.js
similarity index 78%
rename from src/contentScripts/communityConsole/workflows/actionRunners/attribute.js
rename to src/features/workflows/core/communityConsole/actionRunners/attribute.js
index bec2511..a542280 100644
--- a/src/contentScripts/communityConsole/workflows/actionRunners/attribute.js
+++ b/src/features/workflows/core/communityConsole/actionRunners/attribute.js
@@ -1,5 +1,5 @@
-import {CCApi} from '../../../../common/api.js';
-import {getAuthUser} from '../../../../common/communityConsoleUtils.js';
+import {CCApi} from '../../../../../common/api.js';
+import {getAuthUser} from '../../../../../common/communityConsoleUtils.js';
 
 export default class AttributeRunner {
   async execute(attributeAction, thread) {
diff --git a/src/contentScripts/communityConsole/workflows/actionRunners/readState.js b/src/features/workflows/core/communityConsole/actionRunners/readState.js
similarity index 80%
rename from src/contentScripts/communityConsole/workflows/actionRunners/readState.js
rename to src/features/workflows/core/communityConsole/actionRunners/readState.js
index 73d9a4f..f24f7c6 100644
--- a/src/contentScripts/communityConsole/workflows/actionRunners/readState.js
+++ b/src/features/workflows/core/communityConsole/actionRunners/readState.js
@@ -1,5 +1,5 @@
-import {CCApi} from '../../../../common/api.js';
-import {getAuthUser} from '../../../../common/communityConsoleUtils.js';
+import {CCApi} from '../../../../../common/api.js';
+import {getAuthUser} from '../../../../../common/communityConsoleUtils.js';
 
 export default class ReadStateRunner {
   execute(readState, thread) {
diff --git a/src/contentScripts/communityConsole/workflows/actionRunners/replyWithCR.js b/src/features/workflows/core/communityConsole/actionRunners/replyWithCR.js
similarity index 94%
rename from src/contentScripts/communityConsole/workflows/actionRunners/replyWithCR.js
rename to src/features/workflows/core/communityConsole/actionRunners/replyWithCR.js
index e534ef9..347d5c4 100644
--- a/src/contentScripts/communityConsole/workflows/actionRunners/replyWithCR.js
+++ b/src/features/workflows/core/communityConsole/actionRunners/replyWithCR.js
@@ -1,5 +1,5 @@
-import {CCApi} from '../../../../common/api.js';
-import {getAuthUser} from '../../../../common/communityConsoleUtils.js';
+import {CCApi} from '../../../../../common/api.js';
+import {getAuthUser} from '../../../../../common/communityConsoleUtils.js';
 
 const kPiiScanType_ScanNone = 0;
 const kType_Reply = 1;
diff --git a/src/contentScripts/communityConsole/workflows/components/TwptCRImportButton.js b/src/features/workflows/core/communityConsole/components/TwptCRImportButton.js
similarity index 92%
rename from src/contentScripts/communityConsole/workflows/components/TwptCRImportButton.js
rename to src/features/workflows/core/communityConsole/components/TwptCRImportButton.js
index 222416f..0431de7 100644
--- a/src/contentScripts/communityConsole/workflows/components/TwptCRImportButton.js
+++ b/src/features/workflows/core/communityConsole/components/TwptCRImportButton.js
@@ -3,7 +3,7 @@
 
 import {html, LitElement} from 'lit';
 
-import {SHARED_MD3_STYLES} from '../../../../common/styles/md3.js';
+import {SHARED_MD3_STYLES} from '../../../../../common/styles/md3.js';
 
 export default class TwptCRImportButton extends LitElement {
   static properties = {
diff --git a/src/contentScripts/communityConsole/workflows/components/TwptConfirmDialog.js b/src/features/workflows/core/communityConsole/components/TwptConfirmDialog.js
similarity index 95%
rename from src/contentScripts/communityConsole/workflows/components/TwptConfirmDialog.js
rename to src/features/workflows/core/communityConsole/components/TwptConfirmDialog.js
index 98cd9c5..bf80658 100644
--- a/src/contentScripts/communityConsole/workflows/components/TwptConfirmDialog.js
+++ b/src/features/workflows/core/communityConsole/components/TwptConfirmDialog.js
@@ -5,7 +5,7 @@
 import {css, html, LitElement} from 'lit';
 import {createRef, ref} from 'lit/directives/ref.js';
 
-import {SHARED_MD3_STYLES} from '../../../../common/styles/md3.js';
+import {SHARED_MD3_STYLES} from '../../../../../common/styles/md3.js';
 
 export default class TwptConfirmDialog extends LitElement {
   static properties = {
diff --git a/src/contentScripts/communityConsole/workflows/components/TwptWorkflowDialog.js b/src/features/workflows/core/communityConsole/components/TwptWorkflowDialog.js
similarity index 96%
rename from src/contentScripts/communityConsole/workflows/components/TwptWorkflowDialog.js
rename to src/features/workflows/core/communityConsole/components/TwptWorkflowDialog.js
index 8a48649..9e27edf 100644
--- a/src/contentScripts/communityConsole/workflows/components/TwptWorkflowDialog.js
+++ b/src/features/workflows/core/communityConsole/components/TwptWorkflowDialog.js
@@ -5,7 +5,7 @@
 import {css, html, LitElement} from 'lit';
 import {createRef, ref} from 'lit/directives/ref.js';
 
-import {SHARED_MD3_STYLES} from '../../../../common/styles/md3.js';
+import {SHARED_MD3_STYLES} from '../../../../../common/styles/md3.js';
 import WorkflowRunner from '../runner.js';
 
 export default class TwptWorkflowDialog extends LitElement {
diff --git a/src/contentScripts/communityConsole/workflows/components/TwptWorkflowProgress.js b/src/features/workflows/core/communityConsole/components/TwptWorkflowProgress.js
similarity index 92%
rename from src/contentScripts/communityConsole/workflows/components/TwptWorkflowProgress.js
rename to src/features/workflows/core/communityConsole/components/TwptWorkflowProgress.js
index 54e8c63..587db8f 100644
--- a/src/contentScripts/communityConsole/workflows/components/TwptWorkflowProgress.js
+++ b/src/features/workflows/core/communityConsole/components/TwptWorkflowProgress.js
@@ -2,12 +2,12 @@
 import '@material/web/button/filled-button.js';
 import '@material/web/button/text-button.js';
 
-import '../../../../workflows/manager/components/ActionEditor.js';
+import '../../manager/components/ActionEditor.js';
 
 import {css, html, LitElement} from 'lit';
 import {map} from 'lit/directives/map.js';
 
-import {SHARED_MD3_STYLES} from '../../../../common/styles/md3.js';
+import {SHARED_MD3_STYLES} from '../../../../../common/styles/md3.js';
 
 export default class TwptWorkflowProgress extends LitElement {
   static properties = {
diff --git a/src/contentScripts/communityConsole/workflows/components/TwptWorkflowsMenu.js b/src/features/workflows/core/communityConsole/components/TwptWorkflowsMenu.js
similarity index 94%
rename from src/contentScripts/communityConsole/workflows/components/TwptWorkflowsMenu.js
rename to src/features/workflows/core/communityConsole/components/TwptWorkflowsMenu.js
index db7359c..05fb216 100644
--- a/src/contentScripts/communityConsole/workflows/components/TwptWorkflowsMenu.js
+++ b/src/features/workflows/core/communityConsole/components/TwptWorkflowsMenu.js
@@ -4,13 +4,13 @@
 import '@material/web/menu/menu.js';
 import '@material/web/menu/menu-item.js';
 
-import consoleCommonStyles from '!!raw-loader!../../../../static/css/common/console.css';
+import consoleCommonStyles from '!!raw-loader!../../../../../static/css/common/console.css';
 
 import {css, html, LitElement, nothing, unsafeCSS} from 'lit';
 import {map} from 'lit/directives/map.js';
 import {createRef, ref} from 'lit/directives/ref.js';
 
-import {SHARED_MD3_STYLES} from '../../../../common/styles/md3.js';
+import {SHARED_MD3_STYLES} from '../../../../../common/styles/md3.js';
 
 export default class TwptWorkflowsMenu extends LitElement {
   static properties = {
diff --git a/src/contentScripts/communityConsole/workflows/components/index.js b/src/features/workflows/core/communityConsole/components/index.js
similarity index 95%
rename from src/contentScripts/communityConsole/workflows/components/index.js
rename to src/features/workflows/core/communityConsole/components/index.js
index f7651f0..31ac5cd 100644
--- a/src/contentScripts/communityConsole/workflows/components/index.js
+++ b/src/features/workflows/core/communityConsole/components/index.js
@@ -6,7 +6,7 @@
 import {css, html, LitElement} from 'lit';
 import {createRef, ref} from 'lit/directives/ref.js';
 
-import WorkflowsStorage from '../../../../workflows/workflowsStorage.js';
+import WorkflowsStorage from '../../workflowsStorage.js';
 
 export default class TwptWorkflowsInject extends LitElement {
   static properties = {
diff --git a/src/contentScripts/communityConsole/workflows/import.js b/src/features/workflows/core/communityConsole/import.js
similarity index 93%
rename from src/contentScripts/communityConsole/workflows/import.js
rename to src/features/workflows/core/communityConsole/import.js
index c91b541..68eb5a7 100644
--- a/src/contentScripts/communityConsole/workflows/import.js
+++ b/src/features/workflows/core/communityConsole/import.js
@@ -1,8 +1,8 @@
 import {waitFor} from 'poll-until-promise';
 
-import {recursiveParentElement} from '../../../common/commonUtils.js';
-import {injectStylesheet} from '../../../common/contentScriptsUtils.js';
-import {isOptionEnabled} from '../../../common/optionsUtils.js';
+import {recursiveParentElement} from '../../../../common/commonUtils.js';
+import {injectStylesheet} from '../../../../common/contentScriptsUtils.js';
+import {isOptionEnabled} from '../../../../common/optionsUtils.js';
 
 const kListCannedResponsesResponse = 'TWPT_ListCannedResponsesResponse';
 
diff --git a/src/contentScripts/communityConsole/workflows/models/thread.js b/src/features/workflows/core/communityConsole/models/thread.js
similarity index 93%
rename from src/contentScripts/communityConsole/workflows/models/thread.js
rename to src/features/workflows/core/communityConsole/models/thread.js
index 96f69de..2618bcc 100644
--- a/src/contentScripts/communityConsole/workflows/models/thread.js
+++ b/src/features/workflows/core/communityConsole/models/thread.js
@@ -1,8 +1,8 @@
 import {waitFor} from 'poll-until-promise';
 
-import {CCApi} from '../../../../common/api.js';
-import {parseUrl} from '../../../../common/commonUtils.js';
-import {getAuthUser} from '../../../../common/communityConsoleUtils.js';
+import {CCApi} from '../../../../../common/api.js';
+import {parseUrl} from '../../../../../common/commonUtils.js';
+import {getAuthUser} from '../../../../../common/communityConsoleUtils.js';
 
 export default class Thread {
   constructor(forumId, threadId) {
diff --git a/src/contentScripts/communityConsole/workflows/runner.js b/src/features/workflows/core/communityConsole/runner.js
similarity index 96%
rename from src/contentScripts/communityConsole/workflows/runner.js
rename to src/features/workflows/core/communityConsole/runner.js
index 164d226..dfcb4d1 100644
--- a/src/contentScripts/communityConsole/workflows/runner.js
+++ b/src/features/workflows/core/communityConsole/runner.js
@@ -1,5 +1,5 @@
-import {recursiveParentElement} from '../../../common/commonUtils.js';
-import * as pb from '../../../workflows/proto/main_pb.js';
+import {recursiveParentElement} from '../../../../common/commonUtils.js';
+import * as pb from '../proto/main_pb.js';
 
 import AttributeRunner from './actionRunners/attribute.js';
 import ReadStateRunner from './actionRunners/readState.js';
diff --git a/src/contentScripts/communityConsole/workflows/workflows.js b/src/features/workflows/core/communityConsole/workflows.js
similarity index 86%
rename from src/contentScripts/communityConsole/workflows/workflows.js
rename to src/features/workflows/core/communityConsole/workflows.js
index 7ba3cbc..652df29 100644
--- a/src/contentScripts/communityConsole/workflows/workflows.js
+++ b/src/features/workflows/core/communityConsole/workflows.js
@@ -1,6 +1,6 @@
-import {isOptionEnabled} from '../../../common/optionsUtils.js';
-import WorkflowsStorage from '../../../workflows/workflowsStorage.js';
-import {addElementToThreadListActions, shouldAddBtnToActionBar} from '../utils/common.js';
+import {isOptionEnabled} from '../../../../common/optionsUtils.js';
+import WorkflowsStorage from '../workflowsStorage.js';
+import {addElementToThreadListActions, shouldAddBtnToActionBar} from '../../../../contentScripts/communityConsole/utils/common.js';
 
 const wfDebugId = 'twpt-workflows';
 
diff --git a/src/workflows/manager/components/ActionEditor.js b/src/features/workflows/core/manager/components/ActionEditor.js
similarity index 100%
rename from src/workflows/manager/components/ActionEditor.js
rename to src/features/workflows/core/manager/components/ActionEditor.js
diff --git a/src/workflows/manager/components/AddDialog.js b/src/features/workflows/core/manager/components/AddDialog.js
similarity index 100%
rename from src/workflows/manager/components/AddDialog.js
rename to src/features/workflows/core/manager/components/AddDialog.js
diff --git a/src/workflows/manager/components/List.js b/src/features/workflows/core/manager/components/List.js
similarity index 100%
rename from src/workflows/manager/components/List.js
rename to src/features/workflows/core/manager/components/List.js
diff --git a/src/workflows/manager/components/WorkflowDialog.js b/src/features/workflows/core/manager/components/WorkflowDialog.js
similarity index 100%
rename from src/workflows/manager/components/WorkflowDialog.js
rename to src/features/workflows/core/manager/components/WorkflowDialog.js
diff --git a/src/workflows/manager/components/WorkflowEditor.js b/src/features/workflows/core/manager/components/WorkflowEditor.js
similarity index 100%
rename from src/workflows/manager/components/WorkflowEditor.js
rename to src/features/workflows/core/manager/components/WorkflowEditor.js
diff --git a/src/workflows/manager/components/actions/Attribute.js b/src/features/workflows/core/manager/components/actions/Attribute.js
similarity index 96%
rename from src/workflows/manager/components/actions/Attribute.js
rename to src/features/workflows/core/manager/components/actions/Attribute.js
index 7ab9a87..8076026 100644
--- a/src/workflows/manager/components/actions/Attribute.js
+++ b/src/features/workflows/core/manager/components/actions/Attribute.js
@@ -4,7 +4,7 @@
 import {html, LitElement} from 'lit';
 import {createRef, ref} from 'lit/directives/ref.js';
 
-import {SHARED_MD3_STYLES} from '../../../../common/styles/md3.js';
+import {SHARED_MD3_STYLES} from '../../../../../../common/styles/md3.js';
 import * as pb from '../../../proto/main_pb.js';
 
 import {FORM_STYLES} from './common.js';
diff --git a/src/workflows/manager/components/actions/ReplyWithCR.js b/src/features/workflows/core/manager/components/actions/ReplyWithCR.js
similarity index 96%
rename from src/workflows/manager/components/actions/ReplyWithCR.js
rename to src/features/workflows/core/manager/components/actions/ReplyWithCR.js
index e97f6f4..65d04e2 100644
--- a/src/workflows/manager/components/actions/ReplyWithCR.js
+++ b/src/features/workflows/core/manager/components/actions/ReplyWithCR.js
@@ -1,12 +1,12 @@
 import '@material/web/icon/icon.js';
 import '@material/web/switch/switch.js';
 import '@material/web/textfield/outlined-text-field.js';
-import '../../../../common/components/FormField.js';
+import '../../../../../../common/components/FormField.js';
 
 import {css, html, LitElement, nothing} from 'lit';
 import {createRef, ref} from 'lit/directives/ref.js';
 
-import {SHARED_MD3_STYLES} from '../../../../common/styles/md3.js';
+import {SHARED_MD3_STYLES} from '../../../../../../common/styles/md3.js';
 import * as pb from '../../../proto/main_pb.js';
 import { FORM_STYLES } from './common.js';
 
diff --git a/src/workflows/manager/components/actions/common.js b/src/features/workflows/core/manager/components/actions/common.js
similarity index 100%
rename from src/workflows/manager/components/actions/common.js
rename to src/features/workflows/core/manager/components/actions/common.js
diff --git a/src/workflows/manager/index.js b/src/features/workflows/core/manager/index.js
similarity index 95%
rename from src/workflows/manager/index.js
rename to src/features/workflows/core/manager/index.js
index b3b08d2..68bbf21 100644
--- a/src/workflows/manager/index.js
+++ b/src/features/workflows/core/manager/index.js
@@ -7,7 +7,7 @@
 import {css, html, LitElement} from 'lit';
 import {createRef, ref} from 'lit/directives/ref.js';
 
-import {SHARED_MD3_STYLES} from '../../common/styles/md3.js';
+import {SHARED_MD3_STYLES} from '../../../../common/styles/md3.js';
 import {default as WorkflowsStorage, kWorkflowsDataKey} from '../workflowsStorage.js';
 
 export default class WFApp extends LitElement {
diff --git a/src/workflows/manager/shared/actions.js b/src/features/workflows/core/manager/shared/actions.js
similarity index 100%
rename from src/workflows/manager/shared/actions.js
rename to src/features/workflows/core/manager/shared/actions.js
diff --git a/src/workflows/proto/main.proto b/src/features/workflows/core/proto/main.proto
similarity index 100%
rename from src/workflows/proto/main.proto
rename to src/features/workflows/core/proto/main.proto
diff --git a/src/workflows/proto/main_pb.js b/src/features/workflows/core/proto/main_pb.js
similarity index 100%
rename from src/workflows/proto/main_pb.js
rename to src/features/workflows/core/proto/main_pb.js
diff --git a/src/workflows/workflowsStorage.js b/src/features/workflows/core/workflowsStorage.js
similarity index 100%
rename from src/workflows/workflowsStorage.js
rename to src/features/workflows/core/workflowsStorage.js
diff --git a/src/features/workflows/nodeWatcherHandlers/crTags.handler.ts b/src/features/workflows/nodeWatcherHandlers/crTags.handler.ts
new file mode 100644
index 0000000..27c10d9
--- /dev/null
+++ b/src/features/workflows/nodeWatcherHandlers/crTags.handler.ts
@@ -0,0 +1,14 @@
+import CssSelectorNodeWatcherScriptHandler from '../../../common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler';
+import { NodeMutation } from '../../../common/nodeWatcher/NodeWatcherHandler';
+import { WorkflowsNodeWatcherDependencies } from '../scripts/nodeWatcher.script';
+
+/**
+ * Injects the button to import a canned response next to each CR.
+ */
+export default class WorkflowsImportCRTagsHandler extends CssSelectorNodeWatcherScriptHandler<WorkflowsNodeWatcherDependencies> {
+  cssSelector = 'ec-canned-response-row .tags';
+
+  onMutatedNode(mutation: NodeMutation) {
+    this.options.workflowsImport.addButtonIfEnabled(mutation.node);
+  }
+}
diff --git a/src/features/workflows/nodeWatcherHandlers/threadListActionBar.handler.ts b/src/features/workflows/nodeWatcherHandlers/threadListActionBar.handler.ts
new file mode 100644
index 0000000..ff62487
--- /dev/null
+++ b/src/features/workflows/nodeWatcherHandlers/threadListActionBar.handler.ts
@@ -0,0 +1,20 @@
+import { NodeWatcherScriptHandler } from '../../../common/architecture/scripts/nodeWatcher/handlers/NodeWatcherScriptHandler';
+import { NodeMutation } from '../../../common/nodeWatcher/NodeWatcherHandler';
+import { WorkflowsNodeWatcherDependencies } from '../scripts/nodeWatcher.script';
+
+/**
+ * Injects the workflows menu in the thread list.
+ */
+export default class WorkflowsThreadListActionBarHandler extends NodeWatcherScriptHandler<WorkflowsNodeWatcherDependencies> {
+  initialDiscoverySelector =
+    ':is(ec-bulk-actions material-button[debugid="mark-read-button"],' +
+    'ec-bulk-actions material-button[debugid="mark-unread-button"])';
+
+  nodeFilter(mutation: NodeMutation) {
+    return this.options.workflows.shouldAddThreadListBtn(mutation.node);
+  }
+
+  onMutatedNode(mutation: NodeMutation) {
+    this.options.workflows.addThreadListBtnIfEnabled(mutation.node);
+  }
+}
diff --git a/src/features/workflows/scripts/dependenciesSetUpAtMain.script.ts b/src/features/workflows/scripts/dependenciesSetUpAtMain.script.ts
new file mode 100644
index 0000000..37ec85f
--- /dev/null
+++ b/src/features/workflows/scripts/dependenciesSetUpAtMain.script.ts
@@ -0,0 +1,17 @@
+import {
+  Dependency,
+  WorkflowsDependency,
+} from '../../../common/architecture/dependenciesProvider/DependenciesProvider';
+import {
+  ScriptEnvironment,
+  ScriptPage,
+  ScriptRunPhase,
+} from '../../../common/architecture/scripts/Script';
+import SetUpDependenciesScript from '../../../common/architecture/scripts/setUpDependencies/SetUpDependenciesScript';
+
+export default class WorkflowsDependenciesSetUpAtMainScript extends SetUpDependenciesScript {
+  public page = ScriptPage.CommunityConsole;
+  public environment = ScriptEnvironment.ContentScript;
+  public runPhase = ScriptRunPhase.Main;
+  public dependencies: Dependency[] = [WorkflowsDependency];
+}
diff --git a/src/features/workflows/scripts/dependenciesSetUpAtStart.script.ts b/src/features/workflows/scripts/dependenciesSetUpAtStart.script.ts
new file mode 100644
index 0000000..0b52082
--- /dev/null
+++ b/src/features/workflows/scripts/dependenciesSetUpAtStart.script.ts
@@ -0,0 +1,18 @@
+import {
+  Dependency,
+  WorkflowsImportDependency,
+} from '../../../common/architecture/dependenciesProvider/DependenciesProvider';
+import {
+  ScriptEnvironment,
+  ScriptPage,
+  ScriptRunPhase,
+} from '../../../common/architecture/scripts/Script';
+import SetUpDependenciesScript from '../../../common/architecture/scripts/setUpDependencies/SetUpDependenciesScript';
+
+export default class WorkflowsDependenciesSetUpAtStartScript extends SetUpDependenciesScript {
+  public priority = 102;
+  public page = ScriptPage.CommunityConsole;
+  public environment = ScriptEnvironment.ContentScript;
+  public runPhase = ScriptRunPhase.Start;
+  public dependencies: Dependency[] = [WorkflowsImportDependency];
+}
diff --git a/src/features/workflows/scripts/nodeWatcher.script.ts b/src/features/workflows/scripts/nodeWatcher.script.ts
new file mode 100644
index 0000000..8d26c86
--- /dev/null
+++ b/src/features/workflows/scripts/nodeWatcher.script.ts
@@ -0,0 +1,39 @@
+import DependenciesProviderSingleton, {
+  WorkflowsDependency,
+  WorkflowsImportDependency,
+} from '../../../common/architecture/dependenciesProvider/DependenciesProvider';
+import {
+  ScriptEnvironment,
+  ScriptPage,
+  ScriptRunPhase,
+} from '../../../common/architecture/scripts/Script';
+import NodeWatcherScript from '../../../common/architecture/scripts/nodeWatcher/NodeWatcherScript';
+import WorkflowsImport from '../core/communityConsole/import';
+import Workflows from '../core/communityConsole/workflows';
+import WorkflowsImportCRTagsHandler from '../nodeWatcherHandlers/crTags.handler';
+import WorkflowsThreadListActionBarHandler from '../nodeWatcherHandlers/threadListActionBar.handler';
+
+export interface WorkflowsNodeWatcherDependencies {
+  workflows: Workflows;
+  workflowsImport: WorkflowsImport;
+}
+
+export default class WorkflowsNodeWatcherScript extends NodeWatcherScript<WorkflowsNodeWatcherDependencies> {
+  public page = ScriptPage.CommunityConsole;
+  public environment = ScriptEnvironment.ContentScript;
+  public runPhase = ScriptRunPhase.Main;
+  public handlers = new Map([
+    ['workflowsImportCRTags', WorkflowsImportCRTagsHandler],
+    ['workflowsThreadListActionBar', WorkflowsThreadListActionBarHandler],
+  ]);
+
+  protected optionsFactory(): WorkflowsNodeWatcherDependencies {
+    const dependenciesProvider = DependenciesProviderSingleton.getInstance();
+    return {
+      workflows: dependenciesProvider.getDependency(WorkflowsDependency),
+      workflowsImport: dependenciesProvider.getDependency(
+        WorkflowsImportDependency,
+      ),
+    };
+  }
+}
diff --git a/src/features/workflows/workflows.feature.ts b/src/features/workflows/workflows.feature.ts
new file mode 100644
index 0000000..7a6976c
--- /dev/null
+++ b/src/features/workflows/workflows.feature.ts
@@ -0,0 +1,17 @@
+import Feature from '../../common/architecture/features/Feature';
+import { ConcreteScript } from '../../common/architecture/scripts/Script';
+import { OptionCodename } from '../../common/optionsPrototype';
+import WorkflowsDependenciesSetUpAtMainScript from './scripts/dependenciesSetUpAtMain.script';
+import WorkflowsDependenciesSetUpAtStartScript from './scripts/dependenciesSetUpAtStart.script';
+import WorkflowsNodeWatcherScript from './scripts/nodeWatcher.script';
+
+export default class WorkflowsFeature extends Feature {
+  public readonly scripts: ConcreteScript[] = [
+    WorkflowsDependenciesSetUpAtStartScript,
+    WorkflowsDependenciesSetUpAtMainScript,
+    WorkflowsNodeWatcherScript,
+  ];
+
+  readonly codename = 'workflows';
+  readonly relatedOptions: OptionCodename[] = ['workflows'];
+}
diff --git a/src/injections/litComponentsInject.js b/src/injections/litComponentsInject.js
index 3154681..b1c40cf 100644
--- a/src/injections/litComponentsInject.js
+++ b/src/injections/litComponentsInject.js
@@ -2,7 +2,7 @@
 // use LitElement (and thus custom web elements). This is done by injecting this
 // javascript file instead of placing this code directly in the content script
 // because `window.customElements` doesn't exist in content scripts.
-import '../contentScripts/communityConsole/workflows/components/index.js';
+import '../features/workflows/core/communityConsole/components/index.js';
 import '../contentScripts/communityConsole/threadToolbar/components/index.js';
 import '../contentScripts/communityConsole/flattenThreads/components/index.js';
 import '../contentScripts/communityConsole/updateHandler/banner/components/index.js';
diff --git a/src/scripts/Scripts.ts b/src/scripts/Scripts.ts
index 39dd9d6..5696ddb 100644
--- a/src/scripts/Scripts.ts
+++ b/src/scripts/Scripts.ts
@@ -1,5 +1,6 @@
 import Script, { ConcreteScript } from '../common/architecture/scripts/Script';
 import ScriptFilterListProvider from '../common/architecture/scripts/ScriptFilterListProvider';
+import InjectLitComponentsScript from './litComponents/injectLitComponents.script';
 import MWI18nServerScript from './mainWorldServers/MWI18nServerScript.script';
 import MWOptionsWatcherServerScript from './mainWorldServers/MWOptionsWatcherServerScript.script';
 import OptionsProviderSetUpScript from './optionsProvider/optionsProvider.script';
@@ -7,6 +8,7 @@
 
 export default class StandaloneScripts extends ScriptFilterListProvider {
   private scripts: ConcreteScript[] = [
+    InjectLitComponentsScript,
     MWI18nServerScript,
     MWOptionsWatcherServerScript,
     OptionsProviderSetUpScript,
diff --git a/src/scripts/litComponents/injectLitComponents.script.ts b/src/scripts/litComponents/injectLitComponents.script.ts
new file mode 100644
index 0000000..7040b45
--- /dev/null
+++ b/src/scripts/litComponents/injectLitComponents.script.ts
@@ -0,0 +1,12 @@
+import Script, { ScriptEnvironment, ScriptPage, ScriptRunPhase } from "../../common/architecture/scripts/Script"
+import { injectScript } from "../../common/contentScriptsUtils";
+
+export default class InjectLitComponentsScript extends Script {
+  page = ScriptPage.CommunityConsole;
+  environment = ScriptEnvironment.ContentScript;
+  runPhase = ScriptRunPhase.Main;
+
+  execute() {
+    injectScript(chrome.runtime.getURL('litComponentsInject.bundle.js'));
+  }
+}
diff --git a/webpack.config.js b/webpack.config.js
index 06e0b77..dbd9ccc 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -68,7 +68,7 @@
     optionsCommon: './src/options/optionsCommon.js',
 
     // Workflow manager
-    workflowManager: './src/workflows/manager/index.js',
+    workflowManager: './src/features/workflows/core/manager/index.js',
 
     // Common CSS
     mdcStyles: './src/mdc/index.js',