浏览代码

feat: add workflow variable

Ahmad Kholid 3 年之前
父节点
当前提交
dc8242b546
共有 26 个文件被更改,包括 392 次插入262 次删除
  1. 33 23
      src/background/workflow-engine/blocks-handler/handler-interaction-block.js
  2. 6 0
      src/background/workflow-engine/blocks-handler/handler-loop-data.js
  3. 0 2
      src/background/workflow-engine/blocks-handler/handler-new-tab.js
  4. 20 26
      src/background/workflow-engine/blocks-handler/handler-take-screenshot.js
  5. 5 1
      src/background/workflow-engine/engine.js
  6. 35 12
      src/components/newtab/logs/LogsDataViewer.vue
  7. 7 2
      src/components/newtab/workflow/WorkflowEditBlock.vue
  8. 50 44
      src/components/newtab/workflow/edit/EditAttributeValue.vue
  9. 35 27
      src/components/newtab/workflow/edit/EditForms.vue
  10. 60 52
      src/components/newtab/workflow/edit/EditGetText.vue
  11. 0 1
      src/components/newtab/workflow/edit/EditGoogleSheets.vue
  12. 0 1
      src/components/newtab/workflow/edit/EditInteractionBase.vue
  13. 28 13
      src/components/newtab/workflow/edit/EditLoopData.vue
  14. 1 1
      src/components/newtab/workflow/edit/EditNewTab.vue
  15. 34 26
      src/components/newtab/workflow/edit/EditTakeScreenshot.vue
  16. 8 1
      src/components/ui/UiCheckbox.vue
  17. 6 4
      src/components/ui/UiInput.vue
  18. 17 15
      src/components/ui/UiTextarea.vue
  19. 27 7
      src/content/blocks-handler/handler-javascript-code.js
  20. 1 0
      src/locales/en/blocks.json
  21. 8 0
      src/locales/en/newtab.json
  22. 1 1
      src/newtab/pages/Logs.vue
  23. 1 1
      src/newtab/pages/Workflows.vue
  24. 1 1
      src/newtab/pages/logs/[id].vue
  25. 1 1
      src/newtab/pages/workflows/[id].vue
  26. 7 0
      src/utils/shared.js

+ 33 - 23
src/background/workflow-engine/blocks-handler/handler-interaction-block.js

@@ -34,47 +34,57 @@ async function interactionHandler(block, { refData }) {
       frameId: this.activeTab.frameId || 0,
       frameId: this.activeTab.frameId || 0,
     });
     });
 
 
-    if (block.name === 'link')
-      await new Promise((resolve) => setTimeout(resolve, 5000));
-
     if (objectHasKey(block.data, 'dataColumn')) {
     if (objectHasKey(block.data, 'dataColumn')) {
       const dontSaveData =
       const dontSaveData =
         (block.name === 'forms' && !block.data.getValue) ||
         (block.name === 'forms' && !block.data.getValue) ||
         !block.data.saveData;
         !block.data.saveData;
 
 
-      if (dontSaveData)
+      if (dontSaveData) {
         return {
         return {
           data,
           data,
           nextBlockId,
           nextBlockId,
         };
         };
+      }
 
 
       const currentColumnType =
       const currentColumnType =
         this.columns[block.data.dataColumn]?.type || 'any';
         this.columns[block.data.dataColumn]?.type || 'any';
+      const insertDataToColumn = (value) => {
+        this.addDataToColumn(block.data.dataColumn, value);
+
+        const addExtraRow =
+          objectHasKey(block.data, 'extraRowDataColumn') &&
+          block.data.addExtraRow;
+        if (addExtraRow) {
+          this.addDataToColumn(
+            block.data.extraRowDataColumn,
+            block.data.extraRowValue
+          );
+        }
+      };
 
 
       if (Array.isArray(data) && currentColumnType !== 'array') {
       if (Array.isArray(data) && currentColumnType !== 'array') {
-        data.forEach((item) => {
-          this.addDataToColumn(block.data.dataColumn, item);
-          if (objectHasKey(block.data, 'extraRowDataColumn')) {
-            if (block.data.addExtraRow)
-              this.addDataToColumn(
-                block.data.extraRowDataColumn,
-                block.data.extraRowValue
-              );
-          }
+        data.forEach((value) => {
+          insertDataToColumn(value);
         });
         });
       } else {
       } else {
-        this.addDataToColumn(block.data.dataColumn, data);
-        if (objectHasKey(block.data, 'extraRowDataColumn')) {
-          if (block.data.addExtraRow)
-            this.addDataToColumn(
-              block.data.extraRowDataColumn,
-              block.data.extraRowValue
-            );
-        }
+        insertDataToColumn(data);
       }
       }
-    } else if (block.name === 'javascript-code') {
-      const arrData = Array.isArray(data) ? data : [data];
+    }
+
+    const isJavascriptBlock = block.name === 'javascript-code';
+
+    if (block.data.assignVariable && !isJavascriptBlock) {
+      this.referenceData.variables[block.data.variableName] = data;
+    }
+
+    if (isJavascriptBlock) {
+      Object.keys(data.variables).forEach((varName) => {
+        this.referenceData.variables[varName] = data.variables[varName];
+      });
 
 
+      const arrData = Array.isArray(data.columns)
+        ? data.columns
+        : [data.columns];
       this.addDataToColumn(arrData);
       this.addDataToColumn(arrData);
     }
     }
 
 

+ 6 - 0
src/background/workflow-engine/blocks-handler/handler-loop-data.js

@@ -1,4 +1,5 @@
 import { getBlockConnection } from '../helper';
 import { getBlockConnection } from '../helper';
+import { parseJSON } from '@/utils/helper';
 
 
 function loopData(block) {
 function loopData(block) {
   return new Promise((resolve, reject) => {
   return new Promise((resolve, reject) => {
@@ -38,6 +39,11 @@ function loopData(block) {
         case 'custom-data':
         case 'custom-data':
           currLoopData = JSON.parse(data.loopData);
           currLoopData = JSON.parse(data.loopData);
           break;
           break;
+        case 'variable': {
+          const variableVal = this.referenceData.variables[data.variableName];
+          currLoopData = parseJSON(variableVal, variableVal);
+          break;
+        }
         default:
         default:
       }
       }
 
 

+ 0 - 2
src/background/workflow-engine/blocks-handler/handler-new-tab.js

@@ -64,8 +64,6 @@ async function newTab(block) {
       nextBlockId,
       nextBlockId,
     };
     };
   } catch (error) {
   } catch (error) {
-    console.error(error);
-    console.dir(error);
     error.nextBlockId = nextBlockId;
     error.nextBlockId = nextBlockId;
 
 
     throw error;
     throw error;

+ 20 - 26
src/background/workflow-engine/blocks-handler/handler-take-screenshot.js

@@ -20,30 +20,26 @@ function saveImage({ fileName, uri, ext }) {
   image.src = uri;
   image.src = uri;
 }
 }
 
 
-async function takeScreenshot(block) {
-  const nextBlockId = getBlockConnection(block);
-  const {
-    ext,
-    quality,
-    captureActiveTab,
-    fileName,
-    saveToColumn,
-    dataColumn,
-    fullPage,
-  } = block.data;
-
+async function takeScreenshot({ data, outputs, name }) {
+  const nextBlockId = getBlockConnection({ outputs });
   const saveToComputer =
   const saveToComputer =
-    typeof block.data.saveToComputer === 'undefined'
-      ? true
-      : block.data.saveToComputer;
+    typeof data.saveToComputer === 'undefined' || data.saveToComputer;
 
 
   try {
   try {
+    let screenshot = null;
     const options = {
     const options = {
       quality,
       quality,
-      format: ext || 'png',
+      format: data.ext || 'png',
+    };
+    const saveScreenshot = (dataUrl) => {
+      if (data.saveToColumn) this.addDataToColumn(data.dataColumn, dataUrl);
+      if (saveToComputer)
+        saveImage({ fileName: data.fileName, uri: dataUrl, ext: data.ext });
+      if (data.assignVariable)
+        this.referenceData.variables[data.variableName] = dataUrl;
     };
     };
 
 
-    if (captureActiveTab) {
+    if (data.captureActiveTab) {
       if (!this.activeTab.id) {
       if (!this.activeTab.id) {
         throw new Error('no-tab');
         throw new Error('no-tab');
       }
       }
@@ -58,11 +54,11 @@ async function takeScreenshot(block) {
 
 
       await new Promise((resolve) => setTimeout(resolve, 500));
       await new Promise((resolve) => setTimeout(resolve, 500));
 
 
-      const uri = await (fullPage
+      screenshot = await (data.fullPage
         ? this._sendMessageToTab({
         ? this._sendMessageToTab({
-            tabId: this.activeTab.id,
+            name,
             options,
             options,
-            name: block.name,
+            tabId: this.activeTab.id,
           })
           })
         : browser.tabs.captureVisibleTab(options));
         : browser.tabs.captureVisibleTab(options));
 
 
@@ -71,16 +67,14 @@ async function takeScreenshot(block) {
         await browser.tabs.update(tab.id, { active: true });
         await browser.tabs.update(tab.id, { active: true });
       }
       }
 
 
-      if (saveToColumn) this.addDataToColumn(dataColumn, uri);
-      if (saveToComputer) saveImage({ fileName, uri, ext });
+      saveScreenshot(screenshot);
     } else {
     } else {
-      const uri = await browser.tabs.captureVisibleTab(options);
+      screenshot = await browser.tabs.captureVisibleTab(options);
 
 
-      if (saveToColumn) this.addDataToColumn(dataColumn, uri);
-      if (saveToComputer) saveImage({ fileName, uri, ext });
+      saveScreenshot(screenshot);
     }
     }
 
 
-    return { data: '', nextBlockId };
+    return { data: screenshot, nextBlockId };
   } catch (error) {
   } catch (error) {
     error.nextBlockId = nextBlockId;
     error.nextBlockId = nextBlockId;
 
 

+ 5 - 1
src/background/workflow-engine/engine.js

@@ -46,6 +46,7 @@ class WorkflowEngine {
     this.referenceData = {
     this.referenceData = {
       loopData: {},
       loopData: {},
       workflow: {},
       workflow: {},
+      variables: {},
       dataColumns: [],
       dataColumns: [],
       googleSheets: {},
       googleSheets: {},
       globalData: parseJSON(globalDataValue, globalDataValue),
       globalData: parseJSON(globalDataValue, globalDataValue),
@@ -190,7 +191,10 @@ class WorkflowEngine {
           endedAt: endedTimestamp,
           endedAt: endedTimestamp,
           parentLog: this.parentWorkflow,
           parentLog: this.parentWorkflow,
           startedAt: this.startedTimestamp,
           startedAt: this.startedTimestamp,
-          data: this.referenceData.dataColumns,
+          data: {
+            table: this.referenceData.dataColumns,
+            variables: this.referenceData.variables,
+          },
         });
         });
       }
       }
 
 

+ 35 - 12
src/components/newtab/logs/LogsDataViewer.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
-  <div class="flex items-center">
+  <div class="flex items-center mb-2">
     <ui-input
     <ui-input
-      v-model="fileName"
+      v-model="state.fileName"
       :placeholder="t('common.fileName')"
       :placeholder="t('common.fileName')"
       :title="t('common.fileName')"
       :title="t('common.fileName')"
     />
     />
@@ -26,19 +26,27 @@
       </ui-list>
       </ui-list>
     </ui-popover>
     </ui-popover>
   </div>
   </div>
+  <ui-tabs v-if="objectHasKey(log.data, 'table')" v-model="state.activeTab">
+    <ui-tab value="table">
+      {{ t('workflow.table.title') }}
+    </ui-tab>
+    <ui-tab value="variables">
+      {{ t('workflow.variables.title') }}
+    </ui-tab>
+  </ui-tabs>
   <shared-codemirror
   <shared-codemirror
     :model-value="dataStr"
     :model-value="dataStr"
     :class="editorClass"
     :class="editorClass"
     lang="json"
     lang="json"
     readonly
     readonly
-    class="mt-4"
   />
   />
 </template>
 </template>
 <script setup>
 <script setup>
-import { ref, computed, defineAsyncComponent } from 'vue';
+import { shallowReactive, computed, defineAsyncComponent } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useI18n } from 'vue-i18n';
 import { dataExportTypes } from '@/utils/shared';
 import { dataExportTypes } from '@/utils/shared';
-import dataExporter, { generateJSON } from '@/utils/data-exporter';
+import { objectHasKey } from '@/utils/helper';
+import dataExporter from '@/utils/data-exporter';
 
 
 const SharedCodemirror = defineAsyncComponent(() =>
 const SharedCodemirror = defineAsyncComponent(() =>
   import('@/components/newtab/shared/SharedCodemirror.vue')
   import('@/components/newtab/shared/SharedCodemirror.vue')
@@ -57,17 +65,32 @@ const props = defineProps({
 
 
 const { t } = useI18n();
 const { t } = useI18n();
 
 
+const state = shallowReactive({
+  activeTab: 'table',
+  fileName: props.log.name,
+});
+const cache = {
+  table: '',
+  variables: '',
+};
+
 const dataStr = computed(() => {
 const dataStr = computed(() => {
-  const data = Array.isArray(props.log.data)
-    ? props.log.data
-    : generateJSON(Object.keys(props.log.data), props.log.data);
+  if (cache[state.activeTab]) return cache[state.activeTab];
 
 
-  return JSON.stringify(data, null, 2);
-});
+  let { data } = props.log;
 
 
-const fileName = ref(props.log.name);
+  if (objectHasKey(props.log.data, 'table')) {
+    data = props.log.data[state.activeTab];
+  }
+
+  data = JSON.stringify(data, null, 2);
+  /* eslint-disable-next-line */
+  cache[state.activeTab] = data;
+
+  return data;
+});
 
 
 function exportData(type) {
 function exportData(type) {
-  dataExporter(data, { name: fileName.value, type }, true);
+  dataExporter(props.log.data, { name: state.fileName, type }, true);
 }
 }
 </script>
 </script>

+ 7 - 2
src/components/newtab/workflow/WorkflowEditBlock.vue

@@ -1,7 +1,7 @@
 <template>
 <template>
-  <div class="px-4 overflow-auto scroll pb-1">
+  <div id="workflow-edit-block" class="px-4 overflow-auto scroll pb-1">
     <div
     <div
-      class="sticky top-0 z-20 bg-white dark:bg-gray-800 border-b border-gray-100 dark:border-gray-700 pb-4 mb-4 flex items-center"
+      class="sticky top-0 z-20 bg-white dark:bg-gray-800 pb-4 mb-2 flex items-center"
     >
     >
       <button class="mr-2" @click="$emit('close')">
       <button class="mr-2" @click="$emit('close')">
         <v-remixicon name="riArrowLeftLine" />
         <v-remixicon name="riArrowLeftLine" />
@@ -75,3 +75,8 @@ export default {
   },
   },
 };
 };
 </script>
 </script>
+<style>
+#workflow-edit-block hr {
+  @apply dark:border-gray-700 dark:border-opacity-40 my-4;
+}
+</style>

+ 50 - 44
src/components/newtab/workflow/edit/EditAttributeValue.vue

@@ -1,40 +1,54 @@
 <template>
 <template>
   <edit-interaction-base v-bind="{ data }" @change="updateData">
   <edit-interaction-base v-bind="{ data }" @change="updateData">
+    <hr />
     <ui-input
     <ui-input
       :model-value="data.attributeName"
       :model-value="data.attributeName"
       :placeholder="t('workflow.blocks.attribute-value.forms.name')"
       :placeholder="t('workflow.blocks.attribute-value.forms.name')"
-      class="mt-3 w-full"
+      class="w-full"
       @change="updateData({ attributeName: $event })"
       @change="updateData({ attributeName: $event })"
     />
     />
+    <ui-checkbox
+      :model-value="data.assignVariable"
+      block
+      class="mt-4"
+      @change="updateData({ assignVariable: $event })"
+    >
+      {{ t('workflow.variables.assign') }}
+    </ui-checkbox>
+    <ui-input
+      v-if="data.assignVariable"
+      :model-value="data.variableName"
+      :placeholder="t('workflow.variables.name')"
+      :title="t('workflow.variables.name')"
+      class="mt-2 w-full"
+      @change="updateData({ variableName: $event })"
+    />
     <ui-checkbox
     <ui-checkbox
       :model-value="data.saveData"
       :model-value="data.saveData"
-      class="mt-3"
+      class="mt-4"
       @change="updateData({ saveData: $event })"
       @change="updateData({ saveData: $event })"
     >
     >
       {{ t('workflow.blocks.attribute-value.forms.checkbox') }}
       {{ t('workflow.blocks.attribute-value.forms.checkbox') }}
     </ui-checkbox>
     </ui-checkbox>
-    <div v-if="data.saveData" class="flex items-center mt-1">
-      <ui-select
-        :model-value="data.dataColumn"
-        :placeholder="t('workflow.blocks.attribute-value.forms.column')"
-        class="mr-2 flex-1"
-        @change="updateData({ dataColumn: $event })"
+    <ui-select
+      v-if="data.saveData"
+      :model-value="data.dataColumn"
+      :placeholder="t('workflow.blocks.attribute-value.forms.column')"
+      class="w-full mt-2"
+      @change="updateData({ dataColumn: $event })"
+    >
+      <option
+        v-for="column in workflow.data.value.dataColumns"
+        :key="column.name"
+        :value="column.name"
       >
       >
-        <option
-          v-for="column in workflow.data.value.dataColumns"
-          :key="column.name"
-          :value="column.name"
-        >
-          {{ column.name }}
-        </option>
-      </ui-select>
-      <ui-button icon @click="workflow.showDataColumnsModal(true)">
-        <v-remixicon name="riKey2Line" />
-      </ui-button>
-    </div>
+        {{ column.name }}
+      </option>
+    </ui-select>
     <ui-checkbox
     <ui-checkbox
       :model-value="data.addExtraRow"
       :model-value="data.addExtraRow"
-      class="mt-3"
+      class="mt-4"
+      block
       @change="updateData({ addExtraRow: $event })"
       @change="updateData({ addExtraRow: $event })"
     >
     >
       {{ t('workflow.blocks.attribute-value.forms.extraRow.checkbox') }}
       {{ t('workflow.blocks.attribute-value.forms.extraRow.checkbox') }}
@@ -46,32 +60,24 @@
       :placeholder="
       :placeholder="
         t('workflow.blocks.attribute-value.forms.extraRow.placeholder')
         t('workflow.blocks.attribute-value.forms.extraRow.placeholder')
       "
       "
-      class="w-full mt-3 mb-2"
+      class="w-full mt-2 mb-2"
       @change="updateData({ extraRowValue: $event })"
       @change="updateData({ extraRowValue: $event })"
     />
     />
-    <div v-if="data.addExtraRow" class="flex items-center mt-1">
-      <ui-select
-        :model-value="data.extraRowDataColumn"
-        placeholder="Data column"
-        class="mr-2 flex-1"
-        @change="updateData({ extraRowDataColumn: $event })"
-      >
-        <option
-          v-for="column in workflow.data.value.dataColumns"
-          :key="column.name"
-          :value="column.name"
-        >
-          {{ column.name }}
-        </option>
-      </ui-select>
-      <ui-button
-        icon
-        title="Data columns"
-        @click="workflow.showDataColumnsModal(true)"
+    <ui-select
+      v-if="data.addExtraRow"
+      :model-value="data.extraRowDataColumn"
+      placeholder="Select column"
+      class="mt-1 w-full"
+      @change="updateData({ extraRowDataColumn: $event })"
+    >
+      <option
+        v-for="column in workflow.data.value.dataColumns"
+        :key="column.name"
+        :value="column.name"
       >
       >
-        <v-remixicon name="riKey2Line" />
-      </ui-button>
-    </div>
+        {{ column.name }}
+      </option>
+    </ui-select>
   </edit-interaction-base>
   </edit-interaction-base>
 </template>
 </template>
 <script setup>
 <script setup>

+ 35 - 27
src/components/newtab/workflow/edit/EditForms.vue

@@ -1,8 +1,8 @@
 <template>
 <template>
   <edit-interaction-base v-bind="{ data, hide: hideBase }" @change="updateData">
   <edit-interaction-base v-bind="{ data, hide: hideBase }" @change="updateData">
+    <hr />
     <ui-checkbox
     <ui-checkbox
       :model-value="data.getValue"
       :model-value="data.getValue"
-      class="mt-2"
       @change="updateData({ getValue: $event })"
       @change="updateData({ getValue: $event })"
     >
     >
       {{ t('workflow.blocks.forms.getValue') }}
       {{ t('workflow.blocks.forms.getValue') }}
@@ -10,34 +10,43 @@
     <template v-if="data.getValue && !hideBase">
     <template v-if="data.getValue && !hideBase">
       <ui-checkbox
       <ui-checkbox
         :model-value="data.saveData"
         :model-value="data.saveData"
-        class="mb-2 ml-2"
+        block
+        class="mt-4"
         @change="updateData({ saveData: $event })"
         @change="updateData({ saveData: $event })"
       >
       >
         Save data
         Save data
       </ui-checkbox>
       </ui-checkbox>
-      <div class="flex items-center">
-        <ui-select
-          :model-value="data.dataColumn"
-          placeholder="Data column"
-          class="mr-2 flex-1"
-          @change="updateData({ dataColumn: $event })"
-        >
-          <option
-            v-for="column in workflow.data.value.dataColumns"
-            :key="column.name"
-            :value="column.name"
-          >
-            {{ column.name }}
-          </option>
-        </ui-select>
-        <ui-button
-          icon
-          title="Data columns"
-          @click="workflow.showDataColumnsModal(true)"
+      <ui-select
+        v-if="data.saveData"
+        :model-value="data.dataColumn"
+        placeholder="Select column"
+        class="w-full mt-2"
+        @change="updateData({ dataColumn: $event })"
+      >
+        <option
+          v-for="column in workflow.data.value.dataColumns"
+          :key="column.name"
+          :value="column.name"
         >
         >
-          <v-remixicon name="riKey2Line" />
-        </ui-button>
-      </div>
+          {{ column.name }}
+        </option>
+      </ui-select>
+      <ui-checkbox
+        :model-value="data.assignVariable"
+        block
+        class="mt-4"
+        @change="updateData({ assignVariable: $event })"
+      >
+        {{ t('workflow.variables.assign') }}
+      </ui-checkbox>
+      <ui-input
+        v-if="data.assignVariable"
+        :model-value="data.variableName"
+        :placeholder="t('workflow.variables.name')"
+        :title="t('workflow.variables.name')"
+        class="mt-2 w-full"
+        @change="updateData({ variableName: $event })"
+      />
     </template>
     </template>
     <template v-else>
     <template v-else>
       <ui-select
       <ui-select
@@ -61,12 +70,11 @@
         <ui-textarea
         <ui-textarea
           :model-value="data.value"
           :model-value="data.value"
           :placeholder="t('workflow.blocks.forms.text-field.value')"
           :placeholder="t('workflow.blocks.forms.text-field.value')"
-          class="w-full"
+          class="w-full mb-1"
           @change="updateData({ value: $event })"
           @change="updateData({ value: $event })"
         />
         />
         <ui-checkbox
         <ui-checkbox
           :model-value="data.clearValue"
           :model-value="data.clearValue"
-          class="mb-2 ml-1"
           @change="updateData({ clearValue: $event })"
           @change="updateData({ clearValue: $event })"
         >
         >
           {{ t('workflow.blocks.forms.text-field.clearValue') }}
           {{ t('workflow.blocks.forms.text-field.clearValue') }}
@@ -77,7 +85,7 @@
         :model-value="data.delay"
         :model-value="data.delay"
         :label="t('workflow.blocks.forms.text-field.delay.label')"
         :label="t('workflow.blocks.forms.text-field.delay.label')"
         :placeholder="t('workflow.blocks.forms.text-field.delay.placeholder')"
         :placeholder="t('workflow.blocks.forms.text-field.delay.placeholder')"
-        class="w-full"
+        class="w-full mt-1"
         min="0"
         min="0"
         type="number"
         type="number"
         @change="updateData({ delay: +$event })"
         @change="updateData({ delay: +$event })"

+ 60 - 52
src/components/newtab/workflow/edit/EditGetText.vue

@@ -1,6 +1,7 @@
 <template>
 <template>
   <edit-interaction-base v-bind="{ data }" @change="updateData">
   <edit-interaction-base v-bind="{ data }" @change="updateData">
-    <div class="flex rounded-lg bg-input px-4 items-center transition mt-3">
+    <hr />
+    <div class="flex rounded-lg bg-input px-4 items-center transition">
       <span>/</span>
       <span>/</span>
       <input
       <input
         :value="data.regex"
         :value="data.regex"
@@ -25,17 +26,59 @@
         </div>
         </div>
       </ui-popover>
       </ui-popover>
     </div>
     </div>
+    <div class="mt-2 flex space-x-2">
+      <ui-input
+        :model-value="data.prefixText"
+        :title="t('workflow.blocks.get-text.prefixText.title')"
+        :label="t('workflow.blocks.get-text.prefixText.placeholder')"
+        placeholder="Text"
+        class="w-full"
+        @change="updateData({ prefixText: $event })"
+      />
+      <ui-input
+        :model-value="data.suffixText"
+        :title="t('workflow.blocks.get-text.suffixText.title')"
+        :label="t('workflow.blocks.get-text.suffixText.placeholder')"
+        placeholder="Text"
+        class="w-full"
+        @change="updateData({ suffixText: $event })"
+      />
+    </div>
+    <ui-checkbox
+      :model-value="data.includeTags"
+      class="mt-4"
+      @change="updateData({ includeTags: $event })"
+    >
+      {{ t('workflow.blocks.get-text.includeTags') }}
+    </ui-checkbox>
+    <hr />
+    <ui-checkbox
+      :model-value="data.assignVariable"
+      block
+      @change="updateData({ assignVariable: $event })"
+    >
+      {{ t('workflow.variables.assign') }}
+    </ui-checkbox>
+    <ui-input
+      v-if="data.assignVariable"
+      :model-value="data.variableName"
+      :placeholder="t('workflow.variables.name')"
+      :title="t('workflow.variables.name')"
+      class="mt-2 w-full"
+      @change="updateData({ variableName: $event })"
+    />
     <ui-checkbox
     <ui-checkbox
       :model-value="data.saveData"
       :model-value="data.saveData"
-      class="mt-3"
+      block
+      class="mt-4"
       @change="updateData({ saveData: $event })"
       @change="updateData({ saveData: $event })"
     >
     >
       {{ t('workflow.blocks.get-text.checkbox') }}
       {{ t('workflow.blocks.get-text.checkbox') }}
     </ui-checkbox>
     </ui-checkbox>
-    <div v-if="data.saveData" class="flex items-center mt-1">
+    <div v-if="data.saveData" class="flex items-center mt-2 mb-4">
       <ui-select
       <ui-select
         :model-value="data.dataColumn"
         :model-value="data.dataColumn"
-        placeholder="Data column"
+        placeholder="Select column"
         class="mr-2 flex-1"
         class="mr-2 flex-1"
         @change="updateData({ dataColumn: $event })"
         @change="updateData({ dataColumn: $event })"
       >
       >
@@ -47,55 +90,27 @@
           {{ column.name }}
           {{ column.name }}
         </option>
         </option>
       </ui-select>
       </ui-select>
-      <ui-button
-        icon
-        title="Data columns"
-        @click="workflow.showDataColumnsModal(true)"
-      >
-        <v-remixicon name="riKey2Line" />
-      </ui-button>
     </div>
     </div>
-    <ui-input
-      :model-value="data.prefixText"
-      :title="t('workflow.blocks.get-text.prefixText.title')"
-      :placeholder="t('workflow.blocks.get-text.prefixText.placeholder')"
-      class="w-full mt-3 mb-2"
-      @change="updateData({ prefixText: $event })"
-    />
-    <ui-input
-      :model-value="data.suffixText"
-      :title="t('workflow.blocks.get-text.suffixText.title')"
-      :placeholder="t('workflow.blocks.get-text.suffixText.placeholder')"
-      class="w-full"
-      @change="updateData({ suffixText: $event })"
-    />
-    <ui-checkbox
-      :model-value="data.includeTags"
-      class="mt-3"
-      @change="updateData({ includeTags: $event })"
-    >
-      {{ t('workflow.blocks.get-text.includeTags') }}
-    </ui-checkbox>
     <ui-checkbox
     <ui-checkbox
       :model-value="data.addExtraRow"
       :model-value="data.addExtraRow"
-      class="mt-2"
+      block
+      class="mt-4"
       @change="updateData({ addExtraRow: $event })"
       @change="updateData({ addExtraRow: $event })"
     >
     >
       {{ t('workflow.blocks.get-text.extraRow.checkbox') }}
       {{ t('workflow.blocks.get-text.extraRow.checkbox') }}
     </ui-checkbox>
     </ui-checkbox>
-    <ui-input
-      v-if="data.addExtraRow"
-      :model-value="data.extraRowValue"
-      :title="t('workflow.blocks.get-text.extraRow.title')"
-      :placeholder="t('workflow.blocks.get-text.extraRow.placeholder')"
-      class="w-full mt-3 mb-2"
-      @change="updateData({ extraRowValue: $event })"
-    />
-    <div v-if="data.addExtraRow" class="flex items-center mt-1">
+    <template v-if="data.addExtraRow">
+      <ui-input
+        :model-value="data.extraRowValue"
+        :title="t('workflow.blocks.get-text.extraRow.title')"
+        :placeholder="t('workflow.blocks.get-text.extraRow.placeholder')"
+        class="w-full my-2"
+        @change="updateData({ extraRowValue: $event })"
+      />
       <ui-select
       <ui-select
         :model-value="data.extraRowDataColumn"
         :model-value="data.extraRowDataColumn"
-        placeholder="Data column"
-        class="mr-2 flex-1"
+        placeholder="Select column"
+        class="w-full"
         @change="updateData({ extraRowDataColumn: $event })"
         @change="updateData({ extraRowDataColumn: $event })"
       >
       >
         <option
         <option
@@ -106,14 +121,7 @@
           {{ column.name }}
           {{ column.name }}
         </option>
         </option>
       </ui-select>
       </ui-select>
-      <ui-button
-        icon
-        title="Data columns"
-        @click="workflow.showDataColumnsModal(true)"
-      >
-        <v-remixicon name="riKey2Line" />
-      </ui-button>
-    </div>
+    </template>
   </edit-interaction-base>
   </edit-interaction-base>
 </template>
 </template>
 <script setup>
 <script setup>

+ 0 - 1
src/components/newtab/workflow/edit/EditGoogleSheets.vue

@@ -211,7 +211,6 @@ async function previewData() {
 
 
     previewDataState.status = 'idle';
     previewDataState.status = 'idle';
   } catch (error) {
   } catch (error) {
-    console.dir(error);
     previewDataState.data = '';
     previewDataState.data = '';
     previewDataState.status = 'error';
     previewDataState.status = 'error';
     previewDataState.errorMessage = error.message;
     previewDataState.errorMessage = error.message;

+ 0 - 1
src/components/newtab/workflow/edit/EditInteractionBase.vue

@@ -5,7 +5,6 @@
       <ui-textarea
       <ui-textarea
         :model-value="data.description"
         :model-value="data.description"
         :placeholder="t('common.description')"
         :placeholder="t('common.description')"
-        autoresize
         class="w-full mb-2"
         class="w-full mb-2"
         @change="updateData({ description: $event })"
         @change="updateData({ description: $event })"
       />
       />

+ 28 - 13
src/components/newtab/workflow/edit/EditLoopData.vue

@@ -16,7 +16,7 @@
     <ui-select
     <ui-select
       :model-value="data.loopThrough"
       :model-value="data.loopThrough"
       :label="t('workflow.blocks.loop-data.loopThrough.placeholder')"
       :label="t('workflow.blocks.loop-data.loopThrough.placeholder')"
-      class="w-full mb-2"
+      class="w-full"
       @change="
       @change="
         updateData({
         updateData({
           loopThrough: $event,
           loopThrough: $event,
@@ -28,34 +28,43 @@
         {{ t(`workflow.blocks.loop-data.loopThrough.options.${type}`) }}
         {{ t(`workflow.blocks.loop-data.loopThrough.options.${type}`) }}
       </option>
       </option>
     </ui-select>
     </ui-select>
+    <ui-input
+      v-if="data.loopThrough === 'google-sheets'"
+      :model-value="data.referenceKey"
+      :label="t('workflow.blocks.loop-data.refKey')"
+      placeholder="abc123"
+      class="w-full mt-2"
+      @change="updateData({ referenceKey: $event })"
+    />
+    <ui-input
+      v-else-if="data.loopThrough === 'variable'"
+      :model-value="data.variableName"
+      :label="t('workflow.variables.name')"
+      placeholder="abc123"
+      class="w-full mt-2"
+      @change="updateData({ variableName: $event })"
+    />
     <ui-input
     <ui-input
       v-if="data.loopThrough !== 'numbers'"
       v-if="data.loopThrough !== 'numbers'"
       :model-value="data.maxLoop"
       :model-value="data.maxLoop"
       :label="t('workflow.blocks.loop-data.maxLoop.label')"
       :label="t('workflow.blocks.loop-data.maxLoop.label')"
       :title="t('workflow.blocks.loop-data.maxLoop.title')"
       :title="t('workflow.blocks.loop-data.maxLoop.title')"
-      class="w-full mb-4"
+      class="w-full mt-2"
       min="0"
       min="0"
       type="number"
       type="number"
       @change="updateData({ maxLoop: +$event || 0 })"
       @change="updateData({ maxLoop: +$event || 0 })"
     />
     />
     <ui-button
     <ui-button
-      v-if="data.loopThrough === 'custom-data'"
-      class="w-full"
+      v-else-if="data.loopThrough === 'custom-data'"
+      class="w-full mt-4"
       variant="accent"
       variant="accent"
       @click="state.showDataModal = true"
       @click="state.showDataModal = true"
     >
     >
       {{ t('workflow.blocks.loop-data.buttons.insert') }}
       {{ t('workflow.blocks.loop-data.buttons.insert') }}
     </ui-button>
     </ui-button>
-    <ui-input
-      v-else-if="data.loopThrough === 'google-sheets'"
-      :model-value="data.referenceKey"
-      :label="t('workflow.blocks.loop-data.refKey')"
-      class="w-full"
-      @change="updateData({ referenceKey: $event })"
-    />
     <div
     <div
       v-else-if="data.loopThrough === 'numbers'"
       v-else-if="data.loopThrough === 'numbers'"
-      class="flex items-center space-x-2"
+      class="flex items-center space-x-2 mt-2"
     >
     >
       <ui-input
       <ui-input
         :model-value="data.fromNumber"
         :model-value="data.fromNumber"
@@ -146,7 +155,13 @@ const { t } = useI18n();
 const toast = useToast();
 const toast = useToast();
 
 
 const maxFileSize = 1024 * 1024;
 const maxFileSize = 1024 * 1024;
-const loopTypes = ['data-columns', 'numbers', 'google-sheets', 'custom-data'];
+const loopTypes = [
+  'data-columns',
+  'numbers',
+  'google-sheets',
+  'variable',
+  'custom-data',
+];
 
 
 const state = shallowReactive({
 const state = shallowReactive({
   showOptions: false,
   showOptions: false,

+ 1 - 1
src/components/newtab/workflow/edit/EditNewTab.vue

@@ -1,5 +1,5 @@
 <template>
 <template>
-  <div class="mb-2 mt-4">
+  <div>
     <ui-textarea
     <ui-textarea
       :model-value="data.description"
       :model-value="data.description"
       :placeholder="t('common.description')"
       :placeholder="t('common.description')"

+ 34 - 26
src/components/newtab/workflow/edit/EditTakeScreenshot.vue

@@ -1,6 +1,6 @@
 <template>
 <template>
   <p class="text-sm text-gray-600 dark:text-gray-200 ml-2">Image quality</p>
   <p class="text-sm text-gray-600 dark:text-gray-200 ml-2">Image quality</p>
-  <div class="bg-box-transparent px-4 mb-2 py-2 rounded-lg flex items-center">
+  <div class="bg-box-transparent px-4 mb-4 py-2 rounded-lg flex items-center">
     <input
     <input
       :value="data.quality"
       :value="data.quality"
       :title="t('workflow.blocks.take-screenshot.imageQuality')"
       :title="t('workflow.blocks.take-screenshot.imageQuality')"
@@ -15,18 +15,18 @@
   <div class="take-screenshot">
   <div class="take-screenshot">
     <ui-checkbox
     <ui-checkbox
       :model-value="data.fullPage"
       :model-value="data.fullPage"
-      class="mb-2"
       @change="updateData({ fullPage: $event })"
       @change="updateData({ fullPage: $event })"
     >
     >
       {{ t('workflow.blocks.take-screenshot.fullPage') }}
       {{ t('workflow.blocks.take-screenshot.fullPage') }}
     </ui-checkbox>
     </ui-checkbox>
     <ui-checkbox
     <ui-checkbox
       :model-value="data.saveToComputer"
       :model-value="data.saveToComputer"
+      class="mt-4"
       @change="updateData({ saveToComputer: $event })"
       @change="updateData({ saveToComputer: $event })"
     >
     >
       {{ t('workflow.blocks.take-screenshot.saveToComputer') }}
       {{ t('workflow.blocks.take-screenshot.saveToComputer') }}
     </ui-checkbox>
     </ui-checkbox>
-    <div v-if="data.saveToComputer" class="flex items-center mb-2 mt-1">
+    <div v-if="data.saveToComputer" class="flex items-center mt-1">
       <ui-input
       <ui-input
         :model-value="data.fileName"
         :model-value="data.fileName"
         :placeholder="t('common.fileName')"
         :placeholder="t('common.fileName')"
@@ -45,34 +45,42 @@
     </div>
     </div>
     <ui-checkbox
     <ui-checkbox
       :model-value="data.saveToColumn"
       :model-value="data.saveToColumn"
-      class="mt-2"
+      class="mt-4"
       @change="updateData({ saveToColumn: $event })"
       @change="updateData({ saveToColumn: $event })"
     >
     >
       {{ t('workflow.blocks.take-screenshot.saveToColumn') }}
       {{ t('workflow.blocks.take-screenshot.saveToColumn') }}
     </ui-checkbox>
     </ui-checkbox>
-    <div v-if="data.saveToColumn" class="flex items-center mt-1">
-      <ui-select
-        :model-value="data.dataColumn"
-        placeholder="Data column"
-        class="mr-2 flex-1"
-        @change="updateData({ dataColumn: $event })"
-      >
-        <option
-          v-for="column in workflow.data.value.dataColumns"
-          :key="column.name"
-          :value="column.name"
-        >
-          {{ column.name }}
-        </option>
-      </ui-select>
-      <ui-button
-        icon
-        title="Data columns"
-        @click="workflow.showDataColumnsModal(true)"
+    <ui-select
+      v-if="data.saveToColumn"
+      :model-value="data.dataColumn"
+      placeholder="Select column"
+      class="w-full mt-1"
+      @change="updateData({ dataColumn: $event })"
+    >
+      <option
+        v-for="column in workflow.data.value.dataColumns"
+        :key="column.name"
+        :value="column.name"
       >
       >
-        <v-remixicon name="riKey2Line" />
-      </ui-button>
-    </div>
+        {{ column.name }}
+      </option>
+    </ui-select>
+    <ui-checkbox
+      :model-value="data.assignVariable"
+      block
+      class="mt-4"
+      @change="updateData({ assignVariable: $event })"
+    >
+      {{ t('workflow.variables.assign') }}
+    </ui-checkbox>
+    <ui-input
+      v-if="data.assignVariable"
+      :model-value="data.variableName"
+      :placeholder="t('workflow.variables.name')"
+      :title="t('workflow.variables.name')"
+      class="mt-1 w-full"
+      @change="updateData({ variableName: $event })"
+    />
   </div>
   </div>
 </template>
 </template>
 <script setup>
 <script setup>

+ 8 - 1
src/components/ui/UiCheckbox.vue

@@ -1,5 +1,8 @@
 <template>
 <template>
-  <label class="checkbox-ui inline-flex items-center">
+  <label
+    class="checkbox-ui items-center"
+    :class="[block ? 'flex' : 'inline-flex']"
+  >
     <div
     <div
       :class="{ 'pointer-events-none opacity-75': disabled }"
       :class="{ 'pointer-events-none opacity-75': disabled }"
       class="relative h-5 w-5 inline-block focus-within:ring-2 focus-within:ring-accent rounded"
       class="relative h-5 w-5 inline-block focus-within:ring-2 focus-within:ring-accent rounded"
@@ -37,6 +40,10 @@ export default {
       type: Boolean,
       type: Boolean,
       default: null,
       default: null,
     },
     },
+    block: {
+      type: Boolean,
+      default: null,
+    },
   },
   },
   emits: ['update:modelValue', 'change'],
   emits: ['update:modelValue', 'change'],
   setup(props, { emit }) {
   setup(props, { emit }) {

+ 6 - 4
src/components/ui/UiInput.vue

@@ -1,9 +1,11 @@
 <template>
 <template>
   <div class="inline-block input-ui">
   <div class="inline-block input-ui">
-    <label v-if="label || $slots.label" :for="componentId">
-      <span class="text-sm dark:text-gray-200 text-gray-600 ml-1">
-        <slot name="label">{{ label }}</slot>
-      </span>
+    <label
+      v-if="label || $slots.label"
+      :for="componentId"
+      class="text-sm dark:text-gray-200 text-gray-600 ml-1 inline-block leading-none"
+    >
+      <slot name="label">{{ label }}</slot>
     </label>
     </label>
     <div class="flex items-center relative w-full">
     <div class="flex items-center relative w-full">
       <slot name="prepend">
       <slot name="prepend">

+ 17 - 15
src/components/ui/UiTextarea.vue

@@ -1,20 +1,17 @@
 <template>
 <template>
-  <label :class="[block ? 'block' : 'inline-block']">
-    <span v-if="label" class="text-gray-500 text-sm ml-2 block">{{
-      label
-    }}</span>
-    <textarea
-      ref="textarea"
-      v-bind="{ value: modelValue, placeholder, maxlength: max }"
-      class="ui-textarea w-full ui-input rounded-lg px-4 py-2 transition bg-input"
-      :class="{ 'overflow-hidden resize-none': autoresize }"
-      :style="{ height }"
-      @input="emitValue"
-    ></textarea>
-  </label>
+  <textarea
+    v-bind="{ value: modelValue, placeholder, maxlength: max }"
+    :id="textareaId"
+    ref="textarea"
+    class="ui-textarea w-full ui-input rounded-lg px-4 py-2 transition bg-input"
+    :class="{ 'overflow-hidden resize-none': autoresize }"
+    :style="{ height }"
+    @input="emitValue"
+  ></textarea>
 </template>
 </template>
 <script>
 <script>
 import { ref, onMounted } from 'vue';
 import { ref, onMounted } from 'vue';
+import { useComponentId } from '@/composable/componentId';
 
 
 export default {
 export default {
   props: {
   props: {
@@ -38,11 +35,15 @@ export default {
       type: [Number, String],
       type: [Number, String],
       default: '',
       default: '',
     },
     },
-    max: [Number, String],
+    max: {
+      type: [Number, String],
+      default: null,
+    },
     block: Boolean,
     block: Boolean,
   },
   },
   emits: ['update:modelValue', 'change'],
   emits: ['update:modelValue', 'change'],
   setup(props, { emit }) {
   setup(props, { emit }) {
+    const textareaId = useComponentId('textarea');
     const textarea = ref(null);
     const textarea = ref(null);
 
 
     function calcHeight() {
     function calcHeight() {
@@ -53,7 +54,7 @@ export default {
     }
     }
     function emitValue(event) {
     function emitValue(event) {
       let { value } = event.target;
       let { value } = event.target;
-      const maxLength = Math.abs(props.max);
+      const maxLength = Math.abs(props.max) || Infinity;
 
 
       if (value.length > maxLength) {
       if (value.length > maxLength) {
         value = value.slice(0, maxLength);
         value = value.slice(0, maxLength);
@@ -69,6 +70,7 @@ export default {
     return {
     return {
       textarea,
       textarea,
       emitValue,
       emitValue,
+      textareaId,
     };
     };
   },
   },
 };
 };

+ 27 - 7
src/content/blocks-handler/handler-javascript-code.js

@@ -1,7 +1,23 @@
 import { sendMessage } from '@/utils/message';
 import { sendMessage } from '@/utils/message';
 
 
+/*
+setVariable(name, value);
+
+init => set variables to sessionStorage
+invoked => update the variable in the sessionStorage
+nextBlock => include the variables in payload
+*/
+
 function getAutomaScript(blockId) {
 function getAutomaScript(blockId) {
   return `
   return `
+function automaSetVariable(name, value) {
+  const data = JSON.parse(sessionStorage.getItem('automa--${blockId}')) || null;
+
+  if (data === null) return null;
+
+  data.variables[name] = value;
+  sessionStorage.setItem('automa--${blockId}', JSON.stringify(data));
+}
 function automaNextBlock(data) {
 function automaNextBlock(data) {
   window.dispatchEvent(new CustomEvent('__automa-next-block__', { detail: data }));
   window.dispatchEvent(new CustomEvent('__automa-next-block__', { detail: data }));
 }
 }
@@ -14,17 +30,17 @@ function findData(obj, path) {
 
 
   if (paths.length === 0 || isWhitespace) return obj;
   if (paths.length === 0 || isWhitespace) return obj;
 
 
-  let current = obj;
+  let result = obj;
 
 
   for (let i = 0; i < paths.length; i++) {
   for (let i = 0; i < paths.length; i++) {
-    if (current[paths[i]] == undefined) {
+    if (result[paths[i]] == undefined) {
       return undefined;
       return undefined;
     } else {
     } else {
-      current = current[paths[i]];
+      result = result[paths[i]];
     }
     }
   }
   }
 
 
-  return current;
+  return result;
 }
 }
 function automaRefData(keyword, path = '') {
 function automaRefData(keyword, path = '') {
   const data = JSON.parse(sessionStorage.getItem('automa--${blockId}')) || null;
   const data = JSON.parse(sessionStorage.getItem('automa--${blockId}')) || null;
@@ -108,13 +124,17 @@ function javascriptCode(block) {
       script.id = 'automa-custom-js';
       script.id = 'automa-custom-js';
       script.innerHTML = `(() => {\n${automaScript} ${block.data.code}\n})()`;
       script.innerHTML = `(() => {\n${automaScript} ${block.data.code}\n})()`;
 
 
-      const cleanUp = (data = '') => {
+      const cleanUp = (columns = '') => {
+        const storageKey = `automa--${block.id}`;
+        const storageRefData = JSON.parse(sessionStorage.getItem(storageKey));
+
         script.remove();
         script.remove();
         preloadScripts.forEach((item) => {
         preloadScripts.forEach((item) => {
           if (item.removeAfterExec) item.script.remove();
           if (item.removeAfterExec) item.script.remove();
         });
         });
-        sessionStorage.removeItem(`automa--${block.id}`);
-        resolve(data);
+        sessionStorage.removeItem(storageKey);
+
+        resolve({ columns, variables: storageRefData.variables });
       };
       };
 
 
       window.addEventListener('__automa-next-block__', ({ detail }) => {
       window.addEventListener('__automa-next-block__', ({ detail }) => {

+ 1 - 0
src/locales/en/blocks.json

@@ -396,6 +396,7 @@
           "toNumber": "To number",
           "toNumber": "To number",
           "options": {
           "options": {
             "numbers": "Numbers",
             "numbers": "Numbers",
+            "variable": "Variable",
             "data-columns": "Data columns",
             "data-columns": "Data columns",
             "custom-data": "Custom data",
             "custom-data": "Custom data",
             "google-sheets": "Google sheets"
             "google-sheets": "Google sheets"

+ 8 - 0
src/locales/en/newtab.json

@@ -47,6 +47,11 @@
     "add": "Add workflow",
     "add": "Add workflow",
     "clickToEnable": "Click to enable",
     "clickToEnable": "Click to enable",
     "toggleSidebar": "Toggle sidebar",
     "toggleSidebar": "Toggle sidebar",
+    "variables": {
+      "title": "Variables",
+      "name": "Variable name",
+      "assign": "Assign to variable"
+    },
     "protect": {
     "protect": {
       "title": "Protect workflow",
       "title": "Protect workflow",
       "remove": "Remove protection",
       "remove": "Remove protection",
@@ -64,6 +69,9 @@
     "state": {
     "state": {
       "executeBy": "Executed by: \"{name}\""
       "executeBy": "Executed by: \"{name}\""
     },
     },
+    "table": {
+      "title": "Table",
+    },
     "dataColumns": {
     "dataColumns": {
       "title": "Data columns",
       "title": "Data columns",
       "placeholder": "Search or add column",
       "placeholder": "Search or add column",

+ 1 - 1
src/newtab/pages/Logs.vue

@@ -73,7 +73,7 @@
         {{ t('log.deleteSelected') }} ({{ selectedLogs.length }})
         {{ t('log.deleteSelected') }} ({{ selectedLogs.length }})
       </ui-button>
       </ui-button>
     </ui-card>
     </ui-card>
-    <ui-modal v-model="exportDataModal.show">
+    <ui-modal v-model="exportDataModal.show" content-class="max-w-2xl">
       <template #header>
       <template #header>
         <span class="capitalize">{{ t('common.data') }}</span>
         <span class="capitalize">{{ t('common.data') }}</span>
       </template>
       </template>

+ 1 - 1
src/newtab/pages/Workflows.vue

@@ -158,7 +158,7 @@
         v-model="workflowModal.description"
         v-model="workflowModal.description"
         :placeholder="t('common.description')"
         :placeholder="t('common.description')"
         height="165px"
         height="165px"
-        class="w-full dark:text-gray-200 text-right"
+        class="w-full dark:text-gray-200"
         max="300"
         max="300"
       />
       />
       <p class="mb-6 text-right text-gray-600 dark:text-gray-200">
       <p class="mb-6 text-right text-gray-600 dark:text-gray-200">

+ 1 - 1
src/newtab/pages/logs/[id].vue

@@ -197,6 +197,6 @@ onMounted(() => {
 </script>
 </script>
 <style>
 <style>
 .logs-details .cm-editor {
 .logs-details .cm-editor {
-  max-height: calc(100vh - 12rem);
+  max-height: calc(100vh - 15rem);
 }
 }
 </style>
 </style>

+ 1 - 1
src/newtab/pages/workflows/[id].vue

@@ -79,7 +79,7 @@
             {{ t('common.running') }}
             {{ t('common.running') }}
             <span
             <span
               v-if="workflowState.length > 0"
               v-if="workflowState.length > 0"
-              class="ml-2 p-1 text-center inline-block text-xs rounded-full bg-black text-white"
+              class="ml-2 p-1 text-center inline-block text-xs rounded-full bg-accent text-white dark:text-black"
               style="min-width: 25px"
               style="min-width: 25px"
             >
             >
               {{ workflowState.length }}
               {{ workflowState.length }}

+ 7 - 0
src/utils/shared.js

@@ -290,6 +290,8 @@ export const tasks = {
       saveData: true,
       saveData: true,
       includeTags: false,
       includeTags: false,
       addExtraRow: false,
       addExtraRow: false,
+      assignVariable: false,
+      variableName: '',
       extraRowValue: '',
       extraRowValue: '',
       extraRowDataColumn: '',
       extraRowDataColumn: '',
     },
     },
@@ -377,6 +379,8 @@ export const tasks = {
       markEl: false,
       markEl: false,
       multiple: false,
       multiple: false,
       attributeName: '',
       attributeName: '',
+      assignVariable: false,
+      variableName: '',
       dataColumn: '',
       dataColumn: '',
       saveData: true,
       saveData: true,
       addExtraRow: false,
       addExtraRow: false,
@@ -407,6 +411,8 @@ export const tasks = {
       getValue: false,
       getValue: false,
       saveData: true,
       saveData: true,
       dataColumn: '',
       dataColumn: '',
+      assignVariable: false,
+      variableName: '',
       type: 'text-field',
       type: 'text-field',
       value: '',
       value: '',
       delay: 0,
       delay: 0,
@@ -582,6 +588,7 @@ export const tasks = {
       toNumber: 10,
       toNumber: 10,
       loopData: '[]',
       loopData: '[]',
       description: '',
       description: '',
+      variableName: '',
       referenceKey: '',
       referenceKey: '',
       specificRowAndCol: false,
       specificRowAndCol: false,
       loopThrough: 'data-columns',
       loopThrough: 'data-columns',