Ahmad Kholid vor 1 Jahr
Ursprung
Commit
96cd150145

+ 4 - 3
package.json

@@ -1,6 +1,6 @@
 {
   "name": "automa",
-  "version": "1.28.15",
+  "version": "1.28.16",
   "description": "An extension for automating your browser by connecting blocks",
   "repository": {
     "type": "git",
@@ -47,8 +47,9 @@
     "@tiptap/starter-kit": "^2.0.4",
     "@tiptap/vue-3": "^2.0.4",
     "@viselect/vanilla": "^3.2.5",
-    "@vue-flow/additional-components": "^1.3.3",
-    "@vue-flow/core": "^1.22.3",
+    "@vue-flow/background": "^1.2.0",
+    "@vue-flow/core": "^1.23.0",
+    "@vue-flow/minimap": "^1.2.0",
     "@vueuse/head": "^1.3.1",
     "@vueuse/rxjs": "^9.12.0",
     "@vuex-orm/core": "^0.36.4",

+ 1 - 1
src/components/block/BlockGroup.vue

@@ -5,7 +5,7 @@
     :block-id="id"
     :block-data="block"
     class="w-64"
-    content-class="p-0"
+    content-class="!p-0"
     @edit="$emit('edit')"
     @delete="$emit('delete', id)"
     @update="$emit('update', $event)"

+ 1 - 0
src/components/newtab/logs/LogsTable.vue

@@ -53,6 +53,7 @@
     />
     <ui-table
       v-show="state.activeTab === 'table'"
+      with-pagination
       :headers="tableData.header"
       :items="tableData.body"
       :search="state.query"

+ 6 - 4
src/components/newtab/workflow/WorkflowEditor.vue

@@ -86,7 +86,8 @@ import {
   MarkerType,
   getConnectedEdges,
 } from '@vue-flow/core';
-import { Background, MiniMap } from '@vue-flow/additional-components';
+import { Background } from '@vue-flow/background';
+import { MiniMap } from '@vue-flow/minimap';
 import cloneDeep from 'lodash.clonedeep';
 import { useStore } from '@/stores/main';
 import { getBlocks } from '@/utils/getSharedData';
@@ -94,6 +95,7 @@ import { categories } from '@/utils/shared';
 import EditBlockSettings from './edit/EditBlockSettings.vue';
 import EditorCustomEdge from './editor/EditorCustomEdge.vue';
 import EditorSearchBlocks from './editor/EditorSearchBlocks.vue';
+import '@vue-flow/minimap/dist/style.css';
 
 const props = defineProps({
   id: {
@@ -220,7 +222,7 @@ function minimapNodeClassName({ label }) {
 function updateBlockData(nodeId, data = {}) {
   if (isDisabled.value) return;
 
-  const node = editor.getNode.value(nodeId);
+  const node = editor.findNode(nodeId);
   node.data = { ...node.data, ...data };
 
   emit('update:node', node);
@@ -229,7 +231,7 @@ function updateBlockSettingsData(newSettings) {
   if (isDisabled.value) return;
 
   const nodeId = blockSettingsState.data.blockId;
-  const node = editor.getNode.value(nodeId);
+  const node = editor.findNode(nodeId);
 
   if (blockSettingsState.data.itemId) {
     const index = node.data.blocks.findIndex(
@@ -283,7 +285,7 @@ function applyFlowData() {
     props.data?.nodes?.map((node) => ({ ...node, events: {} })) || []
   );
   editor.setEdges(props.data?.edges || []);
-  editor.setTransform({
+  editor.setViewport({
     x: props.data?.x || 0,
     y: props.data?.y || 0,
     zoom: props.data?.zoom || 1,

+ 1 - 1
src/components/ui/UiTable.vue

@@ -110,7 +110,7 @@ const props = defineProps({
   },
   withPagination: {
     type: Boolean,
-    default: true,
+    default: false,
   },
 });
 

+ 15 - 0
src/content/blocksHandler/handlerForms.js

@@ -26,6 +26,21 @@ async function forms(block) {
     if (block.debugMode && data.type === 'text-field') {
       element.focus?.();
 
+      if (data.clearValue) {
+        const backspaceCommands = new Array(element.value?.length ?? 0).fill({
+          type: 'rawKeyDown',
+          unmodifiedText: 'Delete',
+          text: 'Delete',
+          windowsVirtualKeyCode: 46,
+        });
+
+        await sendMessage(
+          'debugger:type',
+          { commands: backspaceCommands, tabId: block.activeTabId, delay: 0 },
+          'background'
+        );
+      }
+
       const commands = data.value.split('').map((char) => ({
         type: 'keyDown',
         text: char === '\n' ? '\r' : char,

+ 87 - 78
src/content/index.js

@@ -240,86 +240,95 @@ async function messageListener({ data, source }) {
 
   automa('content');
 
-  browser.runtime.onMessage.addListener((data) => {
-    return new Promise((resolve, reject) => {
-      if (data.isBlock) {
-        executeBlock(data)
-          .then(resolve)
-          .catch((error) => {
-            console.error(error);
-            const elNotFound = error.message === 'element-not-found';
-            const isLoopItem = data.data?.selector?.includes('automa-loop');
-            if (elNotFound && isLoopItem) {
-              const findLoopEl = data.loopEls.find(({ url }) =>
-                window.location.href.includes(url)
-              );
-
-              const blockData = { ...data.data, ...findLoopEl, multiple: true };
-              const loopBlock = {
-                ...data,
-                onlyGenerate: true,
-                data: blockData,
-              };
-
-              blocksHandler()
-                .loopData(loopBlock)
-                .then(() => {
-                  executeBlock(data).then(resolve).catch(reject);
-                })
-                .catch((blockError) => {
-                  reject(blockError);
-                });
-              return;
-            }
-
-            reject(error);
-          });
-      } else {
-        switch (data.type) {
-          case 'input-workflow-params':
-            window.initPaletteParams?.(data.data);
-            resolve(Boolean(window.initPaletteParams));
-            break;
-          case 'content-script-exists':
-            resolve(true);
-            break;
-          case 'automa-element-selector': {
-            const selectorInstance = elementSelectorInstance();
-
-            resolve(selectorInstance);
-            break;
-          }
-          case 'context-element': {
-            let $ctxElSelector = '';
-
-            if (contextElement) {
-              $ctxElSelector = findSelector(contextElement);
-              contextElement = null;
-            }
-            if (!$ctxTextSelection) {
-              $ctxTextSelection = window.getSelection().toString();
-            }
-
-            const cloneContextData = cloneDeep({
-              $ctxLink,
-              $ctxMediaUrl,
-              $ctxElSelector,
-              $ctxTextSelection,
-            });
-
-            $ctxLink = '';
-            $ctxMediaUrl = '';
-            $ctxElSelector = '';
-            $ctxTextSelection = '';
-
-            resolve(cloneContextData);
-            break;
-          }
-          default:
-            resolve(null);
+  let locked = false;
+  const queue = [];
+
+  browser.runtime.onMessage.addListener(async (data) => {
+    const asyncExecuteBlock = async (block) => {
+      if (locked) {
+        return new Promise((resolve, reject) => {
+          queue.push([block, resolve, reject]);
+        });
+      }
+
+      try {
+        locked = true;
+        const res = await executeBlock(block);
+        return res;
+      } catch (error) {
+        console.error(error);
+        const elNotFound = error.message === 'element-not-found';
+        const isLoopItem = data.data?.selector?.includes('automa-loop');
+
+        if (!elNotFound || !isLoopItem) return Promise.reject(error);
+
+        const findLoopEl = data.loopEls.find(({ url }) =>
+          window.location.href.includes(url)
+        );
+
+        const blockData = { ...data.data, ...findLoopEl, multiple: true };
+        const loopBlock = {
+          ...data,
+          onlyGenerate: true,
+          data: blockData,
+        };
+
+        await blocksHandler().loopData(loopBlock);
+        return executeBlock(block);
+      } finally {
+        locked = false;
+      }
+    };
+
+    if (data.isBlock) {
+      const res = await asyncExecuteBlock(data);
+      while (queue.length) {
+        const [block, resolve, reject] = queue.shift();
+        requestAnimationFrame(() => {
+          asyncExecuteBlock(block).then(resolve).catch(reject);
+        });
+      }
+
+      return res;
+    }
+
+    switch (data.type) {
+      case 'input-workflow-params':
+        window.initPaletteParams?.(data.data);
+        return Boolean(window.initPaletteParams);
+      case 'content-script-exists':
+        return true;
+      case 'automa-element-selector': {
+        return elementSelectorInstance();
+      }
+      case 'context-element': {
+        let $ctxElSelector = '';
+
+        if (contextElement) {
+          $ctxElSelector = findSelector(contextElement);
+          contextElement = null;
+        }
+        if (!$ctxTextSelection) {
+          $ctxTextSelection = window.getSelection().toString();
         }
+
+        const cloneContextData = cloneDeep({
+          $ctxLink,
+          $ctxMediaUrl,
+          $ctxElSelector,
+          $ctxTextSelection,
+        });
+
+        $ctxLink = '';
+        $ctxMediaUrl = '';
+        $ctxElSelector = '';
+        $ctxTextSelection = '';
+
+        return cloneContextData;
       }
-    });
+      default:
+        return null;
+    }
   });
 })();
 

+ 17 - 1
src/locales/zh/blocks.json

@@ -396,6 +396,20 @@
         "useCommas": "使用逗号分隔变量名",
         "insertAllGlobalData": "插入当前工作流的 GlobalData"
       },
+      "google-sheets-drive": {
+        "name": "@:workflow.blocks.google-sheets.name (GDrive)",
+        "description": "@:workflow.blocks.google-sheets.description",
+        "connected": "已连接工作簿",
+        "select": "选择工作簿",
+        "connect": "连接工作簿"
+      },
+      "google-drive": {
+        "name": "Google Drive",
+        "description": "上传文件到 Google Drive",
+        "actions": {
+          "upload": "上传文件"
+        }
+      },
       "google-sheets": {
         "name": "Google sheets",
         "description": "读取或更新 Google sheets 数据",
@@ -430,7 +444,9 @@
           "getRange": "获取电子表格范围",
           "update": "更新电子表格单元格值",
           "append": "追加电子表格单元格值",
-          "clear": "清除电子表格单元格值"
+          "clear": "清除电子表格单元格值",
+          "create": "创建电子表格",
+          "add-sheet": "添加工作簿"
         }
       },
       "active-tab": {

+ 6 - 1
src/locales/zh/common.json

@@ -26,6 +26,9 @@
     "save": "保存",
     "data": "数据",
     "stop": "停止",
+    "sheet": "工作簿",
+    "pause": "暂停",
+    "resume": "恢复",
     "action": "操作 | 操作",
     "packages": "包",
     "storage": "存储",
@@ -43,7 +46,8 @@
     "duplicate": "副本",
     "password": "密码",
     "category": "分类",
-    "optional": "可选"
+    "optional": "可选",
+    "0disable": "0 到禁用"
   },
   "message": {
     "noBlock": "没有模块",
@@ -61,6 +65,7 @@
     "sortBy": "排序方式",
     "name": "名称",
     "createdAt": "创建日期",
+    "updatedAt": "上次更新",
     "mostUsed": "最常用"
   },
   "logStatus": {

+ 35 - 1
src/locales/zh/newtab.json

@@ -14,6 +14,7 @@
     "icon": "包图标",
     "open": "打开包",
     "new": "新建包",
+    "import": "导入包",
     "set": "设置为包",
     "settings": {
       "asBlock": "把包设为模块"
@@ -152,7 +153,9 @@
       "needSignin": "首先你需要登录账户",
       "backup": {
         "button": "备份",
-        "encrypt": "用密码加密"
+        "settings": "备份设置",
+        "encrypt": "用密码加密",
+        "schedule": "本地备份计划"
       },
       "restore": {
         "title": "恢复工作流",
@@ -181,6 +184,31 @@
     }
   },
   "workflow": {
+    "events": {
+      "title": "工作流事件",
+      "add-action": "添加操作",
+      "description": "事件发生时执行操作。",
+      "event": "事件 | 事件",
+      "action": "操作",
+      "actions": {
+        "js-code": {
+          "title": "执行 JS 代码"
+        },
+        "http-request": {
+          "title": "HTTP Request"
+        }
+      },
+      "types": {
+        "finish:success": {
+          "name": "完成 (成功)",
+          "description": "工作流执行已成功完成"
+        },
+        "finish:failed": {
+          "name": "完成 (失败)",
+          "description": "工作流执行已完成,但出现错误。"
+        }
+      }
+    },
       "previewMode": {
       "title": "预览模式",
       "description": "正处于预览模式,你所做的修改不会被保存下来"
@@ -195,6 +223,12 @@
       "preferInTab": "在标签页中输入参数"
     },
     "my": "我的工作流",
+    "testing": {
+      "title": "测试模式",
+      "nextBlock": "下一模块",
+      "startRun": "开始运行于",
+      "disabled": "首先保存更改"
+    },
     "import": "导入工作流",
     "new": "新建工作流",
     "delete": "删除工作流",

+ 3 - 47
src/newtab/pages/storage/Tables.vue

@@ -56,8 +56,9 @@
     </div>
     <div class="scroll w-full overflow-x-auto">
       <ui-table
+        with-pagination
         :headers="table.header"
-        :items="rows"
+        :items="table.body"
         :search="state.query"
         item-key="id"
         class="w-full"
@@ -72,33 +73,6 @@
         </template>
       </ui-table>
     </div>
-    <div
-      v-if="table.body && table.body.length >= 10"
-      class="mt-4 flex flex-col md:flex-row md:items-center md:justify-between"
-    >
-      <div class="mb-4 md:mb-0">
-        {{ t('components.pagination.text1') }}
-        <select v-model="pagination.perPage" class="bg-input rounded-md p-1">
-          <option
-            v-for="num in [10, 15, 25, 50, 100, 150]"
-            :key="num"
-            :value="num"
-          >
-            {{ num }}
-          </option>
-        </select>
-        {{
-          t('components.pagination.text2', {
-            count: table.body.length,
-          })
-        }}
-      </div>
-      <ui-pagination
-        v-model="pagination.currentPage"
-        :per-page="pagination.perPage"
-        :records="table.body.length"
-      />
-    </div>
     <storage-edit-table
       v-model="editState.show"
       :name="editState.name"
@@ -108,14 +82,7 @@
   </div>
 </template>
 <script setup>
-import {
-  watch,
-  shallowRef,
-  shallowReactive,
-  computed,
-  toRaw,
-  triggerRef,
-} from 'vue';
+import { watch, shallowRef, shallowReactive, toRaw, triggerRef } from 'vue';
 import { useRoute, useRouter } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import { useWorkflowStore } from '@/stores/workflow';
@@ -154,17 +121,6 @@ const editState = shallowReactive({
   columns: [],
   show: false,
 });
-const pagination = shallowReactive({
-  perPage: 10,
-  currentPage: 1,
-});
-
-const rows = computed(() =>
-  table.value.body.slice(
-    (pagination.currentPage - 1) * pagination.perPage,
-    pagination.currentPage * pagination.perPage
-  )
-);
 
 function editTable() {
   editState.name = tableDetail.value.name;

+ 1 - 1
src/utils/helper.js

@@ -67,7 +67,7 @@ export function findTriggerBlock(drawflow = {}) {
   if (!drawflow) return null;
 
   if (drawflow.drawflow) {
-    const blocks = Object.values(drawflow.drawflow?.Home?.data);
+    const blocks = Object.values(drawflow.drawflow?.Home?.data ?? {});
     if (!blocks) return null;
 
     return blocks.find(({ name }) => name === 'trigger');

+ 6 - 5
src/workflowEngine/WorkflowEngine.js

@@ -392,17 +392,18 @@ class WorkflowEngine {
     await browser.storage.local.set({ workflowQueue });
   }
 
-  destroyWorker(workerId) {
-    this.workers.delete(workerId);
-
-    if (this.workers.size === 0) {
+  async destroyWorker(workerId) {
+    // is last worker
+    if (this.workers.size === 1 && this.workers.has(workerId)) {
       this.addLogHistory({
         type: 'finish',
         name: 'finish',
       });
       this.dispatchEvent('finish');
-      this.destroy('success');
+      await this.destroy('success');
     }
+    // wait detach debugger
+    this.workers.delete(workerId);
   }
 
   async destroy(status, message, blockDetail) {

+ 24 - 0
src/workflowEngine/WorkflowWorker.js

@@ -141,6 +141,29 @@ class WorkflowWorker {
     prevBlockData,
     nextBlockBreakpointCount = null
   ) {
+    // pre check
+    for (const connection of connections) {
+      const id = typeof connection === 'string' ? connection : connection.id;
+
+      const block = this.engine.blocks[id];
+
+      if (!block) {
+        console.error(`Block ${id} doesn't exist`);
+        this.engine.destroy('stopped');
+        return;
+      }
+
+      // pass disabled block
+      // eslint-disable-next-line no-continue
+      if (block.data.disableBlock) continue;
+
+      // check if the next block is breakpoint
+      if (block.data?.$breakpoint) {
+        // set breakpoint state
+        nextBlockBreakpointCount = 0;
+      }
+    }
+
     connections.forEach((connection, index) => {
       const { id, targetHandle, sourceHandle } =
         typeof connection === 'string'
@@ -166,6 +189,7 @@ class WorkflowWorker {
           currentBlock: this.currentBlock,
           repeatedTasks: this.repeatedTasks,
           preloadScripts: this.preloadScripts,
+          debugAttached: this.debugAttached,
         });
 
         this.engine.addWorker({

+ 15 - 10
yarn.lock

@@ -1816,21 +1816,26 @@
   resolved "https://registry.yarnpkg.com/@viselect/vanilla/-/vanilla-3.3.1.tgz#ec4276b0814157b7ffe8ac1f28fdd70df36e1d76"
   integrity sha512-QqkPGV+uyteX/HqzhHp5ZRfpVYiA0A3umh8HT/nfoBKccoA/kiTdI+5B/8ZPijtanBj9NNymMMPCn0W+/BeEyg==
 
-"@vue-flow/additional-components@^1.3.3":
-  version "1.3.3"
-  resolved "https://registry.yarnpkg.com/@vue-flow/additional-components/-/additional-components-1.3.3.tgz#383fe2f79534a7d3a3fecc51006be790c321ca87"
-  integrity sha512-AZhz0diM7VIN7MGKODiuqiu+xiujFQSs2UdiThgNI5vGSwwizd0g9dGzB+LK0Dt4FCRJ1g64xzxqbrAFFfzuFw==
+"@vue-flow/background@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@vue-flow/background/-/background-1.2.0.tgz#6b7c283fd679838df90dda8dd98f322c368b7e59"
+  integrity sha512-ZqmYhOM/0aRmA5dA3KSHJNYpHnhmG2e6tzjmttoibo4JBI3KNbdyOX+OTlqt7Ic0LYUC6NWzRLAwv4gjJuc6Mg==
+
+"@vue-flow/core@^1.23.0":
+  version "1.23.0"
+  resolved "https://registry.yarnpkg.com/@vue-flow/core/-/core-1.23.0.tgz#f6171f70451d7a4e9804736bd4156659393cee2a"
+  integrity sha512-jfzQNWy5+JVgAmopJciK1acKQcDtPDwu7UM5FUlVXyzNaJgsAJR/8C5guOq7NPzfACz5TodDrBzbDePF40NQZg==
   dependencies:
+    "@vueuse/core" "^10.1.2"
+    d3-drag "^3.0.0"
     d3-selection "^3.0.0"
     d3-zoom "^3.0.0"
 
-"@vue-flow/core@^1.22.3":
-  version "1.22.3"
-  resolved "https://registry.yarnpkg.com/@vue-flow/core/-/core-1.22.3.tgz#2f98bb5185df64ac33e9eaa47093a624ef291698"
-  integrity sha512-1Hz7cnomzbUuBYOghtb+ZUunUAD8UXJC/PrUUjjQQa2HEcaRK9KK3c4sE1QMBbhQh5umcuwjRH/XVJhOVDoIOQ==
+"@vue-flow/minimap@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@vue-flow/minimap/-/minimap-1.2.0.tgz#95770b380dbb55b951bd13339e27429cf171010f"
+  integrity sha512-0Nk7iWjRC9hgyVGEVELKULXZsJtYmzic4CEAM6mIhB/lDVUsQsae9NXB6gYFN0OlBXx71pZHBnqqUYvLxUE0jg==
   dependencies:
-    "@vueuse/core" "^10.1.2"
-    d3-drag "^3.0.0"
     d3-selection "^3.0.0"
     d3-zoom "^3.0.0"