diff --git a/package.json b/package.json index 7a5e3798a..7b03021d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "automa", - "version": "1.5.1", + "version": "1.5.4", "description": "An extension for automating your browser by connecting blocks", "license": "MIT", "repository": { @@ -23,8 +23,9 @@ }, "dependencies": { "@codemirror/basic-setup": "^0.19.1", - "@codemirror/lang-javascript": "0.19.1", - "@codemirror/lang-json": "^0.19.1", + "@codemirror/fold": "^0.19.3", + "@codemirror/lang-javascript": "^0.19.7", + "@codemirror/lang-json": "^0.19.2", "@codemirror/theme-one-dark": "^0.19.1", "@medv/finder": "^2.1.0", "@tiptap/extension-character-count": "^2.0.0-beta.24", diff --git a/src/background/collection-engine/flow-handler.js b/src/background/collection-engine/flow-handler.js index 80ab2d63d..55b0b77e1 100644 --- a/src/background/collection-engine/flow-handler.js +++ b/src/background/collection-engine/flow-handler.js @@ -1,6 +1,6 @@ +import dataExporter from '@/utils/data-exporter'; import WorkflowEngine from '../workflow-engine/engine'; import blocksHandler from '../workflow-engine/blocks-handler'; -import dataExporter from '@/utils/data-exporter'; export function workflow(flow) { return new Promise((resolve, reject) => { diff --git a/src/background/index.js b/src/background/index.js index d2f5e2fde..73a50dfb9 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1,14 +1,14 @@ import browser from 'webextension-polyfill'; import { MessageListener } from '@/utils/message'; -import { registerSpecificDay } from '../utils/workflow-trigger'; import { parseJSON, findTriggerBlock } from '@/utils/helper'; import getFile from '@/utils/get-file'; +import decryptFlow, { getWorkflowPass } from '@/utils/decrypt-flow'; +import { registerSpecificDay } from '../utils/workflow-trigger'; import WorkflowState from './workflow-state'; import CollectionEngine from './collection-engine'; import WorkflowEngine from './workflow-engine/engine'; import blocksHandler from './workflow-engine/blocks-handler'; import WorkflowLogger from './workflow-logger'; -import decryptFlow, { getWorkflowPass } from '@/utils/decrypt-flow'; const validateUrl = (str) => str?.startsWith('http'); const storage = { diff --git a/src/background/workflow-engine/blocks-handler/handler-active-tab.js b/src/background/workflow-engine/blocks-handler/handler-active-tab.js index a9d9425b1..0159c9120 100644 --- a/src/background/workflow-engine/blocks-handler/handler-active-tab.js +++ b/src/background/workflow-engine/blocks-handler/handler-active-tab.js @@ -40,6 +40,13 @@ async function activeTab(block) { }; this.windowId = tab.windowId; + if (this.preloadScripts.length > 0) { + const preloadScripts = this.preloadScripts.map((script) => + this._sendMessageToTab(script) + ); + await Promise.allSettled(preloadScripts); + } + return data; } catch (error) { console.error(error); diff --git a/src/background/workflow-engine/blocks-handler/handler-browser-event.js b/src/background/workflow-engine/blocks-handler/handler-browser-event.js index da8bb81c9..c44eed3a7 100644 --- a/src/background/workflow-engine/blocks-handler/handler-browser-event.js +++ b/src/background/workflow-engine/blocks-handler/handler-browser-event.js @@ -1,6 +1,6 @@ import browser from 'webextension-polyfill'; -import { getBlockConnection } from '../helper'; import { isWhitespace } from '@/utils/helper'; +import { getBlockConnection } from '../helper'; function handleEventListener(target, validate) { return (data, activeTab) => { diff --git a/src/background/workflow-engine/blocks-handler/handler-clipboard.js b/src/background/workflow-engine/blocks-handler/handler-clipboard.js index d17fd501b..a822df405 100644 --- a/src/background/workflow-engine/blocks-handler/handler-clipboard.js +++ b/src/background/workflow-engine/blocks-handler/handler-clipboard.js @@ -21,7 +21,7 @@ export default async function ({ data, outputs }) { const copiedText = textarea.value; if (data.assignVariable) { - this.referenceData.variables[data.variableName] = copiedText; + this.setVariable(data.variableName, copiedText); } if (data.saveData) { this.addDataToColumn(data.dataColumn, copiedText); diff --git a/src/background/workflow-engine/blocks-handler/handler-conditions.js b/src/background/workflow-engine/blocks-handler/handler-conditions.js index 9ae9fd0b9..ed66b79eb 100644 --- a/src/background/workflow-engine/blocks-handler/handler-conditions.js +++ b/src/background/workflow-engine/blocks-handler/handler-conditions.js @@ -1,28 +1,60 @@ -import { getBlockConnection } from '../helper'; import compareBlockValue from '@/utils/compare-block-value'; import mustacheReplacer from '@/utils/reference-data/mustache-replacer'; +import testConditions from '@/utils/test-conditions'; +import { getBlockConnection } from '../helper'; -function conditions({ data, outputs }, { prevBlockData, refData }) { - return new Promise((resolve, reject) => { - if (data.conditions.length === 0) { - reject(new Error('conditions-empty')); - return; - } +async function conditions({ data, outputs }, { prevBlockData, refData }) { + if (data.conditions.length === 0) { + throw new Error('conditions-empty'); + } + + let resultData = ''; + let isConditionMatch = false; + let outputIndex = data.conditions.length + 1; + + const replacedValue = {}; + const condition = data.conditions[0]; + const prevData = Array.isArray(prevBlockData) + ? prevBlockData[0] + : prevBlockData; + + if (condition && condition.conditions) { + const conditionPayload = { + refData, + activeTab: this.activeTab.id, + sendMessage: (payload) => + this._sendMessageToTab({ ...payload, isBlock: false }), + }; + + for (let index = 0; index < data.conditions.length; index += 1) { + const result = await testConditions( + data.conditions[index].conditions, + conditionPayload + ); - let resultData = ''; - let isConditionMatch = false; - let outputIndex = data.conditions.length + 1; - const prevData = Array.isArray(prevBlockData) - ? prevBlockData[0] - : prevBlockData; + Object.assign(replacedValue, result?.replacedValue || {}); + if (result.isMatch) { + isConditionMatch = true; + outputIndex = index + 1; + + break; + } + } + } else { data.conditions.forEach(({ type, value, compareValue }, index) => { if (isConditionMatch) return; const firstValue = mustacheReplacer(compareValue ?? prevData, refData); const secondValue = mustacheReplacer(value, refData); - const isMatch = compareBlockValue(type, firstValue, secondValue); + Object.assign(replacedValue, firstValue.list, secondValue.list); + + const isMatch = compareBlockValue( + type, + firstValue.value, + secondValue.value + ); if (isMatch) { resultData = value; @@ -30,12 +62,13 @@ function conditions({ data, outputs }, { prevBlockData, refData }) { isConditionMatch = true; } }); + } - resolve({ - data: resultData, - nextBlockId: getBlockConnection({ outputs }, outputIndex), - }); - }); + return { + replacedValue, + data: resultData, + nextBlockId: getBlockConnection({ outputs }, outputIndex), + }; } export default conditions; diff --git a/src/background/workflow-engine/blocks-handler/handler-execute-workflow.js b/src/background/workflow-engine/blocks-handler/handler-execute-workflow.js index 3af9deb36..17694e185 100644 --- a/src/background/workflow-engine/blocks-handler/handler-execute-workflow.js +++ b/src/background/workflow-engine/blocks-handler/handler-execute-workflow.js @@ -1,8 +1,8 @@ import browser from 'webextension-polyfill'; -import WorkflowEngine from '../engine'; -import { getBlockConnection } from '../helper'; import { isWhitespace, parseJSON } from '@/utils/helper'; import decryptFlow, { getWorkflowPass } from '@/utils/decrypt-flow'; +import WorkflowEngine from '../engine'; +import { getBlockConnection } from '../helper'; function workflowListener(workflow, options) { return new Promise((resolve, reject) => { diff --git a/src/background/workflow-engine/blocks-handler/handler-export-data.js b/src/background/workflow-engine/blocks-handler/handler-export-data.js index 0003e9634..1102fd154 100644 --- a/src/background/workflow-engine/blocks-handler/handler-export-data.js +++ b/src/background/workflow-engine/blocks-handler/handler-export-data.js @@ -1,6 +1,6 @@ import browser from 'webextension-polyfill'; -import { getBlockConnection } from '../helper'; import { default as dataExporter, files } from '@/utils/data-exporter'; +import { getBlockConnection } from '../helper'; async function exportData({ data, outputs }) { const nextBlockId = getBlockConnection({ outputs }); diff --git a/src/background/workflow-engine/blocks-handler/handler-google-sheets.js b/src/background/workflow-engine/blocks-handler/handler-google-sheets.js index d64ad0616..223d03911 100644 --- a/src/background/workflow-engine/blocks-handler/handler-google-sheets.js +++ b/src/background/workflow-engine/blocks-handler/handler-google-sheets.js @@ -1,4 +1,3 @@ -import { getBlockConnection } from '../helper'; import { googleSheets } from '@/utils/api'; import { convert2DArrayToArrayObj, @@ -6,6 +5,7 @@ import { isWhitespace, parseJSON, } from '@/utils/helper'; +import { getBlockConnection } from '../helper'; async function getSpreadsheetValues({ spreadsheetId, range, firstRowAsKey }) { const response = await googleSheets.getValues({ spreadsheetId, range }); diff --git a/src/background/workflow-engine/blocks-handler/handler-handle-download.js b/src/background/workflow-engine/blocks-handler/handler-handle-download.js index d95ef67c1..7146a872e 100644 --- a/src/background/workflow-engine/blocks-handler/handler-handle-download.js +++ b/src/background/workflow-engine/blocks-handler/handler-handle-download.js @@ -51,7 +51,7 @@ function handleDownload({ data, outputs }) { this.addDataToColumn(data.dataColumn, currentFilename); } if (data.assignVariable) { - this.referenceData.variables[data.variableName] = currentFilename; + this.setVariable(data.variableName, currentFilename); } clearTimeout(timeout); diff --git a/src/background/workflow-engine/blocks-handler/handler-insert-data.js b/src/background/workflow-engine/blocks-handler/handler-insert-data.js index 607f34d84..ea00944bf 100644 --- a/src/background/workflow-engine/blocks-handler/handler-insert-data.js +++ b/src/background/workflow-engine/blocks-handler/handler-insert-data.js @@ -1,21 +1,26 @@ -import { getBlockConnection } from '../helper'; import { parseJSON } from '@/utils/helper'; import mustacheReplacer from '@/utils/reference-data/mustache-replacer'; +import { getBlockConnection } from '../helper'; function insertData({ outputs, data }, { refData }) { return new Promise((resolve) => { + const replacedValueList = {}; data.dataList.forEach(({ name, value, type }) => { const replacedValue = mustacheReplacer(value, refData); - const realValue = parseJSON(replacedValue, replacedValue); + const realValue = parseJSON(replacedValue.value, replacedValue.value); + + Object.assign(replacedValueList, replacedValue.list); if (type === 'table') { this.addDataToColumn(name, realValue); } else { - this.referenceData.variables[name] = realValue; + this.setVariable(name, realValue); } }); resolve({ + data: '', + replacedValue: replacedValueList, nextBlockId: getBlockConnection({ outputs }), }); }); diff --git a/src/background/workflow-engine/blocks-handler/handler-interaction-block.js b/src/background/workflow-engine/blocks-handler/handler-interaction-block.js index 27a77a358..03fc2a450 100644 --- a/src/background/workflow-engine/blocks-handler/handler-interaction-block.js +++ b/src/background/workflow-engine/blocks-handler/handler-interaction-block.js @@ -24,24 +24,13 @@ async function checkAccess(blockName) { return true; } -async function interactionHandler(block, { refData }) { +async function interactionHandler(block) { await checkAccess(block.name); - const { executedBlockOnWeb, debugMode } = this.workflow.settings; - const nextBlockId = getBlockConnection(block); - const messagePayload = { - ...block, - debugMode, - executedBlockOnWeb, - activeTabId: this.activeTab.id, - frameSelector: this.frameSelector, - }; - - if (block.name === 'javascript-code') messagePayload.refData = refData; try { - const data = await this._sendMessageToTab(messagePayload, { + const data = await this._sendMessageToTab(block, { frameId: this.activeTab.frameId || 0, }); @@ -75,23 +64,7 @@ async function interactionHandler(block, { refData }) { } if (block.data.assignVariable) { - this.referenceData.variables[block.data.variableName] = data; - } - - if (block.name === 'javascript-code') { - if (data?.variables) { - Object.keys(data.variables).forEach((varName) => { - this.referenceData.variables[varName] = data.variables[varName]; - }); - } - - if (data?.columns.insert) { - const params = Array.isArray(data.columns.data) - ? data.columns.data - : [data.columns.data]; - - this.addDataToColumn(params); - } + this.setVariable(block.data.variableName, data); } return { diff --git a/src/background/workflow-engine/blocks-handler/handler-javascript-code.js b/src/background/workflow-engine/blocks-handler/handler-javascript-code.js new file mode 100644 index 000000000..08446f016 --- /dev/null +++ b/src/background/workflow-engine/blocks-handler/handler-javascript-code.js @@ -0,0 +1,50 @@ +import { getBlockConnection } from '../helper'; + +export async function javascriptCode({ outputs, data, ...block }, { refData }) { + const nextBlockId = getBlockConnection({ outputs }); + + try { + if (data.everyNewTab) { + const isScriptExist = this.preloadScripts.find( + ({ id }) => id === block.id + ); + + if (!isScriptExist) { + this.preloadScripts.push({ ...block, data }); + } + } + if (!this.activeTab.id) { + if (!data.everyNewTab) { + throw new Error('no-tab'); + } else { + return { data: '', nextBlockId }; + } + } + + const result = await this._sendMessageToTab({ ...block, data, refData }); + + if (result?.variables) { + Object.keys(result.variables).forEach((varName) => { + this.setVariable(varName, result.variables[varName]); + }); + } + if (result?.columns.insert) { + const params = Array.isArray(result.columns.data) + ? result.columns.data + : [result.columns.data]; + + this.addDataToColumn(params); + } + + return { + nextBlockId, + data: result?.columns.data || {}, + }; + } catch (error) { + error.nextBlockId = nextBlockId; + + throw error; + } +} + +export default javascriptCode; diff --git a/src/background/workflow-engine/blocks-handler/handler-loop-data.js b/src/background/workflow-engine/blocks-handler/handler-loop-data.js index 74601a2a2..2acd2672e 100644 --- a/src/background/workflow-engine/blocks-handler/handler-loop-data.js +++ b/src/background/workflow-engine/blocks-handler/handler-loop-data.js @@ -1,5 +1,5 @@ -import { getBlockConnection } from '../helper'; import { parseJSON } from '@/utils/helper'; +import { getBlockConnection } from '../helper'; async function loopData({ data, id, outputs }) { const nextBlockId = getBlockConnection({ outputs }); diff --git a/src/background/workflow-engine/blocks-handler/handler-new-tab.js b/src/background/workflow-engine/blocks-handler/handler-new-tab.js index d8b37c697..7239340ac 100644 --- a/src/background/workflow-engine/blocks-handler/handler-new-tab.js +++ b/src/background/workflow-engine/blocks-handler/handler-new-tab.js @@ -1,10 +1,10 @@ import browser from 'webextension-polyfill'; +import { isWhitespace, sleep } from '@/utils/helper'; import { getBlockConnection, attachDebugger, sendDebugCommand, } from '../helper'; -import { isWhitespace, sleep } from '@/utils/helper'; async function newTab(block) { if (this.windowId) { @@ -84,6 +84,13 @@ async function newTab(block) { chrome.debugger.detach({ tabId: tab.id }); } + if (this.preloadScripts.length > 0) { + const preloadScripts = this.preloadScripts.map((script) => + this._sendMessageToTab(script) + ); + await Promise.allSettled(preloadScripts); + } + return { data: url, nextBlockId, diff --git a/src/background/workflow-engine/blocks-handler/handler-switch-tab.js b/src/background/workflow-engine/blocks-handler/handler-switch-tab.js index 5611c7aea..9e6cf6857 100644 --- a/src/background/workflow-engine/blocks-handler/handler-switch-tab.js +++ b/src/background/workflow-engine/blocks-handler/handler-switch-tab.js @@ -41,6 +41,13 @@ export default async function ({ data, outputs }) { this.activeTab.url = tab.url; this.windowId = tab.windowId; + if (this.preloadScripts.length > 0) { + const preloadScripts = this.preloadScripts.map((script) => + this._sendMessageToTab(script) + ); + await Promise.allSettled(preloadScripts); + } + return { nextBlockId, data: tab.url, diff --git a/src/background/workflow-engine/blocks-handler/handler-take-screenshot.js b/src/background/workflow-engine/blocks-handler/handler-take-screenshot.js index 0c50b40bc..15d2fb9e2 100644 --- a/src/background/workflow-engine/blocks-handler/handler-take-screenshot.js +++ b/src/background/workflow-engine/blocks-handler/handler-take-screenshot.js @@ -1,6 +1,6 @@ import browser from 'webextension-polyfill'; -import { getBlockConnection } from '../helper'; import { fileSaver } from '@/utils/helper'; +import { getBlockConnection } from '../helper'; function saveImage({ fileName, uri, ext }) { const image = new Image(); @@ -35,8 +35,7 @@ async function takeScreenshot({ data, outputs, name }) { 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 (data.assignVariable) this.setVariable(data.variableName, dataUrl); }; if (data.captureActiveTab) { diff --git a/src/background/workflow-engine/blocks-handler/handler-webhook.js b/src/background/workflow-engine/blocks-handler/handler-webhook.js index 9745882be..b9c04fb3e 100644 --- a/src/background/workflow-engine/blocks-handler/handler-webhook.js +++ b/src/background/workflow-engine/blocks-handler/handler-webhook.js @@ -1,7 +1,7 @@ import objectPath from 'object-path'; -import { getBlockConnection } from '../helper'; import { isWhitespace } from '@/utils/helper'; import { executeWebhook } from '@/utils/webhookUtil'; +import { getBlockConnection } from '../helper'; export async function webhook({ data, outputs }) { const nextBlockId = getBlockConnection({ outputs }); @@ -42,7 +42,7 @@ export async function webhook({ data, outputs }) { } if (data.assignVariable) { - this.referenceData.variables[data.variableName] = returnData; + this.setVariable(data.variableName, returnData); } if (data.saveData) { this.addDataToColumn(data.dataColumn, returnData); diff --git a/src/background/workflow-engine/blocks-handler/handler-while-loop.js b/src/background/workflow-engine/blocks-handler/handler-while-loop.js new file mode 100644 index 000000000..4e79390d1 --- /dev/null +++ b/src/background/workflow-engine/blocks-handler/handler-while-loop.js @@ -0,0 +1,21 @@ +import testConditions from '@/utils/test-conditions'; +import { getBlockConnection } from '../helper'; + +async function whileLoop({ data, outputs }, { refData }) { + const conditionPayload = { + refData, + activeTab: this.activeTab.id, + sendMessage: (payload) => + this._sendMessageToTab({ ...payload, isBlock: false }), + }; + const result = await testConditions(data.conditions, conditionPayload); + const nextBlockId = getBlockConnection({ outputs }, result.isMatch ? 1 : 2); + + return { + data: '', + nextBlockId, + replacedValue: result?.replacedValue || {}, + }; +} + +export default whileLoop; diff --git a/src/background/workflow-engine/engine.js b/src/background/workflow-engine/engine.js index 8facc3e30..973667618 100644 --- a/src/background/workflow-engine/engine.js +++ b/src/background/workflow-engine/engine.js @@ -1,7 +1,6 @@ import browser from 'webextension-polyfill'; import { nanoid } from 'nanoid'; import { tasks } from '@/utils/shared'; -import { convertData, waitTabLoaded } from './helper'; import { toCamelCase, sleep, @@ -10,6 +9,7 @@ import { objectHasKey, } from '@/utils/helper'; import referenceData from '@/utils/reference-data'; +import { convertData, waitTabLoaded } from './helper'; import executeContentScript from './execute-content-script'; class WorkflowEngine { @@ -39,7 +39,9 @@ class WorkflowEngine { this.blocks = {}; this.history = []; this.columnsId = {}; + this.historyCtxData = {}; this.eventListeners = {}; + this.preloadScripts = []; this.columns = { column: { index: 0, name: 'column', type: 'any' } }; let variables = {}; @@ -96,6 +98,7 @@ class WorkflowEngine { this.isUsingProxy = false; this.history = []; + this.preloadScripts = []; this.columns = { column: { index: 0, name: 'column', type: 'any' } }; this.activeTab = { @@ -204,6 +207,29 @@ class WorkflowEngine { ) return; + const historyId = nanoid(); + detail.id = historyId; + + if ( + detail.replacedValue || + (tasks[detail.name]?.refDataKeys && this.saveLog) + ) { + const { activeTabUrl, loopData, prevBlockData } = JSON.parse( + JSON.stringify(this.referenceData) + ); + + this.historyCtxData[historyId] = { + referenceData: { + loopData, + activeTabUrl, + prevBlockData, + }, + replacedValue: detail.replacedValue, + }; + + delete detail.replacedValue; + } + this.history.push(detail); } @@ -236,6 +262,10 @@ class WorkflowEngine { currentColumn.index += 1; } + setVariable(name, value) { + this.referenceData.variables[name] = value; + } + async stop() { try { if (this.childWorkflowId) { @@ -285,6 +315,11 @@ class WorkflowEngine { if (!this.workflow.isTesting) { const { name, id } = this.workflow; + let { logsCtxData } = await browser.storage.local.get('logsCtxData'); + if (!logsCtxData) logsCtxData = {}; + logsCtxData[this.id] = this.historyCtxData; + await browser.storage.local.set({ logsCtxData }); + await this.logger.add({ name, status, @@ -312,16 +347,18 @@ class WorkflowEngine { currentBlock: this.currentBlock, }); - browser.storage.local.set({ - [`last-state:${this.workflow.id}`]: { - columns: this.columns, - referenceData: { - table: this.referenceData.table, - variables: this.referenceData.variables, - globalData: this.referenceData.globalData, + if (this.workflow.settings.reuseLastState) { + browser.storage.local.set({ + [`last-state:${this.workflow.id}`]: { + columns: this.columns, + referenceData: { + table: this.referenceData.table, + variables: this.referenceData.variables, + globalData: this.referenceData.globalData, + }, }, - }, - }); + }); + } this.isDestroyed = true; this.eventListeners = {}; @@ -374,10 +411,15 @@ class WorkflowEngine { refData: this.referenceData, }); + if (result.replacedValue) + replacedBlock.replacedValue = result.replacedValue; + this.addLogHistory({ name: block.name, logId: result.logId, type: result.status || 'success', + description: block.data.description, + replacedValue: replacedBlock.replacedValue, duration: Math.round(Date.now() - startExecuteTime), }); @@ -398,6 +440,8 @@ class WorkflowEngine { type: 'error', message: error.message, name: block.name, + description: block.data.description, + replacedValue: replacedBlock.replacedValue, ...(error.data || {}), }); @@ -483,12 +527,25 @@ class WorkflowEngine { } await waitTabLoaded(this.activeTab.id); - await executeContentScript(this.activeTab.id, options.frameId || 0); + await executeContentScript( + this.activeTab.id, + this.activeTab.frameId || 0 + ); + + const { executedBlockOnWeb, debugMode } = this.workflow.settings; + const messagePayload = { + isBlock: true, + debugMode, + executedBlockOnWeb, + activeTabId: this.activeTab.id, + frameSelector: this.frameSelector, + ...payload, + }; const data = await browser.tabs.sendMessage( this.activeTab.id, - { isBlock: true, ...payload }, - options + messagePayload, + { ...options, frameId: this.activeTab.frameId } ); return data; diff --git a/src/components/block/BlockBasic.vue b/src/components/block/BlockBasic.vue index 88530f606..1d10dbffc 100644 --- a/src/components/block/BlockBasic.vue +++ b/src/components/block/BlockBasic.vue @@ -32,7 +32,7 @@ /> - + + + diff --git a/src/components/block/BlockConditions.vue b/src/components/block/BlockConditions.vue index acccf2a6a..615acc9a9 100644 --- a/src/components/block/BlockConditions.vue +++ b/src/components/block/BlockConditions.vue @@ -20,18 +20,23 @@ @click="editBlock" /> -
-
-
+ {{ item.name }} +

+ +

- - ⓘ - - {{ t('common.fallback') }} + + Fallback

-
+
diff --git a/src/components/block/BlockElementExists.vue b/src/components/block/BlockElementExists.vue index 4be9798e6..3308997ff 100644 --- a/src/components/block/BlockElementExists.vue +++ b/src/components/block/BlockElementExists.vue @@ -36,9 +36,9 @@ diff --git a/src/components/newtab/shared/SharedConditionBuilder/index.vue b/src/components/newtab/shared/SharedConditionBuilder/index.vue new file mode 100644 index 000000000..41d109f14 --- /dev/null +++ b/src/components/newtab/shared/SharedConditionBuilder/index.vue @@ -0,0 +1,220 @@ + + + diff --git a/src/components/newtab/workflow/edit/EditClipboard.vue b/src/components/newtab/workflow/edit/EditClipboard.vue index 262455299..10a51edce 100644 --- a/src/components/newtab/workflow/edit/EditClipboard.vue +++ b/src/components/newtab/workflow/edit/EditClipboard.vue @@ -24,8 +24,8 @@ diff --git a/src/components/newtab/workflow/edit/EditJavascriptCode.vue b/src/components/newtab/workflow/edit/EditJavascriptCode.vue index d2f24cbc0..1688e34a9 100644 --- a/src/components/newtab/workflow/edit/EditJavascriptCode.vue +++ b/src/components/newtab/workflow/edit/EditJavascriptCode.vue @@ -8,6 +8,7 @@ @change="updateData({ description: $event })" /> + + {{ t('workflow.blocks.javascript-code.everyNewTab') }} +
- + {{ t('workflow.blocks.javascript-code.removeAfterExec') }}
diff --git a/src/components/newtab/workflow/edit/EditWhileLoop.vue b/src/components/newtab/workflow/edit/EditWhileLoop.vue new file mode 100644 index 000000000..8d712b34b --- /dev/null +++ b/src/components/newtab/workflow/edit/EditWhileLoop.vue @@ -0,0 +1,108 @@ + + diff --git a/src/components/ui/UiAutocomplete.vue b/src/components/ui/UiAutocomplete.vue new file mode 100644 index 000000000..41f8dd566 --- /dev/null +++ b/src/components/ui/UiAutocomplete.vue @@ -0,0 +1,169 @@ + + diff --git a/src/components/ui/UiExpand.vue b/src/components/ui/UiExpand.vue index e17db6bd3..76dc246de 100644 --- a/src/components/ui/UiExpand.vue +++ b/src/components/ui/UiExpand.vue @@ -1,13 +1,22 @@