diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 9b4f4025..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "frontend/packages/crawlab-ui"]
- path = frontend/packages/crawlab-ui
- url = git@github.com:crawlab-team/crawlab-ui.git
diff --git a/frontend/.Dockerfile.swp b/frontend/.Dockerfile.swp
new file mode 100644
index 00000000..7dc4d5c4
Binary files /dev/null and b/frontend/.Dockerfile.swp differ
diff --git a/frontend/.dockerignore b/frontend/.dockerignore
deleted file mode 100644
index 9bc955c7..00000000
--- a/frontend/.dockerignore
+++ /dev/null
@@ -1,33 +0,0 @@
-# Ignore the .npmrc file
-.npmrc
-
-# Ignore node_modules
-node_modules
-
-# local env files
-.env.local
-.env.*.local
-
-# Editor directories and files
-.idea
-.vscode
-*.suo
-*.ntvs*
-*.njsproj
-*.sln
-*.sw?
-
-# Log files
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-pnpm-debug.log*
-
-# Ignore stats.html
-stats.html
-
-# Ignore .turbo
-.turbo
-
-# Ignore tmp directory
-tmp/
diff --git a/frontend/.editorconfig b/frontend/.editorconfig
deleted file mode 100644
index a3425fe4..00000000
--- a/frontend/.editorconfig
+++ /dev/null
@@ -1,40 +0,0 @@
-root = true
-
-[*]
-tab_width = 2
-# charset = utf-8
-# end_of_line = lf
-# indent_size = 4
-# indent_style = space
-# insert_final_newline = true
-# max_line_length = 120
-# tab_width = 4
-# trim_trailing_whitespace = true
-
-[*.ts]
-indent_size = 2
-
-[*.scss]
-indent_size = 2
-
-[{*.ats, *.ts}]
-# indent_size = 2
-# tab_width = 2
-
-[{*.js, *.cjs}]
-# indent_size = 2
-# tab_width = 2
-
-[{*.sht, *.html, *.shtm, *.shtml, *.htm, *.ng}]
-# indent_size = 2
-# tab_width = 2
-
-[{.analysis_options, *.yml, *.yaml}]
-# indent_size = 2
-
-[{.babelrc, .prettierrc, .stylelintrc, .eslintrc, jest.config, *.json, *.jsb3, *.jsb2, *.bowerrc}]
-# indent_size = 2
-
-[vue.config.js]
-indent_size = 2
-tab_width = 2
diff --git a/frontend/.env b/frontend/.env
deleted file mode 100644
index f0fa7c0a..00000000
--- a/frontend/.env
+++ /dev/null
@@ -1 +0,0 @@
-VITE_APP_API_BASE_URL=
diff --git a/frontend/.npmrc b/frontend/.npmrc
deleted file mode 100644
index 38f11c64..00000000
--- a/frontend/.npmrc
+++ /dev/null
@@ -1 +0,0 @@
-registry=https://registry.npmjs.org
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
index 84cefded..69c50de0 100644
--- a/frontend/Dockerfile
+++ b/frontend/Dockerfile
@@ -12,7 +12,7 @@ WORKDIR /app
RUN npm install -g pnpm
# Copy application code
-ADD . .
+ADD crawlab-ui/* .
# Install project dependencies
RUN pnpm install
diff --git a/frontend/apps/crawlab/package.json b/frontend/apps/crawlab/package.json
deleted file mode 100644
index bf020617..00000000
--- a/frontend/apps/crawlab/package.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "name": "crawlab",
- "version": "0.7.0-rc7",
- "scripts": {
- "dev": "pnpm run serve",
- "serve": "vite",
- "preview": "vite preview",
- "build": "vite build",
- "build:analyze": "vite build --mode analyze",
- "build:serve": "serve dist",
- "test": "jest"
- },
- "dependencies": {
- "crawlab-ui": "workspace:crawlab-ui"
- },
- "devDependencies": {
- "@types/node": "^20.14.8",
- "@vitejs/plugin-vue": "^5.0.5",
- "@vue/compiler-sfc": "^3.2.45",
- "pnpm": "^9",
- "rollup-plugin-external-globals": "^0.10.0",
- "rollup-plugin-visualizer": "^5.12.0",
- "sass": "^1.56.1",
- "typescript": "^5",
- "vite": "^5.4.6",
- "vite-aliases": "^0.11.7",
- "vite-plugin-dynamic-import": "^1.5.0",
- "vite-plugin-externalize-deps": "^0.8.0",
- "vue-tsc": "^2.0.22"
- }
-}
diff --git a/frontend/apps/crawlab/pnpm-lock.yaml b/frontend/apps/crawlab/pnpm-lock.yaml
deleted file mode 100644
index e977a6c7..00000000
--- a/frontend/apps/crawlab/pnpm-lock.yaml
+++ /dev/null
@@ -1,4511 +0,0 @@
-lockfileVersion: '9.0'
-
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
-importers:
-
- .:
- dependencies:
- '@element-plus/icons-vue':
- specifier: ^2.3.1
- version: 2.3.1(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- '@fortawesome/fontawesome-common-types':
- specifier: ^6.5.2
- version: 6.6.0
- '@fortawesome/fontawesome-svg-core':
- specifier: ^6.5.2
- version: 6.6.0
- '@fortawesome/free-brands-svg-icons':
- specifier: ^6.5.2
- version: 6.6.0
- '@fortawesome/free-regular-svg-icons':
- specifier: ^6.5.2
- version: 6.6.0
- '@fortawesome/free-solid-svg-icons':
- specifier: ^6.5.2
- version: 6.6.0
- '@fortawesome/vue-fontawesome':
- specifier: ^3.0.8
- version: 3.0.8(@fortawesome/fontawesome-svg-core@6.6.0)(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- '@popperjs/core':
- specifier: ^2.11.8
- version: 2.11.8
- '@types/getos':
- specifier: ^3.0.4
- version: 3.0.4
- '@types/humanize-duration':
- specifier: ^3.27.4
- version: 3.27.4
- '@types/javascript-time-ago':
- specifier: ^2.0.8
- version: 2.0.8
- '@types/md5':
- specifier: ^2.3.5
- version: 2.3.5
- '@types/pinyin':
- specifier: ^2.10.2
- version: 2.10.2
- '@types/showdown':
- specifier: ^2.0.6
- version: 2.0.6
- '@types/url-join':
- specifier: ^4.0.3
- version: 4.0.3
- async-validator:
- specifier: ^4.2.5
- version: 4.2.5
- atom-material-icons:
- specifier: ^3.0.0
- version: 3.0.0
- axios:
- specifier: ^1.7.2
- version: 1.7.7
- chart.js:
- specifier: ^4.4.3
- version: 4.4.5
- chartjs-adapter-date-fns:
- specifier: ^3.0.0
- version: 3.0.0(chart.js@4.4.5)(date-fns@4.1.0)
- core-js:
- specifier: ^3.37.1
- version: 3.38.1
- crawlab-ui:
- specifier: 0.7.0-rc3
- version: 0.7.0-rc3(dlwfpvqjxhvrevoazxvsg7egqu)
- crawlab-vue3-dropzone:
- specifier: 3.0.3
- version: 3.0.3(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- cron-parser:
- specifier: ^4.9.0
- version: 4.9.0
- cronstrue:
- specifier: ^2.50.0
- version: 2.50.0
- dayjs:
- specifier: ^1.11.11
- version: 1.11.13
- element-plus:
- specifier: ^2.7.6
- version: 2.8.5(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- font-awesome:
- specifier: ^4.7.0
- version: 4.7.0
- humanize-duration:
- specifier: ^3.32.1
- version: 3.32.1
- javascript-time-ago:
- specifier: ^2.5.10
- version: 2.5.11
- md5:
- specifier: ^2.3.0
- version: 2.3.0
- monaco-editor:
- specifier: ^0.50.0
- version: 0.50.0
- normalize.css:
- specifier: ^8.0.1
- version: 8.0.1
- os-name:
- specifier: ^5.0.1
- version: 5.0.1
- pinyin:
- specifier: ^2.10.2
- version: 2.11.2
- point-cluster:
- specifier: ^3.1.8
- version: 3.1.8
- showdown:
- specifier: ^2.1.0
- version: 2.1.0
- three:
- specifier: ^0.166.0
- version: 0.166.1
- url-join:
- specifier: ^4.0.1
- version: 4.0.1
- vue:
- specifier: ^3.4
- version: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
- vue-clipboard3:
- specifier: ^2.0.0
- version: 2.0.0
- vue-i18n:
- specifier: 9.13.1
- version: 9.13.1(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- vue-router:
- specifier: 4.4.0
- version: 4.4.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- vuex:
- specifier: ^4.1.0
- version: 4.1.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- devDependencies:
- '@types/node':
- specifier: ^18.11.10
- version: https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz
- '@vitejs/plugin-vue':
- specifier: ^3.2.0
- version: https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz(vite@https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1))(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- '@vue/compiler-sfc':
- specifier: ^3.2.45
- version: https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz
- rollup-plugin-external-globals:
- specifier: ^0.8.0
- version: 0.8.0(rollup@3.26.2)
- rollup-plugin-visualizer:
- specifier: ^5.9.2
- version: 5.9.2(rollup@3.26.2)
- typescript:
- specifier: ^4.6.4
- version: https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz
- vite:
- specifier: ^3.2.4
- version: https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1)
- vite-aliases:
- specifier: ^0.9.7
- version: https://registry.npmmirror.com/vite-aliases/-/vite-aliases-0.9.7.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1)
- vite-plugin-dynamic-import:
- specifier: ^1.2.4
- version: https://registry.npmmirror.com/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.2.4.tgz
- vite-plugin-externalize-deps:
- specifier: ^0.7.0
- version: 0.7.0(vite@https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1))
- vue-tsc:
- specifier: ^1.0.9
- version: https://registry.npmmirror.com/vue-tsc/-/vue-tsc-1.0.11.tgz(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
-packages:
-
- '@ampproject/remapping@2.3.0':
- resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
- engines: {node: '>=6.0.0'}
-
- '@arr/every@1.0.1':
- resolution: {integrity: sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==}
- engines: {node: '>=4'}
-
- '@babel/helper-string-parser@7.25.7':
- resolution: {integrity: sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-string-parser@https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz':
- resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==, tarball: https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz}
- version: 7.19.4
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-validator-identifier@7.25.7':
- resolution: {integrity: sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==}
- engines: {node: '>=6.9.0'}
-
- '@babel/helper-validator-identifier@https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz':
- resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==, tarball: https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz}
- version: 7.19.1
- engines: {node: '>=6.9.0'}
-
- '@babel/parser@7.25.8':
- resolution: {integrity: sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==}
- engines: {node: '>=6.0.0'}
- hasBin: true
-
- '@babel/parser@https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz':
- resolution: {integrity: sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==, tarball: https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz}
- version: 7.20.5
- engines: {node: '>=6.0.0'}
- hasBin: true
-
- '@babel/types@7.25.8':
- resolution: {integrity: sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==}
- engines: {node: '>=6.9.0'}
-
- '@babel/types@https://registry.npmmirror.com/@babel/types/-/types-7.20.5.tgz':
- resolution: {integrity: sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==, tarball: https://registry.npmmirror.com/@babel/types/-/types-7.20.5.tgz}
- version: 7.20.5
- engines: {node: '>=6.9.0'}
-
- '@codemirror/autocomplete@6.18.1':
- resolution: {integrity: sha512-iWHdj/B1ethnHRTwZj+C1obmmuCzquH29EbcKr0qIjA9NfDeBDJ7vs+WOHsFeLeflE4o+dHfYndJloMKHUkWUA==}
- peerDependencies:
- '@codemirror/language': ^6.0.0
- '@codemirror/state': ^6.0.0
- '@codemirror/view': ^6.0.0
- '@lezer/common': ^1.0.0
-
- '@codemirror/commands@6.7.0':
- resolution: {integrity: sha512-+cduIZ2KbesDhbykV02K25A5xIVrquSPz4UxxYBemRlAT2aW8dhwUgLDwej7q/RJUHKk4nALYcR1puecDvbdqw==}
-
- '@codemirror/lang-json@6.0.1':
- resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==}
-
- '@codemirror/language@6.10.3':
- resolution: {integrity: sha512-kDqEU5sCP55Oabl6E7m5N+vZRoc0iWqgDVhEKifcHzPzjqCegcO4amfrYVL9PmPZpl4G0yjkpTpUO/Ui8CzO8A==}
-
- '@codemirror/lint@6.8.2':
- resolution: {integrity: sha512-PDFG5DjHxSEjOXk9TQYYVjZDqlZTFaDBfhQixHnQOEVDDNHUbEh/hstAjcQJaA6FQdZTD1hquXTK0rVBLADR1g==}
-
- '@codemirror/search@6.5.6':
- resolution: {integrity: sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==}
-
- '@codemirror/state@6.4.1':
- resolution: {integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==}
-
- '@codemirror/view@6.34.1':
- resolution: {integrity: sha512-t1zK/l9UiRqwUNPm+pdIT0qzJlzuVckbTEMVNFhfWkGiBQClstzg+78vedCvLSX0xJEZ6lwZbPpnljL7L6iwMQ==}
-
- '@ctrl/tinycolor@3.4.1':
- resolution: {integrity: sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==}
- engines: {node: '>=10'}
-
- '@element-plus/icons-vue@2.3.1':
- resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==}
- version: 2.3.1
- peerDependencies:
- vue: ^3.2.0
-
- '@esbuild/android-arm@0.15.16':
- resolution: {integrity: sha512-nyB6CH++2mSgx3GbnrJsZSxzne5K0HMyNIWafDHqYy7IwxFc4fd/CgHVZXr8Eh+Q3KbIAcAe3vGyqIPhGblvMQ==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [android]
-
- '@esbuild/linux-loong64@0.15.16':
- resolution: {integrity: sha512-SDLfP1uoB0HZ14CdVYgagllgrG7Mdxhkt4jDJOKl/MldKrkQ6vDJMZKl2+5XsEY/Lzz37fjgLQoJBGuAw/x8kQ==}
- engines: {node: '>=12'}
- cpu: [loong64]
- os: [linux]
-
- '@floating-ui/core@1.6.8':
- resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==}
-
- '@floating-ui/dom@1.6.11':
- resolution: {integrity: sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==}
-
- '@floating-ui/utils@0.2.8':
- resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==}
-
- '@fortawesome/fontawesome-common-types@6.6.0':
- resolution: {integrity: sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==}
- engines: {node: '>=6'}
-
- '@fortawesome/fontawesome-svg-core@6.6.0':
- resolution: {integrity: sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==}
- engines: {node: '>=6'}
-
- '@fortawesome/free-brands-svg-icons@6.6.0':
- resolution: {integrity: sha512-1MPD8lMNW/earme4OQi1IFHtmHUwAKgghXlNwWi9GO7QkTfD+IIaYpIai4m2YJEzqfEji3jFHX1DZI5pbY/biQ==}
- engines: {node: '>=6'}
-
- '@fortawesome/free-regular-svg-icons@6.6.0':
- resolution: {integrity: sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==}
- engines: {node: '>=6'}
-
- '@fortawesome/free-solid-svg-icons@6.6.0':
- resolution: {integrity: sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==}
- engines: {node: '>=6'}
-
- '@fortawesome/vue-fontawesome@3.0.8':
- resolution: {integrity: sha512-yyHHAj4G8pQIDfaIsMvQpwKMboIZtcHTUvPqXjOHyldh1O1vZfH4W03VDPv5RvI9P6DLTzJQlmVgj9wCf7c2Fw==}
- version: 3.0.8
- peerDependencies:
- '@fortawesome/fontawesome-svg-core': ~1 || ~6
- vue: '>= 3.0.0 < 4'
-
- '@intlify/core-base@9.13.1':
- resolution: {integrity: sha512-+bcQRkJO9pcX8d0gel9ZNfrzU22sZFSA0WVhfXrf5jdJOS24a+Bp8pozuS9sBI9Hk/tGz83pgKfmqcn/Ci7/8w==}
- engines: {node: '>= 16'}
-
- '@intlify/message-compiler@9.13.1':
- resolution: {integrity: sha512-SKsVa4ajYGBVm7sHMXd5qX70O2XXjm55zdZB3VeMFCvQyvLew/dLvq3MqnaIsTMF1VkkOb9Ttr6tHcMlyPDL9w==}
- engines: {node: '>= 16'}
-
- '@intlify/shared@9.13.1':
- resolution: {integrity: sha512-u3b6BKGhE6j/JeRU6C/RL2FgyJfy6LakbtfeVF8fJXURpZZTzfh3e05J0bu0XPw447Q6/WUp3C4ajv4TMS4YsQ==}
- engines: {node: '>= 16'}
-
- '@jridgewell/gen-mapping@0.3.5':
- resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/resolve-uri@3.1.2':
- resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/set-array@1.2.1':
- resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
- engines: {node: '>=6.0.0'}
-
- '@jridgewell/sourcemap-codec@1.4.15':
- resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
-
- '@jridgewell/sourcemap-codec@1.5.0':
- resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
-
- '@jridgewell/trace-mapping@0.3.25':
- resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
-
- '@jsep-plugin/assignment@1.2.1':
- resolution: {integrity: sha512-gaHqbubTi29aZpVbBlECRpmdia+L5/lh2BwtIJTmtxdbecEyyX/ejAOg7eQDGNvGOUmPY7Z2Yxdy9ioyH/VJeA==}
- engines: {node: '>= 10.16.0'}
- peerDependencies:
- jsep: ^0.4.0||^1.0.0
-
- '@jsep-plugin/regex@1.0.3':
- resolution: {integrity: sha512-XfZgry4DwEZvSFtS/6Y+R48D7qJYJK6R9/yJFyUFHCIUMEEHuJ4X95TDgJp5QkmzfLYvapMPzskV5HpIDrREug==}
- engines: {node: '>= 10.16.0'}
- peerDependencies:
- jsep: ^0.4.0||^1.0.0
-
- '@jsonquerylang/jsonquery@3.1.1':
- resolution: {integrity: sha512-P6Qo5egd3W8TBpqQsqaZtZ9lPO7oXBM21QdkYamCAYZHv9VCPXiI8NeIuSoXdoe5zKVZPUWmqaI14uacJLmcNw==}
- hasBin: true
-
- '@kurkle/color@0.3.2':
- resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
-
- '@lexical/clipboard@0.16.1':
- resolution: {integrity: sha512-0dWs/SwKS5KPpuf6fUVVt9vSCl6HAqcDGhSITw/okv0rrIlXTUT6WhVsMJtXfFxTyVvwMeOecJHvQH3i/jRQtA==}
-
- '@lexical/code@0.16.1':
- resolution: {integrity: sha512-pOC28rRZ2XkmI2nIJm50DbKaCJtk5D0o7r6nORYp4i0z+lxt5Sf2m82DL9ksUHJRqKy87pwJDpoWvJ2SAI0ohw==}
-
- '@lexical/history@0.16.1':
- resolution: {integrity: sha512-WQhScx0TJeKSQAnEkRpIaWdUXqirrNrom2MxbBUc/32zEUMm9FzV7nRGknvUabEFUo7vZq6xTZpOExQJqHInQA==}
-
- '@lexical/html@0.16.1':
- resolution: {integrity: sha512-vbtAdCvQ3PaAqa5mFmtmrvbiAvjCu1iXBAJ0bsHqFXCF2Sba5LwHVe8dUAOTpfEZEMbiHfjul6b5fj4vNPGF2A==}
-
- '@lexical/link@0.16.1':
- resolution: {integrity: sha512-zG36gEnEqbIe6tK/MhXi7wn/XMY/zdivnPcOY5WyC3derkEezeLSSIFsC1u5UNeK5pbpNMSy4LDpLhi1Ww4Y5w==}
-
- '@lexical/list@0.16.1':
- resolution: {integrity: sha512-i9YhLAh5N6YO9dP+R1SIL9WEdCKeTiQQYVUzj84vDvX5DIBxMPUjTmMn3LXu9T+QO3h1s2L/vJusZASrl45eAw==}
-
- '@lexical/markdown@0.16.1':
- resolution: {integrity: sha512-0sBLttMvfQO/hVaIqpHdvDowpgV2CoRuWo2CNwvRLZPPWvPVjL4Nkb73wmi8zAZsAOTbX2aw+g4m/+k5oJqNig==}
-
- '@lexical/rich-text@0.16.1':
- resolution: {integrity: sha512-4uEVXJur7tdSbqbmsToCW4YVm0AMh4y9LK077Yq2O9hSuA5dqpI8UbTDnxZN2D7RfahNvwlqp8eZKFB1yeiJGQ==}
-
- '@lexical/selection@0.16.1':
- resolution: {integrity: sha512-+nK3RvXtyQvQDq7AZ46JpphmM33pwuulwiRfeXR5T9iFQTtgWOEjsAi/KKX7vGm70BxACfiSxy5QCOgBWFwVJg==}
-
- '@lexical/table@0.16.1':
- resolution: {integrity: sha512-GWb0/MM1sVXpi1p2HWWOBldZXASMQ4c6WRNYnRmq7J/aB5N66HqQgJGKp3m66Kz4k1JjhmZfPs7F018qIBhnFQ==}
-
- '@lexical/text@0.16.1':
- resolution: {integrity: sha512-Os/nKQegORTrKKN6vL3/FMVszyzyqaotlisPynvTaHTUC+yY4uyjM2hlF93i5a2ixxyiPLF9bDroxUP96TMPXg==}
-
- '@lexical/utils@0.16.1':
- resolution: {integrity: sha512-BVyJxDQi/rIxFTDjf2zE7rMDKSuEaeJ4dybHRa/hRERt85gavGByQawSLeQlTjLaYLVsy+x7wCcqh2fNhlLf0g==}
-
- '@lezer/common@1.2.3':
- resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==}
-
- '@lezer/highlight@1.2.1':
- resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==}
-
- '@lezer/json@1.0.2':
- resolution: {integrity: sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==}
-
- '@lezer/lr@1.4.2':
- resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==}
-
- '@mapbox/node-pre-gyp@1.0.10':
- resolution: {integrity: sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==}
- hasBin: true
-
- '@nodelib/fs.scandir@https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz':
- resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, tarball: https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz}
- version: 2.1.5
- engines: {node: '>= 8'}
-
- '@nodelib/fs.stat@https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz':
- resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, tarball: https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz}
- version: 2.0.5
- engines: {node: '>= 8'}
-
- '@nodelib/fs.walk@https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz':
- resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, tarball: https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz}
- version: 1.2.8
- engines: {node: '>= 8'}
-
- '@parcel/watcher-android-arm64@2.4.1':
- resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==}
- engines: {node: '>= 10.0.0'}
- cpu: [arm64]
- os: [android]
-
- '@parcel/watcher-darwin-arm64@2.4.1':
- resolution: {integrity: sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==}
- engines: {node: '>= 10.0.0'}
- cpu: [arm64]
- os: [darwin]
-
- '@parcel/watcher-darwin-x64@2.4.1':
- resolution: {integrity: sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==}
- engines: {node: '>= 10.0.0'}
- cpu: [x64]
- os: [darwin]
-
- '@parcel/watcher-freebsd-x64@2.4.1':
- resolution: {integrity: sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==}
- engines: {node: '>= 10.0.0'}
- cpu: [x64]
- os: [freebsd]
-
- '@parcel/watcher-linux-arm-glibc@2.4.1':
- resolution: {integrity: sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==}
- engines: {node: '>= 10.0.0'}
- cpu: [arm]
- os: [linux]
-
- '@parcel/watcher-linux-arm64-glibc@2.4.1':
- resolution: {integrity: sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==}
- engines: {node: '>= 10.0.0'}
- cpu: [arm64]
- os: [linux]
-
- '@parcel/watcher-linux-arm64-musl@2.4.1':
- resolution: {integrity: sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==}
- engines: {node: '>= 10.0.0'}
- cpu: [arm64]
- os: [linux]
-
- '@parcel/watcher-linux-x64-glibc@2.4.1':
- resolution: {integrity: sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==}
- engines: {node: '>= 10.0.0'}
- cpu: [x64]
- os: [linux]
-
- '@parcel/watcher-linux-x64-musl@2.4.1':
- resolution: {integrity: sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==}
- engines: {node: '>= 10.0.0'}
- cpu: [x64]
- os: [linux]
-
- '@parcel/watcher-win32-arm64@2.4.1':
- resolution: {integrity: sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==}
- engines: {node: '>= 10.0.0'}
- cpu: [arm64]
- os: [win32]
-
- '@parcel/watcher-win32-ia32@2.4.1':
- resolution: {integrity: sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==}
- engines: {node: '>= 10.0.0'}
- cpu: [ia32]
- os: [win32]
-
- '@parcel/watcher-win32-x64@2.4.1':
- resolution: {integrity: sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==}
- engines: {node: '>= 10.0.0'}
- cpu: [x64]
- os: [win32]
-
- '@parcel/watcher@2.4.1':
- resolution: {integrity: sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==}
- engines: {node: '>= 10.0.0'}
-
- '@polka/url@0.5.0':
- resolution: {integrity: sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==}
-
- '@polka/url@1.0.0-next.21':
- resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
-
- '@popperjs/core@2.11.8':
- resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
-
- '@replit/codemirror-indentation-markers@6.5.3':
- resolution: {integrity: sha512-hL5Sfvw3C1vgg7GolLe/uxX5T3tmgOA3ZzqlMv47zjU1ON51pzNWiVbS22oh6crYhtVhv8b3gdXwoYp++2ilHw==}
- peerDependencies:
- '@codemirror/language': ^6.0.0
- '@codemirror/state': ^6.0.0
- '@codemirror/view': ^6.0.0
-
- '@rollup/pluginutils@5.0.2':
- resolution: {integrity: sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==}
- engines: {node: '>=14.0.0'}
- peerDependencies:
- rollup: ^1.20.0||^2.0.0||^3.0.0
- peerDependenciesMeta:
- rollup:
- optional: true
-
- '@sphinxxxx/color-conversion@2.2.2':
- resolution: {integrity: sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==}
-
- '@sxzz/popperjs-es@2.11.7':
- resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
-
- '@trysound/sax@0.2.0':
- resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
- engines: {node: '>=10.13.0'}
-
- '@tweenjs/tween.js@23.1.3':
- resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==}
-
- '@types/estree@1.0.0':
- resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
-
- '@types/estree@1.0.6':
- resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
-
- '@types/getos@3.0.4':
- resolution: {integrity: sha512-OsNVsuGEBd5QoeXTgjPmA89a465hjQithLMImsiJlIi0aYP8axKWtWrsmEMfm50w6EL2oRzCz+cLkZmp9/LfZw==}
-
- '@types/humanize-duration@3.27.4':
- resolution: {integrity: sha512-yaf7kan2Sq0goxpbcwTQ+8E9RP6HutFBPv74T/IA/ojcHKhuKVlk2YFYyHhWZeLvZPzzLE3aatuQB4h0iqyyUA==}
-
- '@types/javascript-time-ago@2.0.8':
- resolution: {integrity: sha512-X77q3xUzWVn0qohgurKE1G5NiXZjee8VbLqaukW/HXVkz7bdCFJgOPQ3JVB4IkrDhMS4CviFTmpZuNVg0i2QFA==}
-
- '@types/lodash-es@4.17.12':
- resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
-
- '@types/lodash@4.17.10':
- resolution: {integrity: sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==}
-
- '@types/md5@2.3.5':
- resolution: {integrity: sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==}
-
- '@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz':
- resolution: {integrity: sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==, tarball: https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz}
- version: 18.11.10
-
- '@types/pinyin@2.10.2':
- resolution: {integrity: sha512-jLzlRkaLRLg+lgYPjOuP3HX2cozUkhXls5GTXopsKuKJ9lDGlIAb88OoIztH6TbNUsoJnl/7e/kjaumA5IKKJg==}
-
- '@types/showdown@2.0.6':
- resolution: {integrity: sha512-pTvD/0CIeqe4x23+YJWlX2gArHa8G0J0Oh6GKaVXV7TAeickpkkZiNOgFcFcmLQ5lB/K0qBJL1FtRYltBfbGCQ==}
-
- '@types/stats.js@0.17.3':
- resolution: {integrity: sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==}
-
- '@types/three@0.166.0':
- resolution: {integrity: sha512-FHMnpcdhdbdOOIYbfkTkUVpYMW53odxbTRwd0/xJpYnTzEsjnVnondGAvHZb4z06UW0vo6WPVuvH0/9qrxKx7g==}
-
- '@types/url-join@4.0.3':
- resolution: {integrity: sha512-3l1qMm3wqO0iyC5gkADzT95UVW7C/XXcdvUcShOideKF0ddgVRErEQQJXBd2kvQm+aSgqhBGHGB38TgMeT57Ww==}
-
- '@types/web-bluetooth@0.0.16':
- resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==}
-
- '@types/webxr@0.5.20':
- resolution: {integrity: sha512-JGpU6qiIJQKUuVSKx1GtQnHJGxRjtfGIhzO2ilq43VZZS//f1h1Sgexbdk+Lq+7569a6EYhOWrUpIruR/1Enmg==}
-
- '@vitejs/plugin-vue@https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz':
- resolution: {integrity: sha512-E0tnaL4fr+qkdCNxJ+Xd0yM31UwMkQje76fsDVBBUCoGOUPexu2VDUYHL8P4CwV+zMvWw6nlRw19OnRKmYAJpw==, tarball: https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz}
- version: 3.2.0
- engines: {node: ^14.18.0 || >=16.0.0}
- peerDependencies:
- vite: ^3.0.0
- vue: ^3.2.25
-
- '@volar/language-core@https://registry.npmmirror.com/@volar/language-core/-/language-core-1.0.11.tgz':
- resolution: {integrity: sha512-YwUYKxIyDc+Fq3kQ6BGGfkrKCG5JzE2Yr6vMxrxEXW2rg/gsq3JgMk/4sI8ybRsaTirhCB4V8+AIVYsvcRxgig==, tarball: https://registry.npmmirror.com/@volar/language-core/-/language-core-1.0.11.tgz}
- version: 1.0.11
-
- '@volar/source-map@https://registry.npmmirror.com/@volar/source-map/-/source-map-1.0.11.tgz':
- resolution: {integrity: sha512-tkuV9MD+OuiZfHA0qZXrPdW6F7TvnpnuTan6Qe7UGUs9+sflezlMJdjaYdGgQObfP+06pcT1E3xdkOoi08ZyyQ==, tarball: https://registry.npmmirror.com/@volar/source-map/-/source-map-1.0.11.tgz}
- version: 1.0.11
-
- '@volar/typescript@https://registry.npmmirror.com/@volar/typescript/-/typescript-1.0.11.tgz':
- resolution: {integrity: sha512-mq7wDDAs0Eb43jev2FxbowuiwWqvL3kb+tar1we8VQbdabpyQ5dmbWPwo/IglevMmW3SKo1Et+6rqAeZpXNnPQ==, tarball: https://registry.npmmirror.com/@volar/typescript/-/typescript-1.0.11.tgz}
- version: 1.0.11
-
- '@volar/vue-language-core@https://registry.npmmirror.com/@volar/vue-language-core/-/vue-language-core-1.0.11.tgz':
- resolution: {integrity: sha512-A3ODs0/ua7BcpSSnE7KtO8bzWsYsbOJRyW2Q/2uktxlfHj8srln3JdgK/mNlIgfnWtACbE5K+EfMJOgJKv864A==, tarball: https://registry.npmmirror.com/@volar/vue-language-core/-/vue-language-core-1.0.11.tgz}
- version: 1.0.11
-
- '@volar/vue-typescript@https://registry.npmmirror.com/@volar/vue-typescript/-/vue-typescript-1.0.11.tgz':
- resolution: {integrity: sha512-jlnFPvBcTyPiAbGlgjhKK7fp3Q+Z7Z5eU1NTbTSS0lQC8Gog3sh2UxLAFG5Voe1gHIxasoOEPXzMR0CWF4bKbA==, tarball: https://registry.npmmirror.com/@volar/vue-typescript/-/vue-typescript-1.0.11.tgz}
- version: 1.0.11
-
- '@vue/compiler-core@3.5.12':
- resolution: {integrity: sha512-ISyBTRMmMYagUxhcpyEH0hpXRd/KqDU4ymofPgl2XAkY9ZhQ+h0ovEZJIiPop13UmR/54oA2cgMDjgroRelaEw==}
-
- '@vue/compiler-core@https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz':
- resolution: {integrity: sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A==, tarball: https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz}
- version: 3.2.45
-
- '@vue/compiler-dom@3.5.12':
- resolution: {integrity: sha512-9G6PbJ03uwxLHKQ3P42cMTi85lDRvGLB2rSGOiQqtXELat6uI4n8cNz9yjfVHRPIu+MsK6TE418Giruvgptckg==}
-
- '@vue/compiler-dom@https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz':
- resolution: {integrity: sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw==, tarball: https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz}
- version: 3.2.45
-
- '@vue/compiler-sfc@3.5.12':
- resolution: {integrity: sha512-2k973OGo2JuAa5+ZlekuQJtitI5CgLMOwgl94BzMCsKZCX/xiqzJYzapl4opFogKHqwJk34vfsaKpfEhd1k5nw==}
-
- '@vue/compiler-sfc@https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz':
- resolution: {integrity: sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q==, tarball: https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz}
- version: 3.2.45
-
- '@vue/compiler-ssr@3.5.12':
- resolution: {integrity: sha512-eLwc7v6bfGBSM7wZOGPmRavSWzNFF6+PdRhE+VFJhNCgHiF8AM7ccoqcv5kBXA2eWUfigD7byekvf/JsOfKvPA==}
-
- '@vue/compiler-ssr@https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz':
- resolution: {integrity: sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ==, tarball: https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz}
- version: 3.2.45
-
- '@vue/devtools-api@6.4.5':
- resolution: {integrity: sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ==}
-
- '@vue/devtools-api@6.6.4':
- resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
-
- '@vue/reactivity-transform@https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz':
- resolution: {integrity: sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ==, tarball: https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz}
- version: 3.2.45
-
- '@vue/reactivity@3.5.12':
- resolution: {integrity: sha512-UzaN3Da7xnJXdz4Okb/BGbAaomRHc3RdoWqTzlvd9+WBR5m3J39J1fGcHes7U3za0ruYn/iYy/a1euhMEHvTAg==}
-
- '@vue/reactivity@https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.45.tgz':
- resolution: {integrity: sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==, tarball: https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.45.tgz}
- version: 3.2.45
-
- '@vue/runtime-core@3.5.12':
- resolution: {integrity: sha512-hrMUYV6tpocr3TL3Ad8DqxOdpDe4zuQY4HPY3X/VRh+L2myQO8MFXPAMarIOSGNu0bFAjh1yBkMPXZBqCk62Uw==}
-
- '@vue/runtime-dom@3.5.12':
- resolution: {integrity: sha512-q8VFxR9A2MRfBr6/55Q3umyoN7ya836FzRXajPB6/Vvuv0zOPL+qltd9rIMzG/DbRLAIlREmnLsplEF/kotXKA==}
-
- '@vue/server-renderer@3.5.12':
- resolution: {integrity: sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==}
- version: 3.5.12
- peerDependencies:
- vue: 3.5.12
-
- '@vue/shared@3.5.12':
- resolution: {integrity: sha512-L2RPSAwUFbgZH20etwrXyVyCBu9OxRSi8T/38QsvnkJyvq2LufW2lDCOzm7t/U9C1mkhJGWYfCuFBCmIuNivrg==}
-
- '@vue/shared@https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz':
- resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==, tarball: https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz}
- version: 3.2.45
-
- '@vueuse/core@9.13.0':
- resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==}
- version: 9.13.0
-
- '@vueuse/metadata@9.13.0':
- resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==}
-
- '@vueuse/shared@9.13.0':
- resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==}
- version: 9.13.0
-
- abbrev@1.1.1:
- resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
-
- accepts@1.3.8:
- resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
- engines: {node: '>= 0.6'}
-
- acorn-dynamic-import@4.0.0:
- resolution: {integrity: sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==}
- deprecated: This is probably built in to whatever tool you're using. If you still need it... idk
- peerDependencies:
- acorn: ^6.0.0
-
- acorn-jsx@5.3.2:
- resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
- peerDependencies:
- acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
-
- acorn@6.4.2:
- resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==}
- engines: {node: '>=0.4.0'}
- hasBin: true
-
- acorn@8.13.0:
- resolution: {integrity: sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==}
- engines: {node: '>=0.4.0'}
- hasBin: true
-
- agent-base@6.0.2:
- resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
- engines: {node: '>= 6.0.0'}
-
- ajv@8.17.1:
- resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
-
- ansi-regex@5.0.1:
- resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
- engines: {node: '>=8'}
-
- ansi-styles@3.2.1:
- resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
- engines: {node: '>=4'}
-
- ansi-styles@4.3.0:
- resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
- engines: {node: '>=8'}
-
- anymatch@https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz:
- resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, tarball: https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz}
- version: 3.1.3
- engines: {node: '>= 8'}
-
- aproba@2.0.0:
- resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
-
- are-we-there-yet@2.0.0:
- resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
- engines: {node: '>=10'}
- deprecated: This package is no longer supported.
-
- argparse@1.0.10:
- resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
-
- aria-query@5.3.2:
- resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
- engines: {node: '>= 0.4'}
-
- array-bounds@1.0.1:
- resolution: {integrity: sha512-8wdW3ZGk6UjMPJx/glyEt0sLzzwAE1bhToPsO1W2pbpR2gULyxe3BjSiuJFheP50T/GgODVPz2fuMUmIywt8cQ==}
-
- array-normalize@1.1.4:
- resolution: {integrity: sha512-fCp0wKFLjvSPmCn4F5Tiw4M3lpMZoHlCjfcs7nNzuj3vqQQ1/a8cgB9DXcpDSn18c+coLnaW7rqfcYCvKbyJXg==}
-
- array-timsort@https://registry.npmmirror.com/array-timsort/-/array-timsort-1.0.3.tgz:
- resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==, tarball: https://registry.npmmirror.com/array-timsort/-/array-timsort-1.0.3.tgz}
- version: 1.0.3
-
- async-validator@4.2.5:
- resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
-
- asynckit@0.4.0:
- resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
-
- atom-material-icons@3.0.0:
- resolution: {integrity: sha512-qtMR5KxQh82iLIFOdrQZViq21GTNT+/mccrVAg41hIlrhpwHAEjCeGj91Iyas9GL28ld0D0hagHv6ipGWlLZHw==}
-
- attr-accept@2.2.2:
- resolution: {integrity: sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==}
- engines: {node: '>=4'}
-
- axios@1.7.7:
- resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==}
-
- axobject-query@4.1.0:
- resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
- engines: {node: '>= 0.4'}
-
- balanced-match@1.0.2:
- resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
-
- balanced-match@https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz:
- resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, tarball: https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz}
- version: 1.0.2
-
- binary-extensions@https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz:
- resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==, tarball: https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz}
- version: 2.2.0
- engines: {node: '>=8'}
-
- binary-search-bounds@2.0.5:
- resolution: {integrity: sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==}
-
- boolbase@1.0.0:
- resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
-
- brace-expansion@1.1.11:
- resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
-
- brace-expansion@https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz:
- resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==, tarball: https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz}
- version: 2.0.1
-
- braces@3.0.2:
- resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
- engines: {node: '>=8'}
-
- braces@https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz:
- resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==, tarball: https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz}
- version: 3.0.2
- engines: {node: '>=8'}
-
- buble@0.19.8:
- resolution: {integrity: sha512-IoGZzrUTY5fKXVkgGHw3QeXFMUNBFv+9l8a4QJKG1JhG3nCMHTdEX1DCOg8568E2Q9qvAQIiSokv6Jsgx8p2cA==}
- hasBin: true
-
- bubleify@1.2.1:
- resolution: {integrity: sha512-vp3NHmaQVoKaKWvi15FTMinPNjfp+47+/kFJ9ifezdMF/CBLArCxDVUh+FQE3qRxCRj1qyjJqilTBHHqlM8MaQ==}
- engines: {node: '>=4.0.0'}
-
- bytes@3.0.0:
- resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==}
- engines: {node: '>= 0.8'}
-
- chalk@2.4.2:
- resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
- engines: {node: '>=4'}
-
- charenc@0.0.2:
- resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
-
- chart.js@4.4.5:
- resolution: {integrity: sha512-CVVjg1RYTJV9OCC8WeJPMx8gsV8K6WIyIEQUE3ui4AR9Hfgls9URri6Ja3hyMVBbTF8Q2KFa19PE815gWcWhng==}
- engines: {pnpm: '>=8'}
-
- chartjs-adapter-date-fns@3.0.0:
- resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==}
- peerDependencies:
- chart.js: '>=2.8.0'
- date-fns: '>=2.0.0'
-
- chokidar@4.0.1:
- resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==}
- engines: {node: '>= 14.16.0'}
-
- chokidar@https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz:
- resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==, tarball: https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz}
- version: 3.5.3
- engines: {node: '>= 8.10.0'}
-
- chownr@2.0.0:
- resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
- engines: {node: '>=10'}
-
- clamp@1.0.1:
- resolution: {integrity: sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA==}
-
- clipboard@2.0.11:
- resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==}
-
- cliui@8.0.1:
- resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
- engines: {node: '>=12'}
-
- code-red@1.0.4:
- resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==}
-
- codemirror-wrapped-line-indent@1.0.8:
- resolution: {integrity: sha512-5UwuHCz4oAZuvot1DbfFxSxJacTESdNGa/KpJD7HfpVpDAJdgB1vV9OG4b4pkJqPWuOfIpFLTQEKS85kTpV+XA==}
- peerDependencies:
- '@codemirror/language': ^6.9.0
- '@codemirror/state': ^6.2.1
- '@codemirror/view': ^6.17.1
-
- color-convert@1.9.3:
- resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
-
- color-convert@2.0.1:
- resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
- engines: {node: '>=7.0.0'}
-
- color-name@1.1.3:
- resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
-
- color-name@1.1.4:
- resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-
- color-support@1.1.3:
- resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
- hasBin: true
-
- combined-stream@1.0.8:
- resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
- engines: {node: '>= 0.8'}
-
- commander@1.1.1:
- resolution: {integrity: sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA==}
- engines: {node: '>= 0.6.x'}
-
- commander@7.2.0:
- resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
- engines: {node: '>= 10'}
-
- commander@9.5.0:
- resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
- engines: {node: ^12.20.0 || >=14}
-
- comment-json@https://registry.npmmirror.com/comment-json/-/comment-json-4.2.3.tgz:
- resolution: {integrity: sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==, tarball: https://registry.npmmirror.com/comment-json/-/comment-json-4.2.3.tgz}
- version: 4.2.3
- engines: {node: '>= 6'}
-
- compressible@2.0.18:
- resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==}
- engines: {node: '>= 0.6'}
-
- compression@1.7.4:
- resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==}
- engines: {node: '>= 0.8.0'}
-
- concat-map@0.0.1:
- resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
-
- consola@https://registry.npmmirror.com/consola/-/consola-2.15.3.tgz:
- resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==, tarball: https://registry.npmmirror.com/consola/-/consola-2.15.3.tgz}
- version: 2.15.3
-
- console-clear@1.1.1:
- resolution: {integrity: sha512-pMD+MVR538ipqkG5JXeOEbKWS5um1H4LUUccUQG68qpeqBYbzYy79Gh55jkd2TtPdRfUaLWdv6LPP//5Zt0aPQ==}
- engines: {node: '>=4'}
-
- console-control-strings@1.1.0:
- resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
-
- core-js@3.38.1:
- resolution: {integrity: sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==}
-
- core-util-is@https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz:
- resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, tarball: https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz}
- version: 1.0.3
-
- crawlab-ui@0.7.0-rc3:
- resolution: {integrity: sha512-WF7DLjrXa0J3uY/u7iTtZyqbhCIHZaW2K5dWHMj28GCaT69uvPRR32AfZPWag5sarqXdOmKNSOt5NidAPKqKpQ==}
- peerDependencies:
- '@element-plus/icons-vue': ^2.3.1
- '@fortawesome/fontawesome-common-types': ^6.5.2
- '@fortawesome/fontawesome-svg-core': ^6.5.2
- '@fortawesome/free-brands-svg-icons': ^6.5.2
- '@fortawesome/free-regular-svg-icons': ^6.5.2
- '@fortawesome/free-solid-svg-icons': ^6.5.2
- '@fortawesome/vue-fontawesome': ^3.0.8
- '@lexical/code': ^0.16.1
- '@lexical/history': ^0.16.1
- '@lexical/html': ^0.16.1
- '@lexical/link': ^0.16.1
- '@lexical/list': ^0.16.1
- '@lexical/markdown': ^0.16.1
- '@lexical/rich-text': ^0.16.1
- '@lexical/selection': ^0.16.1
- '@lexical/table': ^0.16.1
- '@lexical/text': ^0.16.1
- '@lexical/utils': ^0.16.1
- '@popperjs/core': ^2.11.8
- '@types/getos': ^3.0.4
- '@types/humanize-duration': ^3.27.4
- '@types/javascript-time-ago': ^2.0.8
- '@types/lodash': ^4.17.6
- '@types/md5': ^2.3.5
- '@types/pinyin': ^2.10.2
- '@types/showdown': ^2.0.6
- '@types/three': ^0.166.0
- '@types/url-join': ^4.0.3
- async-validator: ^4.2.5
- atom-material-icons: ^3.0.0
- axios: ^1.7.2
- chart.js: ^4.4.3
- chartjs-adapter-date-fns: ^3.0.0
- clipboard: ^2.0.11
- core-js: ^3.37.1
- crawlab-vue3-dropzone: 3.0.3
- cron-parser: ^4.9.0
- cronstrue: ^2.50.0
- dayjs: ^1.11.11
- element-plus: ^2.7.6
- font-awesome: ^4.7.0
- humanize-duration: ^3.32.1
- javascript-time-ago: ^2.5.10
- lexical: ^0.16.1
- lodash: ^4.17.21
- md5: ^2.3.0
- mitt: ^3.0.1
- monaco-editor: ^0.50.0
- normalize.css: ^8.0.1
- os-name: ^5.0.1
- pinyin: ^2.10.2
- point-cluster: ^3.1.8
- prismjs: ^1.29.0
- showdown: ^2.1.0
- three: ^0.166.1
- tiny-invariant: ^1.3.3
- url-join: ^4.0.1
- url-regex: ^5.0.0
- vite-svg-loader: ^5.1.0
- vue: ^3.4
- vue-chartjs: ^5.3.1
- vue-clipboard3: ^2.0.0
- vue-i18n: 9.13.1
- vue-router: ^4.4.0
- vuex: ^4.1.0
-
- crawlab-vue3-dropzone@3.0.3:
- resolution: {integrity: sha512-s/GHSTteFqJcMk14Xkp1imwEyrOCa+uwwH1eCsTAxVzaYocpiDpbDjQ3u3vrMyws9InsqqgtmAJhy2vGHEsk/Q==}
- version: 3.0.3
- peerDependencies:
- vue: '>=3.2.45'
-
- crelt@1.0.6:
- resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
-
- cron-parser@4.9.0:
- resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==}
- engines: {node: '>=12.0.0'}
-
- cronstrue@2.50.0:
- resolution: {integrity: sha512-ULYhWIonJzlScCCQrPUG5uMXzXxSixty4djud9SS37DoNxDdkeRocxzHuAo4ImRBUK+mAuU5X9TSwEDccnnuPg==}
- hasBin: true
-
- cross-spawn@7.0.3:
- resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
- engines: {node: '>= 8'}
-
- crypt@0.0.2:
- resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
-
- css-select@5.1.0:
- resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
-
- css-tree@2.2.1:
- resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
- engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
-
- css-tree@2.3.1:
- resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
- engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
-
- css-what@6.1.0:
- resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
- engines: {node: '>= 6'}
-
- csso@5.0.5:
- resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
- engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
-
- csstype@3.1.3:
- resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
-
- date-fns@4.1.0:
- resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
-
- dayjs@1.11.13:
- resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
-
- de-indent@https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz:
- resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==, tarball: https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz}
- version: 1.0.2
-
- debug@2.6.9:
- resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
-
- debug@4.3.4:
- resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
- engines: {node: '>=6.0'}
- peerDependencies:
- supports-color: '*'
- peerDependenciesMeta:
- supports-color:
- optional: true
-
- define-lazy-prop@2.0.0:
- resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
- engines: {node: '>=8'}
-
- defined@1.0.1:
- resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==}
-
- delayed-stream@1.0.0:
- resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
- engines: {node: '>=0.4.0'}
-
- delegate@3.2.0:
- resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==}
-
- delegates@1.0.0:
- resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
-
- detect-libc@1.0.3:
- resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
- engines: {node: '>=0.10'}
- hasBin: true
-
- detect-libc@2.0.1:
- resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
- engines: {node: '>=8'}
-
- diff-sequences@29.6.3:
- resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
- engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-
- dom-serializer@2.0.0:
- resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
-
- domelementtype@2.3.0:
- resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
-
- domhandler@5.0.3:
- resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
- engines: {node: '>= 4'}
-
- domutils@3.1.0:
- resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
-
- dtype@2.0.0:
- resolution: {integrity: sha512-s2YVcLKdFGS0hpFqJaTwscsyt0E8nNFdmo73Ocd81xNPj4URI4rj6D60A+vFMIw7BXWlb4yRkEwfBqcZzPGiZg==}
- engines: {node: '>= 0.8.0'}
-
- element-plus@2.8.5:
- resolution: {integrity: sha512-Px+kPbRTVvn5oa5+9saa7QEOnUweKXm0JVI7yJHzKF/doQGixwcFMsQEF2+3Fy62EA/7dRRKVuhsNGGZYNk3cw==}
- version: 2.8.5
- peerDependencies:
- vue: ^3.2.0
-
- emoji-regex@8.0.0:
- resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
-
- entities@4.5.0:
- resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
- engines: {node: '>=0.12'}
-
- esbuild-android-64@0.15.16:
- resolution: {integrity: sha512-Vwkv/sT0zMSgPSVO3Jlt1pUbnZuOgtOQJkJkyyJFAlLe7BiT8e9ESzo0zQSx4c3wW4T6kGChmKDPMbWTgtliQA==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [android]
-
- esbuild-android-arm64@0.15.16:
- resolution: {integrity: sha512-lqfKuofMExL5niNV3gnhMUYacSXfsvzTa/58sDlBET/hCOG99Zmeh+lz6kvdgvGOsImeo6J9SW21rFCogNPLxg==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [android]
-
- esbuild-darwin-64@0.15.16:
- resolution: {integrity: sha512-wo2VWk/n/9V2TmqUZ/KpzRjCEcr00n7yahEdmtzlrfQ3lfMCf3Wa+0sqHAbjk3C6CKkR3WKK/whkMq5Gj4Da9g==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [darwin]
-
- esbuild-darwin-arm64@0.15.16:
- resolution: {integrity: sha512-fMXaUr5ou0M4WnewBKsspMtX++C1yIa3nJ5R2LSbLCfJT3uFdcRoU/NZjoM4kOMKyOD9Sa/2vlgN8G07K3SJnw==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [darwin]
-
- esbuild-freebsd-64@0.15.16:
- resolution: {integrity: sha512-UzIc0xlRx5x9kRuMr+E3+hlSOxa/aRqfuMfiYBXu2jJ8Mzej4lGL7+o6F5hzhLqWfWm1GWHNakIdlqg1ayaTNQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [freebsd]
-
- esbuild-freebsd-arm64@0.15.16:
- resolution: {integrity: sha512-8xyiYuGc0DLZphFQIiYaLHlfoP+hAN9RHbE+Ibh8EUcDNHAqbQgUrQg7pE7Bo00rXmQ5Ap6KFgcR0b4ALZls1g==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [freebsd]
-
- esbuild-linux-32@0.15.16:
- resolution: {integrity: sha512-iGijUTV+0kIMyUVoynK0v+32Oi8yyp0xwMzX69GX+5+AniNy/C/AL1MjFTsozRp/3xQPl7jVux/PLe2ds10/2w==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [linux]
-
- esbuild-linux-64@0.15.16:
- resolution: {integrity: sha512-tuSOjXdLw7VzaUj89fIdAaQT7zFGbKBcz4YxbWrOiXkwscYgE7HtTxUavreBbnRkGxKwr9iT/gmeJWNm4djy/g==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [linux]
-
- esbuild-linux-arm64@0.15.16:
- resolution: {integrity: sha512-mPYksnfHnemNrvjrDhZyixL/AfbJN0Xn9S34ZOHYdh6/jJcNd8iTsv3JwJoEvTJqjMggjMhGUPJAdjnFBHoH8A==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [linux]
-
- esbuild-linux-arm@0.15.16:
- resolution: {integrity: sha512-XKcrxCEXDTOuoRj5l12tJnkvuxXBMKwEC5j0JISw3ziLf0j4zIwXbKbTmUrKFWbo6ZgvNpa7Y5dnbsjVvH39bQ==}
- engines: {node: '>=12'}
- cpu: [arm]
- os: [linux]
-
- esbuild-linux-mips64le@0.15.16:
- resolution: {integrity: sha512-kSJO2PXaxfm0pWY39+YX+QtpFqyyrcp0ZeI8QPTrcFVQoWEPiPVtOfTZeS3ZKedfH+Ga38c4DSzmKMQJocQv6A==}
- engines: {node: '>=12'}
- cpu: [mips64el]
- os: [linux]
-
- esbuild-linux-ppc64le@0.15.16:
- resolution: {integrity: sha512-NimPikwkBY0yGABw6SlhKrtT35sU4O23xkhlrTT/O6lSxv3Pm5iSc6OYaqVAHWkLdVf31bF4UDVFO+D990WpAA==}
- engines: {node: '>=12'}
- cpu: [ppc64]
- os: [linux]
-
- esbuild-linux-riscv64@0.15.16:
- resolution: {integrity: sha512-ty2YUHZlwFOwp7pR+J87M4CVrXJIf5ZZtU/umpxgVJBXvWjhziSLEQxvl30SYfUPq0nzeWKBGw5i/DieiHeKfw==}
- engines: {node: '>=12'}
- cpu: [riscv64]
- os: [linux]
-
- esbuild-linux-s390x@0.15.16:
- resolution: {integrity: sha512-VkZaGssvPDQtx4fvVdZ9czezmyWyzpQhEbSNsHZZN0BHvxRLOYAQ7sjay8nMQwYswP6O2KlZluRMNPYefFRs+w==}
- engines: {node: '>=12'}
- cpu: [s390x]
- os: [linux]
-
- esbuild-netbsd-64@0.15.16:
- resolution: {integrity: sha512-ElQ9rhdY51et6MJTWrCPbqOd/YuPowD7Cxx3ee8wlmXQQVW7UvQI6nSprJ9uVFQISqSF5e5EWpwWqXZsECLvXg==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [netbsd]
-
- esbuild-openbsd-64@0.15.16:
- resolution: {integrity: sha512-KgxMHyxMCT+NdLQE1zVJEsLSt2QQBAvJfmUGDmgEq8Fvjrf6vSKB00dVHUEDKcJwMID6CdgCpvYNt999tIYhqA==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [openbsd]
-
- esbuild-sunos-64@0.15.16:
- resolution: {integrity: sha512-exSAx8Phj7QylXHlMfIyEfNrmqnLxFqLxdQF6MBHPdHAjT7fsKaX6XIJn+aQEFiOcE4X8e7VvdMCJ+WDZxjSRQ==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [sunos]
-
- esbuild-windows-32@0.15.16:
- resolution: {integrity: sha512-zQgWpY5pUCSTOwqKQ6/vOCJfRssTvxFuEkpB4f2VUGPBpdddZfdj8hbZuFRdZRPIVHvN7juGcpgCA/XCF37mAQ==}
- engines: {node: '>=12'}
- cpu: [ia32]
- os: [win32]
-
- esbuild-windows-64@0.15.16:
- resolution: {integrity: sha512-HjW1hHRLSncnM3MBCP7iquatHVJq9l0S2xxsHHj4yzf4nm9TU4Z7k4NkeMlD/dHQ4jPlQQhwcMvwbJiOefSuZw==}
- engines: {node: '>=12'}
- cpu: [x64]
- os: [win32]
-
- esbuild-windows-arm64@0.15.16:
- resolution: {integrity: sha512-oCcUKrJaMn04Vxy9Ekd8x23O8LoU01+4NOkQ2iBToKgnGj5eo1vU9i27NQZ9qC8NFZgnQQZg5oZWAejmbsppNA==}
- engines: {node: '>=12'}
- cpu: [arm64]
- os: [win32]
-
- esbuild@https://registry.npmmirror.com/esbuild/-/esbuild-0.15.16.tgz:
- resolution: {integrity: sha512-o6iS9zxdHrrojjlj6pNGC2NAg86ECZqIETswTM5KmJitq+R1YmahhWtMumeQp9lHqJaROGnsBi2RLawGnfo5ZQ==, tarball: https://registry.npmmirror.com/esbuild/-/esbuild-0.15.16.tgz}
- version: 0.15.16
- engines: {node: '>=12'}
- hasBin: true
-
- escalade@3.1.1:
- resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
- engines: {node: '>=6'}
-
- escape-html@1.0.3:
- resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
-
- escape-string-regexp@1.0.5:
- resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
- engines: {node: '>=0.8.0'}
-
- esprima@4.0.1:
- resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
- engines: {node: '>=4'}
- hasBin: true
-
- esprima@https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz:
- resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==, tarball: https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz}
- version: 4.0.1
- engines: {node: '>=4'}
- hasBin: true
-
- estree-walker@2.0.2:
- resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
-
- estree-walker@3.0.3:
- resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
-
- estree-walker@https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz:
- resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==, tarball: https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz}
- version: 2.0.2
-
- execa@5.1.1:
- resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
- engines: {node: '>=10'}
-
- fast-deep-equal@3.1.3:
- resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
-
- fast-glob@https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz:
- resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==, tarball: https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz}
- version: 3.2.12
- engines: {node: '>=8.6.0'}
-
- fast-uri@3.0.3:
- resolution: {integrity: sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==}
-
- fastq@https://registry.npmmirror.com/fastq/-/fastq-1.14.0.tgz:
- resolution: {integrity: sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==, tarball: https://registry.npmmirror.com/fastq/-/fastq-1.14.0.tgz}
- version: 1.14.0
-
- fflate@0.8.2:
- resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
-
- file-selector@0.2.4:
- resolution: {integrity: sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==}
- engines: {node: '>= 10'}
-
- fill-range@7.0.1:
- resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
- engines: {node: '>=8'}
-
- fill-range@https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz:
- resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==, tarball: https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz}
- version: 7.0.1
- engines: {node: '>=8'}
-
- flatten-vertex-data@1.0.2:
- resolution: {integrity: sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==}
-
- follow-redirects@1.15.9:
- resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
- engines: {node: '>=4.0'}
- peerDependencies:
- debug: '*'
- peerDependenciesMeta:
- debug:
- optional: true
-
- font-awesome@4.7.0:
- resolution: {integrity: sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg==}
- engines: {node: '>=0.10.3'}
-
- form-data@4.0.1:
- resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
- engines: {node: '>= 6'}
-
- fs-minipass@2.1.0:
- resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
- engines: {node: '>= 8'}
-
- fs.realpath@1.0.0:
- resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
-
- fsevents@2.3.2:
- resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
- engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
- os: [darwin]
-
- function-bind@https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz:
- resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==, tarball: https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz}
- version: 1.1.1
-
- gauge@3.0.2:
- resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
- engines: {node: '>=10'}
- deprecated: This package is no longer supported.
-
- get-caller-file@2.0.5:
- resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
- engines: {node: 6.* || 8.* || >= 10.*}
-
- get-port@3.2.0:
- resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==}
- engines: {node: '>=4'}
-
- get-stream@6.0.1:
- resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
- engines: {node: '>=10'}
-
- glob-parent@https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz:
- resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, tarball: https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz}
- version: 5.1.2
- engines: {node: '>= 6'}
-
- glob@7.2.3:
- resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
- deprecated: Glob versions prior to v9 are no longer supported
-
- good-listener@1.2.2:
- resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==}
-
- has-flag@3.0.0:
- resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
- engines: {node: '>=4'}
-
- has-own-prop@https://registry.npmmirror.com/has-own-prop/-/has-own-prop-2.0.0.tgz:
- resolution: {integrity: sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==, tarball: https://registry.npmmirror.com/has-own-prop/-/has-own-prop-2.0.0.tgz}
- version: 2.0.0
- engines: {node: '>=8'}
-
- has-unicode@2.0.1:
- resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
-
- has@https://registry.npmmirror.com/has/-/has-1.0.3.tgz:
- resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==, tarball: https://registry.npmmirror.com/has/-/has-1.0.3.tgz}
- version: 1.0.3
- engines: {node: '>= 0.4.0'}
-
- he@https://registry.npmmirror.com/he/-/he-1.2.0.tgz:
- resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, tarball: https://registry.npmmirror.com/he/-/he-1.2.0.tgz}
- version: 1.2.0
- hasBin: true
-
- https-proxy-agent@5.0.1:
- resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
- engines: {node: '>= 6'}
-
- human-signals@2.1.0:
- resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
- engines: {node: '>=10.17.0'}
-
- humanize-duration@3.32.1:
- resolution: {integrity: sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g==}
-
- immutable-json-patch@6.0.1:
- resolution: {integrity: sha512-BHL/cXMjwFZlTOffiWNdY8ZTvNyYLrutCnWxrcKPHr5FqpAb6vsO6WWSPnVSys3+DruFN6lhHJJPHi8uELQL5g==}
-
- immutable@4.3.7:
- resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
-
- inflight@1.0.6:
- resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
- deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
-
- inherits@2.0.4:
- resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
-
- ip-regex@4.3.0:
- resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==}
- engines: {node: '>=8'}
-
- is-binary-path@https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz:
- resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==, tarball: https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz}
- version: 2.1.0
- engines: {node: '>=8'}
-
- is-buffer@1.1.6:
- resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
-
- is-core-module@https://registry.npmmirror.com/is-core-module/-/is-core-module-2.11.0.tgz:
- resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==, tarball: https://registry.npmmirror.com/is-core-module/-/is-core-module-2.11.0.tgz}
- version: 2.11.0
-
- is-docker@2.2.1:
- resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
- engines: {node: '>=8'}
- hasBin: true
-
- is-extglob@2.1.1:
- resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
- engines: {node: '>=0.10.0'}
-
- is-extglob@https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz:
- resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, tarball: https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz}
- version: 2.1.1
- engines: {node: '>=0.10.0'}
-
- is-fullwidth-code-point@3.0.0:
- resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
- engines: {node: '>=8'}
-
- is-glob@4.0.3:
- resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
- engines: {node: '>=0.10.0'}
-
- is-glob@https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz:
- resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, tarball: https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz}
- version: 4.0.3
- engines: {node: '>=0.10.0'}
-
- is-number@7.0.0:
- resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
- engines: {node: '>=0.12.0'}
-
- is-number@https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz:
- resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, tarball: https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz}
- version: 7.0.0
- engines: {node: '>=0.12.0'}
-
- is-obj@1.0.1:
- resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==}
- engines: {node: '>=0.10.0'}
-
- is-reference@3.0.1:
- resolution: {integrity: sha512-baJJdQLiYaJdvFbJqXrcGv3WU3QCzBlUcI5QhbesIm6/xPsvmO+2CDoi/GMOFBQEQm+PXkwOPrp9KK5ozZsp2w==}
-
- is-stream@2.0.1:
- resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
- engines: {node: '>=8'}
-
- is-wsl@2.2.0:
- resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
- engines: {node: '>=8'}
-
- isexe@2.0.0:
- resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
-
- javascript-time-ago@2.5.11:
- resolution: {integrity: sha512-Zeyf5R7oM1fSMW9zsU3YgAYwE0bimEeF54Udn2ixGd8PUwu+z1Yc5t4Y8YScJDMHD6uCx6giLt3VJR5K4CMwbg==}
-
- jmespath@0.16.0:
- resolution: {integrity: sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==}
- engines: {node: '>= 0.6.0'}
-
- js-yaml@3.14.1:
- resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
- hasBin: true
-
- jsep@1.3.9:
- resolution: {integrity: sha512-i1rBX5N7VPl0eYb6+mHNp52sEuaS2Wi8CDYx1X5sn9naevL78+265XJqy1qENEk7mRKwS06NHpUqiBwR7qeodw==}
- engines: {node: '>= 10.16.0'}
-
- jsesc@0.5.0:
- resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
- hasBin: true
-
- json-editor-vue@0.17.2:
- resolution: {integrity: sha512-P38IvEwQJe4IHFpZe1YG2CWOK6pCjeHmPu2tLs01IiV0kz8QBoHYC7N5ZORlGTL4tQFVOosmnI1gjXztywHtig==}
- version: 0.17.2
- peerDependencies:
- '@vue/composition-api': '>=1'
- vue: 2||3
- peerDependenciesMeta:
- '@vue/composition-api':
- optional: true
-
- json-schema-traverse@1.0.0:
- resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
-
- json-source-map@0.6.1:
- resolution: {integrity: sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==}
-
- jsonpath-plus@10.0.1:
- resolution: {integrity: sha512-30DeH2QD4nL1IpDLPIFz09G5XyLvh+oNMUI2Zxf4tbrlsVHs0e3VPnwpOnSTFb4yM0dfQK2WGKLsSaAS8V62rw==}
- engines: {node: '>=18.0.0'}
- hasBin: true
-
- jsonrepair@3.8.1:
- resolution: {integrity: sha512-5wnjaO53EJOhfLFY92nvBz2B9gqF9ql/D4HKUb1WOSBaqtVcAifFfmurblnhCJn/ySqKFA8U3n7nhGMAu/hEjQ==}
- hasBin: true
-
- keypress@0.1.0:
- resolution: {integrity: sha512-x0yf9PL/nx9Nw9oLL8ZVErFAk85/lslwEP7Vz7s5SI1ODXZIgit3C5qyWjw4DxOuO/3Hb4866SQh28a1V1d+WA==}
-
- kleur@3.0.3:
- resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
- engines: {node: '>=6'}
-
- lexical@0.16.1:
- resolution: {integrity: sha512-+R05d3+N945OY8pTUjTqQrWoApjC+ctzvjnmNETtx9WmVAaiW0tQVG+AYLt5pDGY8dQXtd4RPorvnxBTECt9SA==}
-
- local-access@1.1.0:
- resolution: {integrity: sha512-XfegD5pyTAfb+GY6chk283Ox5z8WexG56OvM06RWLpAc/UHozO8X6xAxEkIitZOtsSMM1Yr3DkHgW5W+onLhCw==}
- engines: {node: '>=6'}
-
- locate-character@3.0.0:
- resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
-
- lodash-es@4.17.21:
- resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
-
- lodash-unified@1.0.3:
- resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==}
- peerDependencies:
- '@types/lodash-es': '*'
- lodash: '*'
- lodash-es: '*'
-
- lodash._reinterpolate@3.0.0:
- resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==}
-
- lodash.template@4.5.0:
- resolution: {integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==}
-
- lodash.templatesettings@4.2.0:
- resolution: {integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==}
-
- lodash@4.17.21:
- resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
-
- lru-cache@6.0.0:
- resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
- engines: {node: '>=10'}
-
- luxon@3.5.0:
- resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==}
- engines: {node: '>=12'}
-
- macos-release@3.1.0:
- resolution: {integrity: sha512-/M/R0gCDgM+Cv1IuBG1XGdfTFnMEG6PZeT+KGWHO/OG+imqmaD9CH5vHBTycEM3+Kc4uG2Il+tFAuUWLqQOeUA==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
- magic-string@0.25.9:
- resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
-
- magic-string@0.30.1:
- resolution: {integrity: sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==}
- engines: {node: '>=12'}
-
- magic-string@0.30.12:
- resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==}
-
- magic-string@https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz:
- resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==, tarball: https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz}
- version: 0.25.9
-
- make-dir@3.1.0:
- resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
- engines: {node: '>=8'}
-
- matchit@1.1.0:
- resolution: {integrity: sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==}
- engines: {node: '>=6'}
-
- math-log2@1.0.1:
- resolution: {integrity: sha512-9W0yGtkaMAkf74XGYVy4Dqw3YUMnTNB2eeiw9aQbUl4A3KmuCEHTt2DgAB07ENzOYAjsYSAYufkAq0Zd+jU7zA==}
- engines: {node: '>=0.10.0'}
-
- md5@2.3.0:
- resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
-
- mdn-data@2.0.28:
- resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
-
- mdn-data@2.0.30:
- resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
-
- memoize-one@6.0.0:
- resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
-
- merge-stream@2.0.0:
- resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
-
- merge2@https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz:
- resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, tarball: https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz}
- version: 1.4.1
- engines: {node: '>= 8'}
-
- meshoptimizer@0.18.1:
- resolution: {integrity: sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==}
-
- micromatch@4.0.5:
- resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
- engines: {node: '>=8.6'}
-
- micromatch@https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz:
- resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==, tarball: https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz}
- version: 4.0.5
- engines: {node: '>=8.6'}
-
- mime-db@1.52.0:
- resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
- engines: {node: '>= 0.6'}
-
- mime-types@2.1.35:
- resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
- engines: {node: '>= 0.6'}
-
- mimic-fn@2.1.0:
- resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
- engines: {node: '>=6'}
-
- minimatch@3.1.2:
- resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
-
- minimatch@https://registry.npmmirror.com/minimatch/-/minimatch-5.1.1.tgz:
- resolution: {integrity: sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==, tarball: https://registry.npmmirror.com/minimatch/-/minimatch-5.1.1.tgz}
- version: 5.1.1
- engines: {node: '>=10'}
-
- minimist@1.2.7:
- resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
-
- minipass@3.3.6:
- resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
- engines: {node: '>=8'}
-
- minizlib@2.1.2:
- resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
- engines: {node: '>= 8'}
-
- mitt@3.0.1:
- resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
-
- mkdirp@1.0.4:
- resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
- engines: {node: '>=10'}
- hasBin: true
-
- monaco-editor@0.50.0:
- resolution: {integrity: sha512-8CclLCmrRRh+sul7C08BmPBP3P8wVWfBHomsTcndxg5NRCEPfu/mc2AGU8k37ajjDVXcXFc12ORAMUkmk+lkFA==}
-
- mri@1.2.0:
- resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
- engines: {node: '>=4'}
-
- mrmime@1.0.1:
- resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==}
- engines: {node: '>=10'}
-
- ms@2.0.0:
- resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
-
- ms@2.1.2:
- resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
-
- muggle-string@https://registry.npmmirror.com/muggle-string/-/muggle-string-0.1.0.tgz:
- resolution: {integrity: sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==, tarball: https://registry.npmmirror.com/muggle-string/-/muggle-string-0.1.0.tgz}
- version: 0.1.0
-
- nanoid@3.3.7:
- resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
- nanoid@https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz:
- resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==, tarball: https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz}
- version: 3.3.4
- engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
- hasBin: true
-
- natural-compare-lite@1.4.0:
- resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
-
- negotiator@0.6.3:
- resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
- engines: {node: '>= 0.6'}
-
- node-addon-api@3.2.1:
- resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==}
-
- node-addon-api@7.1.1:
- resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
-
- node-fetch@2.6.7:
- resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
- engines: {node: 4.x || >=6.0.0}
- peerDependencies:
- encoding: ^0.1.0
- peerDependenciesMeta:
- encoding:
- optional: true
-
- nodejieba@2.5.2:
- resolution: {integrity: sha512-ByskJvaBrQ2eV+5M0OeD80S5NKoGaHc9zi3Z/PTKl/95eac2YF8RmWduq9AknLpkQLrLAIcqurrtC6BzjpKwwg==}
- engines: {node: '>= 10.20.0'}
-
- nopt@5.0.0:
- resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
- engines: {node: '>=6'}
- hasBin: true
-
- normalize-path@https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz:
- resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, tarball: https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz}
- version: 3.0.0
- engines: {node: '>=0.10.0'}
-
- normalize-wheel-es@1.2.0:
- resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==}
-
- normalize.css@8.0.1:
- resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==}
-
- npm-run-path@4.0.1:
- resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
- engines: {node: '>=8'}
-
- npmlog@5.0.1:
- resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
- deprecated: This package is no longer supported.
-
- nth-check@2.1.1:
- resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
-
- object-assign@4.1.1:
- resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
- engines: {node: '>=0.10.0'}
-
- on-headers@1.0.2:
- resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==}
- engines: {node: '>= 0.8'}
-
- once@1.4.0:
- resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
-
- onetime@5.1.2:
- resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
- engines: {node: '>=6'}
-
- open@8.4.2:
- resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
- engines: {node: '>=12'}
-
- os-homedir@2.0.0:
- resolution: {integrity: sha512-saRNz0DSC5C/I++gFIaJTXoFJMRwiP5zHar5vV3xQ2TkgEw6hDCcU5F272JjUylpiVgBrZNQHnfjkLabTfb92Q==}
- engines: {node: '>=0.10.0'}
- deprecated: This is not needed anymore. Use `require('os').homedir()` instead.
-
- os-name@5.0.1:
- resolution: {integrity: sha512-0EQpaHUHq7olp2/YFUr+0vZi9tMpDTblHGz+Ch5RntKxiRXOAY0JOz1UlxhSjMSksHvkm13eD6elJj3M8Ht/kw==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
- parse-rect@1.2.0:
- resolution: {integrity: sha512-4QZ6KYbnE6RTwg9E0HpLchUM9EZt6DnDxajFZZDSV4p/12ZJEvPO702DZpGvRYEPo00yKDys7jASi+/w7aO8LA==}
-
- path-is-absolute@1.0.1:
- resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
- engines: {node: '>=0.10.0'}
-
- path-key@3.1.1:
- resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
- engines: {node: '>=8'}
-
- path-parse@https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz:
- resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, tarball: https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz}
- version: 1.0.7
-
- periscopic@3.1.0:
- resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
-
- pick-by-alias@1.2.0:
- resolution: {integrity: sha512-ESj2+eBxhGrcA1azgHs7lARG5+5iLakc/6nlfbpjcLl00HuuUOIuORhYXN4D1HfvMSKuVtFQjAlnwi1JHEeDIw==}
-
- picocolors@1.1.1:
- resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
-
- picocolors@https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz:
- resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==, tarball: https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz}
- version: 1.0.0
-
- picomatch@2.3.1:
- resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
- engines: {node: '>=8.6'}
-
- picomatch@https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz:
- resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, tarball: https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz}
- version: 2.3.1
- engines: {node: '>=8.6'}
-
- pinyin@2.11.2:
- resolution: {integrity: sha512-tAWDBcowj09j/vLUjty98nVqrbTVNhutf1VcyID4p0sxTFPzRyXw7n7Ic0HQwBdWFIWrrDP8bYiT64gaT6h3gA==}
- hasBin: true
-
- point-cluster@3.1.8:
- resolution: {integrity: sha512-7klIr45dpMeZuqjIK9+qBg3m2IhyZJNJkdqjJFw0Olq75FM8ojrTMjClVUrMjNYRVqtwztxCHH71Fyjhg+YwyQ==}
-
- polka@0.5.2:
- resolution: {integrity: sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==}
-
- postcss@8.4.47:
- resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==}
- engines: {node: ^10 || ^12 || >=14}
-
- postcss@https://registry.npmmirror.com/postcss/-/postcss-8.4.19.tgz:
- resolution: {integrity: sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==, tarball: https://registry.npmmirror.com/postcss/-/postcss-8.4.19.tgz}
- version: 8.4.19
- engines: {node: ^10 || ^12 || >=14}
-
- prismjs@1.29.0:
- resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
- engines: {node: '>=6'}
-
- proxy-from-env@1.1.0:
- resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
-
- queue-microtask@https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz:
- resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, tarball: https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz}
- version: 1.2.3
-
- readable-stream@3.6.0:
- resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
- engines: {node: '>= 6'}
-
- readdirp@4.0.2:
- resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==}
- engines: {node: '>= 14.16.0'}
-
- readdirp@https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz:
- resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, tarball: https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz}
- version: 3.6.0
- engines: {node: '>=8.10.0'}
-
- regenerate-unicode-properties@9.0.0:
- resolution: {integrity: sha512-3E12UeNSPfjrgwjkR81m5J7Aw/T55Tu7nUyZVQYCKEOs+2dkxEY+DpPtZzO4YruuiPb7NkYLVcyJC4+zCbk5pA==}
- engines: {node: '>=4'}
-
- regenerate@1.4.2:
- resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
-
- regexpu-core@4.8.0:
- resolution: {integrity: sha512-1F6bYsoYiz6is+oz70NWur2Vlh9KWtswuRuzJOfeYUrfPX2o8n74AnUVaOGDbUqVGO9fNHu48/pjJO4sNVwsOg==}
- engines: {node: '>=4'}
-
- regjsgen@0.5.2:
- resolution: {integrity: sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==}
-
- regjsparser@0.7.0:
- resolution: {integrity: sha512-A4pcaORqmNMDVwUjWoTzuhwMGpP+NykpfqAsEgI1FSH/EzC7lrN5TMd+kN8YCovX+jMpu8eaqXgXPCa0g8FQNQ==}
- hasBin: true
-
- relative-time-format@1.1.6:
- resolution: {integrity: sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ==}
-
- repeat-string@https://registry.npmmirror.com/repeat-string/-/repeat-string-1.6.1.tgz:
- resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==, tarball: https://registry.npmmirror.com/repeat-string/-/repeat-string-1.6.1.tgz}
- version: 1.6.1
- engines: {node: '>=0.10'}
-
- require-directory@2.1.1:
- resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
- engines: {node: '>=0.10.0'}
-
- require-from-string@2.0.2:
- resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
- engines: {node: '>=0.10.0'}
-
- resolve@https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz:
- resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==, tarball: https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz}
- version: 1.22.1
- hasBin: true
-
- reusify@https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz:
- resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==, tarball: https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz}
- version: 1.0.4
- engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
-
- rimraf@3.0.2:
- resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
- deprecated: Rimraf versions prior to v4 are no longer supported
- hasBin: true
-
- rollup-plugin-external-globals@0.8.0:
- resolution: {integrity: sha512-c65c7hPMCE//cLzC4dLVE25XkuHsBqSkZp+/5pvtZ1MFwqgQLRRkIfuCvI3PnI7Yj8HoXqYtdsRN9gYF5a4tVQ==}
- peerDependencies:
- rollup: ^2.25.0 || ^3.3.0
-
- rollup-plugin-visualizer@5.9.2:
- resolution: {integrity: sha512-waHktD5mlWrYFrhOLbti4YgQCn1uR24nYsNuXxg7LkPH8KdTXVWR9DNY1WU0QqokyMixVXJS4J04HNrVTMP01A==}
- engines: {node: '>=14'}
- hasBin: true
- peerDependencies:
- rollup: 2.x || 3.x
- peerDependenciesMeta:
- rollup:
- optional: true
-
- rollup@3.26.2:
- resolution: {integrity: sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==}
- engines: {node: '>=14.18.0', npm: '>=8.0.0'}
- hasBin: true
-
- rollup@https://registry.npmmirror.com/rollup/-/rollup-2.79.1.tgz:
- resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==, tarball: https://registry.npmmirror.com/rollup/-/rollup-2.79.1.tgz}
- version: 2.79.1
- engines: {node: '>=10.0.0'}
- hasBin: true
-
- run-parallel@https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz:
- resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, tarball: https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz}
- version: 1.2.0
-
- sade@1.8.1:
- resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
- engines: {node: '>=6'}
-
- safe-buffer@5.1.2:
- resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
-
- safe-buffer@5.2.1:
- resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
-
- sass@1.80.1:
- resolution: {integrity: sha512-9lBwDZ7j3y/1DKj5Ec249EVGo5CVpwnzIyIj+cqlCjKkApLnzsJ/l9SnV4YnORvW9dQwQN+gQvh/mFZ8CnDs7Q==}
- engines: {node: '>=14.0.0'}
- hasBin: true
-
- select-dom@6.0.2:
- resolution: {integrity: sha512-fnRexLCjOE1XtMiV0xGACx4GJFMs/eatOQM0LWL9lxWB8DJ/FeXO39ztqUh6dZCOdoFo/g6jGJIIqNWoMsNLPA==}
-
- select@1.1.2:
- resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==}
-
- semiver@1.1.0:
- resolution: {integrity: sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==}
- engines: {node: '>=6'}
-
- semver@6.3.0:
- resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
- hasBin: true
-
- semver@7.3.8:
- resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
- engines: {node: '>=10'}
- hasBin: true
-
- set-blocking@2.0.0:
- resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
-
- shebang-command@2.0.0:
- resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
- engines: {node: '>=8'}
-
- shebang-regex@3.0.0:
- resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
- engines: {node: '>=8'}
-
- showdown@2.1.0:
- resolution: {integrity: sha512-/6NVYu4U819R2pUIk79n67SYgJHWCce0a5xTP979WbNp0FL9MN1I1QK662IDU1b6JzKTvmhgI7T7JYIxBi3kMQ==}
- hasBin: true
-
- signal-exit@3.0.7:
- resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
-
- sirv-cli@1.0.14:
- resolution: {integrity: sha512-yyUTNr984ANKDloqepkYbBSqvx3buwYg2sQKPWjSU+IBia5loaoka2If8N9CMwt8AfP179cdEl7kYJ//iWJHjQ==}
- engines: {node: '>= 10'}
- hasBin: true
-
- sirv@1.0.19:
- resolution: {integrity: sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==}
- engines: {node: '>= 10'}
-
- source-map-js@1.0.2:
- resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
- engines: {node: '>=0.10.0'}
-
- source-map-js@1.2.1:
- resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
- engines: {node: '>=0.10.0'}
-
- source-map-js@https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz:
- resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==, tarball: https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz}
- version: 1.0.2
- engines: {node: '>=0.10.0'}
-
- source-map@0.7.4:
- resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
- engines: {node: '>= 8'}
-
- source-map@https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz:
- resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, tarball: https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz}
- version: 0.6.1
- engines: {node: '>=0.10.0'}
-
- sourcemap-codec@1.4.8:
- resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
- deprecated: Please use @jridgewell/sourcemap-codec instead
-
- sourcemap-codec@https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz:
- resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==, tarball: https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz}
- version: 1.4.8
-
- sprintf-js@1.0.3:
- resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
-
- string-width@4.2.3:
- resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
- engines: {node: '>=8'}
-
- string_decoder@1.3.0:
- resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
-
- strip-ansi@6.0.1:
- resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
- engines: {node: '>=8'}
-
- strip-final-newline@2.0.0:
- resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
- engines: {node: '>=6'}
-
- style-mod@4.1.2:
- resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==}
-
- supports-color@5.5.0:
- resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
- engines: {node: '>=4'}
-
- supports-preserve-symlinks-flag@https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz:
- resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, tarball: https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz}
- version: 1.0.0
- engines: {node: '>= 0.4'}
-
- svelte@4.2.19:
- resolution: {integrity: sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==}
- engines: {node: '>=16'}
-
- svgo@3.3.2:
- resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==}
- engines: {node: '>=14.0.0'}
- hasBin: true
-
- tar@6.1.12:
- resolution: {integrity: sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==}
- engines: {node: '>=10'}
-
- three@0.166.1:
- resolution: {integrity: sha512-LtuafkKHHzm61AQA1be2MAYIw1IjmhOUxhBa0prrLpEMWbV7ijvxCRHjSgHPGp2493wLBzwKV46tA9nivLEgKg==}
-
- tiny-emitter@2.1.0:
- resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
-
- tiny-invariant@1.3.3:
- resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
-
- tinydate@1.3.0:
- resolution: {integrity: sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==}
- engines: {node: '>=4'}
-
- tlds@1.255.0:
- resolution: {integrity: sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw==}
- hasBin: true
-
- to-fast-properties@2.0.0:
- resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
- engines: {node: '>=4'}
-
- to-fast-properties@https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz:
- resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==, tarball: https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz}
- version: 2.0.0
- engines: {node: '>=4'}
-
- to-regex-range@5.0.1:
- resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
- engines: {node: '>=8.0'}
-
- to-regex-range@https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz:
- resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, tarball: https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz}
- version: 5.0.1
- engines: {node: '>=8.0'}
-
- totalist@1.1.0:
- resolution: {integrity: sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==}
- engines: {node: '>=6'}
-
- tr46@0.0.3:
- resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
-
- trouter@2.0.1:
- resolution: {integrity: sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==}
- engines: {node: '>=6'}
-
- tslib@2.4.1:
- resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==}
-
- typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz:
- resolution: {integrity: sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==, tarball: https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz}
- version: 4.9.3
- engines: {node: '>=4.2.0'}
- hasBin: true
-
- unicode-canonical-property-names-ecmascript@2.0.0:
- resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
- engines: {node: '>=4'}
-
- unicode-match-property-ecmascript@2.0.0:
- resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
- engines: {node: '>=4'}
-
- unicode-match-property-value-ecmascript@2.1.0:
- resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==}
- engines: {node: '>=4'}
-
- unicode-property-aliases-ecmascript@2.1.0:
- resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
- engines: {node: '>=4'}
-
- url-join@4.0.1:
- resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
-
- url-regex@5.0.0:
- resolution: {integrity: sha512-O08GjTiAFNsSlrUWfqF1jH0H1W3m35ZyadHrGv5krdnmPPoxP27oDTqux/579PtaroiSGm5yma6KT1mHFH6Y/g==}
- engines: {node: '>=8'}
-
- util-deprecate@1.0.2:
- resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
-
- uuid@10.0.0:
- resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
- hasBin: true
-
- vanilla-jsoneditor@1.0.8:
- resolution: {integrity: sha512-5tKODR6J3IbGoMeBv4DEEU/Dlam4bPunOarIJKfrd5xs4bUife59Lp8Psjf86Y8OAM9mvxkrwR275XsBnvtO9A==}
-
- vanilla-picker@2.12.3:
- resolution: {integrity: sha512-qVkT1E7yMbUsB2mmJNFmaXMWE2hF8ffqzMMwe9zdAikd8u2VfnsVY2HQcOUi2F38bgbxzlJBEdS1UUhOXdF9GQ==}
-
- vary@1.1.2:
- resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
- engines: {node: '>= 0.8'}
-
- vite-aliases@https://registry.npmmirror.com/vite-aliases/-/vite-aliases-0.9.7.tgz:
- resolution: {integrity: sha512-MiqZx5NJQDn/I7gYzovop8GzlEDYCxR3i3JCCNap5OWEfBmo4b+jIfpmal22IoBREgx1TtkmwRkHJCBUhnL6jA==, tarball: https://registry.npmmirror.com/vite-aliases/-/vite-aliases-0.9.7.tgz}
- version: 0.9.7
- engines: {node: '>=16.6.0', npm: '>=7.0.0'}
-
- vite-plugin-dynamic-import@https://registry.npmmirror.com/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.2.4.tgz:
- resolution: {integrity: sha512-R6spqDhDm8VxaUNyEkybh9PM262ReO8WXJ6rjtyJ/H8Y4b+pq2uM9Xz5DMu7N7OxUGlqo9HSzT3VjhpbySXrng==, tarball: https://registry.npmmirror.com/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.2.4.tgz}
- version: 1.2.4
-
- vite-plugin-externalize-deps@0.7.0:
- resolution: {integrity: sha512-do2gPrR79Tm8UKcqsw3RTAtN4YO8GkVRBckWdJWINZ3Qdp3KN9S1oyUZxKszTB/iyg4zdOUweLOeBI8t86QVow==}
- version: 0.7.0
- peerDependencies:
- vite: ^2.0.0 || ^3.0.0 || ^4.0.0
-
- vite-svg-loader@5.1.0:
- resolution: {integrity: sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==}
- version: 5.1.0
- peerDependencies:
- vue: '>=3.2.13'
-
- vite@https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz:
- resolution: {integrity: sha512-Z2X6SRAffOUYTa+sLy3NQ7nlHFU100xwanq1WDwqaiFiCe+25zdxP1TfCS5ojPV2oDDcXudHIoPnI1Z/66B7Yw==, tarball: https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz}
- version: 3.2.4
- engines: {node: ^14.18.0 || >=16.0.0}
- hasBin: true
- peerDependencies:
- '@types/node': '>= 14'
- less: '*'
- sass: '*'
- stylus: '*'
- sugarss: '*'
- terser: ^5.4.0
- peerDependenciesMeta:
- '@types/node':
- optional: true
- less:
- optional: true
- sass:
- optional: true
- stylus:
- optional: true
- sugarss:
- optional: true
- terser:
- optional: true
-
- vue-chartjs@5.3.1:
- resolution: {integrity: sha512-rZjqcHBxKiHrBl0CIvcOlVEBwRhpWAVf6rDU3vUfa7HuSRmGtCslc0Oc8m16oAVuk0erzc1FCtH1VCriHsrz+A==}
- version: 5.3.1
- peerDependencies:
- chart.js: ^4.1.1
- vue: ^3.0.0-0 || ^2.7.0
-
- vue-clipboard3@2.0.0:
- resolution: {integrity: sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==}
-
- vue-demi@0.14.10:
- resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
- version: 0.14.10
- engines: {node: '>=12'}
- hasBin: true
- peerDependencies:
- '@vue/composition-api': ^1.0.0-rc.1
- vue: ^3.0.0-0 || ^2.6.0
- peerDependenciesMeta:
- '@vue/composition-api':
- optional: true
-
- vue-i18n@9.13.1:
- resolution: {integrity: sha512-mh0GIxx0wPtPlcB1q4k277y0iKgo25xmDPWioVVYanjPufDBpvu5ySTjP5wOrSvlYQ2m1xI+CFhGdauv/61uQg==}
- version: 9.13.1
- engines: {node: '>= 16'}
- peerDependencies:
- vue: ^3.0.0
-
- vue-router@4.4.0:
- resolution: {integrity: sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==}
- version: 4.4.0
- peerDependencies:
- vue: ^3.2.0
-
- vue-template-compiler@https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz:
- resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==, tarball: https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz}
- version: 2.7.14
-
- vue-tsc@https://registry.npmmirror.com/vue-tsc/-/vue-tsc-1.0.11.tgz:
- resolution: {integrity: sha512-lj+6dEroPsE4wmQOPtjCzAf8x363Km5/tuEvMEoQaoRnzs9myBM46FNvCGIIPStYUGuaqF1W1bORmP2KDQEORA==, tarball: https://registry.npmmirror.com/vue-tsc/-/vue-tsc-1.0.11.tgz}
- version: 1.0.11
- hasBin: true
- peerDependencies:
- typescript: '*'
-
- vue@3.5.12:
- resolution: {integrity: sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==}
- version: 3.5.12
- peerDependencies:
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
-
- vuex@4.1.0:
- resolution: {integrity: sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==}
- version: 4.1.0
- peerDependencies:
- vue: ^3.2.0
-
- w3c-keyname@2.2.8:
- resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
-
- webidl-conversions@3.0.1:
- resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
-
- whatwg-url@5.0.0:
- resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
-
- which@2.0.2:
- resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
- engines: {node: '>= 8'}
- hasBin: true
-
- wide-align@1.1.5:
- resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
-
- windows-release@5.0.1:
- resolution: {integrity: sha512-y1xFdFvdMiDXI3xiOhMbJwt1Y7dUxidha0CWPs1NgjZIjZANTcX7+7bMqNjuezhzb8s5JGEiBAbQjQQYYy7ulw==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
- wrap-ansi@7.0.0:
- resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
- engines: {node: '>=10'}
-
- wrappy@1.0.2:
- resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
-
- y18n@5.0.8:
- resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
- engines: {node: '>=10'}
-
- yallist@4.0.0:
- resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
-
- yargs-parser@21.1.1:
- resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
- engines: {node: '>=12'}
-
- yargs@17.7.2:
- resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
- engines: {node: '>=12'}
-
-snapshots:
-
- '@ampproject/remapping@2.3.0':
- dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
-
- '@arr/every@1.0.1': {}
-
- '@babel/helper-string-parser@7.25.7': {}
-
- '@babel/helper-string-parser@https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz': {}
-
- '@babel/helper-validator-identifier@7.25.7': {}
-
- '@babel/helper-validator-identifier@https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz': {}
-
- '@babel/parser@7.25.8':
- dependencies:
- '@babel/types': 7.25.8
-
- '@babel/parser@https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz':
- dependencies:
- '@babel/types': https://registry.npmmirror.com/@babel/types/-/types-7.20.5.tgz
-
- '@babel/types@7.25.8':
- dependencies:
- '@babel/helper-string-parser': 7.25.7
- '@babel/helper-validator-identifier': 7.25.7
- to-fast-properties: 2.0.0
-
- '@babel/types@https://registry.npmmirror.com/@babel/types/-/types-7.20.5.tgz':
- dependencies:
- '@babel/helper-string-parser': https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz
- '@babel/helper-validator-identifier': https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz
- to-fast-properties: https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz
-
- '@codemirror/autocomplete@6.18.1(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.1)(@lezer/common@1.2.3)':
- dependencies:
- '@codemirror/language': 6.10.3
- '@codemirror/state': 6.4.1
- '@codemirror/view': 6.34.1
- '@lezer/common': 1.2.3
-
- '@codemirror/commands@6.7.0':
- dependencies:
- '@codemirror/language': 6.10.3
- '@codemirror/state': 6.4.1
- '@codemirror/view': 6.34.1
- '@lezer/common': 1.2.3
-
- '@codemirror/lang-json@6.0.1':
- dependencies:
- '@codemirror/language': 6.10.3
- '@lezer/json': 1.0.2
-
- '@codemirror/language@6.10.3':
- dependencies:
- '@codemirror/state': 6.4.1
- '@codemirror/view': 6.34.1
- '@lezer/common': 1.2.3
- '@lezer/highlight': 1.2.1
- '@lezer/lr': 1.4.2
- style-mod: 4.1.2
-
- '@codemirror/lint@6.8.2':
- dependencies:
- '@codemirror/state': 6.4.1
- '@codemirror/view': 6.34.1
- crelt: 1.0.6
-
- '@codemirror/search@6.5.6':
- dependencies:
- '@codemirror/state': 6.4.1
- '@codemirror/view': 6.34.1
- crelt: 1.0.6
-
- '@codemirror/state@6.4.1': {}
-
- '@codemirror/view@6.34.1':
- dependencies:
- '@codemirror/state': 6.4.1
- style-mod: 4.1.2
- w3c-keyname: 2.2.8
-
- '@ctrl/tinycolor@3.4.1': {}
-
- '@element-plus/icons-vue@2.3.1(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))':
- dependencies:
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- '@esbuild/android-arm@0.15.16':
- optional: true
-
- '@esbuild/linux-loong64@0.15.16':
- optional: true
-
- '@floating-ui/core@1.6.8':
- dependencies:
- '@floating-ui/utils': 0.2.8
-
- '@floating-ui/dom@1.6.11':
- dependencies:
- '@floating-ui/core': 1.6.8
- '@floating-ui/utils': 0.2.8
-
- '@floating-ui/utils@0.2.8': {}
-
- '@fortawesome/fontawesome-common-types@6.6.0': {}
-
- '@fortawesome/fontawesome-svg-core@6.6.0':
- dependencies:
- '@fortawesome/fontawesome-common-types': 6.6.0
-
- '@fortawesome/free-brands-svg-icons@6.6.0':
- dependencies:
- '@fortawesome/fontawesome-common-types': 6.6.0
-
- '@fortawesome/free-regular-svg-icons@6.6.0':
- dependencies:
- '@fortawesome/fontawesome-common-types': 6.6.0
-
- '@fortawesome/free-solid-svg-icons@6.6.0':
- dependencies:
- '@fortawesome/fontawesome-common-types': 6.6.0
-
- '@fortawesome/vue-fontawesome@3.0.8(@fortawesome/fontawesome-svg-core@6.6.0)(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))':
- dependencies:
- '@fortawesome/fontawesome-svg-core': 6.6.0
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- '@intlify/core-base@9.13.1':
- dependencies:
- '@intlify/message-compiler': 9.13.1
- '@intlify/shared': 9.13.1
-
- '@intlify/message-compiler@9.13.1':
- dependencies:
- '@intlify/shared': 9.13.1
- source-map-js: 1.0.2
-
- '@intlify/shared@9.13.1': {}
-
- '@jridgewell/gen-mapping@0.3.5':
- dependencies:
- '@jridgewell/set-array': 1.2.1
- '@jridgewell/sourcemap-codec': 1.5.0
- '@jridgewell/trace-mapping': 0.3.25
-
- '@jridgewell/resolve-uri@3.1.2': {}
-
- '@jridgewell/set-array@1.2.1': {}
-
- '@jridgewell/sourcemap-codec@1.4.15': {}
-
- '@jridgewell/sourcemap-codec@1.5.0': {}
-
- '@jridgewell/trace-mapping@0.3.25':
- dependencies:
- '@jridgewell/resolve-uri': 3.1.2
- '@jridgewell/sourcemap-codec': 1.5.0
-
- '@jsep-plugin/assignment@1.2.1(jsep@1.3.9)':
- dependencies:
- jsep: 1.3.9
-
- '@jsep-plugin/regex@1.0.3(jsep@1.3.9)':
- dependencies:
- jsep: 1.3.9
-
- '@jsonquerylang/jsonquery@3.1.1': {}
-
- '@kurkle/color@0.3.2': {}
-
- '@lexical/clipboard@0.16.1':
- dependencies:
- '@lexical/html': 0.16.1
- '@lexical/list': 0.16.1
- '@lexical/selection': 0.16.1
- '@lexical/utils': 0.16.1
- lexical: 0.16.1
-
- '@lexical/code@0.16.1':
- dependencies:
- '@lexical/utils': 0.16.1
- lexical: 0.16.1
- prismjs: 1.29.0
-
- '@lexical/history@0.16.1':
- dependencies:
- '@lexical/utils': 0.16.1
- lexical: 0.16.1
-
- '@lexical/html@0.16.1':
- dependencies:
- '@lexical/selection': 0.16.1
- '@lexical/utils': 0.16.1
- lexical: 0.16.1
-
- '@lexical/link@0.16.1':
- dependencies:
- '@lexical/utils': 0.16.1
- lexical: 0.16.1
-
- '@lexical/list@0.16.1':
- dependencies:
- '@lexical/utils': 0.16.1
- lexical: 0.16.1
-
- '@lexical/markdown@0.16.1':
- dependencies:
- '@lexical/code': 0.16.1
- '@lexical/link': 0.16.1
- '@lexical/list': 0.16.1
- '@lexical/rich-text': 0.16.1
- '@lexical/text': 0.16.1
- '@lexical/utils': 0.16.1
- lexical: 0.16.1
-
- '@lexical/rich-text@0.16.1':
- dependencies:
- '@lexical/clipboard': 0.16.1
- '@lexical/selection': 0.16.1
- '@lexical/utils': 0.16.1
- lexical: 0.16.1
-
- '@lexical/selection@0.16.1':
- dependencies:
- lexical: 0.16.1
-
- '@lexical/table@0.16.1':
- dependencies:
- '@lexical/utils': 0.16.1
- lexical: 0.16.1
-
- '@lexical/text@0.16.1':
- dependencies:
- lexical: 0.16.1
-
- '@lexical/utils@0.16.1':
- dependencies:
- '@lexical/list': 0.16.1
- '@lexical/selection': 0.16.1
- '@lexical/table': 0.16.1
- lexical: 0.16.1
-
- '@lezer/common@1.2.3': {}
-
- '@lezer/highlight@1.2.1':
- dependencies:
- '@lezer/common': 1.2.3
-
- '@lezer/json@1.0.2':
- dependencies:
- '@lezer/common': 1.2.3
- '@lezer/highlight': 1.2.1
- '@lezer/lr': 1.4.2
-
- '@lezer/lr@1.4.2':
- dependencies:
- '@lezer/common': 1.2.3
-
- '@mapbox/node-pre-gyp@1.0.10':
- dependencies:
- detect-libc: 2.0.1
- https-proxy-agent: 5.0.1
- make-dir: 3.1.0
- node-fetch: 2.6.7
- nopt: 5.0.0
- npmlog: 5.0.1
- rimraf: 3.0.2
- semver: 7.3.8
- tar: 6.1.12
- transitivePeerDependencies:
- - encoding
- - supports-color
- optional: true
-
- '@nodelib/fs.scandir@https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz':
- dependencies:
- '@nodelib/fs.stat': https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz
- run-parallel: https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz
-
- '@nodelib/fs.stat@https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz': {}
-
- '@nodelib/fs.walk@https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz':
- dependencies:
- '@nodelib/fs.scandir': https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz
- fastq: https://registry.npmmirror.com/fastq/-/fastq-1.14.0.tgz
-
- '@parcel/watcher-android-arm64@2.4.1':
- optional: true
-
- '@parcel/watcher-darwin-arm64@2.4.1':
- optional: true
-
- '@parcel/watcher-darwin-x64@2.4.1':
- optional: true
-
- '@parcel/watcher-freebsd-x64@2.4.1':
- optional: true
-
- '@parcel/watcher-linux-arm-glibc@2.4.1':
- optional: true
-
- '@parcel/watcher-linux-arm64-glibc@2.4.1':
- optional: true
-
- '@parcel/watcher-linux-arm64-musl@2.4.1':
- optional: true
-
- '@parcel/watcher-linux-x64-glibc@2.4.1':
- optional: true
-
- '@parcel/watcher-linux-x64-musl@2.4.1':
- optional: true
-
- '@parcel/watcher-win32-arm64@2.4.1':
- optional: true
-
- '@parcel/watcher-win32-ia32@2.4.1':
- optional: true
-
- '@parcel/watcher-win32-x64@2.4.1':
- optional: true
-
- '@parcel/watcher@2.4.1':
- dependencies:
- detect-libc: 1.0.3
- is-glob: 4.0.3
- micromatch: 4.0.5
- node-addon-api: 7.1.1
- optionalDependencies:
- '@parcel/watcher-android-arm64': 2.4.1
- '@parcel/watcher-darwin-arm64': 2.4.1
- '@parcel/watcher-darwin-x64': 2.4.1
- '@parcel/watcher-freebsd-x64': 2.4.1
- '@parcel/watcher-linux-arm-glibc': 2.4.1
- '@parcel/watcher-linux-arm64-glibc': 2.4.1
- '@parcel/watcher-linux-arm64-musl': 2.4.1
- '@parcel/watcher-linux-x64-glibc': 2.4.1
- '@parcel/watcher-linux-x64-musl': 2.4.1
- '@parcel/watcher-win32-arm64': 2.4.1
- '@parcel/watcher-win32-ia32': 2.4.1
- '@parcel/watcher-win32-x64': 2.4.1
-
- '@polka/url@0.5.0': {}
-
- '@polka/url@1.0.0-next.21': {}
-
- '@popperjs/core@2.11.8': {}
-
- '@replit/codemirror-indentation-markers@6.5.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.1)':
- dependencies:
- '@codemirror/language': 6.10.3
- '@codemirror/state': 6.4.1
- '@codemirror/view': 6.34.1
-
- '@rollup/pluginutils@5.0.2(rollup@3.26.2)':
- dependencies:
- '@types/estree': 1.0.0
- estree-walker: 2.0.2
- picomatch: 2.3.1
- optionalDependencies:
- rollup: 3.26.2
-
- '@sphinxxxx/color-conversion@2.2.2': {}
-
- '@sxzz/popperjs-es@2.11.7': {}
-
- '@trysound/sax@0.2.0': {}
-
- '@tweenjs/tween.js@23.1.3': {}
-
- '@types/estree@1.0.0': {}
-
- '@types/estree@1.0.6': {}
-
- '@types/getos@3.0.4': {}
-
- '@types/humanize-duration@3.27.4': {}
-
- '@types/javascript-time-ago@2.0.8': {}
-
- '@types/lodash-es@4.17.12':
- dependencies:
- '@types/lodash': 4.17.10
-
- '@types/lodash@4.17.10': {}
-
- '@types/md5@2.3.5': {}
-
- '@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz': {}
-
- '@types/pinyin@2.10.2': {}
-
- '@types/showdown@2.0.6': {}
-
- '@types/stats.js@0.17.3': {}
-
- '@types/three@0.166.0':
- dependencies:
- '@tweenjs/tween.js': 23.1.3
- '@types/stats.js': 0.17.3
- '@types/webxr': 0.5.20
- fflate: 0.8.2
- meshoptimizer: 0.18.1
-
- '@types/url-join@4.0.3': {}
-
- '@types/web-bluetooth@0.0.16': {}
-
- '@types/webxr@0.5.20': {}
-
- '@vitejs/plugin-vue@https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-3.2.0.tgz(vite@https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1))(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))':
- dependencies:
- vite: https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1)
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- '@volar/language-core@https://registry.npmmirror.com/@volar/language-core/-/language-core-1.0.11.tgz':
- dependencies:
- '@volar/source-map': https://registry.npmmirror.com/@volar/source-map/-/source-map-1.0.11.tgz
- '@vue/reactivity': https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.45.tgz
- muggle-string: https://registry.npmmirror.com/muggle-string/-/muggle-string-0.1.0.tgz
-
- '@volar/source-map@https://registry.npmmirror.com/@volar/source-map/-/source-map-1.0.11.tgz':
- dependencies:
- muggle-string: https://registry.npmmirror.com/muggle-string/-/muggle-string-0.1.0.tgz
-
- '@volar/typescript@https://registry.npmmirror.com/@volar/typescript/-/typescript-1.0.11.tgz':
- dependencies:
- '@volar/language-core': https://registry.npmmirror.com/@volar/language-core/-/language-core-1.0.11.tgz
-
- '@volar/vue-language-core@https://registry.npmmirror.com/@volar/vue-language-core/-/vue-language-core-1.0.11.tgz':
- dependencies:
- '@volar/language-core': https://registry.npmmirror.com/@volar/language-core/-/language-core-1.0.11.tgz
- '@volar/source-map': https://registry.npmmirror.com/@volar/source-map/-/source-map-1.0.11.tgz
- '@vue/compiler-dom': https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz
- '@vue/compiler-sfc': https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz
- '@vue/reactivity': https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.45.tgz
- '@vue/shared': https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz
- minimatch: https://registry.npmmirror.com/minimatch/-/minimatch-5.1.1.tgz
- vue-template-compiler: https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz
-
- '@volar/vue-typescript@https://registry.npmmirror.com/@volar/vue-typescript/-/vue-typescript-1.0.11.tgz':
- dependencies:
- '@volar/typescript': https://registry.npmmirror.com/@volar/typescript/-/typescript-1.0.11.tgz
- '@volar/vue-language-core': https://registry.npmmirror.com/@volar/vue-language-core/-/vue-language-core-1.0.11.tgz
-
- '@vue/compiler-core@3.5.12':
- dependencies:
- '@babel/parser': 7.25.8
- '@vue/shared': 3.5.12
- entities: 4.5.0
- estree-walker: 2.0.2
- source-map-js: 1.2.1
-
- '@vue/compiler-core@https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz':
- dependencies:
- '@babel/parser': https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz
- '@vue/shared': https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz
- estree-walker: https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz
- source-map: https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz
-
- '@vue/compiler-dom@3.5.12':
- dependencies:
- '@vue/compiler-core': 3.5.12
- '@vue/shared': 3.5.12
-
- '@vue/compiler-dom@https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz':
- dependencies:
- '@vue/compiler-core': https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz
- '@vue/shared': https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz
-
- '@vue/compiler-sfc@3.5.12':
- dependencies:
- '@babel/parser': 7.25.8
- '@vue/compiler-core': 3.5.12
- '@vue/compiler-dom': 3.5.12
- '@vue/compiler-ssr': 3.5.12
- '@vue/shared': 3.5.12
- estree-walker: 2.0.2
- magic-string: 0.30.12
- postcss: 8.4.47
- source-map-js: 1.2.1
-
- '@vue/compiler-sfc@https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz':
- dependencies:
- '@babel/parser': https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz
- '@vue/compiler-core': https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz
- '@vue/compiler-dom': https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz
- '@vue/compiler-ssr': https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz
- '@vue/reactivity-transform': https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz
- '@vue/shared': https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz
- estree-walker: https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz
- magic-string: https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz
- postcss: https://registry.npmmirror.com/postcss/-/postcss-8.4.19.tgz
- source-map: https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz
-
- '@vue/compiler-ssr@3.5.12':
- dependencies:
- '@vue/compiler-dom': 3.5.12
- '@vue/shared': 3.5.12
-
- '@vue/compiler-ssr@https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz':
- dependencies:
- '@vue/compiler-dom': https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz
- '@vue/shared': https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz
-
- '@vue/devtools-api@6.4.5': {}
-
- '@vue/devtools-api@6.6.4': {}
-
- '@vue/reactivity-transform@https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz':
- dependencies:
- '@babel/parser': https://registry.npmmirror.com/@babel/parser/-/parser-7.20.5.tgz
- '@vue/compiler-core': https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz
- '@vue/shared': https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz
- estree-walker: https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz
- magic-string: https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz
-
- '@vue/reactivity@3.5.12':
- dependencies:
- '@vue/shared': 3.5.12
-
- '@vue/reactivity@https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.45.tgz':
- dependencies:
- '@vue/shared': https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz
-
- '@vue/runtime-core@3.5.12':
- dependencies:
- '@vue/reactivity': 3.5.12
- '@vue/shared': 3.5.12
-
- '@vue/runtime-dom@3.5.12':
- dependencies:
- '@vue/reactivity': 3.5.12
- '@vue/runtime-core': 3.5.12
- '@vue/shared': 3.5.12
- csstype: 3.1.3
-
- '@vue/server-renderer@3.5.12(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))':
- dependencies:
- '@vue/compiler-ssr': 3.5.12
- '@vue/shared': 3.5.12
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- '@vue/shared@3.5.12': {}
-
- '@vue/shared@https://registry.npmmirror.com/@vue/shared/-/shared-3.2.45.tgz': {}
-
- '@vueuse/core@9.13.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))':
- dependencies:
- '@types/web-bluetooth': 0.0.16
- '@vueuse/metadata': 9.13.0
- '@vueuse/shared': 9.13.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- vue-demi: 0.14.10(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- transitivePeerDependencies:
- - '@vue/composition-api'
- - vue
-
- '@vueuse/metadata@9.13.0': {}
-
- '@vueuse/shared@9.13.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))':
- dependencies:
- vue-demi: 0.14.10(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- transitivePeerDependencies:
- - '@vue/composition-api'
- - vue
-
- abbrev@1.1.1:
- optional: true
-
- accepts@1.3.8:
- dependencies:
- mime-types: 2.1.35
- negotiator: 0.6.3
-
- acorn-dynamic-import@4.0.0(acorn@6.4.2):
- dependencies:
- acorn: 6.4.2
-
- acorn-jsx@5.3.2(acorn@6.4.2):
- dependencies:
- acorn: 6.4.2
-
- acorn@6.4.2: {}
-
- acorn@8.13.0: {}
-
- agent-base@6.0.2:
- dependencies:
- debug: 4.3.4
- transitivePeerDependencies:
- - supports-color
- optional: true
-
- ajv@8.17.1:
- dependencies:
- fast-deep-equal: 3.1.3
- fast-uri: 3.0.3
- json-schema-traverse: 1.0.0
- require-from-string: 2.0.2
-
- ansi-regex@5.0.1: {}
-
- ansi-styles@3.2.1:
- dependencies:
- color-convert: 1.9.3
-
- ansi-styles@4.3.0:
- dependencies:
- color-convert: 2.0.1
-
- anymatch@https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz:
- dependencies:
- normalize-path: https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz
- picomatch: https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz
-
- aproba@2.0.0:
- optional: true
-
- are-we-there-yet@2.0.0:
- dependencies:
- delegates: 1.0.0
- readable-stream: 3.6.0
- optional: true
-
- argparse@1.0.10:
- dependencies:
- sprintf-js: 1.0.3
-
- aria-query@5.3.2: {}
-
- array-bounds@1.0.1: {}
-
- array-normalize@1.1.4:
- dependencies:
- array-bounds: 1.0.1
-
- array-timsort@https://registry.npmmirror.com/array-timsort/-/array-timsort-1.0.3.tgz: {}
-
- async-validator@4.2.5: {}
-
- asynckit@0.4.0: {}
-
- atom-material-icons@3.0.0:
- dependencies:
- compression: 1.7.4
- js-yaml: 3.14.1
- polka: 0.5.2
- select-dom: 6.0.2
- sirv: 1.0.19
- sirv-cli: 1.0.14
- optionalDependencies:
- lodash.template: 4.5.0
- minimist: 1.2.7
- transitivePeerDependencies:
- - supports-color
-
- attr-accept@2.2.2: {}
-
- axios@1.7.7:
- dependencies:
- follow-redirects: 1.15.9
- form-data: 4.0.1
- proxy-from-env: 1.1.0
- transitivePeerDependencies:
- - debug
-
- axobject-query@4.1.0: {}
-
- balanced-match@1.0.2:
- optional: true
-
- balanced-match@https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz: {}
-
- binary-extensions@https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz: {}
-
- binary-search-bounds@2.0.5: {}
-
- boolbase@1.0.0: {}
-
- brace-expansion@1.1.11:
- dependencies:
- balanced-match: 1.0.2
- concat-map: 0.0.1
- optional: true
-
- brace-expansion@https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz:
- dependencies:
- balanced-match: https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz
-
- braces@3.0.2:
- dependencies:
- fill-range: 7.0.1
-
- braces@https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz:
- dependencies:
- fill-range: https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz
-
- buble@0.19.8:
- dependencies:
- acorn: 6.4.2
- acorn-dynamic-import: 4.0.0(acorn@6.4.2)
- acorn-jsx: 5.3.2(acorn@6.4.2)
- chalk: 2.4.2
- magic-string: 0.25.9
- minimist: 1.2.7
- os-homedir: 2.0.0
- regexpu-core: 4.8.0
-
- bubleify@1.2.1:
- dependencies:
- buble: 0.19.8
-
- bytes@3.0.0: {}
-
- chalk@2.4.2:
- dependencies:
- ansi-styles: 3.2.1
- escape-string-regexp: 1.0.5
- supports-color: 5.5.0
-
- charenc@0.0.2: {}
-
- chart.js@4.4.5:
- dependencies:
- '@kurkle/color': 0.3.2
-
- chartjs-adapter-date-fns@3.0.0(chart.js@4.4.5)(date-fns@4.1.0):
- dependencies:
- chart.js: 4.4.5
- date-fns: 4.1.0
-
- chokidar@4.0.1:
- dependencies:
- readdirp: 4.0.2
-
- chokidar@https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz:
- dependencies:
- anymatch: https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz
- braces: https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz
- glob-parent: https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz
- is-binary-path: https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz
- is-glob: https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz
- normalize-path: https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz
- readdirp: https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz
- optionalDependencies:
- fsevents: 2.3.2
-
- chownr@2.0.0:
- optional: true
-
- clamp@1.0.1: {}
-
- clipboard@2.0.11:
- dependencies:
- good-listener: 1.2.2
- select: 1.1.2
- tiny-emitter: 2.1.0
-
- cliui@8.0.1:
- dependencies:
- string-width: 4.2.3
- strip-ansi: 6.0.1
- wrap-ansi: 7.0.0
-
- code-red@1.0.4:
- dependencies:
- '@jridgewell/sourcemap-codec': 1.5.0
- '@types/estree': 1.0.6
- acorn: 8.13.0
- estree-walker: 3.0.3
- periscopic: 3.1.0
-
- codemirror-wrapped-line-indent@1.0.8(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.1):
- dependencies:
- '@codemirror/language': 6.10.3
- '@codemirror/state': 6.4.1
- '@codemirror/view': 6.34.1
-
- color-convert@1.9.3:
- dependencies:
- color-name: 1.1.3
-
- color-convert@2.0.1:
- dependencies:
- color-name: 1.1.4
-
- color-name@1.1.3: {}
-
- color-name@1.1.4: {}
-
- color-support@1.1.3:
- optional: true
-
- combined-stream@1.0.8:
- dependencies:
- delayed-stream: 1.0.0
-
- commander@1.1.1:
- dependencies:
- keypress: 0.1.0
-
- commander@7.2.0: {}
-
- commander@9.5.0: {}
-
- comment-json@https://registry.npmmirror.com/comment-json/-/comment-json-4.2.3.tgz:
- dependencies:
- array-timsort: https://registry.npmmirror.com/array-timsort/-/array-timsort-1.0.3.tgz
- core-util-is: https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz
- esprima: https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz
- has-own-prop: https://registry.npmmirror.com/has-own-prop/-/has-own-prop-2.0.0.tgz
- repeat-string: https://registry.npmmirror.com/repeat-string/-/repeat-string-1.6.1.tgz
-
- compressible@2.0.18:
- dependencies:
- mime-db: 1.52.0
-
- compression@1.7.4:
- dependencies:
- accepts: 1.3.8
- bytes: 3.0.0
- compressible: 2.0.18
- debug: 2.6.9
- on-headers: 1.0.2
- safe-buffer: 5.1.2
- vary: 1.1.2
- transitivePeerDependencies:
- - supports-color
-
- concat-map@0.0.1:
- optional: true
-
- consola@https://registry.npmmirror.com/consola/-/consola-2.15.3.tgz: {}
-
- console-clear@1.1.1: {}
-
- console-control-strings@1.1.0:
- optional: true
-
- core-js@3.38.1: {}
-
- core-util-is@https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz: {}
-
- crawlab-ui@0.7.0-rc3(dlwfpvqjxhvrevoazxvsg7egqu):
- dependencies:
- '@element-plus/icons-vue': 2.3.1(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- '@fortawesome/fontawesome-common-types': 6.6.0
- '@fortawesome/fontawesome-svg-core': 6.6.0
- '@fortawesome/free-brands-svg-icons': 6.6.0
- '@fortawesome/free-regular-svg-icons': 6.6.0
- '@fortawesome/free-solid-svg-icons': 6.6.0
- '@fortawesome/vue-fontawesome': 3.0.8(@fortawesome/fontawesome-svg-core@6.6.0)(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- '@lexical/code': 0.16.1
- '@lexical/history': 0.16.1
- '@lexical/html': 0.16.1
- '@lexical/link': 0.16.1
- '@lexical/list': 0.16.1
- '@lexical/markdown': 0.16.1
- '@lexical/rich-text': 0.16.1
- '@lexical/selection': 0.16.1
- '@lexical/table': 0.16.1
- '@lexical/text': 0.16.1
- '@lexical/utils': 0.16.1
- '@popperjs/core': 2.11.8
- '@types/getos': 3.0.4
- '@types/humanize-duration': 3.27.4
- '@types/javascript-time-ago': 2.0.8
- '@types/lodash': 4.17.10
- '@types/md5': 2.3.5
- '@types/pinyin': 2.10.2
- '@types/showdown': 2.0.6
- '@types/three': 0.166.0
- '@types/url-join': 4.0.3
- async-validator: 4.2.5
- atom-material-icons: 3.0.0
- axios: 1.7.7
- chart.js: 4.4.5
- chartjs-adapter-date-fns: 3.0.0(chart.js@4.4.5)(date-fns@4.1.0)
- clipboard: 2.0.11
- core-js: 3.38.1
- crawlab-vue3-dropzone: 3.0.3(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- cron-parser: 4.9.0
- cronstrue: 2.50.0
- dayjs: 1.11.13
- element-plus: 2.8.5(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- font-awesome: 4.7.0
- humanize-duration: 3.32.1
- javascript-time-ago: 2.5.11
- json-editor-vue: 0.17.2(@lezer/common@1.2.3)(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- lexical: 0.16.1
- lodash: 4.17.21
- md5: 2.3.0
- mitt: 3.0.1
- monaco-editor: 0.50.0
- normalize.css: 8.0.1
- os-name: 5.0.1
- pinyin: 2.11.2
- point-cluster: 3.1.8
- prismjs: 1.29.0
- showdown: 2.1.0
- three: 0.166.1
- tiny-invariant: 1.3.3
- url-join: 4.0.1
- url-regex: 5.0.0
- uuid: 10.0.0
- vite-svg-loader: 5.1.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
- vue-chartjs: 5.3.1(chart.js@4.4.5)(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- vue-clipboard3: 2.0.0
- vue-i18n: 9.13.1(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- vue-router: 4.4.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- vuex: 4.1.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- transitivePeerDependencies:
- - '@lezer/common'
- - '@vue/composition-api'
-
- crawlab-vue3-dropzone@3.0.3(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)):
- dependencies:
- attr-accept: 2.2.2
- file-selector: 0.2.4
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- crelt@1.0.6: {}
-
- cron-parser@4.9.0:
- dependencies:
- luxon: 3.5.0
-
- cronstrue@2.50.0: {}
-
- cross-spawn@7.0.3:
- dependencies:
- path-key: 3.1.1
- shebang-command: 2.0.0
- which: 2.0.2
-
- crypt@0.0.2: {}
-
- css-select@5.1.0:
- dependencies:
- boolbase: 1.0.0
- css-what: 6.1.0
- domhandler: 5.0.3
- domutils: 3.1.0
- nth-check: 2.1.1
-
- css-tree@2.2.1:
- dependencies:
- mdn-data: 2.0.28
- source-map-js: 1.2.1
-
- css-tree@2.3.1:
- dependencies:
- mdn-data: 2.0.30
- source-map-js: 1.2.1
-
- css-what@6.1.0: {}
-
- csso@5.0.5:
- dependencies:
- css-tree: 2.2.1
-
- csstype@3.1.3: {}
-
- date-fns@4.1.0: {}
-
- dayjs@1.11.13: {}
-
- de-indent@https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz: {}
-
- debug@2.6.9:
- dependencies:
- ms: 2.0.0
-
- debug@4.3.4:
- dependencies:
- ms: 2.1.2
- optional: true
-
- define-lazy-prop@2.0.0: {}
-
- defined@1.0.1: {}
-
- delayed-stream@1.0.0: {}
-
- delegate@3.2.0: {}
-
- delegates@1.0.0:
- optional: true
-
- detect-libc@1.0.3: {}
-
- detect-libc@2.0.1:
- optional: true
-
- diff-sequences@29.6.3: {}
-
- dom-serializer@2.0.0:
- dependencies:
- domelementtype: 2.3.0
- domhandler: 5.0.3
- entities: 4.5.0
-
- domelementtype@2.3.0: {}
-
- domhandler@5.0.3:
- dependencies:
- domelementtype: 2.3.0
-
- domutils@3.1.0:
- dependencies:
- dom-serializer: 2.0.0
- domelementtype: 2.3.0
- domhandler: 5.0.3
-
- dtype@2.0.0: {}
-
- element-plus@2.8.5(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)):
- dependencies:
- '@ctrl/tinycolor': 3.4.1
- '@element-plus/icons-vue': 2.3.1(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- '@floating-ui/dom': 1.6.11
- '@popperjs/core': '@sxzz/popperjs-es@2.11.7'
- '@types/lodash': 4.17.10
- '@types/lodash-es': 4.17.12
- '@vueuse/core': 9.13.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- async-validator: 4.2.5
- dayjs: 1.11.13
- escape-html: 1.0.3
- lodash: 4.17.21
- lodash-es: 4.17.21
- lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
- memoize-one: 6.0.0
- normalize-wheel-es: 1.2.0
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
- transitivePeerDependencies:
- - '@vue/composition-api'
-
- emoji-regex@8.0.0: {}
-
- entities@4.5.0: {}
-
- esbuild-android-64@0.15.16:
- optional: true
-
- esbuild-android-arm64@0.15.16:
- optional: true
-
- esbuild-darwin-64@0.15.16:
- optional: true
-
- esbuild-darwin-arm64@0.15.16:
- optional: true
-
- esbuild-freebsd-64@0.15.16:
- optional: true
-
- esbuild-freebsd-arm64@0.15.16:
- optional: true
-
- esbuild-linux-32@0.15.16:
- optional: true
-
- esbuild-linux-64@0.15.16:
- optional: true
-
- esbuild-linux-arm64@0.15.16:
- optional: true
-
- esbuild-linux-arm@0.15.16:
- optional: true
-
- esbuild-linux-mips64le@0.15.16:
- optional: true
-
- esbuild-linux-ppc64le@0.15.16:
- optional: true
-
- esbuild-linux-riscv64@0.15.16:
- optional: true
-
- esbuild-linux-s390x@0.15.16:
- optional: true
-
- esbuild-netbsd-64@0.15.16:
- optional: true
-
- esbuild-openbsd-64@0.15.16:
- optional: true
-
- esbuild-sunos-64@0.15.16:
- optional: true
-
- esbuild-windows-32@0.15.16:
- optional: true
-
- esbuild-windows-64@0.15.16:
- optional: true
-
- esbuild-windows-arm64@0.15.16:
- optional: true
-
- esbuild@https://registry.npmmirror.com/esbuild/-/esbuild-0.15.16.tgz:
- optionalDependencies:
- '@esbuild/android-arm': 0.15.16
- '@esbuild/linux-loong64': 0.15.16
- esbuild-android-64: 0.15.16
- esbuild-android-arm64: 0.15.16
- esbuild-darwin-64: 0.15.16
- esbuild-darwin-arm64: 0.15.16
- esbuild-freebsd-64: 0.15.16
- esbuild-freebsd-arm64: 0.15.16
- esbuild-linux-32: 0.15.16
- esbuild-linux-64: 0.15.16
- esbuild-linux-arm: 0.15.16
- esbuild-linux-arm64: 0.15.16
- esbuild-linux-mips64le: 0.15.16
- esbuild-linux-ppc64le: 0.15.16
- esbuild-linux-riscv64: 0.15.16
- esbuild-linux-s390x: 0.15.16
- esbuild-netbsd-64: 0.15.16
- esbuild-openbsd-64: 0.15.16
- esbuild-sunos-64: 0.15.16
- esbuild-windows-32: 0.15.16
- esbuild-windows-64: 0.15.16
- esbuild-windows-arm64: 0.15.16
-
- escalade@3.1.1: {}
-
- escape-html@1.0.3: {}
-
- escape-string-regexp@1.0.5: {}
-
- esprima@4.0.1: {}
-
- esprima@https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz: {}
-
- estree-walker@2.0.2: {}
-
- estree-walker@3.0.3:
- dependencies:
- '@types/estree': 1.0.0
-
- estree-walker@https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz: {}
-
- execa@5.1.1:
- dependencies:
- cross-spawn: 7.0.3
- get-stream: 6.0.1
- human-signals: 2.1.0
- is-stream: 2.0.1
- merge-stream: 2.0.0
- npm-run-path: 4.0.1
- onetime: 5.1.2
- signal-exit: 3.0.7
- strip-final-newline: 2.0.0
-
- fast-deep-equal@3.1.3: {}
-
- fast-glob@https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz:
- dependencies:
- '@nodelib/fs.stat': https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz
- '@nodelib/fs.walk': https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz
- glob-parent: https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz
- merge2: https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz
- micromatch: https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz
-
- fast-uri@3.0.3: {}
-
- fastq@https://registry.npmmirror.com/fastq/-/fastq-1.14.0.tgz:
- dependencies:
- reusify: https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz
-
- fflate@0.8.2: {}
-
- file-selector@0.2.4:
- dependencies:
- tslib: 2.4.1
-
- fill-range@7.0.1:
- dependencies:
- to-regex-range: 5.0.1
-
- fill-range@https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz:
- dependencies:
- to-regex-range: https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz
-
- flatten-vertex-data@1.0.2:
- dependencies:
- dtype: 2.0.0
-
- follow-redirects@1.15.9: {}
-
- font-awesome@4.7.0: {}
-
- form-data@4.0.1:
- dependencies:
- asynckit: 0.4.0
- combined-stream: 1.0.8
- mime-types: 2.1.35
-
- fs-minipass@2.1.0:
- dependencies:
- minipass: 3.3.6
- optional: true
-
- fs.realpath@1.0.0:
- optional: true
-
- fsevents@2.3.2:
- optional: true
-
- function-bind@https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz: {}
-
- gauge@3.0.2:
- dependencies:
- aproba: 2.0.0
- color-support: 1.1.3
- console-control-strings: 1.1.0
- has-unicode: 2.0.1
- object-assign: 4.1.1
- signal-exit: 3.0.7
- string-width: 4.2.3
- strip-ansi: 6.0.1
- wide-align: 1.1.5
- optional: true
-
- get-caller-file@2.0.5: {}
-
- get-port@3.2.0: {}
-
- get-stream@6.0.1: {}
-
- glob-parent@https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz:
- dependencies:
- is-glob: https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz
-
- glob@7.2.3:
- dependencies:
- fs.realpath: 1.0.0
- inflight: 1.0.6
- inherits: 2.0.4
- minimatch: 3.1.2
- once: 1.4.0
- path-is-absolute: 1.0.1
- optional: true
-
- good-listener@1.2.2:
- dependencies:
- delegate: 3.2.0
-
- has-flag@3.0.0: {}
-
- has-own-prop@https://registry.npmmirror.com/has-own-prop/-/has-own-prop-2.0.0.tgz: {}
-
- has-unicode@2.0.1:
- optional: true
-
- has@https://registry.npmmirror.com/has/-/has-1.0.3.tgz:
- dependencies:
- function-bind: https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz
-
- he@https://registry.npmmirror.com/he/-/he-1.2.0.tgz: {}
-
- https-proxy-agent@5.0.1:
- dependencies:
- agent-base: 6.0.2
- debug: 4.3.4
- transitivePeerDependencies:
- - supports-color
- optional: true
-
- human-signals@2.1.0: {}
-
- humanize-duration@3.32.1: {}
-
- immutable-json-patch@6.0.1: {}
-
- immutable@4.3.7: {}
-
- inflight@1.0.6:
- dependencies:
- once: 1.4.0
- wrappy: 1.0.2
- optional: true
-
- inherits@2.0.4:
- optional: true
-
- ip-regex@4.3.0: {}
-
- is-binary-path@https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz:
- dependencies:
- binary-extensions: https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz
-
- is-buffer@1.1.6: {}
-
- is-core-module@https://registry.npmmirror.com/is-core-module/-/is-core-module-2.11.0.tgz:
- dependencies:
- has: https://registry.npmmirror.com/has/-/has-1.0.3.tgz
-
- is-docker@2.2.1: {}
-
- is-extglob@2.1.1: {}
-
- is-extglob@https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz: {}
-
- is-fullwidth-code-point@3.0.0: {}
-
- is-glob@4.0.3:
- dependencies:
- is-extglob: 2.1.1
-
- is-glob@https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz:
- dependencies:
- is-extglob: https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz
-
- is-number@7.0.0: {}
-
- is-number@https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz: {}
-
- is-obj@1.0.1: {}
-
- is-reference@3.0.1:
- dependencies:
- '@types/estree': 1.0.0
-
- is-stream@2.0.1: {}
-
- is-wsl@2.2.0:
- dependencies:
- is-docker: 2.2.1
-
- isexe@2.0.0: {}
-
- javascript-time-ago@2.5.11:
- dependencies:
- relative-time-format: 1.1.6
-
- jmespath@0.16.0: {}
-
- js-yaml@3.14.1:
- dependencies:
- argparse: 1.0.10
- esprima: 4.0.1
-
- jsep@1.3.9: {}
-
- jsesc@0.5.0: {}
-
- json-editor-vue@0.17.2(@lezer/common@1.2.3)(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)):
- dependencies:
- vanilla-jsoneditor: 1.0.8(@lezer/common@1.2.3)
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
- vue-demi: 0.14.10(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- transitivePeerDependencies:
- - '@lezer/common'
-
- json-schema-traverse@1.0.0: {}
-
- json-source-map@0.6.1: {}
-
- jsonpath-plus@10.0.1:
- dependencies:
- '@jsep-plugin/assignment': 1.2.1(jsep@1.3.9)
- '@jsep-plugin/regex': 1.0.3(jsep@1.3.9)
- jsep: 1.3.9
-
- jsonrepair@3.8.1: {}
-
- keypress@0.1.0: {}
-
- kleur@3.0.3: {}
-
- lexical@0.16.1: {}
-
- local-access@1.1.0: {}
-
- locate-character@3.0.0: {}
-
- lodash-es@4.17.21: {}
-
- lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21):
- dependencies:
- '@types/lodash-es': 4.17.12
- lodash: 4.17.21
- lodash-es: 4.17.21
-
- lodash._reinterpolate@3.0.0:
- optional: true
-
- lodash.template@4.5.0:
- dependencies:
- lodash._reinterpolate: 3.0.0
- lodash.templatesettings: 4.2.0
- optional: true
-
- lodash.templatesettings@4.2.0:
- dependencies:
- lodash._reinterpolate: 3.0.0
- optional: true
-
- lodash@4.17.21: {}
-
- lru-cache@6.0.0:
- dependencies:
- yallist: 4.0.0
- optional: true
-
- luxon@3.5.0: {}
-
- macos-release@3.1.0: {}
-
- magic-string@0.25.9:
- dependencies:
- sourcemap-codec: 1.4.8
-
- magic-string@0.30.1:
- dependencies:
- '@jridgewell/sourcemap-codec': 1.4.15
-
- magic-string@0.30.12:
- dependencies:
- '@jridgewell/sourcemap-codec': 1.5.0
-
- magic-string@https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz:
- dependencies:
- sourcemap-codec: https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz
-
- make-dir@3.1.0:
- dependencies:
- semver: 6.3.0
- optional: true
-
- matchit@1.1.0:
- dependencies:
- '@arr/every': 1.0.1
-
- math-log2@1.0.1: {}
-
- md5@2.3.0:
- dependencies:
- charenc: 0.0.2
- crypt: 0.0.2
- is-buffer: 1.1.6
-
- mdn-data@2.0.28: {}
-
- mdn-data@2.0.30: {}
-
- memoize-one@6.0.0: {}
-
- merge-stream@2.0.0: {}
-
- merge2@https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz: {}
-
- meshoptimizer@0.18.1: {}
-
- micromatch@4.0.5:
- dependencies:
- braces: 3.0.2
- picomatch: 2.3.1
-
- micromatch@https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz:
- dependencies:
- braces: https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz
- picomatch: https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz
-
- mime-db@1.52.0: {}
-
- mime-types@2.1.35:
- dependencies:
- mime-db: 1.52.0
-
- mimic-fn@2.1.0: {}
-
- minimatch@3.1.2:
- dependencies:
- brace-expansion: 1.1.11
- optional: true
-
- minimatch@https://registry.npmmirror.com/minimatch/-/minimatch-5.1.1.tgz:
- dependencies:
- brace-expansion: https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz
-
- minimist@1.2.7: {}
-
- minipass@3.3.6:
- dependencies:
- yallist: 4.0.0
- optional: true
-
- minizlib@2.1.2:
- dependencies:
- minipass: 3.3.6
- yallist: 4.0.0
- optional: true
-
- mitt@3.0.1: {}
-
- mkdirp@1.0.4:
- optional: true
-
- monaco-editor@0.50.0: {}
-
- mri@1.2.0: {}
-
- mrmime@1.0.1: {}
-
- ms@2.0.0: {}
-
- ms@2.1.2:
- optional: true
-
- muggle-string@https://registry.npmmirror.com/muggle-string/-/muggle-string-0.1.0.tgz: {}
-
- nanoid@3.3.7: {}
-
- nanoid@https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz: {}
-
- natural-compare-lite@1.4.0: {}
-
- negotiator@0.6.3: {}
-
- node-addon-api@3.2.1:
- optional: true
-
- node-addon-api@7.1.1: {}
-
- node-fetch@2.6.7:
- dependencies:
- whatwg-url: 5.0.0
- optional: true
-
- nodejieba@2.5.2:
- dependencies:
- '@mapbox/node-pre-gyp': 1.0.10
- node-addon-api: 3.2.1
- transitivePeerDependencies:
- - encoding
- - supports-color
- optional: true
-
- nopt@5.0.0:
- dependencies:
- abbrev: 1.1.1
- optional: true
-
- normalize-path@https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz: {}
-
- normalize-wheel-es@1.2.0: {}
-
- normalize.css@8.0.1: {}
-
- npm-run-path@4.0.1:
- dependencies:
- path-key: 3.1.1
-
- npmlog@5.0.1:
- dependencies:
- are-we-there-yet: 2.0.0
- console-control-strings: 1.1.0
- gauge: 3.0.2
- set-blocking: 2.0.0
- optional: true
-
- nth-check@2.1.1:
- dependencies:
- boolbase: 1.0.0
-
- object-assign@4.1.1: {}
-
- on-headers@1.0.2: {}
-
- once@1.4.0:
- dependencies:
- wrappy: 1.0.2
- optional: true
-
- onetime@5.1.2:
- dependencies:
- mimic-fn: 2.1.0
-
- open@8.4.2:
- dependencies:
- define-lazy-prop: 2.0.0
- is-docker: 2.2.1
- is-wsl: 2.2.0
-
- os-homedir@2.0.0: {}
-
- os-name@5.0.1:
- dependencies:
- macos-release: 3.1.0
- windows-release: 5.0.1
-
- parse-rect@1.2.0:
- dependencies:
- pick-by-alias: 1.2.0
-
- path-is-absolute@1.0.1:
- optional: true
-
- path-key@3.1.1: {}
-
- path-parse@https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz: {}
-
- periscopic@3.1.0:
- dependencies:
- '@types/estree': 1.0.6
- estree-walker: 3.0.3
- is-reference: 3.0.1
-
- pick-by-alias@1.2.0: {}
-
- picocolors@1.1.1: {}
-
- picocolors@https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz: {}
-
- picomatch@2.3.1: {}
-
- picomatch@https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz: {}
-
- pinyin@2.11.2:
- dependencies:
- commander: 1.1.1
- object-assign: 4.1.1
- optionalDependencies:
- nodejieba: 2.5.2
- transitivePeerDependencies:
- - encoding
- - supports-color
-
- point-cluster@3.1.8:
- dependencies:
- array-bounds: 1.0.1
- array-normalize: 1.1.4
- binary-search-bounds: 2.0.5
- bubleify: 1.2.1
- clamp: 1.0.1
- defined: 1.0.1
- dtype: 2.0.0
- flatten-vertex-data: 1.0.2
- is-obj: 1.0.1
- math-log2: 1.0.1
- parse-rect: 1.2.0
- pick-by-alias: 1.2.0
-
- polka@0.5.2:
- dependencies:
- '@polka/url': 0.5.0
- trouter: 2.0.1
-
- postcss@8.4.47:
- dependencies:
- nanoid: 3.3.7
- picocolors: 1.1.1
- source-map-js: 1.2.1
-
- postcss@https://registry.npmmirror.com/postcss/-/postcss-8.4.19.tgz:
- dependencies:
- nanoid: https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz
- picocolors: https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz
- source-map-js: https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz
-
- prismjs@1.29.0: {}
-
- proxy-from-env@1.1.0: {}
-
- queue-microtask@https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz: {}
-
- readable-stream@3.6.0:
- dependencies:
- inherits: 2.0.4
- string_decoder: 1.3.0
- util-deprecate: 1.0.2
- optional: true
-
- readdirp@4.0.2: {}
-
- readdirp@https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz:
- dependencies:
- picomatch: https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz
-
- regenerate-unicode-properties@9.0.0:
- dependencies:
- regenerate: 1.4.2
-
- regenerate@1.4.2: {}
-
- regexpu-core@4.8.0:
- dependencies:
- regenerate: 1.4.2
- regenerate-unicode-properties: 9.0.0
- regjsgen: 0.5.2
- regjsparser: 0.7.0
- unicode-match-property-ecmascript: 2.0.0
- unicode-match-property-value-ecmascript: 2.1.0
-
- regjsgen@0.5.2: {}
-
- regjsparser@0.7.0:
- dependencies:
- jsesc: 0.5.0
-
- relative-time-format@1.1.6: {}
-
- repeat-string@https://registry.npmmirror.com/repeat-string/-/repeat-string-1.6.1.tgz: {}
-
- require-directory@2.1.1: {}
-
- require-from-string@2.0.2: {}
-
- resolve@https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz:
- dependencies:
- is-core-module: https://registry.npmmirror.com/is-core-module/-/is-core-module-2.11.0.tgz
- path-parse: https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz
- supports-preserve-symlinks-flag: https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz
-
- reusify@https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz: {}
-
- rimraf@3.0.2:
- dependencies:
- glob: 7.2.3
- optional: true
-
- rollup-plugin-external-globals@0.8.0(rollup@3.26.2):
- dependencies:
- '@rollup/pluginutils': 5.0.2(rollup@3.26.2)
- estree-walker: 3.0.3
- is-reference: 3.0.1
- magic-string: 0.30.1
- rollup: 3.26.2
-
- rollup-plugin-visualizer@5.9.2(rollup@3.26.2):
- dependencies:
- open: 8.4.2
- picomatch: 2.3.1
- source-map: 0.7.4
- yargs: 17.7.2
- optionalDependencies:
- rollup: 3.26.2
-
- rollup@3.26.2:
- optionalDependencies:
- fsevents: 2.3.2
-
- rollup@https://registry.npmmirror.com/rollup/-/rollup-2.79.1.tgz:
- optionalDependencies:
- fsevents: 2.3.2
-
- run-parallel@https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz:
- dependencies:
- queue-microtask: https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz
-
- sade@1.8.1:
- dependencies:
- mri: 1.2.0
-
- safe-buffer@5.1.2: {}
-
- safe-buffer@5.2.1:
- optional: true
-
- sass@1.80.1:
- dependencies:
- '@parcel/watcher': 2.4.1
- chokidar: 4.0.1
- immutable: 4.3.7
- source-map-js: 1.2.1
-
- select-dom@6.0.2: {}
-
- select@1.1.2: {}
-
- semiver@1.1.0: {}
-
- semver@6.3.0:
- optional: true
-
- semver@7.3.8:
- dependencies:
- lru-cache: 6.0.0
- optional: true
-
- set-blocking@2.0.0:
- optional: true
-
- shebang-command@2.0.0:
- dependencies:
- shebang-regex: 3.0.0
-
- shebang-regex@3.0.0: {}
-
- showdown@2.1.0:
- dependencies:
- commander: 9.5.0
-
- signal-exit@3.0.7: {}
-
- sirv-cli@1.0.14:
- dependencies:
- console-clear: 1.1.1
- get-port: 3.2.0
- kleur: 3.0.3
- local-access: 1.1.0
- sade: 1.8.1
- semiver: 1.1.0
- sirv: 1.0.19
- tinydate: 1.3.0
-
- sirv@1.0.19:
- dependencies:
- '@polka/url': 1.0.0-next.21
- mrmime: 1.0.1
- totalist: 1.1.0
-
- source-map-js@1.0.2: {}
-
- source-map-js@1.2.1: {}
-
- source-map-js@https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz: {}
-
- source-map@0.7.4: {}
-
- source-map@https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz: {}
-
- sourcemap-codec@1.4.8: {}
-
- sourcemap-codec@https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz: {}
-
- sprintf-js@1.0.3: {}
-
- string-width@4.2.3:
- dependencies:
- emoji-regex: 8.0.0
- is-fullwidth-code-point: 3.0.0
- strip-ansi: 6.0.1
-
- string_decoder@1.3.0:
- dependencies:
- safe-buffer: 5.2.1
- optional: true
-
- strip-ansi@6.0.1:
- dependencies:
- ansi-regex: 5.0.1
-
- strip-final-newline@2.0.0: {}
-
- style-mod@4.1.2: {}
-
- supports-color@5.5.0:
- dependencies:
- has-flag: 3.0.0
-
- supports-preserve-symlinks-flag@https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz: {}
-
- svelte@4.2.19:
- dependencies:
- '@ampproject/remapping': 2.3.0
- '@jridgewell/sourcemap-codec': 1.5.0
- '@jridgewell/trace-mapping': 0.3.25
- '@types/estree': 1.0.6
- acorn: 8.13.0
- aria-query: 5.3.2
- axobject-query: 4.1.0
- code-red: 1.0.4
- css-tree: 2.3.1
- estree-walker: 3.0.3
- is-reference: 3.0.1
- locate-character: 3.0.0
- magic-string: 0.30.12
- periscopic: 3.1.0
-
- svgo@3.3.2:
- dependencies:
- '@trysound/sax': 0.2.0
- commander: 7.2.0
- css-select: 5.1.0
- css-tree: 2.3.1
- css-what: 6.1.0
- csso: 5.0.5
- picocolors: 1.1.1
-
- tar@6.1.12:
- dependencies:
- chownr: 2.0.0
- fs-minipass: 2.1.0
- minipass: 3.3.6
- minizlib: 2.1.2
- mkdirp: 1.0.4
- yallist: 4.0.0
- optional: true
-
- three@0.166.1: {}
-
- tiny-emitter@2.1.0: {}
-
- tiny-invariant@1.3.3: {}
-
- tinydate@1.3.0: {}
-
- tlds@1.255.0: {}
-
- to-fast-properties@2.0.0: {}
-
- to-fast-properties@https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz: {}
-
- to-regex-range@5.0.1:
- dependencies:
- is-number: 7.0.0
-
- to-regex-range@https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz:
- dependencies:
- is-number: https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz
-
- totalist@1.1.0: {}
-
- tr46@0.0.3:
- optional: true
-
- trouter@2.0.1:
- dependencies:
- matchit: 1.1.0
-
- tslib@2.4.1: {}
-
- typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz: {}
-
- unicode-canonical-property-names-ecmascript@2.0.0: {}
-
- unicode-match-property-ecmascript@2.0.0:
- dependencies:
- unicode-canonical-property-names-ecmascript: 2.0.0
- unicode-property-aliases-ecmascript: 2.1.0
-
- unicode-match-property-value-ecmascript@2.1.0: {}
-
- unicode-property-aliases-ecmascript@2.1.0: {}
-
- url-join@4.0.1: {}
-
- url-regex@5.0.0:
- dependencies:
- ip-regex: 4.3.0
- tlds: 1.255.0
-
- util-deprecate@1.0.2:
- optional: true
-
- uuid@10.0.0: {}
-
- vanilla-jsoneditor@1.0.8(@lezer/common@1.2.3):
- dependencies:
- '@codemirror/autocomplete': 6.18.1(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.1)(@lezer/common@1.2.3)
- '@codemirror/commands': 6.7.0
- '@codemirror/lang-json': 6.0.1
- '@codemirror/language': 6.10.3
- '@codemirror/lint': 6.8.2
- '@codemirror/search': 6.5.6
- '@codemirror/state': 6.4.1
- '@codemirror/view': 6.34.1
- '@fortawesome/free-regular-svg-icons': 6.6.0
- '@fortawesome/free-solid-svg-icons': 6.6.0
- '@jsonquerylang/jsonquery': 3.1.1
- '@lezer/highlight': 1.2.1
- '@replit/codemirror-indentation-markers': 6.5.3(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.1)
- ajv: 8.17.1
- codemirror-wrapped-line-indent: 1.0.8(@codemirror/language@6.10.3)(@codemirror/state@6.4.1)(@codemirror/view@6.34.1)
- diff-sequences: 29.6.3
- immutable-json-patch: 6.0.1
- jmespath: 0.16.0
- json-source-map: 0.6.1
- jsonpath-plus: 10.0.1
- jsonrepair: 3.8.1
- lodash-es: 4.17.21
- memoize-one: 6.0.0
- natural-compare-lite: 1.4.0
- sass: 1.80.1
- svelte: 4.2.19
- vanilla-picker: 2.12.3
- transitivePeerDependencies:
- - '@lezer/common'
-
- vanilla-picker@2.12.3:
- dependencies:
- '@sphinxxxx/color-conversion': 2.2.2
-
- vary@1.1.2: {}
-
- vite-aliases@https://registry.npmmirror.com/vite-aliases/-/vite-aliases-0.9.7.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1):
- dependencies:
- chokidar: https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz
- comment-json: https://registry.npmmirror.com/comment-json/-/comment-json-4.2.3.tgz
- consola: https://registry.npmmirror.com/consola/-/consola-2.15.3.tgz
- fast-glob: https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz
- vite: https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1)
- transitivePeerDependencies:
- - '@types/node'
- - less
- - sass
- - stylus
- - sugarss
- - terser
-
- vite-plugin-dynamic-import@https://registry.npmmirror.com/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.2.4.tgz:
- dependencies:
- fast-glob: https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.12.tgz
-
- vite-plugin-externalize-deps@0.7.0(vite@https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1)):
- dependencies:
- vite: https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1)
-
- vite-svg-loader@5.1.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)):
- dependencies:
- svgo: 3.3.2
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- vite@https://registry.npmmirror.com/vite/-/vite-3.2.4.tgz(@types/node@https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz)(sass@1.80.1):
- dependencies:
- esbuild: https://registry.npmmirror.com/esbuild/-/esbuild-0.15.16.tgz
- postcss: https://registry.npmmirror.com/postcss/-/postcss-8.4.19.tgz
- resolve: https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz
- rollup: https://registry.npmmirror.com/rollup/-/rollup-2.79.1.tgz
- optionalDependencies:
- '@types/node': https://registry.npmmirror.com/@types/node/-/node-18.11.10.tgz
- fsevents: 2.3.2
- sass: 1.80.1
-
- vue-chartjs@5.3.1(chart.js@4.4.5)(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)):
- dependencies:
- chart.js: 4.4.5
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- vue-clipboard3@2.0.0:
- dependencies:
- clipboard: 2.0.11
-
- vue-demi@0.14.10(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)):
- dependencies:
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- vue-i18n@9.13.1(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)):
- dependencies:
- '@intlify/core-base': 9.13.1
- '@intlify/shared': 9.13.1
- '@vue/devtools-api': 6.6.4
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- vue-router@4.4.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)):
- dependencies:
- '@vue/devtools-api': 6.6.4
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- vue-template-compiler@https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz:
- dependencies:
- de-indent: https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz
- he: https://registry.npmmirror.com/he/-/he-1.2.0.tgz
-
- vue-tsc@https://registry.npmmirror.com/vue-tsc/-/vue-tsc-1.0.11.tgz(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz):
- dependencies:
- '@volar/vue-language-core': https://registry.npmmirror.com/@volar/vue-language-core/-/vue-language-core-1.0.11.tgz
- '@volar/vue-typescript': https://registry.npmmirror.com/@volar/vue-typescript/-/vue-typescript-1.0.11.tgz
- typescript: https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz
-
- vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz):
- dependencies:
- '@vue/compiler-dom': 3.5.12
- '@vue/compiler-sfc': 3.5.12
- '@vue/runtime-dom': 3.5.12
- '@vue/server-renderer': 3.5.12(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz))
- '@vue/shared': 3.5.12
- optionalDependencies:
- typescript: https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz
-
- vuex@4.1.0(vue@3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)):
- dependencies:
- '@vue/devtools-api': 6.4.5
- vue: 3.5.12(typescript@https://registry.npmmirror.com/typescript/-/typescript-4.9.3.tgz)
-
- w3c-keyname@2.2.8: {}
-
- webidl-conversions@3.0.1:
- optional: true
-
- whatwg-url@5.0.0:
- dependencies:
- tr46: 0.0.3
- webidl-conversions: 3.0.1
- optional: true
-
- which@2.0.2:
- dependencies:
- isexe: 2.0.0
-
- wide-align@1.1.5:
- dependencies:
- string-width: 4.2.3
- optional: true
-
- windows-release@5.0.1:
- dependencies:
- execa: 5.1.1
-
- wrap-ansi@7.0.0:
- dependencies:
- ansi-styles: 4.3.0
- string-width: 4.2.3
- strip-ansi: 6.0.1
-
- wrappy@1.0.2:
- optional: true
-
- y18n@5.0.8: {}
-
- yallist@4.0.0:
- optional: true
-
- yargs-parser@21.1.1: {}
-
- yargs@17.7.2:
- dependencies:
- cliui: 8.0.1
- escalade: 3.1.1
- get-caller-file: 2.0.5
- require-directory: 2.1.1
- string-width: 4.2.3
- y18n: 5.0.8
- yargs-parser: 21.1.1
diff --git a/frontend/apps/crawlab/src/main.ts b/frontend/apps/crawlab/src/main.ts
deleted file mode 100644
index c63091ef..00000000
--- a/frontend/apps/crawlab/src/main.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import 'crawlab-ui/dist/style.css';
-import 'vue';
-import {createApp} from 'crawlab-ui';
-
-(async function () {
- await createApp();
-})();
diff --git a/frontend/apps/crawlab/src/shims-vue.d.ts b/frontend/apps/crawlab/src/shims-vue.d.ts
deleted file mode 100644
index ebcd61d0..00000000
--- a/frontend/apps/crawlab/src/shims-vue.d.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-declare module '*.scss';
-declare module '*.vue' {
- import {DefineComponent} from 'vue';
- const component: DefineComponent<{}, {}, any>;
- export default component;
-}
diff --git a/frontend/apps/crawlab/tsconfig.json b/frontend/apps/crawlab/tsconfig.json
deleted file mode 100644
index 3b5835eb..00000000
--- a/frontend/apps/crawlab/tsconfig.json
+++ /dev/null
@@ -1,41 +0,0 @@
-{
- "compilerOptions": {
- "declaration": true,
- "target": "esnext",
- "module": "esnext",
- "strict": true,
- "jsx": "preserve",
- "importHelpers": true,
- "moduleResolution": "node",
- "skipLibCheck": true,
- "esModuleInterop": true,
- "allowSyntheticDefaultImports": true,
- "sourceMap": true,
- "baseUrl": ".",
- "paths": {
- "@/*": [
- "src/*"
- ]
- },
- "lib": [
- "esnext",
- "dom",
- "dom.iterable",
- "scripthost"
- ],
- "typeRoots": [
- "src/interfaces"
- ]
- },
- "include": [
- "src/shims-vue.d.ts",
- "src/**/*.ts",
- "src/**/*.tsx",
- "src/**/*.vue",
- "__test__/**/*.spec.ts"
- ],
- "exclude": [
- "node_modules",
- "src/**/*.js"
- ]
-}
diff --git a/frontend/apps/crawlab/vite.config.ts b/frontend/apps/crawlab/vite.config.ts
deleted file mode 100644
index 180ffd21..00000000
--- a/frontend/apps/crawlab/vite.config.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { resolve } from 'path';
-import { defineConfig, UserConfig } from 'vite';
-import vue from '@vitejs/plugin-vue';
-import dynamicImport from 'vite-plugin-dynamic-import';
-import { visualizer } from 'rollup-plugin-visualizer';
-
-export default defineConfig(({ mode }) => {
- const config: UserConfig = {
- build: {
- rollupOptions: {
- output: {
- manualChunks(id, meta) {
- if (id.includes('node_modules')) {
- const arr = id.toString().split('node_modules/');
- const modulePath = arr[arr.length - 1];
- return modulePath?.split('/')?.[0];
- }
- if (id.includes('three.min.js')) {
- return 'three';
- }
- },
- },
- },
- },
- optimizeDeps: {
- include: ['element-plus', 'axios'],
- },
- resolve: {
- dedupe: ['vue', 'vue-router', 'vuex', 'axios', 'element-plus'],
- alias: {
- '@': resolve(__dirname, 'src'),
- },
- extensions: ['.js', '.ts', '.jsx', '.tsx', '.json', '.vue', '.scss'],
- },
- plugins: [vue(), dynamicImport()],
- server: {
- cors: true,
- },
- };
-
- if (mode === 'analyze') {
- config.plugins.push(visualizer({ open: true, gzipSize: true }));
- }
-
- return config;
-});
diff --git a/frontend/crawlab-ui/.browserslistrc b/frontend/crawlab-ui/.browserslistrc
new file mode 100644
index 00000000..214388fe
--- /dev/null
+++ b/frontend/crawlab-ui/.browserslistrc
@@ -0,0 +1,3 @@
+> 1%
+last 2 versions
+not dead
diff --git a/frontend/crawlab-ui/.dockerignore b/frontend/crawlab-ui/.dockerignore
new file mode 100644
index 00000000..c2658d7d
--- /dev/null
+++ b/frontend/crawlab-ui/.dockerignore
@@ -0,0 +1 @@
+node_modules/
diff --git a/frontend/crawlab-ui/.editorconfig b/frontend/crawlab-ui/.editorconfig
new file mode 100644
index 00000000..ec9574ea
--- /dev/null
+++ b/frontend/crawlab-ui/.editorconfig
@@ -0,0 +1,11 @@
+root = true
+
+[*]
+tab_width = 2
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+max_line_length = 120
+trim_trailing_whitespace = true
diff --git a/frontend/crawlab-ui/.env b/frontend/crawlab-ui/.env
new file mode 100644
index 00000000..f5303165
--- /dev/null
+++ b/frontend/crawlab-ui/.env
@@ -0,0 +1,2 @@
+VITE_APP_ENV=development
+VITE_APP_API_BASE_URL=http://localhost:8000
diff --git a/frontend/.env.development b/frontend/crawlab-ui/.env.development
similarity index 67%
rename from frontend/.env.development
rename to frontend/crawlab-ui/.env.development
index dd2e38d0..7a6b5c0f 100644
--- a/frontend/.env.development
+++ b/frontend/crawlab-ui/.env.development
@@ -1 +1,2 @@
+NODE_ENV=development
VITE_APP_API_BASE_URL=http://localhost:8000
diff --git a/frontend/crawlab-ui/.env.production b/frontend/crawlab-ui/.env.production
new file mode 100644
index 00000000..46edac91
--- /dev/null
+++ b/frontend/crawlab-ui/.env.production
@@ -0,0 +1,2 @@
+VITE_APP_ENV=production
+VITE_APP_API_BASE_URL=http://localhost:8000
diff --git a/frontend/crawlab-ui/.env.test b/frontend/crawlab-ui/.env.test
new file mode 100644
index 00000000..02404873
--- /dev/null
+++ b/frontend/crawlab-ui/.env.test
@@ -0,0 +1,3 @@
+NODE_ENV=development
+VITE_APP_ENV=test
+VITE_APP_API_BASE_URL=http://localhost:8000
diff --git a/frontend/crawlab-ui/.eslintignore b/frontend/crawlab-ui/.eslintignore
new file mode 100644
index 00000000..b16742a5
--- /dev/null
+++ b/frontend/crawlab-ui/.eslintignore
@@ -0,0 +1,3 @@
+./src/i18n/**/*.ts
+*/**/*.js
+*.js
diff --git a/frontend/crawlab-ui/.gitattributes b/frontend/crawlab-ui/.gitattributes
new file mode 100644
index 00000000..142c2927
--- /dev/null
+++ b/frontend/crawlab-ui/.gitattributes
@@ -0,0 +1,5 @@
+*.ts linguist-detectable=false
+*.css linguist-detectable=false
+*.scss linguist-detectable=false
+*.js linguist-detectable=true
+*.vue linguist-detectable=true
diff --git a/frontend/crawlab-ui/.github/.git_commit_template.txt b/frontend/crawlab-ui/.github/.git_commit_template.txt
new file mode 100644
index 00000000..5040eb39
--- /dev/null
+++ b/frontend/crawlab-ui/.github/.git_commit_template.txt
@@ -0,0 +1,2 @@
+# [TYPE](SCOPE):DESCRIPTION#[ISSUE]
+# example feat(button):add type 'button' for form usage #1234
diff --git a/frontend/crawlab-ui/.github/CONTRIBUTING.en-US.md b/frontend/crawlab-ui/.github/CONTRIBUTING.en-US.md
new file mode 100644
index 00000000..0b97154c
--- /dev/null
+++ b/frontend/crawlab-ui/.github/CONTRIBUTING.en-US.md
@@ -0,0 +1,34 @@
+# Element Plus Contributing Guide
+
+Hi! Thank you for choosing Element Plus.
+
+Element Plus is a Vue 3.0 based component library for developers, designers and product managers.
+
+We are excited that you are interested in contributing to Element Plus. Before submitting your contribution though, please make sure to take a moment and read through the following guidelines.
+
+## Issue Guidelines
+
+- Issues are exclusively for bug reports, feature requests and design-related topics. Other questions may be closed directly. If any questions come up when you are using Element Plus, please hit [Discord](https://discord.link/ElementPlus) for help.
+
+- Before submitting an issue, please check if similar problems have already been issued.
+
+- Please specify which version of `Element Plus` and `Vue` you are using, and provide OS and browser information. [JSFiddle](https://jsfiddle.net/) is recommended to build a live demo so that your issue can be reproduced clearly.
+
+## Pull Request Guidelines
+
+- Fork this repository to your own account. Do not create branches here.
+
+- Commit info should be formatted as `[Component Name]: Info about commit.` (e.g. `Button: Fix xxx bug`)
+
+- **DO NOT** include files inside `lib` directory.
+
+- Make sure that running `npm run build` outputs the correct files.
+
+- Rebase before creating a PR to keep commit history clear.
+
+- Make sure PRs are created to `dev` branch instead of `master` branch.
+
+- If your PR fixes a bug, please provide a description about the related bug.
+
+- Merging a PR takes two maintainers: one approves the changes after reviewing, and then the other reviews and merges.
+
diff --git a/frontend/crawlab-ui/.github/CONTRIBUTING.zh-CN.md b/frontend/crawlab-ui/.github/CONTRIBUTING.zh-CN.md
new file mode 100644
index 00000000..be70646c
--- /dev/null
+++ b/frontend/crawlab-ui/.github/CONTRIBUTING.zh-CN.md
@@ -0,0 +1,31 @@
+# Element Plus 贡献指南
+
+Hi! 首先感谢你使用 Element Plus。
+
+Element Plus 是一套为开发者、设计师和产品经理准备的开源组件库,旨在快速搭建页面。它基于 Vue 3.0 开发,并提供了配套的设计资源,充分满足可定制化的需求。
+
+Element Plus 的成长离不开大家的支持,如果你愿意为 Element Plus 贡献代码或提供建议,请阅读以下内容。
+
+## Issue 规范
+- issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。如果你在使用时产生了疑问,请到 Slack 或 [Discord](https://discord.link/ElementPlus) 里咨询。
+
+- 在提交 issue 之前,请搜索相关内容是否已被提出。
+
+- 请说明 Element Plus 和 Vue 的版本号,并提供操作系统和浏览器信息。推荐使用 [JSFiddle](https://jsfiddle.net/) 生成在线 demo,这能够更直观地重现问题。
+
+## Pull Request 规范
+- 请先 fork 一份到自己的项目下,不要直接在仓库下建分支。
+
+- commit 信息要以`[组件名]: 描述信息` 的形式填写,例如 `Button: fix xxx bug`。
+
+- **不要提交** `lib` 里面打包的文件。
+
+- 执行 `npm run build` 后可以正确打包文件。
+
+- 提交 PR 前请 rebase,确保 commit 记录的整洁。
+
+- 确保 PR 是提交到 `dev` 分支,而不是 `master` 分支。
+
+- 如果是修复 bug,请在 PR 中给出描述信息。
+
+- 合并代码需要两名维护人员参与:一人进行 review 后 approve,另一人再次 review,通过后即可合并。
diff --git a/frontend/crawlab-ui/.github/ISSUE_TEMPLATE.md b/frontend/crawlab-ui/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 00000000..446ac677
--- /dev/null
+++ b/frontend/crawlab-ui/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,15 @@
+
diff --git a/frontend/crawlab-ui/.github/ISSUE_TEMPLATE/config.yml b/frontend/crawlab-ui/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000..77a8fa2b
--- /dev/null
+++ b/frontend/crawlab-ui/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: true
+contact_links:
+ - name: Create new issue
+ url: https://elementui.github.io/issue-generator/#/en-US?repo=element-plus
+ about: The issue which is not created via https://elementui.github.io/issue-generator will be closed immediately.
diff --git a/frontend/crawlab-ui/.github/pull_request_template.md b/frontend/crawlab-ui/.github/pull_request_template.md
new file mode 100644
index 00000000..55103bc4
--- /dev/null
+++ b/frontend/crawlab-ui/.github/pull_request_template.md
@@ -0,0 +1,5 @@
+Please make sure these boxes are checked before submitting your PR, thank you!
+
+* [ ] Make sure you follow Element's contributing guide [English](https://github.com/element-plus/element-plus/blob/master/.github/CONTRIBUTING.en-US.md) | ([中文](https://github.com/element-plus/element-plus/blob/master/.github/CONTRIBUTING.zh-CN.md) | [Español](https://github.com/element-plus/element-plus/blob/master/.github/CONTRIBUTING.es.md) | [Français](https://github.com/element-plus/element-plus/blob/master/.github/CONTRIBUTING.fr-FR.md)).
+* [ ] Make sure you are merging your commits to `dev` branch.
+* [ ] Add some descriptions and refer to relative issues for your PR.
diff --git a/frontend/crawlab-ui/.github/workflows/publish-npm.yml b/frontend/crawlab-ui/.github/workflows/publish-npm.yml
new file mode 100644
index 00000000..a1941ca3
--- /dev/null
+++ b/frontend/crawlab-ui/.github/workflows/publish-npm.yml
@@ -0,0 +1,37 @@
+name: Publish to NPM registry
+
+on:
+ pull_request:
+ branches: [ main ]
+ push:
+ branches: [ main ]
+ release:
+ types: [ created ]
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-node@v2
+ with:
+ node-version: '20.13.1'
+ registry-url: https://registry.npmjs.com/
+ - name: Get version
+ run: echo "TAG_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
+ - name: Initial setup
+ run: |
+ npm i -g pnpm@9
+ - name: Install dependencies
+ run: pnpm install
+ - name: Build
+ run: pnpm run build
+ env:
+ TAG_VERSION: ${{env.TAG_VERSION}}
+ - if: ${{ github.event_name == 'release' }}
+ name: Publish npm
+ run: npm publish --registry ${REGISTRY}
+ env:
+ NODE_AUTH_TOKEN: ${{secrets.NPM_PUBLISH_TOKEN}}
+ TAG_VERSION: ${{env.TAG_VERSION}}
+ REGISTRY: https://registry.npmjs.com/
diff --git a/frontend/.gitignore b/frontend/crawlab-ui/.gitignore
similarity index 86%
rename from frontend/.gitignore
rename to frontend/crawlab-ui/.gitignore
index 87f50dc6..0e95d095 100644
--- a/frontend/.gitignore
+++ b/frontend/crawlab-ui/.gitignore
@@ -4,7 +4,7 @@ node_modules
# local env files
-.env.local
+.env.development.local
.env.*.local
# Log files
@@ -29,6 +29,8 @@ lib/
**/dist
**/node_modules
**/package-lock.json
+**/pkg
+dist_local/
stats.html
-.turbo
\ No newline at end of file
+.turbo
diff --git a/frontend/crawlab-ui/.prettierrc b/frontend/crawlab-ui/.prettierrc
new file mode 100644
index 00000000..61d6aaae
--- /dev/null
+++ b/frontend/crawlab-ui/.prettierrc
@@ -0,0 +1,12 @@
+{
+ "semi": true,
+ "trailingComma": "es5",
+ "singleQuote": true,
+ "printWidth": 80,
+ "tabWidth": 2,
+ "useTabs": false,
+ "bracketSpacing": true,
+ "jsxBracketSameLine": false,
+ "arrowParens": "avoid",
+ "endOfLine": "lf"
+}
diff --git a/frontend/crawlab-ui/LICENSE b/frontend/crawlab-ui/LICENSE
new file mode 100644
index 00000000..e41311d2
--- /dev/null
+++ b/frontend/crawlab-ui/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2020, Crawlab Team
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/frontend/crawlab-ui/README.md b/frontend/crawlab-ui/README.md
new file mode 100644
index 00000000..760ac6bd
--- /dev/null
+++ b/frontend/crawlab-ui/README.md
@@ -0,0 +1,126 @@
+# Crawlab-UI
+
+This is the UI components and modules to support the frontend development
+for [Crawlab](https://github.com/crawlab-team/crawlab).
+
+## How to Install
+
+Use `npm` or `yarn` to install `crawlab-ui`.
+
+```
+# npm
+npm install crawlab-ui -S
+
+# or use yarn
+yarn add crawlab-ui -S
+```
+
+## How to Use
+
+It is similar to [Element-Plus](https://github.com/element-plus/element-plus), you can import components from
+Crawlab-UI. Crawlab-UI is built based on Element-Plus so that you can comfortably use it with Element-Plus.
+
+### Use Globally Installed Components
+
+Below is an example of entry file (main.ts) using Crawlab-UI globally.
+
+```ts
+import {createApp} from 'vue';
+import CrawlabUI from 'crawlab-ui';
+
+const app = createApp(App);
+app
+ .use(CrawlabUI) // install globally
+ .mount('#app');
+```
+
+Below is an example of using globally installed Crawlab-UI in a Vue 3 component.
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Use Standalone Components
+
+Below is an example of using standalone components in a Vue 3 component.
+
+```vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Use Web Application
+
+Crawlab-UI has a built-in web application for Crawlab frontend. You can simply use it to start Crawlab frontend Vue 3
+SPA.
+
+```ts
+// index.ts or index.js or other entry file
+import {createApp} from 'crawlab-ui';
+
+createApp();
+```
+
+And that's it! After you build or start serving it, you can view the Crawlab frontend web application in the browser.
+
+## Development
+
+### Add a new model
+
+Related directories:
+
+- `src/views`
+- `src/components`
+- `src/store`
+- `src/router`
+- `src/i18n`
+- `src/services`
+- `src/interfaces/i18n`
diff --git a/frontend/apps/crawlab/index.html b/frontend/crawlab-ui/index.html
similarity index 61%
rename from frontend/apps/crawlab/index.html
rename to frontend/crawlab-ui/index.html
index 8e829861..1b705548 100644
--- a/frontend/apps/crawlab/index.html
+++ b/frontend/crawlab-ui/index.html
@@ -4,16 +4,16 @@
-
+
Crawlab
-
-
+
+
-
+
diff --git a/frontend/crawlab-ui/package.json b/frontend/crawlab-ui/package.json
new file mode 100644
index 00000000..b60cc301
--- /dev/null
+++ b/frontend/crawlab-ui/package.json
@@ -0,0 +1,175 @@
+{
+ "name": "crawlab-ui",
+ "version": "0.7.0-rc7",
+ "private": false,
+ "author": {
+ "name": "Marvin Zhang",
+ "email": "tikazyq@163.com"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/crawlab-team/crawlab-ui"
+ },
+ "license": "BSD-3-Clause",
+ "type": "module",
+ "files": [
+ "dist",
+ "public",
+ "src",
+ "typings",
+ "vite.config.js"
+ ],
+ "typings": "typings/index.d.ts",
+ "main": "./dist/crawlab-ui.umd.cjs",
+ "module": "./dist/crawlab-ui.umd.cjs",
+ "exports": {
+ ".": {
+ "import": "./dist/crawlab-ui.js",
+ "require": "./dist/crawlab-ui.umd.cjs"
+ },
+ "./dist/style.css": {
+ "import": "./dist/style.css",
+ "require": "./dist/style.css"
+ }
+ },
+ "scripts": {
+ "dev": "vite build --watch --mode development",
+ "serve": "vite",
+ "serve:host": "vite --host",
+ "serve:local": "vite serve --port=8081 --mode local",
+ "serve:dist": "serve dist",
+ "gen:index": "node ./scripts/gen-index.js",
+ "gen:svg": "node ./scripts/gen-svg.js",
+ "gen:dts": "node ./scripts/gen-dts.js",
+ "gen:dts:check": "vue-tsc --noEmit --rootDir ./src",
+ "gen:interfaces": "node ./scripts/gen-interfaces.js",
+ "prebuild": "pnpm run prebuild:gen",
+ "prebuild:gen": "pnpm run gen:index && pnpm run gen:svg",
+ "postbuild": "pnpm run postbuild:gen",
+ "postbuild:gen": "pnpm run gen:interfaces",
+ "build": "pnpm run prebuild && pnpm run build:lib && pnpm run postbuild",
+ "build:local": "vite build --mode local",
+ "build:lib": "vite build",
+ "lint": "vite lint",
+ "test": "jest"
+ },
+ "peerDependencies": {
+ "@element-plus/icons-vue": "^2.3.1",
+ "@fortawesome/fontawesome-common-types": "^6.5.2",
+ "@fortawesome/fontawesome-svg-core": "^6.5.2",
+ "@fortawesome/free-brands-svg-icons": "^6.5.2",
+ "@fortawesome/free-regular-svg-icons": "^6.5.2",
+ "@fortawesome/free-solid-svg-icons": "^6.5.2",
+ "@fortawesome/vue-fontawesome": "^3.0.8",
+ "@lexical/code": "^0.16.1",
+ "@lexical/history": "^0.16.1",
+ "@lexical/html": "^0.16.1",
+ "@lexical/link": "^0.16.1",
+ "@lexical/list": "^0.16.1",
+ "@lexical/markdown": "^0.16.1",
+ "@lexical/rich-text": "^0.16.1",
+ "@lexical/selection": "^0.16.1",
+ "@lexical/table": "^0.16.1",
+ "@lexical/text": "^0.16.1",
+ "@lexical/utils": "^0.16.1",
+ "@popperjs/core": "^2.11.8",
+ "@types/getos": "^3.0.4",
+ "@types/humanize-duration": "^3.27.4",
+ "@types/javascript-time-ago": "^2.0.8",
+ "@types/lodash": "^4.17.6",
+ "@types/markdown-it": "^14.1.2",
+ "@types/md5": "^2.3.5",
+ "@types/pinyin": "^2.10.2",
+ "@types/three": "^0.166.0",
+ "@types/url-join": "^4.0.3",
+ "async-validator": "^4.2.5",
+ "atom-material-icons": "^3.0.0",
+ "axios": "^1.7.2",
+ "chalk": "^5.3.0",
+ "chart.js": "^4.4.3",
+ "chartjs-adapter-date-fns": "^3.0.0",
+ "clipboard": "^2.0.11",
+ "core-js": "^3.37.1",
+ "crawlab-vue3-dropzone": "3.0.3",
+ "cron-parser": "^4.9.0",
+ "cronstrue": "^2.50.0",
+ "dayjs": "^1.11.11",
+ "dompurify": "^3.2.4",
+ "element-plus": "^2.7.6",
+ "font-awesome": "^4.7.0",
+ "humanize-duration": "^3.32.1",
+ "javascript-time-ago": "^2.5.10",
+ "lexical": "^0.16.1",
+ "lodash": "^4.17.21",
+ "markdown-it": "^14.1.0",
+ "md5": "^2.3.0",
+ "mitt": "^3.0.1",
+ "monaco-editor": "^0.50.0",
+ "normalize.css": "^8.0.1",
+ "os-name": "^5.0.1",
+ "pinyin": "^2.10.2",
+ "point-cluster": "^3.1.8",
+ "prismjs": "^1.29.0",
+ "three": "^0.166.1",
+ "tiny-invariant": "^1.3.3",
+ "url-join": "^4.0.1",
+ "url-regex": "^5.0.0",
+ "vite-svg-loader": "^5.1.0",
+ "vue": "^3.4",
+ "vue-chartjs": "^5.3.1",
+ "vue-clipboard3": "^2.0.0",
+ "vue-i18n": "9.13.1",
+ "vue-router": "^4.4.0",
+ "vuex": "^4.1.0"
+ },
+ "devDependencies": {
+ "@lexical/code": "^0.16.1",
+ "@lexical/html": "^0.16.1",
+ "@lexical/link": "^0.16.1",
+ "@lexical/list": "^0.16.1",
+ "@lexical/markdown": "^0.16.1",
+ "@lexical/selection": "^0.16.1",
+ "@lexical/table": "^0.16.1",
+ "@types/jest": "^29.5.12",
+ "@types/node": "^20.14.9",
+ "@types/node-os-utils": "^1.3.4",
+ "@types/three": "^0.166.0",
+ "@vitejs/plugin-vue": "^5.2.1",
+ "@vitejs/plugin-vue-jsx": "^4.1.1",
+ "@vue/compiler-sfc": "^3.4.31",
+ "autoprefixer": "^10.4.19",
+ "base64-img": "^1.0.4",
+ "chartjs-adapter-date-fns": "^3.0.0",
+ "cross-env": "^7.0.3",
+ "esno": "^4.7.0",
+ "fast-glob": "^3.3.2",
+ "fs-extra": "^11.2.0",
+ "globals": "^15.8.0",
+ "highlight.js": "^11.11.1",
+ "increase-memory-limit": "^1.0.7",
+ "jest": "^29.7.0",
+ "klaw-sync": "^6.0.0",
+ "mitt": "^3.0.1",
+ "postcss": "^8.4.39",
+ "prettier": "^3.3.2",
+ "prismjs": "^1.29.0",
+ "rollup-plugin-external-globals": "^0.10.0",
+ "rollup-plugin-visualizer": "^5.12.0",
+ "sass": "^1.80.4",
+ "serve": "^14.2.3",
+ "tailwindcss": "^3.4.4",
+ "typescript": "^5.5.3",
+ "vite": "^6.0.9",
+ "vite-plugin-dynamic-import": "^1.5.0",
+ "vite-plugin-static-copy": "^2.2.0",
+ "vite-svg-loader": "^5.1.0",
+ "vue-chartjs": "^5.3.1",
+ "vue-tsc": "^2.0.24"
+ },
+ "dependencies": {
+ "@types/semver": "^7.5.8",
+ "json-editor-vue": "^0.17.0",
+ "semver": "^7.6.3",
+ "uuid": "^10.0.0"
+ }
+}
diff --git a/frontend/crawlab-ui/postcss.config.js b/frontend/crawlab-ui/postcss.config.js
new file mode 100644
index 00000000..2aa7205d
--- /dev/null
+++ b/frontend/crawlab-ui/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/frontend/crawlab-ui/public/css/font-awesome.min.css b/frontend/crawlab-ui/public/css/font-awesome.min.css
new file mode 100644
index 00000000..540440ce
--- /dev/null
+++ b/frontend/crawlab-ui/public/css/font-awesome.min.css
@@ -0,0 +1,4 @@
+/*!
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
+ */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}
diff --git a/frontend/crawlab-ui/public/css/style.css b/frontend/crawlab-ui/public/css/style.css
new file mode 100644
index 00000000..cef78f8c
--- /dev/null
+++ b/frontend/crawlab-ui/public/css/style.css
@@ -0,0 +1,179 @@
+#loading-placeholder {
+ position: fixed;
+ background: white;
+ z-index: -1;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+#loading-placeholder .title-wrapper {
+ height: 54px;
+}
+
+#loading-placeholder .title {
+ font-family: "Verdana", serif;
+ font-weight: 600;
+ font-size: 48px;
+ color: #409EFF;
+ text-align: center;
+ cursor: default;
+ letter-spacing: -5px;
+ margin: 0;
+}
+
+#loading-placeholder .title > span {
+ display: inline-block;
+ animation: change-shape 1s infinite;
+}
+
+#loading-placeholder .title > span:nth-child(1) {
+ animation-delay: calc(1s / 7 * 0 / 2);
+}
+
+#loading-placeholder .title > span:nth-child(2) {
+ animation-delay: calc(1s / 7 * 1 / 2);
+}
+
+#loading-placeholder .title > span:nth-child(3) {
+ animation-delay: calc(1s / 7 * 2 / 2);
+}
+
+#loading-placeholder .title > span:nth-child(4) {
+ animation-delay: calc(1s / 7 * 3 / 2);
+}
+
+#loading-placeholder .title > span:nth-child(5) {
+ animation-delay: calc(1s / 7 * 4 / 2);
+}
+
+#loading-placeholder .title > span:nth-child(6) {
+ animation-delay: calc(1s / 7 * 5 / 2);
+}
+
+#loading-placeholder .title > span:nth-child(7) {
+ animation-delay: calc(1s / 7 * 6 / 2);
+}
+
+#loading-placeholder .sub-title-wrapper {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 10px;
+ height: 28px;
+}
+
+#loading-placeholder .sub-title-wrapper .sub-title {
+ position: absolute;
+ font-size: 18px;
+ font-weight: 300;
+ font-family: "Verdana", serif;
+ font-style: italic;
+ color: #67C23A;
+ transform: rotate3d(1, 0, 0, 90deg);
+ animation: flip 20s infinite;
+ /*color: #E6A23C;*/
+ /*color: #F56C6C;*/
+}
+
+#loading-placeholder .sub-title-wrapper > .sub-title:nth-child(1) {
+ animation-delay: calc(20s / 4 * 0);
+}
+
+#loading-placeholder .sub-title-wrapper > .sub-title:nth-child(2) {
+ animation-delay: calc(20s / 4 * 1);
+}
+
+#loading-placeholder .sub-title-wrapper > .sub-title:nth-child(3) {
+ animation-delay: calc(20s / 4 * 2);
+}
+
+#loading-placeholder .sub-title-wrapper > .sub-title:nth-child(4) {
+ animation-delay: calc(20s / 4 * 3);
+}
+
+#loading-placeholder .loading-text {
+ text-align: center;
+ font-weight: bolder;
+ font-family: "Verdana", serif;
+ font-style: italic;
+ color: #889aa4;
+ font-size: 14px;
+ animation: blink-loading 2s ease-in infinite;
+}
+
+@keyframes blink-loading {
+ 0% {
+ opacity: 100%;
+ }
+
+ 50% {
+ opacity: 50%;
+ }
+
+ 100% {
+ opacity: 100%;
+ }
+}
+
+@keyframes change-shape {
+ 0% {
+ transform: scale(1);
+ }
+
+ 25% {
+ transform: scale(1.2);
+ }
+
+ 50% {
+ transform: scale(1);
+ }
+
+ 100% {
+ transform: scale(1);
+ }
+}
+
+@keyframes flip {
+ 0% {
+ transform: rotate3d(1, 0, 0, 90deg);
+ }
+
+ 2% {
+ transform: rotate3d(1, 0, 0, 90deg);
+ }
+
+ 7% {
+ transform: rotate3d(1, 0, 0, 0);
+ }
+
+ 23% {
+ transform: rotate3d(1, 0, 0, 0);
+ }
+
+ 27% {
+ transform: rotate3d(1, 0, 0, 90deg);
+ }
+
+ 50% {
+ transform: rotate3d(1, 0, 0, 90deg);
+ }
+
+ 100% {
+ transform: rotate3d(1, 0, 0, 90deg);
+ }
+}
+
+#login-canvas {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ z-index: -1;
+}
diff --git a/frontend/crawlab-ui/public/favicon.ico b/frontend/crawlab-ui/public/favicon.ico
new file mode 100644
index 00000000..3fb253bd
Binary files /dev/null and b/frontend/crawlab-ui/public/favicon.ico differ
diff --git a/frontend/crawlab-ui/public/fonts/FontAwesome.otf b/frontend/crawlab-ui/public/fonts/FontAwesome.otf
new file mode 100644
index 00000000..401ec0f3
Binary files /dev/null and b/frontend/crawlab-ui/public/fonts/FontAwesome.otf differ
diff --git a/frontend/crawlab-ui/public/fonts/fontawesome-webfont.eot b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.eot
new file mode 100644
index 00000000..e9f60ca9
Binary files /dev/null and b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.eot differ
diff --git a/frontend/crawlab-ui/public/fonts/fontawesome-webfont.svg b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.svg
new file mode 100644
index 00000000..855c845e
--- /dev/null
+++ b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.svg
@@ -0,0 +1,2671 @@
+
+
+
diff --git a/frontend/crawlab-ui/public/fonts/fontawesome-webfont.ttf b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.ttf
new file mode 100644
index 00000000..35acda2f
Binary files /dev/null and b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.ttf differ
diff --git a/frontend/crawlab-ui/public/fonts/fontawesome-webfont.woff b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.woff
new file mode 100644
index 00000000..400014a4
Binary files /dev/null and b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.woff differ
diff --git a/frontend/crawlab-ui/public/fonts/fontawesome-webfont.woff2 b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.woff2
new file mode 100644
index 00000000..4d13fc60
Binary files /dev/null and b/frontend/crawlab-ui/public/fonts/fontawesome-webfont.woff2 differ
diff --git a/frontend/crawlab-ui/public/js/login-canvas-three.js b/frontend/crawlab-ui/public/js/login-canvas-three.js
new file mode 100644
index 00000000..8577ea07
--- /dev/null
+++ b/frontend/crawlab-ui/public/js/login-canvas-three.js
@@ -0,0 +1,281 @@
+const THREE = window.THREE;
+
+class ThreeJSApp {
+ constructor() {
+ this.frameId = null;
+ this.fps = 60;
+ this.fpsInterval = 1000 / this.fps;
+ this.then = Date.now();
+ this.cameraRange = 3;
+ this.scene = new THREE.Scene();
+ this.renderer = new THREE.WebGLRenderer({ antialias: true });
+ this.camera = new THREE.PerspectiveCamera(
+ 35,
+ window.innerWidth / window.innerHeight,
+ 1,
+ 500
+ );
+
+ this.sceneGroup = new THREE.Object3D();
+ this.particularGroup = new THREE.Object3D();
+ this.modularGroup = new THREE.Object3D();
+
+ this.mouse = new THREE.Vector2();
+ this.INTERSECTED = null;
+ this.cameraValue = false;
+ this.uSpeed = 0.1;
+
+ this.initRenderer();
+ this.initScene();
+ this.initCamera();
+ this.initLights();
+ this.initObjects();
+ this.initRaycaster();
+ this.animate();
+ }
+
+ initRenderer() {
+ this.renderer.setSize(window.innerWidth, window.innerHeight);
+ this.renderer.shadowMap.enabled = false;
+ this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
+ let container = document.querySelector('#login-canvas');
+ container.appendChild(this.renderer.domElement);
+ }
+
+ initScene() {
+ const setColor = 0x000000;
+ this.scene.background = new THREE.Color(setColor);
+ this.scene.fog = new THREE.Fog(setColor, 2.5, 3.5);
+ this.scene.add(this.sceneGroup);
+ this.scene.add(this.modularGroup);
+ }
+
+ initCamera() {
+ this.camera.position.set(0, 0, this.cameraRange);
+ window.addEventListener('resize', () => this.onWindowResize(), false);
+ }
+
+ initLights() {
+ const light = new THREE.SpotLight(0xffffff, 1);
+ light.position.set(5, 5, 2);
+ light.castShadow = true;
+ light.shadow.mapSize.width = 10000;
+ light.shadow.mapSize.height = 10000;
+ light.penumbra = 0.5;
+
+ const lightBack = new THREE.PointLight(0x409eff, 1);
+ lightBack.position.set(0, -3, -1);
+
+ const rectLight = new THREE.RectAreaLight(0x409eff, 50, 1, 1);
+ rectLight.position.set(0, 0, 1);
+ rectLight.lookAt(0, 0, 0);
+
+ this.scene.add(light);
+ this.scene.add(lightBack);
+ this.scene.add(rectLight);
+ }
+
+ generateParticle(num, amp = 2) {
+ const material = new THREE.MeshPhysicalMaterial({
+ color: 0xffffff,
+ side: THREE.DoubleSide,
+ });
+ const geometry = new THREE.CircleGeometry(0.2, 5);
+
+ for (let i = 1; i < num; i++) {
+ const pScale = 0.001 + Math.abs(this.mathRandom(0.03));
+ const particular = new THREE.Mesh(geometry, material);
+ particular.position.set(
+ this.mathRandom(amp),
+ this.mathRandom(amp),
+ this.mathRandom(amp)
+ );
+ particular.rotation.set(
+ this.mathRandom(),
+ this.mathRandom(),
+ this.mathRandom()
+ );
+ particular.scale.set(pScale, pScale, pScale);
+ particular.speedValue = this.mathRandom(1);
+ this.particularGroup.add(particular);
+ }
+ }
+
+ mathRandom(num = 1) {
+ return -Math.random() * num + Math.random() * num;
+ }
+
+ initObjects() {
+ this.generateParticle(500, 2);
+ this.sceneGroup.add(this.particularGroup);
+
+ for (let i = 0; i < 10; i++) {
+ const geometry = new THREE.IcosahedronGeometry(1);
+ const material = new THREE.MeshStandardMaterial({
+ flatShading: THREE.SmoothShading,
+ color: 0x111111,
+ transparent: false,
+ opacity: 1,
+ wireframe: false,
+ });
+ const cube = new THREE.Mesh(geometry, material);
+ cube.speedRotation = Math.random() * 0.2;
+ cube.positionX = this.mathRandom();
+ cube.positionY = this.mathRandom();
+ cube.positionZ = this.mathRandom();
+ cube.castShadow = true;
+ cube.receiveShadow = true;
+
+ const newScaleValue = this.mathRandom(0.1);
+ cube.scale.set(newScaleValue, newScaleValue, newScaleValue);
+ cube.rotation.set(
+ this.mathRandom((180 * Math.PI) / 180),
+ this.mathRandom((180 * Math.PI) / 180),
+ this.mathRandom((180 * Math.PI) / 180)
+ );
+ cube.position.set(cube.positionX, cube.positionY, cube.positionZ);
+ this.modularGroup.add(cube);
+ }
+ }
+
+ initRaycaster() {
+ this.raycaster = new THREE.Raycaster();
+ }
+
+ addEventListeners() {
+ window.addEventListener(
+ 'mousedown',
+ event => this.onMouseDown(event),
+ false
+ );
+ window.addEventListener('mouseup', event => this.onMouseUp(event), false);
+ window.addEventListener(
+ 'mousemove',
+ event => this.onMouseMove(event),
+ false
+ );
+ }
+
+ onWindowResize() {
+ this.camera.aspect = window.innerWidth / window.innerHeight;
+ this.camera.updateProjectionMatrix();
+ this.renderer.setSize(window.innerWidth, window.innerHeight);
+ }
+
+ onMouseMove(event) {
+ event.preventDefault();
+ this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
+ this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
+ }
+
+ onMouseDown(event) {
+ event.preventDefault();
+ this.onMouseMove(event);
+ this.raycaster.setFromCamera(this.mouse, this.camera);
+ const intersects = this.raycaster.intersectObjects(
+ this.modularGroup.children
+ );
+ if (intersects.length > 0) {
+ this.cameraValue = false;
+ if (this.INTERSECTED !== intersects[0].object) {
+ if (this.INTERSECTED) {
+ this.INTERSECTED.material.emissive.setHex(
+ this.INTERSECTED.currentHex
+ );
+ }
+ this.INTERSECTED = intersects[0].object;
+ this.INTERSECTED.currentHex =
+ this.INTERSECTED.material.emissive.getHex();
+ this.INTERSECTED.material.emissive.setHex(0xffff00);
+
+ TweenMax.to(this.camera.position, 1, {
+ x: this.INTERSECTED.position.x,
+ y: this.INTERSECTED.position.y,
+ z: this.INTERSECTED.position.z + 3,
+ ease: Power2.easeInOut,
+ });
+ } else {
+ if (this.INTERSECTED) {
+ this.INTERSECTED.material.emissive.setHex(
+ this.INTERSECTED.currentHex
+ );
+ }
+ this.INTERSECTED = null;
+ }
+ }
+ }
+
+ onMouseUp(event) {
+ // Add your onMouseUp logic if needed
+ }
+
+ animate() {
+ const time = performance.now() * 0.0003;
+ this.frameId = requestAnimationFrame(() => this.animate());
+
+ for (const object of this.particularGroup.children) {
+ object.rotation.x += object.speedValue / 10;
+ object.rotation.y += object.speedValue / 10;
+ object.rotation.z += object.speedValue / 10;
+ }
+
+ const ratio = 0.1;
+
+ for (const cube of this.modularGroup.children) {
+ cube.rotation.x += 0.008 * ratio;
+ cube.rotation.y += 0.005 * ratio;
+ cube.rotation.z += 0.003 * ratio;
+ cube.position.x = Math.sin(time * cube.positionZ) * cube.positionY;
+ cube.position.y = Math.cos(time * cube.positionX) * cube.positionZ;
+ cube.position.z = Math.sin(time * cube.positionY) * cube.positionX;
+ }
+
+ this.particularGroup.rotation.y += 0.005 * ratio;
+ this.modularGroup.rotation.y -=
+ (this.mouse.x * 4 + this.modularGroup.rotation.y) * this.uSpeed * ratio;
+ this.modularGroup.rotation.x -=
+ (-this.mouse.y * 4 + this.modularGroup.rotation.x) * this.uSpeed * ratio;
+ this.camera.lookAt(this.scene.position);
+
+ // this.logoGroup.rotation.x += 0.01;
+ // this.logoGroup.rotation.y += 0.01;
+
+ const now = Date.now();
+ const elapsed = now - this.then;
+ if (elapsed > this.fpsInterval) {
+ this.then = now - (elapsed % this.fpsInterval);
+ this.renderer.render(this.scene, this.camera);
+ }
+ }
+
+ stopAnimation() {
+ if (this.frameId) {
+ cancelAnimationFrame(this.frameId); // 使用frameId来停止动画
+ this.frameId = null; // 清空frameId
+ }
+ }
+
+ dispose() {
+ this.stopAnimation(); // 停止动画
+ this.scene.traverse(object => {
+ if (object.material) {
+ object.material.dispose();
+ }
+ if (object.geometry) {
+ object.geometry.dispose();
+ }
+ });
+ this.renderer.dispose();
+ this.renderer.domElement.remove();
+ }
+}
+
+window.initCanvas = function () {
+ window.threeJSApp = new ThreeJSApp();
+};
+window.resetCanvas = function () {
+ if (window.threeJSApp) {
+ window.threeJSApp.dispose();
+ window.threeJSApp = null;
+ }
+};
diff --git a/frontend/crawlab-ui/public/js/login-canvas.js b/frontend/crawlab-ui/public/js/login-canvas.js
new file mode 100644
index 00000000..cee41edf
--- /dev/null
+++ b/frontend/crawlab-ui/public/js/login-canvas.js
@@ -0,0 +1,292 @@
+function initCanvas() {
+ let canvas,
+ ctx,
+ circ,
+ nodes,
+ mouse,
+ SENSITIVITY,
+ SIBLINGS_LIMIT,
+ DENSITY,
+ NODES_QTY,
+ ANCHOR_LENGTH,
+ MOUSE_RADIUS,
+ TURBULENCE,
+ MOUSE_MOVING_TURBULENCE,
+ MOUSE_ANGLE_TURBULENCE,
+ MOUSE_MOVING_RADIUS,
+ BASE_BRIGHTNESS,
+ RADIUS_DEGRADE,
+ SAMPLE_SIZE;
+
+ let handle;
+
+ // how close next node must be to activate connection (in px)
+ // shorter distance == better connection (line width)
+ SENSITIVITY = 200;
+ // note that siblings limit is not 'accurate' as the node can actually have more connections than this value that's because the node accepts sibling nodes with no regard to their current connections this is acceptable because potential fix would not result in significant visual difference
+ // more siblings == bigger node
+ SIBLINGS_LIMIT = 10;
+ // default node margin
+ DENSITY = 100;
+ // total number of nodes used (incremented after creation)
+ NODES_QTY = 0;
+ // avoid nodes spreading
+ ANCHOR_LENGTH = 100;
+ // highlight radius
+ MOUSE_RADIUS = 200;
+ // turbulence of randomness
+ TURBULENCE = 3;
+ // turbulence of mouse moving
+ MOUSE_MOVING_TURBULENCE = 50;
+ // turbulence of mouse moving angle
+ MOUSE_ANGLE_TURBULENCE = 0.002;
+ // moving radius of mouse
+ MOUSE_MOVING_RADIUS = 600;
+ // base brightness
+ BASE_BRIGHTNESS = 0.12;
+ // radius degrade
+ RADIUS_DEGRADE = 0.4;
+ // sample size
+ SAMPLE_SIZE = 0.5;
+
+ circ = 2 * Math.PI;
+ nodes = [];
+
+ canvas = document.querySelector('#canvas');
+ if (!canvas) return;
+ resizeWindow();
+ ctx = canvas.getContext('2d');
+ if (!ctx) {
+ alert("Ooops! Your browser does not support canvas :'(");
+ }
+
+ function Mouse(x, y) {
+ this.anchorX = x;
+ this.anchorY = y;
+ this.x = x;
+ this.y = y - MOUSE_RADIUS / 2;
+ this.angle = 0;
+ }
+
+ Mouse.prototype.computePosition = function () {
+ // this.x = this.anchorX + MOUSE_MOVING_RADIUS / 2 * Math.sin(this.angle)
+ // this.y = this.anchorY - MOUSE_MOVING_RADIUS / 2 * Math.cos(this.angle)
+ };
+
+ Mouse.prototype.move = function () {
+ let vx = Math.random() * MOUSE_MOVING_TURBULENCE;
+ let vy = Math.random() * MOUSE_MOVING_TURBULENCE;
+ if (
+ this.x + vx + MOUSE_RADIUS / 2 > window.innerWidth ||
+ this.x + vx - MOUSE_RADIUS / 2 < 0
+ ) {
+ vx = -vx;
+ }
+ if (
+ this.y + vy + MOUSE_RADIUS / 2 > window.innerHeight ||
+ this.y + vy - MOUSE_RADIUS / 2 < 0
+ ) {
+ vy = -vy;
+ }
+ this.x += vx;
+ this.y += vy;
+ // this.angle += Math.random() * MOUSE_ANGLE_TURBULENCE * 2 * Math.PI
+ // this.angle -= Math.floor(this.angle / (2 * Math.PI)) * 2 * Math.PI
+ // this.computePosition()
+ };
+
+ function Node(x, y) {
+ this.anchorX = x;
+ this.anchorY = y;
+ this.x = Math.random() * (x - (x - ANCHOR_LENGTH)) + (x - ANCHOR_LENGTH);
+ this.y = Math.random() * (y - (y - ANCHOR_LENGTH)) + (y - ANCHOR_LENGTH);
+ this.vx = Math.random() * TURBULENCE - 1;
+ this.vy = Math.random() * TURBULENCE - 1;
+ this.energy = Math.random() * 100;
+ this.radius = Math.random();
+ this.siblings = [];
+ this.brightness = 0;
+ }
+
+ Node.prototype.drawNode = function () {
+ let color = 'rgba(64, 156, 255, ' + this.brightness + ')';
+ ctx.beginPath();
+ ctx.arc(
+ this.x,
+ this.y,
+ 2 * this.radius + (2 * this.siblings.length) / SIBLINGS_LIMIT / 1.5,
+ 0,
+ circ
+ );
+ ctx.fillStyle = color;
+ ctx.fill();
+ };
+
+ Node.prototype.drawConnections = function () {
+ for (let i = 0; i < this.siblings.length; i++) {
+ let color = 'rgba(64, 156, 255, ' + this.brightness + ')';
+ ctx.beginPath();
+ ctx.moveTo(this.x, this.y);
+ ctx.lineTo(this.siblings[i].x, this.siblings[i].y);
+ ctx.lineWidth = 1 - calcDistance(this, this.siblings[i]) / SENSITIVITY;
+ ctx.strokeStyle = color;
+ ctx.stroke();
+ }
+ };
+
+ Node.prototype.moveNode = function () {
+ this.energy -= 2;
+ if (this.energy < 1) {
+ this.energy = Math.random() * 100;
+ if (this.x - this.anchorX < -ANCHOR_LENGTH) {
+ this.vx = Math.random() * TURBULENCE;
+ } else if (this.x - this.anchorX > ANCHOR_LENGTH) {
+ this.vx = Math.random() * -TURBULENCE;
+ } else {
+ this.vx = Math.random() * 2 * TURBULENCE - TURBULENCE;
+ }
+ if (this.y - this.anchorY < -ANCHOR_LENGTH) {
+ this.vy = Math.random() * TURBULENCE;
+ } else if (this.y - this.anchorY > ANCHOR_LENGTH) {
+ this.vy = Math.random() * -TURBULENCE;
+ } else {
+ this.vy = Math.random() * 2 * TURBULENCE - TURBULENCE;
+ }
+ }
+ this.x += (this.vx * this.energy) / 100;
+ this.y += (this.vy * this.energy) / 100;
+ };
+
+ function Handle() {
+ this.isStopped = false;
+ }
+
+ Handle.prototype.stop = function () {
+ this.isStopped = true;
+ };
+
+ function initNodes() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ nodes = [];
+ for (let i = DENSITY; i < canvas.width; i += DENSITY) {
+ for (let j = DENSITY; j < canvas.height; j += DENSITY) {
+ nodes.push(new Node(i, j));
+ NODES_QTY++;
+ }
+ }
+ }
+
+ function initMouse() {
+ mouse = new Mouse(canvas.width / 2, canvas.height / 2);
+ }
+
+ function initHandle() {
+ handle = new Handle();
+ }
+
+ function calcDistance(node1, node2) {
+ return Math.sqrt(
+ Math.pow(node1.x - node2.x, 2) + Math.pow(node1.y - node2.y, 2)
+ );
+ }
+
+ function findSiblings() {
+ let node1, node2, distance;
+ for (let i = 0; i < NODES_QTY; i++) {
+ node1 = nodes[i];
+ node1.siblings = [];
+ for (let j = 0; j < NODES_QTY; j++) {
+ node2 = nodes[j];
+ if (node1 !== node2) {
+ distance = calcDistance(node1, node2);
+ if (distance < SENSITIVITY) {
+ if (node1.siblings.length < SIBLINGS_LIMIT) {
+ node1.siblings.push(node2);
+ } else {
+ let node_sibling_distance = 0;
+ let max_distance = 0;
+ let s;
+ for (let k = 0; k < SIBLINGS_LIMIT; k++) {
+ node_sibling_distance = calcDistance(node1, node1.siblings[k]);
+ if (node_sibling_distance > max_distance) {
+ max_distance = node_sibling_distance;
+ s = k;
+ }
+ }
+ if (distance < max_distance) {
+ node1.siblings.splice(s, 1);
+ node1.siblings.push(node2);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ function redrawScene() {
+ if (handle && handle.isStopped) {
+ return;
+ }
+ resizeWindow();
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ findSiblings();
+ let i, node, distance;
+ for (i = 0; i < NODES_QTY; i++) {
+ node = nodes[i];
+ distance = calcDistance(
+ {
+ x: mouse.x,
+ y: mouse.y,
+ },
+ node
+ );
+ node.brightness =
+ (1 - Math.log((distance / MOUSE_RADIUS) * RADIUS_DEGRADE)) *
+ BASE_BRIGHTNESS;
+ }
+ for (i = 0; i < NODES_QTY; i++) {
+ node = nodes[i];
+ if (node.brightness) {
+ node.drawNode();
+ node.drawConnections();
+ }
+ node.moveNode();
+ }
+ // mouse.move()
+ setTimeout(() => {
+ requestAnimationFrame(redrawScene);
+ }, 50);
+ }
+
+ function initHandlers() {
+ document.addEventListener('resize', resizeWindow, { passive: true });
+ // canvas.addEventListener('mousemove', mousemoveHandler, false)
+ }
+
+ function resizeWindow() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ }
+
+ function mousemoveHandler(e) {
+ mouse.x = e.clientX;
+ mouse.y = e.clientY;
+ }
+
+ function init() {
+ initHandlers();
+ initNodes();
+ initMouse();
+ initHandle();
+ redrawScene();
+ }
+
+ function reset() {
+ handle.isStopped = true;
+ }
+
+ init();
+
+ window.disposeCanvas = reset;
+}
diff --git a/frontend/crawlab-ui/public/svg/logo-main.svg b/frontend/crawlab-ui/public/svg/logo-main.svg
new file mode 100644
index 00000000..aed3ae3a
--- /dev/null
+++ b/frontend/crawlab-ui/public/svg/logo-main.svg
@@ -0,0 +1,161 @@
+
+
+
diff --git a/frontend/crawlab-ui/public/three/loaders/SVGLoader.js b/frontend/crawlab-ui/public/three/loaders/SVGLoader.js
new file mode 100644
index 00000000..12a547e6
--- /dev/null
+++ b/frontend/crawlab-ui/public/three/loaders/SVGLoader.js
@@ -0,0 +1,2619 @@
+( function () {
+
+ class SVGLoader extends THREE.Loader {
+
+ constructor( manager ) {
+
+ super( manager ); // Default dots per inch
+
+ this.defaultDPI = 90; // Accepted units: 'mm', 'cm', 'in', 'pt', 'pc', 'px'
+
+ this.defaultUnit = 'px';
+
+ }
+
+ load( url, onLoad, onProgress, onError ) {
+
+ const scope = this;
+ const loader = new THREE.FileLoader( scope.manager );
+ loader.setPath( scope.path );
+ loader.setRequestHeader( scope.requestHeader );
+ loader.setWithCredentials( scope.withCredentials );
+ loader.load( url, function ( text ) {
+
+ try {
+
+ onLoad( scope.parse( text ) );
+
+ } catch ( e ) {
+
+ if ( onError ) {
+
+ onError( e );
+
+ } else {
+
+ console.error( e );
+
+ }
+
+ scope.manager.itemError( url );
+
+ }
+
+ }, onProgress, onError );
+
+ }
+
+ parse( text ) {
+
+ const scope = this;
+
+ function parseNode( node, style ) {
+
+ if ( node.nodeType !== 1 ) return;
+ const transform = getNodeTransform( node );
+ let traverseChildNodes = true;
+ let path = null;
+
+ switch ( node.nodeName ) {
+
+ case 'svg':
+ break;
+
+ case 'style':
+ parseCSSStylesheet( node );
+ break;
+
+ case 'g':
+ style = parseStyle( node, style );
+ break;
+
+ case 'path':
+ style = parseStyle( node, style );
+ if ( node.hasAttribute( 'd' ) ) path = parsePathNode( node );
+ break;
+
+ case 'rect':
+ style = parseStyle( node, style );
+ path = parseRectNode( node );
+ break;
+
+ case 'polygon':
+ style = parseStyle( node, style );
+ path = parsePolygonNode( node );
+ break;
+
+ case 'polyline':
+ style = parseStyle( node, style );
+ path = parsePolylineNode( node );
+ break;
+
+ case 'circle':
+ style = parseStyle( node, style );
+ path = parseCircleNode( node );
+ break;
+
+ case 'ellipse':
+ style = parseStyle( node, style );
+ path = parseEllipseNode( node );
+ break;
+
+ case 'line':
+ style = parseStyle( node, style );
+ path = parseLineNode( node );
+ break;
+
+ case 'defs':
+ traverseChildNodes = false;
+ break;
+
+ case 'use':
+ style = parseStyle( node, style );
+ const usedNodeId = node.href.baseVal.substring( 1 );
+ const usedNode = node.viewportElement.getElementById( usedNodeId );
+
+ if ( usedNode ) {
+
+ parseNode( usedNode, style );
+
+ } else {
+
+ console.warn( 'SVGLoader: \'use node\' references non-existent node id: ' + usedNodeId );
+
+ }
+
+ break;
+
+ default: // console.log( node );
+
+ }
+
+ if ( path ) {
+
+ if ( style.fill !== undefined && style.fill !== 'none' ) {
+
+ path.color.setStyle( style.fill );
+
+ }
+
+ transformPath( path, currentTransform );
+ paths.push( path );
+ path.userData = {
+ node: node,
+ style: style
+ };
+
+ }
+
+ if ( traverseChildNodes ) {
+
+ const nodes = node.childNodes;
+
+ for ( let i = 0; i < nodes.length; i ++ ) {
+
+ parseNode( nodes[ i ], style );
+
+ }
+
+ }
+
+ if ( transform ) {
+
+ transformStack.pop();
+
+ if ( transformStack.length > 0 ) {
+
+ currentTransform.copy( transformStack[ transformStack.length - 1 ] );
+
+ } else {
+
+ currentTransform.identity();
+
+ }
+
+ }
+
+ }
+
+ function parsePathNode( node ) {
+
+ const path = new THREE.ShapePath();
+ const point = new THREE.Vector2();
+ const control = new THREE.Vector2();
+ const firstPoint = new THREE.Vector2();
+ let isFirstPoint = true;
+ let doSetFirstPoint = false;
+ const d = node.getAttribute( 'd' ); // console.log( d );
+
+ const commands = d.match( /[a-df-z][^a-df-z]*/ig );
+
+ for ( let i = 0, l = commands.length; i < l; i ++ ) {
+
+ const command = commands[ i ];
+ const type = command.charAt( 0 );
+ const data = command.substr( 1 ).trim();
+
+ if ( isFirstPoint === true ) {
+
+ doSetFirstPoint = true;
+ isFirstPoint = false;
+
+ }
+
+ let numbers;
+
+ switch ( type ) {
+
+ case 'M':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 2 ) {
+
+ point.x = numbers[ j + 0 ];
+ point.y = numbers[ j + 1 ];
+ control.x = point.x;
+ control.y = point.y;
+
+ if ( j === 0 ) {
+
+ path.moveTo( point.x, point.y );
+
+ } else {
+
+ path.lineTo( point.x, point.y );
+
+ }
+
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'H':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j ++ ) {
+
+ point.x = numbers[ j ];
+ control.x = point.x;
+ control.y = point.y;
+ path.lineTo( point.x, point.y );
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'V':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j ++ ) {
+
+ point.y = numbers[ j ];
+ control.x = point.x;
+ control.y = point.y;
+ path.lineTo( point.x, point.y );
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'L':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 2 ) {
+
+ point.x = numbers[ j + 0 ];
+ point.y = numbers[ j + 1 ];
+ control.x = point.x;
+ control.y = point.y;
+ path.lineTo( point.x, point.y );
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'C':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 6 ) {
+
+ path.bezierCurveTo( numbers[ j + 0 ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ], numbers[ j + 4 ], numbers[ j + 5 ] );
+ control.x = numbers[ j + 2 ];
+ control.y = numbers[ j + 3 ];
+ point.x = numbers[ j + 4 ];
+ point.y = numbers[ j + 5 ];
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'S':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 4 ) {
+
+ path.bezierCurveTo( getReflection( point.x, control.x ), getReflection( point.y, control.y ), numbers[ j + 0 ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ] );
+ control.x = numbers[ j + 0 ];
+ control.y = numbers[ j + 1 ];
+ point.x = numbers[ j + 2 ];
+ point.y = numbers[ j + 3 ];
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'Q':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 4 ) {
+
+ path.quadraticCurveTo( numbers[ j + 0 ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ] );
+ control.x = numbers[ j + 0 ];
+ control.y = numbers[ j + 1 ];
+ point.x = numbers[ j + 2 ];
+ point.y = numbers[ j + 3 ];
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'T':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 2 ) {
+
+ const rx = getReflection( point.x, control.x );
+ const ry = getReflection( point.y, control.y );
+ path.quadraticCurveTo( rx, ry, numbers[ j + 0 ], numbers[ j + 1 ] );
+ control.x = rx;
+ control.y = ry;
+ point.x = numbers[ j + 0 ];
+ point.y = numbers[ j + 1 ];
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'A':
+ numbers = parseFloats( data, [ 3, 4 ], 7 );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 7 ) {
+
+ // skip command if start point == end point
+ if ( numbers[ j + 5 ] == point.x && numbers[ j + 6 ] == point.y ) continue;
+ const start = point.clone();
+ point.x = numbers[ j + 5 ];
+ point.y = numbers[ j + 6 ];
+ control.x = point.x;
+ control.y = point.y;
+ parseArcCommand( path, numbers[ j ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ], numbers[ j + 4 ], start, point );
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'm':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 2 ) {
+
+ point.x += numbers[ j + 0 ];
+ point.y += numbers[ j + 1 ];
+ control.x = point.x;
+ control.y = point.y;
+
+ if ( j === 0 ) {
+
+ path.moveTo( point.x, point.y );
+
+ } else {
+
+ path.lineTo( point.x, point.y );
+
+ }
+
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'h':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j ++ ) {
+
+ point.x += numbers[ j ];
+ control.x = point.x;
+ control.y = point.y;
+ path.lineTo( point.x, point.y );
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'v':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j ++ ) {
+
+ point.y += numbers[ j ];
+ control.x = point.x;
+ control.y = point.y;
+ path.lineTo( point.x, point.y );
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'l':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 2 ) {
+
+ point.x += numbers[ j + 0 ];
+ point.y += numbers[ j + 1 ];
+ control.x = point.x;
+ control.y = point.y;
+ path.lineTo( point.x, point.y );
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'c':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 6 ) {
+
+ path.bezierCurveTo( point.x + numbers[ j + 0 ], point.y + numbers[ j + 1 ], point.x + numbers[ j + 2 ], point.y + numbers[ j + 3 ], point.x + numbers[ j + 4 ], point.y + numbers[ j + 5 ] );
+ control.x = point.x + numbers[ j + 2 ];
+ control.y = point.y + numbers[ j + 3 ];
+ point.x += numbers[ j + 4 ];
+ point.y += numbers[ j + 5 ];
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 's':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 4 ) {
+
+ path.bezierCurveTo( getReflection( point.x, control.x ), getReflection( point.y, control.y ), point.x + numbers[ j + 0 ], point.y + numbers[ j + 1 ], point.x + numbers[ j + 2 ], point.y + numbers[ j + 3 ] );
+ control.x = point.x + numbers[ j + 0 ];
+ control.y = point.y + numbers[ j + 1 ];
+ point.x += numbers[ j + 2 ];
+ point.y += numbers[ j + 3 ];
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'q':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 4 ) {
+
+ path.quadraticCurveTo( point.x + numbers[ j + 0 ], point.y + numbers[ j + 1 ], point.x + numbers[ j + 2 ], point.y + numbers[ j + 3 ] );
+ control.x = point.x + numbers[ j + 0 ];
+ control.y = point.y + numbers[ j + 1 ];
+ point.x += numbers[ j + 2 ];
+ point.y += numbers[ j + 3 ];
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 't':
+ numbers = parseFloats( data );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 2 ) {
+
+ const rx = getReflection( point.x, control.x );
+ const ry = getReflection( point.y, control.y );
+ path.quadraticCurveTo( rx, ry, point.x + numbers[ j + 0 ], point.y + numbers[ j + 1 ] );
+ control.x = rx;
+ control.y = ry;
+ point.x = point.x + numbers[ j + 0 ];
+ point.y = point.y + numbers[ j + 1 ];
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'a':
+ numbers = parseFloats( data, [ 3, 4 ], 7 );
+
+ for ( let j = 0, jl = numbers.length; j < jl; j += 7 ) {
+
+ // skip command if no displacement
+ if ( numbers[ j + 5 ] == 0 && numbers[ j + 6 ] == 0 ) continue;
+ const start = point.clone();
+ point.x += numbers[ j + 5 ];
+ point.y += numbers[ j + 6 ];
+ control.x = point.x;
+ control.y = point.y;
+ parseArcCommand( path, numbers[ j ], numbers[ j + 1 ], numbers[ j + 2 ], numbers[ j + 3 ], numbers[ j + 4 ], start, point );
+ if ( j === 0 && doSetFirstPoint === true ) firstPoint.copy( point );
+
+ }
+
+ break;
+
+ case 'Z':
+ case 'z':
+ path.currentPath.autoClose = true;
+
+ if ( path.currentPath.curves.length > 0 ) {
+
+ // Reset point to beginning of THREE.Path
+ point.copy( firstPoint );
+ path.currentPath.currentPoint.copy( point );
+ isFirstPoint = true;
+
+ }
+
+ break;
+
+ default:
+ console.warn( command );
+
+ } // console.log( type, parseFloats( data ), parseFloats( data ).length )
+
+
+ doSetFirstPoint = false;
+
+ }
+
+ return path;
+
+ }
+
+ function parseCSSStylesheet( node ) {
+
+ if ( ! node.sheet || ! node.sheet.cssRules || ! node.sheet.cssRules.length ) return;
+
+ for ( let i = 0; i < node.sheet.cssRules.length; i ++ ) {
+
+ const stylesheet = node.sheet.cssRules[ i ];
+ if ( stylesheet.type !== 1 ) continue;
+ const selectorList = stylesheet.selectorText.split( /,/gm ).filter( Boolean ).map( i => i.trim() );
+
+ for ( let j = 0; j < selectorList.length; j ++ ) {
+
+ stylesheets[ selectorList[ j ] ] = Object.assign( stylesheets[ selectorList[ j ] ] || {}, stylesheet.style );
+
+ }
+
+ }
+
+ }
+ /**
+ * https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+ * https://mortoray.com/2017/02/16/rendering-an-svg-elliptical-arc-as-bezier-curves/ Appendix: Endpoint to center arc conversion
+ * From
+ * rx ry x-axis-rotation large-arc-flag sweep-flag x y
+ * To
+ * aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation
+ */
+
+
+ function parseArcCommand( path, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, start, end ) {
+
+ if ( rx == 0 || ry == 0 ) {
+
+ // draw a line if either of the radii == 0
+ path.lineTo( end.x, end.y );
+ return;
+
+ }
+
+ x_axis_rotation = x_axis_rotation * Math.PI / 180; // Ensure radii are positive
+
+ rx = Math.abs( rx );
+ ry = Math.abs( ry ); // Compute (x1', y1')
+
+ const dx2 = ( start.x - end.x ) / 2.0;
+ const dy2 = ( start.y - end.y ) / 2.0;
+ const x1p = Math.cos( x_axis_rotation ) * dx2 + Math.sin( x_axis_rotation ) * dy2;
+ const y1p = - Math.sin( x_axis_rotation ) * dx2 + Math.cos( x_axis_rotation ) * dy2; // Compute (cx', cy')
+
+ let rxs = rx * rx;
+ let rys = ry * ry;
+ const x1ps = x1p * x1p;
+ const y1ps = y1p * y1p; // Ensure radii are large enough
+
+ const cr = x1ps / rxs + y1ps / rys;
+
+ if ( cr > 1 ) {
+
+ // scale up rx,ry equally so cr == 1
+ const s = Math.sqrt( cr );
+ rx = s * rx;
+ ry = s * ry;
+ rxs = rx * rx;
+ rys = ry * ry;
+
+ }
+
+ const dq = rxs * y1ps + rys * x1ps;
+ const pq = ( rxs * rys - dq ) / dq;
+ let q = Math.sqrt( Math.max( 0, pq ) );
+ if ( large_arc_flag === sweep_flag ) q = - q;
+ const cxp = q * rx * y1p / ry;
+ const cyp = - q * ry * x1p / rx; // Step 3: Compute (cx, cy) from (cx', cy')
+
+ const cx = Math.cos( x_axis_rotation ) * cxp - Math.sin( x_axis_rotation ) * cyp + ( start.x + end.x ) / 2;
+ const cy = Math.sin( x_axis_rotation ) * cxp + Math.cos( x_axis_rotation ) * cyp + ( start.y + end.y ) / 2; // Step 4: Compute θ1 and Δθ
+
+ const theta = svgAngle( 1, 0, ( x1p - cxp ) / rx, ( y1p - cyp ) / ry );
+ const delta = svgAngle( ( x1p - cxp ) / rx, ( y1p - cyp ) / ry, ( - x1p - cxp ) / rx, ( - y1p - cyp ) / ry ) % ( Math.PI * 2 );
+ path.currentPath.absellipse( cx, cy, rx, ry, theta, theta + delta, sweep_flag === 0, x_axis_rotation );
+
+ }
+
+ function svgAngle( ux, uy, vx, vy ) {
+
+ const dot = ux * vx + uy * vy;
+ const len = Math.sqrt( ux * ux + uy * uy ) * Math.sqrt( vx * vx + vy * vy );
+ let ang = Math.acos( Math.max( - 1, Math.min( 1, dot / len ) ) ); // floating point precision, slightly over values appear
+
+ if ( ux * vy - uy * vx < 0 ) ang = - ang;
+ return ang;
+
+ }
+ /*
+ * According to https://www.w3.org/TR/SVG/shapes.html#RectElementRXAttribute
+ * rounded corner should be rendered to elliptical arc, but bezier curve does the job well enough
+ */
+
+
+ function parseRectNode( node ) {
+
+ const x = parseFloatWithUnits( node.getAttribute( 'x' ) || 0 );
+ const y = parseFloatWithUnits( node.getAttribute( 'y' ) || 0 );
+ const rx = parseFloatWithUnits( node.getAttribute( 'rx' ) || 0 );
+ const ry = parseFloatWithUnits( node.getAttribute( 'ry' ) || 0 );
+ const w = parseFloatWithUnits( node.getAttribute( 'width' ) );
+ const h = parseFloatWithUnits( node.getAttribute( 'height' ) );
+ const path = new THREE.ShapePath();
+ path.moveTo( x + 2 * rx, y );
+ path.lineTo( x + w - 2 * rx, y );
+ if ( rx !== 0 || ry !== 0 ) path.bezierCurveTo( x + w, y, x + w, y, x + w, y + 2 * ry );
+ path.lineTo( x + w, y + h - 2 * ry );
+ if ( rx !== 0 || ry !== 0 ) path.bezierCurveTo( x + w, y + h, x + w, y + h, x + w - 2 * rx, y + h );
+ path.lineTo( x + 2 * rx, y + h );
+
+ if ( rx !== 0 || ry !== 0 ) {
+
+ path.bezierCurveTo( x, y + h, x, y + h, x, y + h - 2 * ry );
+
+ }
+
+ path.lineTo( x, y + 2 * ry );
+
+ if ( rx !== 0 || ry !== 0 ) {
+
+ path.bezierCurveTo( x, y, x, y, x + 2 * rx, y );
+
+ }
+
+ return path;
+
+ }
+
+ function parsePolygonNode( node ) {
+
+ function iterator( match, a, b ) {
+
+ const x = parseFloatWithUnits( a );
+ const y = parseFloatWithUnits( b );
+
+ if ( index === 0 ) {
+
+ path.moveTo( x, y );
+
+ } else {
+
+ path.lineTo( x, y );
+
+ }
+
+ index ++;
+
+ }
+
+ const regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g;
+ const path = new THREE.ShapePath();
+ let index = 0;
+ node.getAttribute( 'points' ).replace( regex, iterator );
+ path.currentPath.autoClose = true;
+ return path;
+
+ }
+
+ function parsePolylineNode( node ) {
+
+ function iterator( match, a, b ) {
+
+ const x = parseFloatWithUnits( a );
+ const y = parseFloatWithUnits( b );
+
+ if ( index === 0 ) {
+
+ path.moveTo( x, y );
+
+ } else {
+
+ path.lineTo( x, y );
+
+ }
+
+ index ++;
+
+ }
+
+ const regex = /(-?[\d\.?]+)[,|\s](-?[\d\.?]+)/g;
+ const path = new THREE.ShapePath();
+ let index = 0;
+ node.getAttribute( 'points' ).replace( regex, iterator );
+ path.currentPath.autoClose = false;
+ return path;
+
+ }
+
+ function parseCircleNode( node ) {
+
+ const x = parseFloatWithUnits( node.getAttribute( 'cx' ) || 0 );
+ const y = parseFloatWithUnits( node.getAttribute( 'cy' ) || 0 );
+ const r = parseFloatWithUnits( node.getAttribute( 'r' ) || 0 );
+ const subpath = new THREE.Path();
+ subpath.absarc( x, y, r, 0, Math.PI * 2 );
+ const path = new THREE.ShapePath();
+ path.subPaths.push( subpath );
+ return path;
+
+ }
+
+ function parseEllipseNode( node ) {
+
+ const x = parseFloatWithUnits( node.getAttribute( 'cx' ) || 0 );
+ const y = parseFloatWithUnits( node.getAttribute( 'cy' ) || 0 );
+ const rx = parseFloatWithUnits( node.getAttribute( 'rx' ) || 0 );
+ const ry = parseFloatWithUnits( node.getAttribute( 'ry' ) || 0 );
+ const subpath = new THREE.Path();
+ subpath.absellipse( x, y, rx, ry, 0, Math.PI * 2 );
+ const path = new THREE.ShapePath();
+ path.subPaths.push( subpath );
+ return path;
+
+ }
+
+ function parseLineNode( node ) {
+
+ const x1 = parseFloatWithUnits( node.getAttribute( 'x1' ) || 0 );
+ const y1 = parseFloatWithUnits( node.getAttribute( 'y1' ) || 0 );
+ const x2 = parseFloatWithUnits( node.getAttribute( 'x2' ) || 0 );
+ const y2 = parseFloatWithUnits( node.getAttribute( 'y2' ) || 0 );
+ const path = new THREE.ShapePath();
+ path.moveTo( x1, y1 );
+ path.lineTo( x2, y2 );
+ path.currentPath.autoClose = false;
+ return path;
+
+ } //
+
+
+ function parseStyle( node, style ) {
+
+ style = Object.assign( {}, style ); // clone style
+
+ let stylesheetStyles = {};
+
+ if ( node.hasAttribute( 'class' ) ) {
+
+ const classSelectors = node.getAttribute( 'class' ).split( /\s/ ).filter( Boolean ).map( i => i.trim() );
+
+ for ( let i = 0; i < classSelectors.length; i ++ ) {
+
+ stylesheetStyles = Object.assign( stylesheetStyles, stylesheets[ '.' + classSelectors[ i ] ] );
+
+ }
+
+ }
+
+ if ( node.hasAttribute( 'id' ) ) {
+
+ stylesheetStyles = Object.assign( stylesheetStyles, stylesheets[ '#' + node.getAttribute( 'id' ) ] );
+
+ }
+
+ function addStyle( svgName, jsName, adjustFunction ) {
+
+ if ( adjustFunction === undefined ) adjustFunction = function copy( v ) {
+
+ if ( v.startsWith( 'url' ) ) console.warn( 'SVGLoader: url access in attributes is not implemented.' );
+ return v;
+
+ };
+
+ if ( node.hasAttribute( svgName ) ) style[ jsName ] = adjustFunction( node.getAttribute( svgName ) );
+ if ( stylesheetStyles[ svgName ] ) style[ jsName ] = adjustFunction( stylesheetStyles[ svgName ] );
+ if ( node.style && node.style[ svgName ] !== '' ) style[ jsName ] = adjustFunction( node.style[ svgName ] );
+
+ }
+
+ function clamp( v ) {
+
+ return Math.max( 0, Math.min( 1, parseFloatWithUnits( v ) ) );
+
+ }
+
+ function positive( v ) {
+
+ return Math.max( 0, parseFloatWithUnits( v ) );
+
+ }
+
+ addStyle( 'fill', 'fill' );
+ addStyle( 'fill-opacity', 'fillOpacity', clamp );
+ addStyle( 'opacity', 'opacity', clamp );
+ addStyle( 'stroke', 'stroke' );
+ addStyle( 'stroke-opacity', 'strokeOpacity', clamp );
+ addStyle( 'stroke-width', 'strokeWidth', positive );
+ addStyle( 'stroke-linejoin', 'strokeLineJoin' );
+ addStyle( 'stroke-linecap', 'strokeLineCap' );
+ addStyle( 'stroke-miterlimit', 'strokeMiterLimit', positive );
+ addStyle( 'visibility', 'visibility' );
+ return style;
+
+ } // http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
+
+
+ function getReflection( a, b ) {
+
+ return a - ( b - a );
+
+ } // from https://github.com/ppvg/svg-numbers (MIT License)
+
+
+ function parseFloats( input, flags, stride ) {
+
+ if ( typeof input !== 'string' ) {
+
+ throw new TypeError( 'Invalid input: ' + typeof input );
+
+ } // Character groups
+
+
+ const RE = {
+ SEPARATOR: /[ \t\r\n\,.\-+]/,
+ WHITESPACE: /[ \t\r\n]/,
+ DIGIT: /[\d]/,
+ SIGN: /[-+]/,
+ POINT: /\./,
+ COMMA: /,/,
+ EXP: /e/i,
+ FLAGS: /[01]/
+ }; // States
+
+ const SEP = 0;
+ const INT = 1;
+ const FLOAT = 2;
+ const EXP = 3;
+ let state = SEP;
+ let seenComma = true;
+ let number = '',
+ exponent = '';
+ const result = [];
+
+ function throwSyntaxError( current, i, partial ) {
+
+ const error = new SyntaxError( 'Unexpected character "' + current + '" at index ' + i + '.' );
+ error.partial = partial;
+ throw error;
+
+ }
+
+ function newNumber() {
+
+ if ( number !== '' ) {
+
+ if ( exponent === '' ) result.push( Number( number ) ); else result.push( Number( number ) * Math.pow( 10, Number( exponent ) ) );
+
+ }
+
+ number = '';
+ exponent = '';
+
+ }
+
+ let current;
+ const length = input.length;
+
+ for ( let i = 0; i < length; i ++ ) {
+
+ current = input[ i ]; // check for flags
+
+ if ( Array.isArray( flags ) && flags.includes( result.length % stride ) && RE.FLAGS.test( current ) ) {
+
+ state = INT;
+ number = current;
+ newNumber();
+ continue;
+
+ } // parse until next number
+
+
+ if ( state === SEP ) {
+
+ // eat whitespace
+ if ( RE.WHITESPACE.test( current ) ) {
+
+ continue;
+
+ } // start new number
+
+
+ if ( RE.DIGIT.test( current ) || RE.SIGN.test( current ) ) {
+
+ state = INT;
+ number = current;
+ continue;
+
+ }
+
+ if ( RE.POINT.test( current ) ) {
+
+ state = FLOAT;
+ number = current;
+ continue;
+
+ } // throw on double commas (e.g. "1, , 2")
+
+
+ if ( RE.COMMA.test( current ) ) {
+
+ if ( seenComma ) {
+
+ throwSyntaxError( current, i, result );
+
+ }
+
+ seenComma = true;
+
+ }
+
+ } // parse integer part
+
+
+ if ( state === INT ) {
+
+ if ( RE.DIGIT.test( current ) ) {
+
+ number += current;
+ continue;
+
+ }
+
+ if ( RE.POINT.test( current ) ) {
+
+ number += current;
+ state = FLOAT;
+ continue;
+
+ }
+
+ if ( RE.EXP.test( current ) ) {
+
+ state = EXP;
+ continue;
+
+ } // throw on double signs ("-+1"), but not on sign as separator ("-1-2")
+
+
+ if ( RE.SIGN.test( current ) && number.length === 1 && RE.SIGN.test( number[ 0 ] ) ) {
+
+ throwSyntaxError( current, i, result );
+
+ }
+
+ } // parse decimal part
+
+
+ if ( state === FLOAT ) {
+
+ if ( RE.DIGIT.test( current ) ) {
+
+ number += current;
+ continue;
+
+ }
+
+ if ( RE.EXP.test( current ) ) {
+
+ state = EXP;
+ continue;
+
+ } // throw on double decimal points (e.g. "1..2")
+
+
+ if ( RE.POINT.test( current ) && number[ number.length - 1 ] === '.' ) {
+
+ throwSyntaxError( current, i, result );
+
+ }
+
+ } // parse exponent part
+
+
+ if ( state === EXP ) {
+
+ if ( RE.DIGIT.test( current ) ) {
+
+ exponent += current;
+ continue;
+
+ }
+
+ if ( RE.SIGN.test( current ) ) {
+
+ if ( exponent === '' ) {
+
+ exponent += current;
+ continue;
+
+ }
+
+ if ( exponent.length === 1 && RE.SIGN.test( exponent ) ) {
+
+ throwSyntaxError( current, i, result );
+
+ }
+
+ }
+
+ } // end of number
+
+
+ if ( RE.WHITESPACE.test( current ) ) {
+
+ newNumber();
+ state = SEP;
+ seenComma = false;
+
+ } else if ( RE.COMMA.test( current ) ) {
+
+ newNumber();
+ state = SEP;
+ seenComma = true;
+
+ } else if ( RE.SIGN.test( current ) ) {
+
+ newNumber();
+ state = INT;
+ number = current;
+
+ } else if ( RE.POINT.test( current ) ) {
+
+ newNumber();
+ state = FLOAT;
+ number = current;
+
+ } else {
+
+ throwSyntaxError( current, i, result );
+
+ }
+
+ } // add the last number found (if any)
+
+
+ newNumber();
+ return result;
+
+ } // Units
+
+
+ const units = [ 'mm', 'cm', 'in', 'pt', 'pc', 'px' ]; // Conversion: [ fromUnit ][ toUnit ] (-1 means dpi dependent)
+
+ const unitConversion = {
+ 'mm': {
+ 'mm': 1,
+ 'cm': 0.1,
+ 'in': 1 / 25.4,
+ 'pt': 72 / 25.4,
+ 'pc': 6 / 25.4,
+ 'px': - 1
+ },
+ 'cm': {
+ 'mm': 10,
+ 'cm': 1,
+ 'in': 1 / 2.54,
+ 'pt': 72 / 2.54,
+ 'pc': 6 / 2.54,
+ 'px': - 1
+ },
+ 'in': {
+ 'mm': 25.4,
+ 'cm': 2.54,
+ 'in': 1,
+ 'pt': 72,
+ 'pc': 6,
+ 'px': - 1
+ },
+ 'pt': {
+ 'mm': 25.4 / 72,
+ 'cm': 2.54 / 72,
+ 'in': 1 / 72,
+ 'pt': 1,
+ 'pc': 6 / 72,
+ 'px': - 1
+ },
+ 'pc': {
+ 'mm': 25.4 / 6,
+ 'cm': 2.54 / 6,
+ 'in': 1 / 6,
+ 'pt': 72 / 6,
+ 'pc': 1,
+ 'px': - 1
+ },
+ 'px': {
+ 'px': 1
+ }
+ };
+
+ function parseFloatWithUnits( string ) {
+
+ let theUnit = 'px';
+
+ if ( typeof string === 'string' || string instanceof String ) {
+
+ for ( let i = 0, n = units.length; i < n; i ++ ) {
+
+ const u = units[ i ];
+
+ if ( string.endsWith( u ) ) {
+
+ theUnit = u;
+ string = string.substring( 0, string.length - u.length );
+ break;
+
+ }
+
+ }
+
+ }
+
+ let scale = undefined;
+
+ if ( theUnit === 'px' && scope.defaultUnit !== 'px' ) {
+
+ // Conversion scale from pixels to inches, then to default units
+ scale = unitConversion[ 'in' ][ scope.defaultUnit ] / scope.defaultDPI;
+
+ } else {
+
+ scale = unitConversion[ theUnit ][ scope.defaultUnit ];
+
+ if ( scale < 0 ) {
+
+ // Conversion scale to pixels
+ scale = unitConversion[ theUnit ][ 'in' ] * scope.defaultDPI;
+
+ }
+
+ }
+
+ return scale * parseFloat( string );
+
+ } // Transforms
+
+
+ function getNodeTransform( node ) {
+
+ if ( ! ( node.hasAttribute( 'transform' ) || node.nodeName === 'use' && ( node.hasAttribute( 'x' ) || node.hasAttribute( 'y' ) ) ) ) {
+
+ return null;
+
+ }
+
+ const transform = parseNodeTransform( node );
+
+ if ( transformStack.length > 0 ) {
+
+ transform.premultiply( transformStack[ transformStack.length - 1 ] );
+
+ }
+
+ currentTransform.copy( transform );
+ transformStack.push( transform );
+ return transform;
+
+ }
+
+ function parseNodeTransform( node ) {
+
+ const transform = new THREE.Matrix3();
+ const currentTransform = tempTransform0;
+
+ if ( node.nodeName === 'use' && ( node.hasAttribute( 'x' ) || node.hasAttribute( 'y' ) ) ) {
+
+ const tx = parseFloatWithUnits( node.getAttribute( 'x' ) );
+ const ty = parseFloatWithUnits( node.getAttribute( 'y' ) );
+ transform.translate( tx, ty );
+
+ }
+
+ if ( node.hasAttribute( 'transform' ) ) {
+
+ const transformsTexts = node.getAttribute( 'transform' ).split( ')' );
+
+ for ( let tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex -- ) {
+
+ const transformText = transformsTexts[ tIndex ].trim();
+ if ( transformText === '' ) continue;
+ const openParPos = transformText.indexOf( '(' );
+ const closeParPos = transformText.length;
+
+ if ( openParPos > 0 && openParPos < closeParPos ) {
+
+ const transformType = transformText.substr( 0, openParPos );
+ const array = parseFloats( transformText.substr( openParPos + 1, closeParPos - openParPos - 1 ) );
+ currentTransform.identity();
+
+ switch ( transformType ) {
+
+ case 'translate':
+ if ( array.length >= 1 ) {
+
+ const tx = array[ 0 ];
+ let ty = tx;
+
+ if ( array.length >= 2 ) {
+
+ ty = array[ 1 ];
+
+ }
+
+ currentTransform.translate( tx, ty );
+
+ }
+
+ break;
+
+ case 'rotate':
+ if ( array.length >= 1 ) {
+
+ let angle = 0;
+ let cx = 0;
+ let cy = 0; // Angle
+
+ angle = - array[ 0 ] * Math.PI / 180;
+
+ if ( array.length >= 3 ) {
+
+ // Center x, y
+ cx = array[ 1 ];
+ cy = array[ 2 ];
+
+ } // Rotate around center (cx, cy)
+
+
+ tempTransform1.identity().translate( - cx, - cy );
+ tempTransform2.identity().rotate( angle );
+ tempTransform3.multiplyMatrices( tempTransform2, tempTransform1 );
+ tempTransform1.identity().translate( cx, cy );
+ currentTransform.multiplyMatrices( tempTransform1, tempTransform3 );
+
+ }
+
+ break;
+
+ case 'scale':
+ if ( array.length >= 1 ) {
+
+ const scaleX = array[ 0 ];
+ let scaleY = scaleX;
+
+ if ( array.length >= 2 ) {
+
+ scaleY = array[ 1 ];
+
+ }
+
+ currentTransform.scale( scaleX, scaleY );
+
+ }
+
+ break;
+
+ case 'skewX':
+ if ( array.length === 1 ) {
+
+ currentTransform.set( 1, Math.tan( array[ 0 ] * Math.PI / 180 ), 0, 0, 1, 0, 0, 0, 1 );
+
+ }
+
+ break;
+
+ case 'skewY':
+ if ( array.length === 1 ) {
+
+ currentTransform.set( 1, 0, 0, Math.tan( array[ 0 ] * Math.PI / 180 ), 1, 0, 0, 0, 1 );
+
+ }
+
+ break;
+
+ case 'matrix':
+ if ( array.length === 6 ) {
+
+ currentTransform.set( array[ 0 ], array[ 2 ], array[ 4 ], array[ 1 ], array[ 3 ], array[ 5 ], 0, 0, 1 );
+
+ }
+
+ break;
+
+ }
+
+ }
+
+ transform.premultiply( currentTransform );
+
+ }
+
+ }
+
+ return transform;
+
+ }
+
+ function transformPath( path, m ) {
+
+ function transfVec2( v2 ) {
+
+ tempV3.set( v2.x, v2.y, 1 ).applyMatrix3( m );
+ v2.set( tempV3.x, tempV3.y );
+
+ }
+
+ const isRotated = isTransformRotated( m );
+ const subPaths = path.subPaths;
+
+ for ( let i = 0, n = subPaths.length; i < n; i ++ ) {
+
+ const subPath = subPaths[ i ];
+ const curves = subPath.curves;
+
+ for ( let j = 0; j < curves.length; j ++ ) {
+
+ const curve = curves[ j ];
+
+ if ( curve.isLineCurve ) {
+
+ transfVec2( curve.v1 );
+ transfVec2( curve.v2 );
+
+ } else if ( curve.isCubicBezierCurve ) {
+
+ transfVec2( curve.v0 );
+ transfVec2( curve.v1 );
+ transfVec2( curve.v2 );
+ transfVec2( curve.v3 );
+
+ } else if ( curve.isQuadraticBezierCurve ) {
+
+ transfVec2( curve.v0 );
+ transfVec2( curve.v1 );
+ transfVec2( curve.v2 );
+
+ } else if ( curve.isEllipseCurve ) {
+
+ if ( isRotated ) {
+
+ console.warn( 'SVGLoader: Elliptic arc or ellipse rotation or skewing is not implemented.' );
+
+ }
+
+ tempV2.set( curve.aX, curve.aY );
+ transfVec2( tempV2 );
+ curve.aX = tempV2.x;
+ curve.aY = tempV2.y;
+ curve.xRadius *= getTransformScaleX( m );
+ curve.yRadius *= getTransformScaleY( m );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ function isTransformRotated( m ) {
+
+ return m.elements[ 1 ] !== 0 || m.elements[ 3 ] !== 0;
+
+ }
+
+ function getTransformScaleX( m ) {
+
+ const te = m.elements;
+ return Math.sqrt( te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] );
+
+ }
+
+ function getTransformScaleY( m ) {
+
+ const te = m.elements;
+ return Math.sqrt( te[ 3 ] * te[ 3 ] + te[ 4 ] * te[ 4 ] );
+
+ } //
+
+
+ const paths = [];
+ const stylesheets = {};
+ const transformStack = [];
+ const tempTransform0 = new THREE.Matrix3();
+ const tempTransform1 = new THREE.Matrix3();
+ const tempTransform2 = new THREE.Matrix3();
+ const tempTransform3 = new THREE.Matrix3();
+ const tempV2 = new THREE.Vector2();
+ const tempV3 = new THREE.Vector3();
+ const currentTransform = new THREE.Matrix3();
+ const xml = new DOMParser().parseFromString( text, 'image/svg+xml' ); // application/xml
+
+ parseNode( xml.documentElement, {
+ fill: '#000',
+ fillOpacity: 1,
+ strokeOpacity: 1,
+ strokeWidth: 1,
+ strokeLineJoin: 'miter',
+ strokeLineCap: 'butt',
+ strokeMiterLimit: 4
+ } );
+ const data = {
+ paths: paths,
+ xml: xml.documentElement
+ }; // console.log( paths );
+
+ return data;
+
+ }
+
+ static createShapes( shapePath ) {
+
+ // Param shapePath: a shapepath as returned by the parse function of this class
+ // Returns THREE.Shape object
+ const BIGNUMBER = 999999999;
+ const IntersectionLocationType = {
+ ORIGIN: 0,
+ DESTINATION: 1,
+ BETWEEN: 2,
+ LEFT: 3,
+ RIGHT: 4,
+ BEHIND: 5,
+ BEYOND: 6
+ };
+ const classifyResult = {
+ loc: IntersectionLocationType.ORIGIN,
+ t: 0
+ };
+
+ function findEdgeIntersection( a0, a1, b0, b1 ) {
+
+ const x1 = a0.x;
+ const x2 = a1.x;
+ const x3 = b0.x;
+ const x4 = b1.x;
+ const y1 = a0.y;
+ const y2 = a1.y;
+ const y3 = b0.y;
+ const y4 = b1.y;
+ const nom1 = ( x4 - x3 ) * ( y1 - y3 ) - ( y4 - y3 ) * ( x1 - x3 );
+ const nom2 = ( x2 - x1 ) * ( y1 - y3 ) - ( y2 - y1 ) * ( x1 - x3 );
+ const denom = ( y4 - y3 ) * ( x2 - x1 ) - ( x4 - x3 ) * ( y2 - y1 );
+ const t1 = nom1 / denom;
+ const t2 = nom2 / denom;
+
+ if ( denom === 0 && nom1 !== 0 || t1 <= 0 || t1 >= 1 || t2 < 0 || t2 > 1 ) {
+
+ //1. lines are parallel or edges don't intersect
+ return null;
+
+ } else if ( nom1 === 0 && denom === 0 ) {
+
+ //2. lines are colinear
+ //check if endpoints of edge2 (b0-b1) lies on edge1 (a0-a1)
+ for ( let i = 0; i < 2; i ++ ) {
+
+ classifyPoint( i === 0 ? b0 : b1, a0, a1 ); //find position of this endpoints relatively to edge1
+
+ if ( classifyResult.loc == IntersectionLocationType.ORIGIN ) {
+
+ const point = i === 0 ? b0 : b1;
+ return {
+ x: point.x,
+ y: point.y,
+ t: classifyResult.t
+ };
+
+ } else if ( classifyResult.loc == IntersectionLocationType.BETWEEN ) {
+
+ const x = + ( x1 + classifyResult.t * ( x2 - x1 ) ).toPrecision( 10 );
+ const y = + ( y1 + classifyResult.t * ( y2 - y1 ) ).toPrecision( 10 );
+ return {
+ x: x,
+ y: y,
+ t: classifyResult.t
+ };
+
+ }
+
+ }
+
+ return null;
+
+ } else {
+
+ //3. edges intersect
+ for ( let i = 0; i < 2; i ++ ) {
+
+ classifyPoint( i === 0 ? b0 : b1, a0, a1 );
+
+ if ( classifyResult.loc == IntersectionLocationType.ORIGIN ) {
+
+ const point = i === 0 ? b0 : b1;
+ return {
+ x: point.x,
+ y: point.y,
+ t: classifyResult.t
+ };
+
+ }
+
+ }
+
+ const x = + ( x1 + t1 * ( x2 - x1 ) ).toPrecision( 10 );
+ const y = + ( y1 + t1 * ( y2 - y1 ) ).toPrecision( 10 );
+ return {
+ x: x,
+ y: y,
+ t: t1
+ };
+
+ }
+
+ }
+
+ function classifyPoint( p, edgeStart, edgeEnd ) {
+
+ const ax = edgeEnd.x - edgeStart.x;
+ const ay = edgeEnd.y - edgeStart.y;
+ const bx = p.x - edgeStart.x;
+ const by = p.y - edgeStart.y;
+ const sa = ax * by - bx * ay;
+
+ if ( p.x === edgeStart.x && p.y === edgeStart.y ) {
+
+ classifyResult.loc = IntersectionLocationType.ORIGIN;
+ classifyResult.t = 0;
+ return;
+
+ }
+
+ if ( p.x === edgeEnd.x && p.y === edgeEnd.y ) {
+
+ classifyResult.loc = IntersectionLocationType.DESTINATION;
+ classifyResult.t = 1;
+ return;
+
+ }
+
+ if ( sa < - Number.EPSILON ) {
+
+ classifyResult.loc = IntersectionLocationType.LEFT;
+ return;
+
+ }
+
+ if ( sa > Number.EPSILON ) {
+
+ classifyResult.loc = IntersectionLocationType.RIGHT;
+ return;
+
+ }
+
+ if ( ax * bx < 0 || ay * by < 0 ) {
+
+ classifyResult.loc = IntersectionLocationType.BEHIND;
+ return;
+
+ }
+
+ if ( Math.sqrt( ax * ax + ay * ay ) < Math.sqrt( bx * bx + by * by ) ) {
+
+ classifyResult.loc = IntersectionLocationType.BEYOND;
+ return;
+
+ }
+
+ let t;
+
+ if ( ax !== 0 ) {
+
+ t = bx / ax;
+
+ } else {
+
+ t = by / ay;
+
+ }
+
+ classifyResult.loc = IntersectionLocationType.BETWEEN;
+ classifyResult.t = t;
+
+ }
+
+ function getIntersections( path1, path2 ) {
+
+ const intersectionsRaw = [];
+ const intersections = [];
+
+ for ( let index = 1; index < path1.length; index ++ ) {
+
+ const path1EdgeStart = path1[ index - 1 ];
+ const path1EdgeEnd = path1[ index ];
+
+ for ( let index2 = 1; index2 < path2.length; index2 ++ ) {
+
+ const path2EdgeStart = path2[ index2 - 1 ];
+ const path2EdgeEnd = path2[ index2 ];
+ const intersection = findEdgeIntersection( path1EdgeStart, path1EdgeEnd, path2EdgeStart, path2EdgeEnd );
+
+ if ( intersection !== null && intersectionsRaw.find( i => i.t <= intersection.t + Number.EPSILON && i.t >= intersection.t - Number.EPSILON ) === undefined ) {
+
+ intersectionsRaw.push( intersection );
+ intersections.push( new THREE.Vector2( intersection.x, intersection.y ) );
+
+ }
+
+ }
+
+ }
+
+ return intersections;
+
+ }
+
+ function getScanlineIntersections( scanline, boundingBox, paths ) {
+
+ const center = new THREE.Vector2();
+ boundingBox.getCenter( center );
+ const allIntersections = [];
+ paths.forEach( path => {
+
+ // check if the center of the bounding box is in the bounding box of the paths.
+ // this is a pruning method to limit the search of intersections in paths that can't envelop of the current path.
+ // if a path envelops another path. The center of that oter path, has to be inside the bounding box of the enveloping path.
+ if ( path.boundingBox.containsPoint( center ) ) {
+
+ const intersections = getIntersections( scanline, path.points );
+ intersections.forEach( p => {
+
+ allIntersections.push( {
+ identifier: path.identifier,
+ isCW: path.isCW,
+ point: p
+ } );
+
+ } );
+
+ }
+
+ } );
+ allIntersections.sort( ( i1, i2 ) => {
+
+ return i1.point.x - i2.point.x;
+
+ } );
+ return allIntersections;
+
+ }
+
+ function isHoleTo( simplePath, allPaths, scanlineMinX, scanlineMaxX, _fillRule ) {
+
+ if ( _fillRule === null || _fillRule === undefined || _fillRule === '' ) {
+
+ _fillRule = 'nonzero';
+
+ }
+
+ const centerBoundingBox = new THREE.Vector2();
+ simplePath.boundingBox.getCenter( centerBoundingBox );
+ const scanline = [ new THREE.Vector2( scanlineMinX, centerBoundingBox.y ), new THREE.Vector2( scanlineMaxX, centerBoundingBox.y ) ];
+ const scanlineIntersections = getScanlineIntersections( scanline, simplePath.boundingBox, allPaths );
+ scanlineIntersections.sort( ( i1, i2 ) => {
+
+ return i1.point.x - i2.point.x;
+
+ } );
+ const baseIntersections = [];
+ const otherIntersections = [];
+ scanlineIntersections.forEach( i => {
+
+ if ( i.identifier === simplePath.identifier ) {
+
+ baseIntersections.push( i );
+
+ } else {
+
+ otherIntersections.push( i );
+
+ }
+
+ } );
+ const firstXOfPath = baseIntersections[ 0 ].point.x; // build up the path hierarchy
+
+ const stack = [];
+ let i = 0;
+
+ while ( i < otherIntersections.length && otherIntersections[ i ].point.x < firstXOfPath ) {
+
+ if ( stack.length > 0 && stack[ stack.length - 1 ] === otherIntersections[ i ].identifier ) {
+
+ stack.pop();
+
+ } else {
+
+ stack.push( otherIntersections[ i ].identifier );
+
+ }
+
+ i ++;
+
+ }
+
+ stack.push( simplePath.identifier );
+
+ if ( _fillRule === 'evenodd' ) {
+
+ const isHole = stack.length % 2 === 0 ? true : false;
+ const isHoleFor = stack[ stack.length - 2 ];
+ return {
+ identifier: simplePath.identifier,
+ isHole: isHole,
+ for: isHoleFor
+ };
+
+ } else if ( _fillRule === 'nonzero' ) {
+
+ // check if path is a hole by counting the amount of paths with alternating rotations it has to cross.
+ let isHole = true;
+ let isHoleFor = null;
+ let lastCWValue = null;
+
+ for ( let i = 0; i < stack.length; i ++ ) {
+
+ const identifier = stack[ i ];
+
+ if ( isHole ) {
+
+ lastCWValue = allPaths[ identifier ].isCW;
+ isHole = false;
+ isHoleFor = identifier;
+
+ } else if ( lastCWValue !== allPaths[ identifier ].isCW ) {
+
+ lastCWValue = allPaths[ identifier ].isCW;
+ isHole = true;
+
+ }
+
+ }
+
+ return {
+ identifier: simplePath.identifier,
+ isHole: isHole,
+ for: isHoleFor
+ };
+
+ } else {
+
+ console.warn( 'fill-rule: "' + _fillRule + '" is currently not implemented.' );
+
+ }
+
+ } // check for self intersecting paths
+ // TODO
+ // check intersecting paths
+ // TODO
+ // prepare paths for hole detection
+
+
+ let identifier = 0;
+ let scanlineMinX = BIGNUMBER;
+ let scanlineMaxX = - BIGNUMBER;
+ let simplePaths = shapePath.subPaths.map( p => {
+
+ const points = p.getPoints();
+ let maxY = - BIGNUMBER;
+ let minY = BIGNUMBER;
+ let maxX = - BIGNUMBER;
+ let minX = BIGNUMBER; //points.forEach(p => p.y *= -1);
+
+ for ( let i = 0; i < points.length; i ++ ) {
+
+ const p = points[ i ];
+
+ if ( p.y > maxY ) {
+
+ maxY = p.y;
+
+ }
+
+ if ( p.y < minY ) {
+
+ minY = p.y;
+
+ }
+
+ if ( p.x > maxX ) {
+
+ maxX = p.x;
+
+ }
+
+ if ( p.x < minX ) {
+
+ minX = p.x;
+
+ }
+
+ } //
+
+
+ if ( scanlineMaxX <= maxX ) {
+
+ scanlineMaxX = maxX + 1;
+
+ }
+
+ if ( scanlineMinX >= minX ) {
+
+ scanlineMinX = minX - 1;
+
+ }
+
+ return {
+ points: points,
+ isCW: THREE.ShapeUtils.isClockWise( points ),
+ identifier: identifier ++,
+ boundingBox: new THREE.Box2( new THREE.Vector2( minX, minY ), new THREE.Vector2( maxX, maxY ) )
+ };
+
+ } );
+ simplePaths = simplePaths.filter( sp => sp.points.length > 0 ); // check if path is solid or a hole
+
+ const isAHole = simplePaths.map( p => isHoleTo( p, simplePaths, scanlineMinX, scanlineMaxX, shapePath.userData.style.fillRule ) );
+ const shapesToReturn = [];
+ simplePaths.forEach( p => {
+
+ const amIAHole = isAHole[ p.identifier ];
+
+ if ( ! amIAHole.isHole ) {
+
+ const shape = new THREE.Shape( p.points );
+ const holes = isAHole.filter( h => h.isHole && h.for === p.identifier );
+ holes.forEach( h => {
+
+ const path = simplePaths[ h.identifier ];
+ shape.holes.push( new THREE.Path( path.points ) );
+
+ } );
+ shapesToReturn.push( shape );
+
+ }
+
+ } );
+ return shapesToReturn;
+
+ }
+
+ static getStrokeStyle( width, color, lineJoin, lineCap, miterLimit ) {
+
+ // Param width: Stroke width
+ // Param color: As returned by THREE.Color.getStyle()
+ // Param lineJoin: One of "round", "bevel", "miter" or "miter-limit"
+ // Param lineCap: One of "round", "square" or "butt"
+ // Param miterLimit: Maximum join length, in multiples of the "width" parameter (join is truncated if it exceeds that distance)
+ // Returns style object
+ width = width !== undefined ? width : 1;
+ color = color !== undefined ? color : '#000';
+ lineJoin = lineJoin !== undefined ? lineJoin : 'miter';
+ lineCap = lineCap !== undefined ? lineCap : 'butt';
+ miterLimit = miterLimit !== undefined ? miterLimit : 4;
+ return {
+ strokeColor: color,
+ strokeWidth: width,
+ strokeLineJoin: lineJoin,
+ strokeLineCap: lineCap,
+ strokeMiterLimit: miterLimit
+ };
+
+ }
+
+ static pointsToStroke( points, style, arcDivisions, minDistance ) {
+
+ // Generates a stroke with some witdh around the given path.
+ // The path can be open or closed (last point equals to first point)
+ // Param points: Array of Vector2D (the path). Minimum 2 points.
+ // Param style: Object with SVG properties as returned by SVGLoader.getStrokeStyle(), or SVGLoader.parse() in the path.userData.style object
+ // Params arcDivisions: Arc divisions for round joins and endcaps. (Optional)
+ // Param minDistance: Points closer to this distance will be merged. (Optional)
+ // Returns THREE.BufferGeometry with stroke triangles (In plane z = 0). UV coordinates are generated ('u' along path. 'v' across it, from left to right)
+ const vertices = [];
+ const normals = [];
+ const uvs = [];
+
+ if ( SVGLoader.pointsToStrokeWithBuffers( points, style, arcDivisions, minDistance, vertices, normals, uvs ) === 0 ) {
+
+ return null;
+
+ }
+
+ const geometry = new THREE.BufferGeometry();
+ geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( vertices, 3 ) );
+ geometry.setAttribute( 'normal', new THREE.Float32BufferAttribute( normals, 3 ) );
+ geometry.setAttribute( 'uv', new THREE.Float32BufferAttribute( uvs, 2 ) );
+ return geometry;
+
+ }
+
+ static pointsToStrokeWithBuffers( points, style, arcDivisions, minDistance, vertices, normals, uvs, vertexOffset ) {
+
+ // This function can be called to update existing arrays or buffers.
+ // Accepts same parameters as pointsToStroke, plus the buffers and optional offset.
+ // Param vertexOffset: Offset vertices to start writing in the buffers (3 elements/vertex for vertices and normals, and 2 elements/vertex for uvs)
+ // Returns number of written vertices / normals / uvs pairs
+ // if 'vertices' parameter is undefined no triangles will be generated, but the returned vertices count will still be valid (useful to preallocate the buffers)
+ // 'normals' and 'uvs' buffers are optional
+ const tempV2_1 = new THREE.Vector2();
+ const tempV2_2 = new THREE.Vector2();
+ const tempV2_3 = new THREE.Vector2();
+ const tempV2_4 = new THREE.Vector2();
+ const tempV2_5 = new THREE.Vector2();
+ const tempV2_6 = new THREE.Vector2();
+ const tempV2_7 = new THREE.Vector2();
+ const lastPointL = new THREE.Vector2();
+ const lastPointR = new THREE.Vector2();
+ const point0L = new THREE.Vector2();
+ const point0R = new THREE.Vector2();
+ const currentPointL = new THREE.Vector2();
+ const currentPointR = new THREE.Vector2();
+ const nextPointL = new THREE.Vector2();
+ const nextPointR = new THREE.Vector2();
+ const innerPoint = new THREE.Vector2();
+ const outerPoint = new THREE.Vector2();
+ arcDivisions = arcDivisions !== undefined ? arcDivisions : 12;
+ minDistance = minDistance !== undefined ? minDistance : 0.001;
+ vertexOffset = vertexOffset !== undefined ? vertexOffset : 0; // First ensure there are no duplicated points
+
+ points = removeDuplicatedPoints( points );
+ const numPoints = points.length;
+ if ( numPoints < 2 ) return 0;
+ const isClosed = points[ 0 ].equals( points[ numPoints - 1 ] );
+ let currentPoint;
+ let previousPoint = points[ 0 ];
+ let nextPoint;
+ const strokeWidth2 = style.strokeWidth / 2;
+ const deltaU = 1 / ( numPoints - 1 );
+ let u0 = 0,
+ u1;
+ let innerSideModified;
+ let joinIsOnLeftSide;
+ let isMiter;
+ let initialJoinIsOnLeftSide = false;
+ let numVertices = 0;
+ let currentCoordinate = vertexOffset * 3;
+ let currentCoordinateUV = vertexOffset * 2; // Get initial left and right stroke points
+
+ getNormal( points[ 0 ], points[ 1 ], tempV2_1 ).multiplyScalar( strokeWidth2 );
+ lastPointL.copy( points[ 0 ] ).sub( tempV2_1 );
+ lastPointR.copy( points[ 0 ] ).add( tempV2_1 );
+ point0L.copy( lastPointL );
+ point0R.copy( lastPointR );
+
+ for ( let iPoint = 1; iPoint < numPoints; iPoint ++ ) {
+
+ currentPoint = points[ iPoint ]; // Get next point
+
+ if ( iPoint === numPoints - 1 ) {
+
+ if ( isClosed ) {
+
+ // Skip duplicated initial point
+ nextPoint = points[ 1 ];
+
+ } else nextPoint = undefined;
+
+ } else {
+
+ nextPoint = points[ iPoint + 1 ];
+
+ } // Normal of previous segment in tempV2_1
+
+
+ const normal1 = tempV2_1;
+ getNormal( previousPoint, currentPoint, normal1 );
+ tempV2_3.copy( normal1 ).multiplyScalar( strokeWidth2 );
+ currentPointL.copy( currentPoint ).sub( tempV2_3 );
+ currentPointR.copy( currentPoint ).add( tempV2_3 );
+ u1 = u0 + deltaU;
+ innerSideModified = false;
+
+ if ( nextPoint !== undefined ) {
+
+ // Normal of next segment in tempV2_2
+ getNormal( currentPoint, nextPoint, tempV2_2 );
+ tempV2_3.copy( tempV2_2 ).multiplyScalar( strokeWidth2 );
+ nextPointL.copy( currentPoint ).sub( tempV2_3 );
+ nextPointR.copy( currentPoint ).add( tempV2_3 );
+ joinIsOnLeftSide = true;
+ tempV2_3.subVectors( nextPoint, previousPoint );
+
+ if ( normal1.dot( tempV2_3 ) < 0 ) {
+
+ joinIsOnLeftSide = false;
+
+ }
+
+ if ( iPoint === 1 ) initialJoinIsOnLeftSide = joinIsOnLeftSide;
+ tempV2_3.subVectors( nextPoint, currentPoint );
+ tempV2_3.normalize();
+ const dot = Math.abs( normal1.dot( tempV2_3 ) ); // If path is straight, don't create join
+
+ if ( dot !== 0 ) {
+
+ // Compute inner and outer segment intersections
+ const miterSide = strokeWidth2 / dot;
+ tempV2_3.multiplyScalar( - miterSide );
+ tempV2_4.subVectors( currentPoint, previousPoint );
+ tempV2_5.copy( tempV2_4 ).setLength( miterSide ).add( tempV2_3 );
+ innerPoint.copy( tempV2_5 ).negate();
+ const miterLength2 = tempV2_5.length();
+ const segmentLengthPrev = tempV2_4.length();
+ tempV2_4.divideScalar( segmentLengthPrev );
+ tempV2_6.subVectors( nextPoint, currentPoint );
+ const segmentLengthNext = tempV2_6.length();
+ tempV2_6.divideScalar( segmentLengthNext ); // Check that previous and next segments doesn't overlap with the innerPoint of intersection
+
+ if ( tempV2_4.dot( innerPoint ) < segmentLengthPrev && tempV2_6.dot( innerPoint ) < segmentLengthNext ) {
+
+ innerSideModified = true;
+
+ }
+
+ outerPoint.copy( tempV2_5 ).add( currentPoint );
+ innerPoint.add( currentPoint );
+ isMiter = false;
+
+ if ( innerSideModified ) {
+
+ if ( joinIsOnLeftSide ) {
+
+ nextPointR.copy( innerPoint );
+ currentPointR.copy( innerPoint );
+
+ } else {
+
+ nextPointL.copy( innerPoint );
+ currentPointL.copy( innerPoint );
+
+ }
+
+ } else {
+
+ // The segment triangles are generated here if there was overlapping
+ makeSegmentTriangles();
+
+ }
+
+ switch ( style.strokeLineJoin ) {
+
+ case 'bevel':
+ makeSegmentWithBevelJoin( joinIsOnLeftSide, innerSideModified, u1 );
+ break;
+
+ case 'round':
+ // Segment triangles
+ createSegmentTrianglesWithMiddleSection( joinIsOnLeftSide, innerSideModified ); // Join triangles
+
+ if ( joinIsOnLeftSide ) {
+
+ makeCircularSector( currentPoint, currentPointL, nextPointL, u1, 0 );
+
+ } else {
+
+ makeCircularSector( currentPoint, nextPointR, currentPointR, u1, 1 );
+
+ }
+
+ break;
+
+ case 'miter':
+ case 'miter-clip':
+ default:
+ const miterFraction = strokeWidth2 * style.strokeMiterLimit / miterLength2;
+
+ if ( miterFraction < 1 ) {
+
+ // The join miter length exceeds the miter limit
+ if ( style.strokeLineJoin !== 'miter-clip' ) {
+
+ makeSegmentWithBevelJoin( joinIsOnLeftSide, innerSideModified, u1 );
+ break;
+
+ } else {
+
+ // Segment triangles
+ createSegmentTrianglesWithMiddleSection( joinIsOnLeftSide, innerSideModified ); // Miter-clip join triangles
+
+ if ( joinIsOnLeftSide ) {
+
+ tempV2_6.subVectors( outerPoint, currentPointL ).multiplyScalar( miterFraction ).add( currentPointL );
+ tempV2_7.subVectors( outerPoint, nextPointL ).multiplyScalar( miterFraction ).add( nextPointL );
+ addVertex( currentPointL, u1, 0 );
+ addVertex( tempV2_6, u1, 0 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( tempV2_6, u1, 0 );
+ addVertex( tempV2_7, u1, 0 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( tempV2_7, u1, 0 );
+ addVertex( nextPointL, u1, 0 );
+
+ } else {
+
+ tempV2_6.subVectors( outerPoint, currentPointR ).multiplyScalar( miterFraction ).add( currentPointR );
+ tempV2_7.subVectors( outerPoint, nextPointR ).multiplyScalar( miterFraction ).add( nextPointR );
+ addVertex( currentPointR, u1, 1 );
+ addVertex( tempV2_6, u1, 1 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( tempV2_6, u1, 1 );
+ addVertex( tempV2_7, u1, 1 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( tempV2_7, u1, 1 );
+ addVertex( nextPointR, u1, 1 );
+
+ }
+
+ }
+
+ } else {
+
+ // Miter join segment triangles
+ if ( innerSideModified ) {
+
+ // Optimized segment + join triangles
+ if ( joinIsOnLeftSide ) {
+
+ addVertex( lastPointR, u0, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( outerPoint, u1, 0 );
+ addVertex( lastPointR, u0, 1 );
+ addVertex( outerPoint, u1, 0 );
+ addVertex( innerPoint, u1, 1 );
+
+ } else {
+
+ addVertex( lastPointR, u0, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( outerPoint, u1, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( innerPoint, u1, 0 );
+ addVertex( outerPoint, u1, 1 );
+
+ }
+
+ if ( joinIsOnLeftSide ) {
+
+ nextPointL.copy( outerPoint );
+
+ } else {
+
+ nextPointR.copy( outerPoint );
+
+ }
+
+ } else {
+
+ // Add extra miter join triangles
+ if ( joinIsOnLeftSide ) {
+
+ addVertex( currentPointL, u1, 0 );
+ addVertex( outerPoint, u1, 0 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( outerPoint, u1, 0 );
+ addVertex( nextPointL, u1, 0 );
+
+ } else {
+
+ addVertex( currentPointR, u1, 1 );
+ addVertex( outerPoint, u1, 1 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( outerPoint, u1, 1 );
+ addVertex( nextPointR, u1, 1 );
+
+ }
+
+ }
+
+ isMiter = true;
+
+ }
+
+ break;
+
+ }
+
+ } else {
+
+ // The segment triangles are generated here when two consecutive points are collinear
+ makeSegmentTriangles();
+
+ }
+
+ } else {
+
+ // The segment triangles are generated here if it is the ending segment
+ makeSegmentTriangles();
+
+ }
+
+ if ( ! isClosed && iPoint === numPoints - 1 ) {
+
+ // Start line endcap
+ addCapGeometry( points[ 0 ], point0L, point0R, joinIsOnLeftSide, true, u0 );
+
+ } // Increment loop variables
+
+
+ u0 = u1;
+ previousPoint = currentPoint;
+ lastPointL.copy( nextPointL );
+ lastPointR.copy( nextPointR );
+
+ }
+
+ if ( ! isClosed ) {
+
+ // Ending line endcap
+ addCapGeometry( currentPoint, currentPointL, currentPointR, joinIsOnLeftSide, false, u1 );
+
+ } else if ( innerSideModified && vertices ) {
+
+ // Modify path first segment vertices to adjust to the segments inner and outer intersections
+ let lastOuter = outerPoint;
+ let lastInner = innerPoint;
+
+ if ( initialJoinIsOnLeftSide !== joinIsOnLeftSide ) {
+
+ lastOuter = innerPoint;
+ lastInner = outerPoint;
+
+ }
+
+ if ( joinIsOnLeftSide ) {
+
+ if ( isMiter || initialJoinIsOnLeftSide ) {
+
+ lastInner.toArray( vertices, 0 * 3 );
+ lastInner.toArray( vertices, 3 * 3 );
+
+ if ( isMiter ) {
+
+ lastOuter.toArray( vertices, 1 * 3 );
+
+ }
+
+ }
+
+ } else {
+
+ if ( isMiter || ! initialJoinIsOnLeftSide ) {
+
+ lastInner.toArray( vertices, 1 * 3 );
+ lastInner.toArray( vertices, 3 * 3 );
+
+ if ( isMiter ) {
+
+ lastOuter.toArray( vertices, 0 * 3 );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ return numVertices; // -- End of algorithm
+ // -- Functions
+
+ function getNormal( p1, p2, result ) {
+
+ result.subVectors( p2, p1 );
+ return result.set( - result.y, result.x ).normalize();
+
+ }
+
+ function addVertex( position, u, v ) {
+
+ if ( vertices ) {
+
+ vertices[ currentCoordinate ] = position.x;
+ vertices[ currentCoordinate + 1 ] = position.y;
+ vertices[ currentCoordinate + 2 ] = 0;
+
+ if ( normals ) {
+
+ normals[ currentCoordinate ] = 0;
+ normals[ currentCoordinate + 1 ] = 0;
+ normals[ currentCoordinate + 2 ] = 1;
+
+ }
+
+ currentCoordinate += 3;
+
+ if ( uvs ) {
+
+ uvs[ currentCoordinateUV ] = u;
+ uvs[ currentCoordinateUV + 1 ] = v;
+ currentCoordinateUV += 2;
+
+ }
+
+ }
+
+ numVertices += 3;
+
+ }
+
+ function makeCircularSector( center, p1, p2, u, v ) {
+
+ // param p1, p2: Points in the circle arc.
+ // p1 and p2 are in clockwise direction.
+ tempV2_1.copy( p1 ).sub( center ).normalize();
+ tempV2_2.copy( p2 ).sub( center ).normalize();
+ let angle = Math.PI;
+ const dot = tempV2_1.dot( tempV2_2 );
+ if ( Math.abs( dot ) < 1 ) angle = Math.abs( Math.acos( dot ) );
+ angle /= arcDivisions;
+ tempV2_3.copy( p1 );
+
+ for ( let i = 0, il = arcDivisions - 1; i < il; i ++ ) {
+
+ tempV2_4.copy( tempV2_3 ).rotateAround( center, angle );
+ addVertex( tempV2_3, u, v );
+ addVertex( tempV2_4, u, v );
+ addVertex( center, u, 0.5 );
+ tempV2_3.copy( tempV2_4 );
+
+ }
+
+ addVertex( tempV2_4, u, v );
+ addVertex( p2, u, v );
+ addVertex( center, u, 0.5 );
+
+ }
+
+ function makeSegmentTriangles() {
+
+ addVertex( lastPointR, u0, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( currentPointL, u1, 0 );
+ addVertex( lastPointR, u0, 1 );
+ addVertex( currentPointL, u1, 1 );
+ addVertex( currentPointR, u1, 0 );
+
+ }
+
+ function makeSegmentWithBevelJoin( joinIsOnLeftSide, innerSideModified, u ) {
+
+ if ( innerSideModified ) {
+
+ // Optimized segment + bevel triangles
+ if ( joinIsOnLeftSide ) {
+
+ // THREE.Path segments triangles
+ addVertex( lastPointR, u0, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( currentPointL, u1, 0 );
+ addVertex( lastPointR, u0, 1 );
+ addVertex( currentPointL, u1, 0 );
+ addVertex( innerPoint, u1, 1 ); // Bevel join triangle
+
+ addVertex( currentPointL, u, 0 );
+ addVertex( nextPointL, u, 0 );
+ addVertex( innerPoint, u, 0.5 );
+
+ } else {
+
+ // THREE.Path segments triangles
+ addVertex( lastPointR, u0, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( currentPointR, u1, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( innerPoint, u1, 0 );
+ addVertex( currentPointR, u1, 1 ); // Bevel join triangle
+
+ addVertex( currentPointR, u, 1 );
+ addVertex( nextPointR, u, 0 );
+ addVertex( innerPoint, u, 0.5 );
+
+ }
+
+ } else {
+
+ // Bevel join triangle. The segment triangles are done in the main loop
+ if ( joinIsOnLeftSide ) {
+
+ addVertex( currentPointL, u, 0 );
+ addVertex( nextPointL, u, 0 );
+ addVertex( currentPoint, u, 0.5 );
+
+ } else {
+
+ addVertex( currentPointR, u, 1 );
+ addVertex( nextPointR, u, 0 );
+ addVertex( currentPoint, u, 0.5 );
+
+ }
+
+ }
+
+ }
+
+ function createSegmentTrianglesWithMiddleSection( joinIsOnLeftSide, innerSideModified ) {
+
+ if ( innerSideModified ) {
+
+ if ( joinIsOnLeftSide ) {
+
+ addVertex( lastPointR, u0, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( currentPointL, u1, 0 );
+ addVertex( lastPointR, u0, 1 );
+ addVertex( currentPointL, u1, 0 );
+ addVertex( innerPoint, u1, 1 );
+ addVertex( currentPointL, u0, 0 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( innerPoint, u1, 1 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( nextPointL, u0, 0 );
+ addVertex( innerPoint, u1, 1 );
+
+ } else {
+
+ addVertex( lastPointR, u0, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( currentPointR, u1, 1 );
+ addVertex( lastPointL, u0, 0 );
+ addVertex( innerPoint, u1, 0 );
+ addVertex( currentPointR, u1, 1 );
+ addVertex( currentPointR, u0, 1 );
+ addVertex( innerPoint, u1, 0 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( currentPoint, u1, 0.5 );
+ addVertex( innerPoint, u1, 0 );
+ addVertex( nextPointR, u0, 1 );
+
+ }
+
+ }
+
+ }
+
+ function addCapGeometry( center, p1, p2, joinIsOnLeftSide, start, u ) {
+
+ // param center: End point of the path
+ // param p1, p2: Left and right cap points
+ switch ( style.strokeLineCap ) {
+
+ case 'round':
+ if ( start ) {
+
+ makeCircularSector( center, p2, p1, u, 0.5 );
+
+ } else {
+
+ makeCircularSector( center, p1, p2, u, 0.5 );
+
+ }
+
+ break;
+
+ case 'square':
+ if ( start ) {
+
+ tempV2_1.subVectors( p1, center );
+ tempV2_2.set( tempV2_1.y, - tempV2_1.x );
+ tempV2_3.addVectors( tempV2_1, tempV2_2 ).add( center );
+ tempV2_4.subVectors( tempV2_2, tempV2_1 ).add( center ); // Modify already existing vertices
+
+ if ( joinIsOnLeftSide ) {
+
+ tempV2_3.toArray( vertices, 1 * 3 );
+ tempV2_4.toArray( vertices, 0 * 3 );
+ tempV2_4.toArray( vertices, 3 * 3 );
+
+ } else {
+
+ tempV2_3.toArray( vertices, 1 * 3 );
+ tempV2_3.toArray( vertices, 3 * 3 );
+ tempV2_4.toArray( vertices, 0 * 3 );
+
+ }
+
+ } else {
+
+ tempV2_1.subVectors( p2, center );
+ tempV2_2.set( tempV2_1.y, - tempV2_1.x );
+ tempV2_3.addVectors( tempV2_1, tempV2_2 ).add( center );
+ tempV2_4.subVectors( tempV2_2, tempV2_1 ).add( center );
+ const vl = vertices.length; // Modify already existing vertices
+
+ if ( joinIsOnLeftSide ) {
+
+ tempV2_3.toArray( vertices, vl - 1 * 3 );
+ tempV2_4.toArray( vertices, vl - 2 * 3 );
+ tempV2_4.toArray( vertices, vl - 4 * 3 );
+
+ } else {
+
+ tempV2_3.toArray( vertices, vl - 2 * 3 );
+ tempV2_4.toArray( vertices, vl - 1 * 3 );
+ tempV2_4.toArray( vertices, vl - 4 * 3 );
+
+ }
+
+ }
+
+ break;
+
+ case 'butt':
+ default:
+ // Nothing to do here
+ break;
+
+ }
+
+ }
+
+ function removeDuplicatedPoints( points ) {
+
+ // Creates a new array if necessary with duplicated points removed.
+ // This does not remove duplicated initial and ending points of a closed path.
+ let dupPoints = false;
+
+ for ( let i = 1, n = points.length - 1; i < n; i ++ ) {
+
+ if ( points[ i ].distanceTo( points[ i + 1 ] ) < minDistance ) {
+
+ dupPoints = true;
+ break;
+
+ }
+
+ }
+
+ if ( ! dupPoints ) return points;
+ const newPoints = [];
+ newPoints.push( points[ 0 ] );
+
+ for ( let i = 1, n = points.length - 1; i < n; i ++ ) {
+
+ if ( points[ i ].distanceTo( points[ i + 1 ] ) >= minDistance ) {
+
+ newPoints.push( points[ i ] );
+
+ }
+
+ }
+
+ newPoints.push( points[ points.length - 1 ] );
+ return newPoints;
+
+ }
+
+ }
+
+ }
+
+ THREE.SVGLoader = SVGLoader;
+
+} )();
diff --git a/frontend/apps/crawlab/public/three/three.min.js b/frontend/crawlab-ui/public/three/three.min.js
similarity index 100%
rename from frontend/apps/crawlab/public/three/three.min.js
rename to frontend/crawlab-ui/public/three/three.min.js
diff --git a/frontend/crawlab-ui/scripts/gen-dts.js b/frontend/crawlab-ui/scripts/gen-dts.js
new file mode 100644
index 00000000..a6d10255
--- /dev/null
+++ b/frontend/crawlab-ui/scripts/gen-dts.js
@@ -0,0 +1,33 @@
+import fs from 'fs';
+import { spawn } from 'child_process';
+import { resolve, join, dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { log } from './utils.js';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+
+function main() {
+ const typingsPath = resolve(join(__dirname, '..', 'typings'));
+ if (fs.existsSync(typingsPath)) {
+ fs.rmSync(typingsPath, { recursive: true });
+ }
+
+ const child = spawn(
+ 'vue-tsc',
+ '--declaration --emitDeclarationOnly --declarationDir ./typings --rootDir ./src'.split(
+ ' '
+ ),
+ { shell: true }
+ );
+ child.stdout.on('data', data => {
+ log(`${data}`, 'info');
+ });
+ child.stderr.on('data', data => {
+ log(`${data}`, 'error');
+ });
+ child.on('close', code => {
+ log(`child process exited with code ${code}`, 'info');
+ });
+}
+
+main();
diff --git a/frontend/crawlab-ui/scripts/gen-index.js b/frontend/crawlab-ui/scripts/gen-index.js
new file mode 100644
index 00000000..5367937f
--- /dev/null
+++ b/frontend/crawlab-ui/scripts/gen-index.js
@@ -0,0 +1,137 @@
+import path from 'path';
+import fs from 'fs';
+import os from 'os';
+
+const EXPORT_MODULES = ['components', 'views', 'directives', 'layouts'];
+
+const COMPONENT_PREFIX = 'Cl';
+const INDEX_COMP_NAME = 'index';
+
+function isWindows() {
+ return os.platform() === 'win32';
+}
+
+function getModulePath(moduleName) {
+ let modulePath = path.resolve(`./src/${moduleName}`);
+ if (isWindows()) {
+ modulePath = modulePath.replace(/\\/g, '/');
+ }
+ return modulePath;
+}
+
+function readFileAndModify(filePath, componentName) {
+ const fileContent = fs.readFileSync(filePath, 'utf8');
+ let newFileContent = '';
+ newFileContent = addComponentName(fileContent, componentName);
+ if (newFileContent !== fileContent) {
+ fs.writeFileSync(filePath, newFileContent);
+ }
+}
+
+function processFile(filePath, moduleName) {
+ const fileName = path.basename(filePath);
+ const relPath = `.${filePath.replace(getModulePath(moduleName), '')}`;
+
+ // skip index.ts
+ if (fileName.split('.')[0] === INDEX_COMP_NAME) {
+ return;
+ }
+
+ if (filePath.endsWith('.vue')) {
+ const compName = fileName.replace('.vue', '');
+ const importLine = `import ${compName} from '${relPath}';`;
+ const exportLine = `${compName} as ${COMPONENT_PREFIX}${compName},`;
+
+ readFileAndModify(filePath, compName);
+ return { importLine, exportLine };
+ } else if (
+ !filePath.endsWith('.d.ts') &&
+ (filePath.endsWith('.ts') || filePath.endsWith('.tsx')) &&
+ fileName !== INDEX_COMP_NAME
+ ) {
+ let compName = fileName.replace(/.tsx?$/, '');
+ compName += compName === 'export' ? '_' : '';
+
+ let importLine;
+ if (compName.startsWith('use')) {
+ importLine = `import ${compName} from '${relPath.replace(/.tsx?$/, '')}';`;
+ } else {
+ importLine = `import * as ${compName} from '${relPath.replace(/.tsx?$/, '')}';`;
+ }
+ const exportLine = `${compName} as ${compName},`;
+ return { importLine, exportLine };
+ }
+}
+
+function addComponentName(content, componentName) {
+ const setupScriptTagRegex = /(
diff --git a/frontend/crawlab-ui/src/admin/baidu.ts b/frontend/crawlab-ui/src/admin/baidu.ts
new file mode 100644
index 00000000..b097991d
--- /dev/null
+++ b/frontend/crawlab-ui/src/admin/baidu.ts
@@ -0,0 +1,12 @@
+// baidu tongji
+export const initBaiduTonji = () => {
+ if (localStorage.getItem('useStats') !== '0') {
+ window._hmt = window._hmt || [];
+ (function () {
+ const hm = document.createElement('script');
+ hm.src = 'https://hm.baidu.com/hm.js?c35e3a563a06caee2524902c81975add';
+ const s = document.getElementsByTagName('script')[0];
+ s?.parentNode?.insertBefore(hm, s);
+ })();
+ }
+};
diff --git a/frontend/crawlab-ui/src/admin/clarity.ts b/frontend/crawlab-ui/src/admin/clarity.ts
new file mode 100644
index 00000000..c0c5324c
--- /dev/null
+++ b/frontend/crawlab-ui/src/admin/clarity.ts
@@ -0,0 +1,6 @@
+// baidu tongji
+export const initClarity = () => {
+ if (localStorage.getItem('useStats') !== '0') {
+ import('@/assets/js/clarity.js');
+ }
+};
diff --git a/frontend/crawlab-ui/src/admin/umeng.ts b/frontend/crawlab-ui/src/admin/umeng.ts
new file mode 100644
index 00000000..e4aeece3
--- /dev/null
+++ b/frontend/crawlab-ui/src/admin/umeng.ts
@@ -0,0 +1,18 @@
+import useRequest from '@/services/request';
+
+const { get } = useRequest();
+export const getEventParamsWrapped = (
+ eventParams?: TrackEventParams
+): TrackEventParamsWrapped => {
+ if (!eventParams) return {};
+ const res: TrackEventParamsWrapped = {};
+ Object.keys(eventParams).forEach(key => {
+ const value = eventParams[key];
+ if (typeof value === 'function') {
+ res[key] = value();
+ } else {
+ res[key] = value;
+ }
+ });
+ return res;
+};
diff --git a/frontend/crawlab-ui/src/assets/favicon.ico b/frontend/crawlab-ui/src/assets/favicon.ico
new file mode 100644
index 00000000..12b5c475
Binary files /dev/null and b/frontend/crawlab-ui/src/assets/favicon.ico differ
diff --git a/frontend/crawlab-ui/src/assets/js/clarity.js b/frontend/crawlab-ui/src/assets/js/clarity.js
new file mode 100644
index 00000000..ae014ea6
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/clarity.js
@@ -0,0 +1,12 @@
+(function (c, l, a, r, i, t, y) {
+ c[a] =
+ c[a] ||
+ function () {
+ (c[a].q = c[a].q || []).push(arguments);
+ };
+ t = l.createElement(r);
+ t.async = 1;
+ t.src = 'https://www.clarity.ms/tag/' + i;
+ y = l.getElementsByTagName(r)[0];
+ y.parentNode.insertBefore(t, y);
+})(window, document, 'clarity', 'script', 'htjm67dldb');
diff --git a/frontend/crawlab-ui/src/assets/js/svg/163.js b/frontend/crawlab-ui/src/assets/js/svg/163.js
new file mode 100644
index 00000000..42b084b6
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/163.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzIxMjk0NzgxODMzIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjE1MjYiIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj48cGF0aCBkPSJNMTAxOC4yNCA1MDIuNGMtNS4xMi0yNzAuNzItMjI1LjkyLTQ5MS41Mi00OTYuNjQtNDk2LjY0LTI4Ny4zNi01LjEyLTUyMC45NiAyMjguNDgtNTE1Ljg0IDUxNS44NCA1LjEyIDI3MC43MiAyMjUuOTIgNDkxLjUyIDQ5Ni42NCA0OTYuNjRDNzg5Ljc2IDEwMjMuMzYgMTAyMy4zNiA3ODkuNzYgMTAxOC4yNCA1MDIuNHpNMzEzLjQzIDUzNC43MThjMzkuMjUgMCAxMDMuOTItMjYuMDExIDEyMi41NTgtNDguNjY2IDcuMzEzLTcuOTY5IDIuNjM3LTEwLjAxLTI4LjY0NS0xMi42NDQtNDEuOTUxLTMuMjkzLTU2LjYzNy0xNi42NjMtNjQuNjA2LTU4LjYxNS03LjM3Ni0zNS45NTgtNS45OTQtOTkuMjQ1IDEuMzE1LTExMy45MzEgOC4wMzYtMTMuOTYyIDMwLjAzMS0xNy4zMiAzNy4zNC01LjMzNCAyLjcwMiA0LjY3NSA0LjY3NiAyOS4zMDcgNS4zMzUgNTUuOTc4IDEuMzE5IDczLjI5OCA1LjMzNCA3Ni42NTggMTAyLjYwNSA3My4yOTggNDMuMzM0LTEuMzgzIDkwLjYyLTIuNjk4IDEwNi42MjEtMy4zNTcgNDIuNjExIDAgNTcuMjk3LTE3Ljk3OSA1MS45NjItNjQuNjA2LTQuNjc1LTQxLjk1MS0xNC4wMjctNTkuMjcxLTM3LjM0LTY3Ljk2Mi0yMC42MTItOC42OTYtMTM3LjkwNC0xMS4zMjktMTg0LjUzLTQuMDItMzQuNjQyIDQuNjc1LTM1LjI5OCA0LjY3NS0zNS4yOTgtMTEuOTg1IDAtMTkuOTUzIDcuMzA5LTI1LjI4OCAzOS45NzMtMzEuOTQxIDM1Ljk1OC03LjMwOCAxNTMuMTgxLTUuOTk0IDE4My4yMTIgMS45NzggNTYuNjM4IDE0LjY4NiA2NS45MjQgMjQuNjI5IDgyLjY1MiA5NC4wNDIgOS45NDMgNDEuMjkyIDkuMjg0IDU5LjkzLTIuNzAxIDg4LjU3OS0xMy4zMDMgMzIuNjY0LTM4LjY1OSA0MC42MzMtMTE5LjkyNSAzOS45NzMtNjUuMzI5LTAuNjU5LTY2LjY0OCAwLTc3Ljk3MyAxNy4zMTktMTQuMDMgMjIuNTkxLTcwLjY2NCA2Ny4yNC05NS45NTYgNzYuNTktNzYuNTkgMjcuMjY1LTExOS4yNjUgMjkuOTY3LTEyNS45MTkgOC42MjhDMjY3LjQ2MyA1NDcuMzY2IDI4Mi4wODQgNTM0LjcxOCAzMTMuNDMgNTM0LjcxOHpNNDI5LjMzOSAzMzkuNTIyYzQuNjc1LTUuMzM0IDMwLjAzMS03Ljk2OSA3OS4yOTEtNy4zNzcgOTQuNTY5IDAuNzI0IDg5Ljg5My0wLjY1OSA4Ny4yNiAyMC42OGwtMS45NzcgMTcuOTc5LTgzLjMwOSAwLjY1OWMtNzQuNjE1IDAtODMuMzA4LTEuMzE5LTg1LjI4Mi0xMS45ODhDNDI0LjAwNCAzNTIuODI1IDQyNS45NzkgMzQ0LjE5OCA0MjkuMzM5IDMzOS41MjJ6TTQ3MC42MzEgNzI3LjIxN2MtNDAuNjMzIDI4LjY0OC0xMTUuMjQ5IDYwLjY1Ny0xNDEuMjY0IDYwLjY1Ny00Mi42MDggMC0zOS45NzMtNDEuOTUxIDMuMzYtNTMuOTQgNzMuOTU3LTIwLjAxNyAxNDkuOTU1LTYyLjYyOCAyMzkuODUtMTMzLjg4MyAzNi4wMjEtMjcuOTg5IDM5Ljk3My0yOS4zNzIgNTAuNjQzLTE2LjEzNiA5LjI4NyAxMS4zMjkgNy4zMDkgMTMuMzY3LTU0LjY1OSA2Ni42NDgtMjAuNjggMTcuOTc5LTM3Ljk5OSAzNi42MTYtMzcuOTk5IDQxLjk1MSAwIDQuNjc2LTUuMzM1IDguNjkyLTExLjMyOSA4LjY5MlM0OTEuMjQzIDcxMi41MzEgNDcwLjYzMSA3MjcuMjE3ek03NTMuMTU1IDYzNi43MzJjLTMuMjkzIDQxLjI5Mi0xNC42MjIgNjIuNjI4LTQ4LjYwMSA5My4yNTQtNTMuMzQ1IDQ3Ljk0My0xMjcuOTYgNzEuMjU2LTE1My4yNDggNDguNjAyLTE2LjY2My0xNS4zNDUtMjkuOTY3LTQ4LjAxLTI0LjYzMi02MC42NTQgMy4yOTMtOS4zNTIgNS45OTQtOS4zNTIgMjQuNjMyIDAuNjU5IDQxLjk1MSAyMS4zMzYgMTAxLjk0Ni0xLjMxOSAxMzYuNTg1LTUxLjMwMyAzNC42NDItNTAuNjQzIDE5Ljk1Ni0xMDUuOTY1LTI3Ljk4OS0xMDUuOTY1LTE3LjMyIDAtMjMuOTY5LTMuOTQ5LTMwLjYyMi0xOC42MzUtMTIuNzEyLTI1Ljk1MS0zOC0yNy4yNjUtNjYuNjQ4LTIuNjM3QzQyOS40MDMgNjU1LjMwMyAzMDUuNDYyIDcxNy45MzQgMjc3LjQ3MyA2ODQuNjFjLTE3Ljk3OS0yMS4yNzEtNC4wMTYtMzEuOTQgNjkuOTQtNTMuMjc2IDI1LjM1Ni03LjM3NyA2Ny4zMDQtMjkuMzA4IDExMy45MzEtNjAuNjU0IDY2LjY0OC00My45MjkgNzMuOTU2LTUwLjU3OSA3MS4zMjMtNjUuOTI0LTIuNzAxLTEyLjY0NCAwLTE3Ljk3OSAxMS45ODUtMjIuNjU1IDguNjI3LTMuMzYgMjMuMzEzLTQuNjc1IDMyLjY2NC0zLjI5MyA5LjI4NyAwLjY1OSAzMC42MjcgMy4zNTcgNDcuOTQ2IDUuMzM1QzcwMS44NTMgNDkyLjgzNCA3NTkuMTQ5IDU2Mi4xMTYgNzUzLjE1NSA2MzYuNzMyeiIgZmlsbD0iI2Q4MWUwNiIgcC1pZD0iMTUyNyI+PC9wYXRoPjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/aol.js b/frontend/crawlab-ui/src/assets/js/svg/aol.js
new file mode 100644
index 00000000..52911441
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/aol.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c29kaXBvZGk9Imh0dHA6Ly9zb2RpcG9kaS5zb3VyY2Vmb3JnZS5uZXQvRFREL3NvZGlwb2RpLTAuZHRkIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgd2lkdGg9IjY0IgogICBoZWlnaHQ9IjY0IgogICB2aWV3Qm94PSIwIDAgNC45MzQ1NTM2IDQuOTM0NTUzNiIKICAgdmVyc2lvbj0iMS4xIgogICBpZD0ic3ZnMTIiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImFvbC1pY29uLnN2ZyIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC45Mi4yIDVjM2U4MGQsIDIwMTctMDgtMDYiPgogIDxtZXRhZGF0YQogICAgIGlkPSJtZXRhZGF0YTE4Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZGVmcwogICAgIGlkPSJkZWZzMTYiIC8+CiAgPHNvZGlwb2RpOm5hbWVkdmlldwogICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIKICAgICBib3JkZXJjb2xvcj0iIzY2NjY2NiIKICAgICBib3JkZXJvcGFjaXR5PSIxIgogICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiCiAgICAgZ3JpZHRvbGVyYW5jZT0iMTAiCiAgICAgZ3VpZGV0b2xlcmFuY2U9IjEwIgogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIgogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiCiAgICAgaW5rc2NhcGU6d2luZG93LXdpZHRoPSIxNDQwIgogICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijc5OSIKICAgICBpZD0ibmFtZWR2aWV3MTQiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGlua3NjYXBlOnpvb209IjYuOTE0OCIKICAgICBpbmtzY2FwZTpjeD0iNTUuNDU0NDMxIgogICAgIGlua3NjYXBlOmN5PSIzMC4wNzI1NDEiCiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjAiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9IjEiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJzdmcxMiIgLz4KICA8cGF0aAogICAgIHN0eWxlPSJjbGlwLXJ1bGU6ZXZlbm9kZDtmaWxsOiMwMDAwMDA7ZmlsbC1ydWxlOmV2ZW5vZGQ7c3Ryb2tlLXdpZHRoOjAuMjA0MTg1NjEiCiAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICBpZD0icGF0aDQiCiAgICAgZD0ibSAzLjE5NjczMTgsMS41OTgxOTE4IGMgLTAuMjYyMzc5LC0wLjE5Njg0IC0wLjczNzcyMywtMC4xNDc2MyAtMS4wMTY0MzYsMC4wMTYzIC0wLjUyNDU1MywwLjI5NTA1IC0wLjg2ODgxLDAuODUyNDggLTAuOTE4MDE5LDEuNDA5OSAwLjE0NzYyNywtMC4zOTM0NiAwLjUwODIxOCwtMC44MTk2IDAuOTM0MzU0LC0wLjk1MDg5IDAuMzQ0MjU3LC0wLjExNDc1IDAuNzA0ODQ4LC0wLjAxNjMgMC45MTgyMjIsMC4yNDU4NCAwLjQ5MTg4NCwwLjU5MDMgMC4wMzI2NywxLjQ1ODkxIDAuMDMyNjcsMS40NTg5MSAwLjM3NDQzOCwtMC4zNDMxMiAwLjU3MzM4LC0wLjgzNzMyIDAuNTQxMDkyLC0xLjM0NDE2IGwgMS4yNDU5NDEsMi4xNjQxNyBoIC0yLjA5ODQxNiBjIDAuNTU3MjIzLC0wLjI3ODcyIDAuODg1MTQ1LC0wLjg2OTAyIDAuOTM0MzUzLC0xLjQ0Mjc4IC0wLjE5NjgzNCwwLjQ5MjA5IC0wLjY4ODUxMywxLjAxNjY0IC0xLjI2MjI3NSwxLjAxNjY0IC0wLjI5NTA0OCwwIC0wLjU3Mzc2MiwtMC4xNjM5NiAtMC43MjEzODgsLTAuNDI2MzQgLTAuMjEzMTcsLTAuMzkzMjYgLTAuMTE0NzUyLC0wLjkxODAyIDAuMDY1NTQsLTEuMjc4NjEgLTAuMzkzNDY2LDAuMzYwOCAtMC41NzM3NjIsMC45MDE4OSAtMC41MDgyMTgsMS40NzUyNCAwLjA2NTU0LDAuMjQ2MDUgMC4xODAyOTYsMC41MjQ3NiAwLjQwOTgsMC42NTYwNSBIIC0yLjJlLTYgbCAyLjQ3NTU0NiwtNC4yNjIzNyB6IgogICAgIHNvZGlwb2RpOm5vZGV0eXBlcz0iY2NjY2NjY2NjY3NjY2NjY2NjIiAvPgo8L3N2Zz4K`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/arrow-clockwise.js b/frontend/crawlab-ui/src/assets/js/svg/arrow-clockwise.js
new file mode 100644
index 00000000..e8682613
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/arrow-clockwise.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktYXJyb3ctY2xvY2t3aXNlIiB2aWV3Qm94PSIwIDAgMTYgMTYiPgogIDxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTggM2E1IDUgMCAxIDAgNC41NDYgMi45MTQuNS41IDAgMCAxIC45MDgtLjQxN0E2IDYgMCAxIDEgOCAydjF6Ii8+CiAgPHBhdGggZD0iTTggNC40NjZWLjUzNGEuMjUuMjUgMCAwIDEgLjQxLS4xOTJsMi4zNiAxLjk2NmMuMTIuMS4xMi4yODQgMCAuMzg0TDguNDEgNC42NThBLjI1LjI1IDAgMCAxIDggNC40NjZ6Ii8+Cjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/arrow-counterclockwise.js b/frontend/crawlab-ui/src/assets/js/svg/arrow-counterclockwise.js
new file mode 100644
index 00000000..40e163ca
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/arrow-counterclockwise.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktYXJyb3ctY291bnRlcmNsb2Nrd2lzZSIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik04IDNhNSA1IDAgMSAxLTQuNTQ2IDIuOTE0LjUuNSAwIDAgMC0uOTA4LS40MTdBNiA2IDAgMSAwIDggMnYxeiIvPgogIDxwYXRoIGQ9Ik04IDQuNDY2Vi41MzRhLjI1LjI1IDAgMCAwLS40MS0uMTkyTDUuMjMgMi4zMDhhLjI1LjI1IDAgMCAwIDAgLjM4NGwyLjM2IDEuOTY2QS4yNS4yNSAwIDAgMCA4IDQuNDY2eiIvPgo8L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/cassandra.js b/frontend/crawlab-ui/src/assets/js/svg/cassandra.js
new file mode 100644
index 00000000..fd18e5c4
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/cassandra.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48cGF0aCBmaWxsPSIjYmFlNmZhIiBkPSJNNjMuNzk3IDM4LjcwMWMtMTIuMTQyLjI3My0yNC41IDQuMDEtMzguNDc5IDExLjc1Mi0yLjUzOCAyLjkzMy0zLjg0OCA2LjExMi0zLjU3IDkuMzQuOTQgMTAuOTcgMTkuNzI1IDE4LjMxNSA0MS45NTYgMTYuNDA4IDIyLjIzNS0xLjkwOCAzOS40OTUtMTIuMzQ2IDM4LjU1NC0yMy4zMTMtLjE3Ny0yLjA1OC0uOTgyLTMuOTktMi4zMi01Ljc1NS0xMi43Mi01LjctMjQuMzM1LTguNjk5LTM2LjE0LTguNDMyWiIvPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik02NS4zNjkgMzguNjg0Yy05Ljg3Ny0uMDE4LTE5Ljg1IDIuMjMyLTMwLjY4OSA3LjA2Mi0uNDU1IDEuNzQtLjcgMy41NS0uNyA1LjQxNSAwIDEzLjAxIDExLjc0OCAyMy41NTkgMjYuMjQgMjMuNTU5IDE0LjQ5IDAgMjYuMjM5LTEwLjU0OCAyNi4yMzktMjMuNTU5IDAtMy41MDYtLjg2Mi02LjgzLTIuMzkzLTkuODItNi4zMi0xLjcyMi0xMi40OS0yLjY0NS0xOC42OTctMi42NTdaIiBvcGFjaXR5PSIuMzUiLz48cGF0aCBmaWxsPSIjZmZmIiBkPSJNNjAuMjIgMzcuNzYzYy00LjY5Ny0uMjg3LTEyLjc5IDIuMTYzLTE5LjUzNyA0LjYwN2EyMC4wMzEgMjAuMDMxIDAgMCAwLTEuNTk0IDcuODQ2YzAgMTEuNCAxMC4zMzUgMjAuNjQyIDIxLjI2OCAyMC42NDIgMTAuODYgMCAyMC40Ny05LjI1OCAyMS4yNjgtMjAuNjQuMjYtMy43MDktMS4wMTctNy4yLTIuNzg3LTEwLjIxNC02Ljk5Ny0xLjIzLTE0LjQyLTEuOTg0LTE4LjYxOS0yLjI0Ii8+PHBhdGggZmlsbD0iIzM1MzMzMyIgZD0iTTQ4LjE3IDQ4LjcyYy41NzYtMS4yNzYgMS40NDgtMS45IDIuMjkzLTIuOTY5LS4wOTMtLjI1My0uMy0xLjA4Mi0uMy0xLjM3YTIuMzIyIDIuMzIyIDAgMCAxIDMuMTc4LTIuMTU1IDE2LjkxMyAxNi45MTMgMCAwIDEgMTMuOTg2LTMuMDdjLjQ2Ni4xMDUuOTIuMjMxIDEuMzY3LjM3MS01LjAyNS0uNjk3LTEwLjY4OCAxLjA4My0xNC41NCA0LjE5Mi4xMTcuMjc3LjQ0MyAxLjA3LjQ0MyAxLjM5MSAwIDEuMjgyLS44MjkgMS41OTMtMi4xMSAxLjU5My0uMjkzIDAtLjU3LS4wNTYtLjgyOC0uMTUzLS44NyAxLjE2OC0xLjkzNyAzLjM5NC0yLjQ1OSA0Ljc4NiAyLjEzIDEuOTA2IDQuMDU2IDIuNTk1IDYuNTYzIDMuNTM0LS4wMDUtLjEyLjA4Ni0uMjMuMDg2LS4zNTIgMC0zLjg2NiAzLjQwMy03LjAwNyA3LjI3LTcuMDA3YTYuOTkzIDYuOTkzIDAgMCAxIDYuODkyIDUuODI0YzEuNzMtMS4xIDMuNC0xLjk0MyA0LjY3NC0zLjU3My0uMjk2LS4zNjItLjYxOC0xLjM5LS42MTgtMS44OTUgMC0xLjE2Mi45NDQtMi4xMDUgMi4xMDktMi4xMDUuMTY0IDAgLjMyMi4wMjMuNDc3LjA2YTE4LjQxOCAxOC40MTggMCAwIDAgMS40NDUtNC4xOTJjLTYuMDQ1LTQuNDc0LTE3Ljc0LTMuODYzLTE3Ljc0LTMuODYzcy05LjMwOC0uNDg2LTE1LjYwOCAyLjU4NGMuNDIgMy4wMzggMS42MzIgNS45MjUgMy40MiA4LjM3Ii8+PHBhdGggZmlsbD0iIzM1MzMzMyIgZD0iTTc3LjA2MiA0Ni45NzdjLjQyOS4zODQuNjYyLjcyMy44NDQgMS4wNTIuNTY3IDEuMDE2LS41NjggMS45NDctMS43MyAxLjk0N2EyLjEyIDIuMTIgMCAwIDEtLjM0Ny0uMDM1Yy0xLjMxMSAyLjA0Ny0zLjYwNiA0LjY3NC01LjY0NiA1Ljk5NiAzLjEyOC41MTMgNi4wNzcgMS43NjQgOC40MjQgMy43IDEuNzA4LTMuMDYzIDMuMDItNy40ODMgMy4wMi0xMS4yMzcgMC0yLjg2LTEuMjY0LTQuOTU1LTMuMTY3LTYuNDkyLS4xMDEgMS41MzEtLjkyIDMuNjc0LTEuMzk4IDUuMDdtLTI1LjM4NCAxOS4zOWMxLjA0MS0yLjg3NyAzLjYwNS01LjEwNyA2LjAzLTcuMDY4LS4zNTYtLjQ4Ni0uODg3LTEuNjg4LTEuMTA4LTIuMjU4YTE2Ljk2NSAxNi45NjUgMCAwIDEtNy42MDUtNC44MTJjLS4wMTMuMDY1LS4wMy4xMy0uMDQ4LjE5NGExNS4zNCAxNS4zNCAwIDAgMC0uMjYgNS4xMDIgMy4wNDcgMy4wNDcgMCAwIDEgMi40ODYgMi45OThjMCAuODg1LS43NzcgMi4xNjYtMS4zNzggMi43MjUuNjU1IDEuNDEzIDEuMSAyLjA5NSAxLjg4MyAzLjEyIi8+PHBhdGggZmlsbD0iIzM1MzMzMyIgZD0iTTUxLjQ5NSA2OC43ODVjLTEuMjQ0LTEuMjM3LTIuMTAzLTMuNzAyLTIuOTEtNS4yNWEyLjkzIDIuOTMgMCAwIDEtLjQ2My4wNGMtMS42ODYgMC0zLjQ1Mi0xLjQxNi0zLjA1Mi0zLjA1My4yMjItLjkwNi44NDYtMS40OTggMS44NjUtMS45OTItLjIzMy0xLjg3Ny0uMjk2LTQuNTc1LjE0LTYuNTI1LjEyNS0uNTU3LjM5LS44Mi41NjYtMS4zNS0xLjg2LTIuNzIzLTMuMDYzLTYuMjk2LTMuMDYzLTkuODM5IDAtLjEzLjAwOC0uMjU3LjAxLS4zODUtMy4xNDMgMS41NzYtNS41IDQuMDYtNS41IDcuOTY3IDAgOC42NDggNC45OCAxNy4xMzIgMTIuMzkgMjAuNDU2LjAwNi0uMDIzLjAxLS4wNDcuMDE3LS4wN002OS43MTcgNTYuODNhNi45OTcgNi45OTcgMCAwIDEtNi41OTkgNC42OCA2Ljk1IDYuOTUgMCAwIDEtNC4zMTgtMS41Yy0yLjQ1NCAxLjkxLTQuNjggNS4yNjctNS44MSA4LjE3OS41MDYuNTQ1Ljg2Ljk5NiAxLjQzIDEuNDggMS43MTYuNDQ4IDQuMDggMCA1LjkzNyAwIDcuNDczIDAgMTQuMDktMy4zMjUgMTcuODgzLTkuMTUzLTIuNTIzLTEuNzQ3LTUuNDk4LTMuNDA5LTguNTIzLTMuNjg2Ii8+PHBhdGggZmlsbD0iIzExODViMCIgZD0iTTQ3Ljk3OCA0OS43M2ExNi45MDggMTYuOTA4IDAgMCAxIDIuMTQzLTMuNTI0IDIuMzIzIDIuMzIzIDAgMCAxIDIuMTc0LTMuMTM1Yy4zMDEgMCAuNTkuMDYuODU1LjE2NWExNi45MjIgMTYuOTIyIDAgMCAxIDE1LjM1My0yLjY5OGMtNS4wMjYtLjY5Ni0xMC4yMTcuODQzLTE0LjA3IDMuOTUyYTIuMzIyIDIuMzIyIDAgMCAxLTIuOTY2IDMuMDcgMTUuNjY0IDE1LjY2NCAwIDAgMC0yLjEwMyAzLjg1MmMxLjgzIDEuOTcxIDQuMDc0IDMuNTMgNi41ODIgNC40NjgtLjAwNi0uMTItLjAxNy0uMjM3LS4wMTctLjM2YTYuOTk3IDYuOTk3IDAgMCAxIDYuOTk3LTYuOTk5YzMuNDY2IDAgNi4zMzUgMi41MiA2Ljg5NSA1LjgyNGExOC4xMzIgMTguMTMyIDAgMCAwIDQuNTM3LTQuMTQyIDIuMDg2IDIuMDg2IDAgMCAxLS40ODEtMS4zMjRjMC0xLjE2NC45NDMtMi4xMDcgMi4xMDctMi4xMDcuMTY0IDAgLjMyMi4wMjQuNDc3LjA2YTE4LjQwMyAxOC40MDMgMCAwIDAgMS40NDUtNC4xOWMtNi4wNDMtNC40NzctMTcuNzQtMy44NjYtMTcuNzQtMy44NjZzLTkuMzA4LS40ODYtMTUuNjA4IDIuNTg2Yy40MjEgMy4wMzcgMS42MzIgNS45MjYgMy40MiA4LjM2OCIvPjxwYXRoIGZpbGw9IiMxMTg1YjAiIGQ9Ik03Ny4zODggNDcuMzIxYy40MjkuMzg0LjcwMy45MzcuNzAzIDEuNTU4YTIuMTA4IDIuMTA4IDAgMCAxLTIuMTA3IDIuMTA3Yy0uMTE4IDAtLjIzMy0uMDE3LS4zNDctLjAzNmExNy4wNDMgMTcuMDQzIDAgMCAxLTUuMDg3IDUuMTE4IDE3LjAyNSAxNy4wMjUgMCAwIDEgOC4xOTggMy42ODQgMjEuMTU4IDIxLjE1OCAwIDAgMCAyLjY4Ny0xMC4zNDJjMC0yLjg2LTEuMjYzLTQuOTU0LTMuMTY3LTYuNDktLjEgMS41My0uNCAzLjAwNi0uODggNC40MDFNNTIgNjcuMDZhMTYuOTc4IDE2Ljk3OCAwIDAgMSA1LjI3OC03LjQyIDYuOTcxIDYuOTcxIDAgMCAxLS44Ny0xLjU4NiAxNi45NjUgMTYuOTY1IDAgMCAxLTcuNjAzLTQuODEyYy0uMDE1LjA2NS0uMDMzLjEyOC0uMDQ4LjE5MmExNS4yOTUgMTUuMjk1IDAgMCAwLS4yNjMgNS4xMDJBMy4wNSAzLjA1IDAgMCAxIDUwIDYzLjc3M2ExOC4zMzEgMTguMzMxIDAgMCAwIDIgMy4yODciLz48cGF0aCBmaWxsPSIjMTE4NWIwIiBkPSJNNTEuNDg2IDY4Ljc0OWExNi45NiAxNi45NiAwIDAgMS0zLjA5LTQuMjAyIDMgMyAwIDAgMS0uNDY2LjAzOSAzLjA1MyAzLjA1MyAwIDAgMS0zLjA1Mi0zLjA1M2MwLTEuMjA5LjcwNS0yLjI1IDEuNzI0LTIuNzQ0YTE2Ljg4NCAxNi44ODQgMCAwIDEgLjI4My01Ljc3MyAxNy4xMyAxNy4xMyAwIDAgMSAuNDUtMS42MzMgMTYuODg1IDE2Ljg4NSAwIDAgMS0yLjk0OC05LjU1N2MwLS4xMjkuMDA3LS4yNTcuMDEtLjM4Ni0zLjE0NCAxLjU3Ni01LjUgNC4wNjEtNS41IDcuOTcgMCA4LjY0NyA1LjE2NCAxNi4wODUgMTIuNTc0IDE5LjQxbC4wMTUtLjA3MW0xOC4wMzktMTAuOTFhNi45OTcgNi45OTcgMCAwIDEtNi41OTkgNC42OCA2Ljk2IDYuOTYgMCAwIDEtNC4zMTgtMS40OThjLTIuNDU0IDEuOTExLTQuMzA1IDQuNTE1LTUuNDM2IDcuNDI2YTE4LjY3IDE4LjY3IDAgMCAwIDEuNjIgMS41NDcgMjEuMzggMjEuMzggMCAwIDAgNS4zNzMuNjg1YzcuNDczIDAgMTQuMDQtMy44NTggMTcuODM0LTkuNjg2LTIuNTIyLTEuNzQ3LTUuNDQ4LTIuODc2LTguNDc0LTMuMTU0Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZD0ibTYwLjk1IDQ5LjU2LTMuNjYzLTYuNTUgMS45ODIgNi45NTMtNS45NDItNC4zMiA0LjY1NSA1Ljg0OC03LjM3Ny0yLjcxMyA1LjgyMSA0LjMwNy03LjQ2NC4yMyA3LjYzMiAxLjk3LTcuNjQgMS42NTMgNy41NDEuNTQ1LTYuMzYgNC4wNzMgNi41NS0yLjM3NC00LjU0NSA1Ljc1NiA1Ljc0MS00LjYzMi0yLjA2NiA3LjcyMiA0LjQzMi02LjI4NC0uNjIyIDcuNDIzIDIuNTE3LTYuNzMgMi4xNDMgNy4wNzIuNTItNy4wOTMgMy4xOCA2LjA1Mi0xLjg3OC02LjkyIDUuODIgNC41MDgtMy43NTgtNS43NSA2LjM4NiAyLjQ5LTQuODgzLTQuOCA2LjU0OC41MTQtNi40NS0yLjQ3NCA2LjQ2Ny0xLjU0Mi02LjQ5LS4zMzIgNS4yOC00LjAzNy01Ljk5MiAyLjE0OCA0LjA3My01LjY0NS01LjYyNiAzLjc0IDEuOTY3LTYuNjA4LTMuODY0IDUuNDQ3LS4wODctNi44ODQtMi41NDQgNi4xMzEtMi4xMTgtNi4yMDN6bTAgMCIvPjxwYXRoIGZpbGw9IiMzNTMzMzMiIGQ9Ik0uOTMgNjguNDFDMjAuOTgyIDQ4LjM1NCAzNyA0Mi4xNjQgNTEuNTM1IDM5LjMzN2MxLjkyNS0uMzc0IDIuMjg4LTQuNTc3IDIuMjg4LTQuNTc3cy4yNjggMy4wOTYgMS42MTUgMy41YzEuMzQ1LjQwMyAzLjA5Ni00Ljg0NyAzLjA5Ni00Ljg0N3MtMS42MTUgNC43MTEgMCA0Ljk4MmMxLjYxNS4yNjggNC41NzUtNC41NzggNC41NzUtNC41NzhzLTEuMjEgNC4xNzMtLjQwMiA0LjQ0MmMuODA3LjI3IDQuODQ0LTYuMDU3IDQuODQ0LTYuMDU3cy0yLjQyMSA0LjE3My0uMTM0IDQuNTc3YzIuMjkuNDA1IDUuNjI4LTIuODQ4IDUuNjI4LTIuODQ4cy0yLjYyNSAyLjk3NS0xLjQ1NSAzLjM4N2M0Ljk4IDEuNzUgOS4wMS00LjcyMyA5LjAxLTQuNzIzcy0uOTM0IDIuODM4LTMuMzU2IDUuOTM0YzUuMjQ4IDEuMzQ3IDkuMTQ3LTYuNTYgOS4xNDctNi41NmwtMy45IDcuOTA3YzIuMDIgMS4wNzYgMTAuMDk2LTguMjEgMTAuMDk2LTguMjFzLTQuMTczIDcuMjY3LTYuNTk1IDguNzQ2YzEuMzQ1IDEuMDggNi4xOS0zLjIyOCA2LjE5LTMuMjI4cy0zLjkwMiA0Ljg0My0yLjQyMSA1LjExM2MyLjE1MyAxLjc1IDEwLjA5My05LjAxOCAxMC4wOTMtOS4wMThzLTMuMDk0IDYuMzI2LTcuNjcyIDExLjMwNmMzLjgyNSAxLjkxMyAxMy4zMjYtMTAuMzYzIDEzLjMyNi0xMC4zNjNzLS4yNyAzLjkwMi03LjEzMyAxMC4wOTVjNS4xMTMtLjY3MyAxMS43MS0xMC4zNjQgMTEuNzEtMTAuMzY0cy0yLjQyNCA3LjI2OC04LjIxMiAxMi4zODFjNS4wMjItLjUyIDEzLjA1NS0xMi45MiAxMy4wNTUtMTIuOTJzLTMuMDk2IDguNjEzLTkuNDIgMTMuNTk0YzYuOTk4IDEuNTUgMTcuMDkzLTguODg0IDE3LjA5My04Ljg4NHMtMy45NzEgNy40MDQtOS4xNTIgMTAuNDMzYzUuNzg4IDIuMjIgMTMuNzI4LTYuOTMyIDEzLjcyOC02LjkzMnMtNy45NCAxMi4yNDgtMjAuNTkzIDExLjcxYy00LjE1LS4xNzctMTYuNzA2LTEyLjU5NS00My42MDktMTEuNTc2QzI3LjQ0NSA0My4xMDYgMTkuOTA2IDU4LjE4LjkzIDY4LjQxIiBvcGFjaXR5PSIuMzUiLz48cGF0aCBmaWxsPSIjMzUzMzMzIiBkPSJNOTMuNDA5IDMwLjE0MnMtOC4wNzUgOS4yODctMTAuMDk1IDguMjFsMy44OTktNy45MDhzLTMuOSA3LjkwNy05LjE0NiA2LjU2MmMyLjQyLTMuMDk2IDMuMzU1LTUuOTM1IDMuMzU1LTUuOTM1cy00LjAzIDYuNDc0LTkuMDEgNC43MjNjLTEuMTctLjQxIDEuNDU1LTMuMzg1IDEuNDU1LTMuMzg1cy0zLjM0IDMuMjUxLTUuNjI3IDIuODQ3Yy0yLjI4OC0uNDAzLjEzNC00LjU3Ni4xMzQtNC41NzZzLTQuMDM3IDYuMzI3LTQuODQ3IDYuMDU3Yy0uODA2LS4yNjguNDA1LTQuNDQyLjQwNS00LjQ0MnMtMi45NiA0Ljg0Ni00LjU3NSA0LjU3N2MtMS42MTUtLjI3IDAtNC45OCAwLTQuOThzLTEuNzUgNS4yNS0zLjA5NiA0Ljg0NWMtMS4zNDctLjQwMy0xLjYxNS0zLjQ5OS0xLjYxNS0zLjQ5OXMtLjM2NCA0LjIwMy0yLjI4OCA0LjU3NWMtMTQuNTM2IDIuODI4LTMwLjU1MyA5LjAyLTUwLjYwOCAyOS4wNzMgMTguOTc3LTEwLjIzIDI2LjUxNC0yNS4zMDQgNjIuMDQ3LTI2LjY0OSAyNi45MDMtMS4wMiAzOS40NTggMTEuMzk3IDQzLjYwOSAxMS41NzQgMTIuNjUzLjU0IDIwLjU5NC0xMS43MSAyMC41OTQtMTEuNzFzLTcuOTQxIDkuMTUyLTEzLjczIDYuOTMxYzUuMTgzLTMuMDI3IDkuMTUzLTEwLjQzIDkuMTUzLTEwLjQzcy0xMC4wOTUgMTAuNDMtMTcuMDkyIDguODgyYzYuMzI0LTQuOTc5IDkuNDE5LTEzLjU5IDkuNDE5LTEzLjU5cy04LjAzMyAxMi4zOTctMTMuMDU1IDEyLjkyYzUuNzg4LTUuMTE1IDguMjEtMTIuMzg1IDguMjEtMTIuMzg1cy02LjU5NSA5LjY5Mi0xMS43MSAxMC4zNjZjNi44NjYtNi4xOTIgNy4xMzYtMTAuMDk1IDcuMTM2LTEwLjA5NXMtOS41IDEyLjI3Ni0xMy4zMjUgMTAuMzYzYzQuNTczLTQuOTggNy42Ny0xMS4zMDYgNy42Ny0xMS4zMDZzLTcuOTQgMTAuNzY4LTEwLjA5NCA5LjAxOGMtMS40ODEtLjI3IDIuNDI0LTUuMTE1IDIuNDI0LTUuMTE1cy00Ljg0NSA0LjMwNy02LjE5MiAzLjIzYzIuNDIyLTEuNDggNi41OTQtOC43NDggNi41OTQtOC43NDh6bTEyLjkxNiAyMi42MzhjLTEuMzU0LS4wOTItMi41NDguNDE0LTMuMTU4IDIuMDc1Qzk2LjkwNyA2Ni43NjcgODMuNzg1IDczLjQzIDY4LjQ0IDc1LjY1Yy0xNS4zNDQgMi4yMi0yNi42NS0xLjAwNi0zNS45MzctMy42MjNDMjAuMjkgNjguNTgzIDEwLjYgNzQuNzA5IDAgNjkuMzkyYzIuNDUyIDEuOTQgNS4yNSAzLjgzNiAxMy4zMjUgMy44NiAyLjczOC4wMSAxMS41MDctLjQzIDEzLjEyMi45ODMgMS42MTUgMS40MTQtNS40NSA4LjA3Ni01LjQ1IDguMDc2czExLjk1NS0xMC4xMDggMTMuMTIyLTYuNjYyYy43MjYgMi4xNDUtMy40MzIgNy44NzUtMy40MzIgNy44NzVzNC4xNy01LjY1OCA3LjI3LTYuODY1YzIuMTI2LS44MyAzLjkyLS43MyA1LjQ1IDEuNDE0IDEuMDEgMS40MTItNC44NDUgOC4yNzctNC44NDUgOC4yNzdzNy44NzItNy42NzEgOS4yODYtNy4yNjdjMS40MTQuNDAyIDAgNy4yNjcgMCA3LjI2N3MyLjg5Ni02Ljk1NiA0LjQ0LTcuNDdjMi4xMi0uNzA2LTQuODQ1IDE2LjE1LTQuODQ1IDE2LjE1czguNTA3LTE1LjU5NSAxMC4wOTUtMTUuOTQ2YzIuNzI2LS42MDYgNC41NDQgMTAuOSA0LjU0NCAxMC45cy0xLjMyOC0xMC4wNTIuMS0xMC43YzkuMDQtNC4wOSAzLjgzOCAxNy4zNjMgMy44MzggMTcuMzYzczUuOTEyLTE0LjU1OCAyLjI4LTE3Ljc4N2M5LjU4OCA2LjM1OCA4LjQyIDE3Ljc4NyA4LjQyIDE3Ljc4N3MyLjUyLTUuNDQyLTQuNTUyLTE4LjgyM2MzLjgzOC0uMiA5LjU5NiA5Ljk0IDkuNTk2IDkuOTRzLTYuMjU3LTExLjEwNC0yLjAxNy0xMS4zMDVjOC40MjQtLjQgOS4yODcgMTQuNzM3IDkuMjg3IDE0LjczN3MyLjUyMy0yLjEyLTQuMjQtMTYuOTZjNC4zMDYtMi43NTggMTQuNzM4IDEzLjczIDE0LjczOCAxMy43M3MtOS42MjMtMTYuMDg0LTguMDc1LTE3LjE2MWMxLjU0Ni0xLjA3NyA3LjIwMiA1LjExNiA3LjIwMiA1LjExNnMtNC4zMDctNS45MjItMy4xNjUtNi41MjhjMS4xNDQtLjYwNyAxMi41ODYgMTEuNzc3IDEyLjU4NiAxMS43NzdzLTEwLjA5Ni0xMi4xMTUtOC4zNDYtMTMuNTk2YzEuMDM1LS44NzcgNC4wMzUuNzQ1IDYuMzI1IDIuMjE2LTMuMjEtMi4yOTYtNy44MjEtNS44ODktNi4zMjUtNi42NjYgMy4wOTYtMS42MDcgMTEuOTEzIDMuODQ0IDExLjkxMyAzLjg0NHMtNS4zMTctNC4zNzQtNC40NDMtNS40NWMuODc1LTEuMDc4IDExLjMwNSA2Ljg2NCAxMS4zMDUgNi44NjRzLTkuMjItNy4yNy05LjY5LTkuMDg2Yy0uNDcyLTEuODE2IDcuMjcuNjA2IDcuMjcuNjA2cy04LjU0OC0zLjk3LTguNjgyLTUuMTE0Yy0uMTM2LTEuMTQ2IDUuNjUyIDEuMjc4IDUuNjUyIDEuMjc4cy0zLjc1NC0zLjA4Ni02LjczNC0zLjI4NnptLS4yNjYgMTcuMDFhMTA5Ljc4NiAxMDkuNzg2IDAgMCAwIDIuODI3IDEuOTU2cy0xLjI1LS45NDItMi44MjctMS45NTZ6Ii8+PHBhdGggZmlsbD0iIzM1MzMzMyIgZD0iTS40MDQgNzAuNjAzYzEwLjU5OCA1LjMxNyAyMC4yOS0uODA3IDMyLjUwNCAyLjYzNCA5LjI4NiAyLjYxNyAyMC41OTIgNS44NDYgMzUuOTM1IDMuNjIzIDE1LjM0NS0yLjIxOCAyOC40NjctOC44ODIgMzQuNzI4LTIwLjc5NCAxLjk1LTUuMzE3IDkuODkyIDEuMjEgOS44OTIgMS4yMXMtNS43ODgtMi40Mi01LjY1NC0xLjI3N2MuMTM2IDEuMTQ0IDguNjggNS4xMTMgOC42OCA1LjExM3MtNy43MzctMi40Mi03LjI2Ni0uNjA1Yy40NzIgMS44MTggOS42ODkgOS4wODYgOS42ODkgOS4wODZzLTEwLjQyOC03Ljk0Mi0xMS4zMDQtNi44NjZjLS44NzUgMS4wNzcgNC40NCA1LjQ1NCA0LjQ0IDUuNDU0cy04LjgxNC01LjQ1NC0xMS45MS0zLjg0NWMtMi4yMjYgMS4xNTUgOS4xNTIgOC42MjMgOS4xNTIgOC42MjNzLTcuNDAzLTUuNjUzLTkuMTUyLTQuMTcyYy0xLjc1IDEuNDggOC4zNDYgMTMuNTkzIDguMzQ2IDEzLjU5M1M5Ny4wNCA2OS45OTcgOTUuODk4IDcwLjYwM2MtMS4xNDYuNjA1IDMuMTYzIDYuNTI3IDMuMTYzIDYuNTI3cy01LjY1NC02LjE5LTcuMi01LjExM2MtMS41NDggMS4wNzcgOC4wNzYgMTcuMTU5IDguMDc2IDE3LjE1OXMtMTAuNDMyLTE2LjQ4Ny0xNC43NC0xMy43MjhjNi43NjUgMTQuODM4IDQuMjQgMTYuOTYgNC4yNCAxNi45NnMtLjg2Mi0xNS4xNC05LjI4Ni0xNC43NGMtNC4yNC4yMDIgMi4wMTggMTEuMzA3IDIuMDE4IDExLjMwN3MtNS43Ni0xMC4xNDItOS41OTYtOS45NGM3LjA3IDEzLjM4IDQuNTUgMTguODIgNC41NSAxOC44MlM3OC4yOSA4Ni40MzIgNjguNyA4MC4wNzJjMy42MzQgMy4yMy0yLjI4IDE3Ljc4Ny0yLjI4IDE3Ljc4N3M1LjIwNC0yMS40NTMtMy44MzYtMTcuMzYzYy0xLjQyOS42NDYtLjEgMTAuNy0uMSAxMC43cy0xLjgxOS0xMS41MS00LjU0Mi0xMC45MDFjLTEuNTkuMzUyLTEwLjA5NSAxNS45NDgtMTAuMDk1IDE1Ljk0OHM2Ljk2NS0xNi44NTggNC44NDUtMTYuMTVjLTEuNTQ0LjUxNS00LjQ0IDcuNDctNC40NCA3LjQ3czEuNDExLTYuODY0IDAtNy4yNjhjLTEuNDE1LS40MDUtOS4yODkgNy4yNjgtOS4yODkgNy4yNjhzNS44NTctNi44NjQgNC44NDUtOC4yNzhjLTEuNTMtMi4xNDItMy4zMjMtMi4yNDMtNS40NS0xLjQxNC0zLjA5NyAxLjIwNy03LjI2OCA2Ljg2NS03LjI2OCA2Ljg2NXM0LjE1OC01LjczIDMuNDMyLTcuODc1QzMzLjM1NiA3My40MTUgMjEuNCA4My41MjUgMjEuNCA4My41MjVzNy4wNjUtNi42NjQgNS40NS04LjA3NmMtMS42MTUtMS40MTQtMTAuMzg1LS45NzYtMTMuMTIyLS45ODNDNS42NTMgNzQuNDQgMi44NTUgNzIuNTQ1LjQwMyA3MC42MDQiIG9wYWNpdHk9Ii4zNSIvPjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/chat-square-quote.js b/frontend/crawlab-ui/src/assets/js/svg/chat-square-quote.js
new file mode 100644
index 00000000..9672cb1e
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/chat-square-quote.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktY2hhdC1zcXVhcmUtcXVvdGUiIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZD0iTTE0IDFhMSAxIDAgMCAxIDEgMXY4YTEgMSAwIDAgMS0xIDFoLTIuNWEyIDIgMCAwIDAtMS42LjhMOCAxNC4zMzMgNi4xIDExLjhhMiAyIDAgMCAwLTEuNi0uOEgyYTEgMSAwIDAgMS0xLTFWMmExIDEgMCAwIDEgMS0xaDEyek0yIDBhMiAyIDAgMCAwLTIgMnY4YTIgMiAwIDAgMCAyIDJoMi41YTEgMSAwIDAgMSAuOC40bDEuOSAyLjUzM2ExIDEgMCAwIDAgMS42IDBsMS45LTIuNTMzYTEgMSAwIDAgMSAuOC0uNEgxNGEyIDIgMCAwIDAgMi0yVjJhMiAyIDAgMCAwLTItMkgyeiIvPgogIDxwYXRoIGQ9Ik03LjA2NiA0Ljc2QTEuNjY1IDEuNjY1IDAgMCAwIDQgNS42NjhhMS42NjcgMS42NjcgMCAwIDAgMi41NjEgMS40MDZjLS4xMzEuMzg5LS4zNzUuODA0LS43NzcgMS4yMmEuNDE3LjQxNyAwIDEgMCAuNi41OGMxLjQ4Ni0xLjU0IDEuMjkzLTMuMjE0LjY4Mi00LjExMnptNCAwQTEuNjY1IDEuNjY1IDAgMCAwIDggNS42NjhhMS42NjcgMS42NjcgMCAwIDAgMi41NjEgMS40MDZjLS4xMzEuMzg5LS4zNzUuODA0LS43NzcgMS4yMmEuNDE3LjQxNyAwIDEgMCAuNi41OGMxLjQ4Ni0xLjU0IDEuMjkzLTMuMjE0LjY4Mi00LjExMnoiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/cheerio.js b/frontend/crawlab-ui/src/assets/js/svg/cheerio.js
new file mode 100644
index 00000000..e3a17738
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/cheerio.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjQwMCIgdmlld0JveD0iMCAwIDQwMCA0MDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHJlY3Qgd2lkdGg9IjQwMCIgaGVpZ2h0PSI0MDAiIHJ4PSIzMCIgZmlsbD0iI0U4OEMxRiIvPgogIDxwYXRoIGQ9Im0gMzEyIDEyMi41IGMgMC4yIC0yLjEgLTEuNCAtMy4xIC0yLjUgLTQuNSBjIC0xMy4xIC0xNS44IC0yOS4xIC0yNy45IC00Ny42IC0zNi4zIGMgLTEwLjYgLTQuOCAtMjIgLTggLTMzLjkgLTkuNCBjIC0xMS40IC0xLjQgLTIyLjYgLTIuMSAtMzQgLTAuOCBjIC0xNC41IDEuNiAtMjguMyA1LjggLTQxLjUgMTIgYyAtMjEuMSA5LjkgLTM4IDI0LjkgLTUxLjMgNDMuNiBjIC04LjUgMTEuOCAtMTQuNiAyNS4xIC0xOC4yIDM5LjQgYyAtMy43IDE0LjYgLTUuOCAyOS4zIC00LjIgNDQuNSBjIDEuMSAxMC40IDIuNiAyMC41IDUuNyAzMC41IGMgNC41IDE0LjYgMTEuOCAyNy45IDIxLjEgMzkuOSBjIDEwLjYgMTMuNyAyMy40IDI1LjEgMzguNSAzMy41IGMgMTcuNSA5LjcgMzYuMyAxNS41IDU2LjQgMTYuNyBjIDcuNCAwLjQgMTQuOCAwLjUgMjIgLTAuNCBjIDguNiAtMSAxNy4yIC0yLjkgMjUuNiAtNS41IGMgMTAuNCAtMy4zIDIwLjIgLTcuNiAyOS40IC0xMy4zIGMgMTMuMiAtOC4yIDI1LjEgLTE4IDM0LjQgLTMxLjEgYyAtMjEuOSAtMTYuNSAtNDMuOCAtMzIuOCAtNjUuOSAtNDkuNCBjIC0xMC4xIDExLjQgLTIyLjQgMTcuMiAtMzcuNCAxNy4xIGMgLTEzLjYgLTAuMiAtMjUuMSAtNS43IC0zMy45IC0xNS42IGMgLTE3LjggLTIwIC0xNS4yIC01MS4xIDUuOCAtNjggYyAxOC43IC0xNS4xIDQ5LjMgLTEzIDY1LjUgNy4yIGMgMjIgLTE2LjUgNDQgLTMzIDY2IC01MC4xIHoiIGZpbGw9IiNGRkYiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIxOCIvPgo8L3N2Zz4K`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/chevron-down.js b/frontend/crawlab-ui/src/assets/js/svg/chevron-down.js
new file mode 100644
index 00000000..cdf26337
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/chevron-down.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktY2hldnJvbi1kb3duIiB2aWV3Qm94PSIwIDAgMTYgMTYiPgogIDxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTEuNjQ2IDQuNjQ2YS41LjUgMCAwIDEgLjcwOCAwTDggMTAuMjkzbDUuNjQ2LTUuNjQ3YS41LjUgMCAwIDEgLjcwOC43MDhsLTYgNmEuNS41IDAgMCAxLS43MDggMGwtNi02YS41LjUgMCAwIDEgMC0uNzA4eiIvPgo8L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/chromium.js b/frontend/crawlab-ui/src/assets/js/svg/chromium.js
new file mode 100644
index 00000000..c7914350
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/chromium.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgdmVyc2lvbj0iMS4xIgogICBpZD0ic3ZnNDQiCiAgIHdpZHRoPSI1MTEuOTg0ODkiCiAgIGhlaWdodD0iNTExLjk4NDg5IgogICB2aWV3Qm94PSIwIDAgNTExLjk4NDg5IDUxMS45ODQ4OSIKICAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGRlZnMKICAgICBpZD0iZGVmczE4Ij4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NDk3NSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDQ2MzMiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMjMxLjYyNTc1LDAsMCwyMzEuNjI0NzIsMTExLjExMDEzLDE1OS45OTM2MykiCiAgICAgICB4Mj0iMC41NTY1NjM1IgogICAgICAgeDE9IjAuNDY1MjEyODgiCiAgICAgICB5MT0iLTAuNjczOTA2NTEiCiAgICAgICB5Mj0iMC44MTEyOTg2NyIgLz4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NDk3NSI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMxOTcyZTciCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A0OTcxIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMTk2OWQ1IgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wNDk3MyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHhsaW5rOmhyZWY9IiMzIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MTMzMSIKICAgICAgIHgxPSIxMDEuNzQzODEiCiAgICAgICB5MT0iMzMuNzI2MTg5IgogICAgICAgeDI9IjEwMS41OTkxNSIKICAgICAgIHkyPSIxMzUuNDY2IgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDMuNzc5NDIzNSwwLDAsMy43Nzk0MDY3LDAuMDAxNTE1NTUsMC4wMDM3Nzg2NSkiIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSIzIgogICAgICAgeDI9IjEiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDYxLjI4NiwwLDAsNjEuMjg2LDI5LjM5OSw0Mi4zMzMpIgogICAgICAgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgogICAgICA8c3RvcAogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMTM5NyIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6I2FmY2NmYiIgLz4KICAgICAgPHN0b3AKICAgICAgICAgb2Zmc2V0PSIxIgogICAgICAgICBpZD0ic3RvcDEzOTkiCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiM4YmI1ZjgiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICB4bGluazpocmVmPSIjMSIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDI5NjIiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoOTQuOTMxNTU5LDE2NC40MjY4NywtMTY0LjQyNzYsOTQuOTMxMTM3LDk3LjU1NTk5MSwxNzMuNjEwODMpIgogICAgICAgeDI9IjEuNzY5NTU0MSIKICAgICAgIHgxPSIwLjAxODIwMjU0NyIKICAgICAgIHkxPSItMC41MTE3MDE1OCIKICAgICAgIHkyPSIwLjQ5OTQzMzciIC8+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSIxIgogICAgICAgeDI9IjEiCiAgICAgICBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDI1LjExOCw0My41MDYsLTQzLjUwNiwyNS4xMTgsMjUuODEyLDQ1LjkzNSkiCiAgICAgICBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CiAgICAgIDxzdG9wCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzMTIyIgogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojNjU5Y2Y2IiAvPgogICAgICA8c3RvcAogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMzEyNCIKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzQyODVmNCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIHhsaW5rOmhyZWY9IiMyIgogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50MjY4OCIKICAgICAgIHgxPSI2Ny40NTIzNzciCiAgICAgICB5MT0iNDAuMzIwNjk0IgogICAgICAgeDI9IjY3LjczMzAwMiIKICAgICAgIHkyPSI5NS4yNSIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIgogICAgICAgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgzLjc3OTQyMzUsMCwwLDMuNzc5NDA2NywwLjAwMTUwMDQzLDAuMDAzNzc4NjUpIiAvPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0iMiI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMzNjgwZjAiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AyNjgyIiAvPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMjY3OGVjIgogICAgICAgICBvZmZzZXQ9IjEiCiAgICAgICAgIGlkPSJzdG9wMjY4NCIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgPC9kZWZzPgogIDxwYXRoCiAgICAgZD0ibSAyNTUuOTkzMTksMjU1Ljk5NDMzIDExMC44NTA0OSw2My45OTY3MSAtMTEwLjg1MDQ5LDE5MS45OTM4NSBjIDE0MS4zODA2OCwwIDI1NS45OTE3LC0xMTQuNjEwNTEgMjU1Ljk5MTcsLTI1NS45OTA1NiAwLC00Ni42NDE2NSAtMTIuNTM1NTksLTkwLjMzMTYgLTM0LjMzMTE1LC0xMjcuOTk3MTYgaCAtMjIxLjY2MzIgeiIKICAgICBpZD0icGF0aDM0LTQiCiAgICAgc3R5bGU9ImZpbGw6dXJsKCNsaW5lYXJHcmFkaWVudDEzMzEpIiAvPgogIDxwYXRoCiAgICAgZD0iTSAyNTUuOTkwNTQsMCBDIDE2MS4yNDA0LDAgNzguNTc2ODQ4LDUxLjUxMzMxNCAzNC4zMTIyNCwxMjguMDI3NCBsIDExMC44Mjc4MSwxOTEuOTYzNjMgMTEwLjg1MDQ5LC02My45OTY3IFYgMTI3Ljk5NzE3IGggMjIxLjY2MzIgQyA0MzMuMzgxNTcsNTEuNTAxOTc1IDM1MC43MjkzNiwwIDI1NS45OTA1NCwwIFoiCiAgICAgaWQ9InBhdGgzNi0xIgogICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQ0NjMzKSIgLz4KICA8cGF0aAogICAgIGQ9Im0gMC4wMDE1MTE3NywyNTUuOTk0MzMgYyAwLDE0MS4zODAwNSAxMTQuNjA3MjM4MjMsMjU1Ljk5MDU2IDI1NS45OTE2ODgyMywyNTUuOTkwNTYgTCAzNjYuODQzNjgsMzE5Ljk5MTAzIDI1NS45OTMyLDI1NS45OTQzMyAxNDUuMTQyNzEsMzE5Ljk5MTAzIDM0LjMxNDg5NywxMjguMDI3NCBDIDEyLjUzMTQzNCwxNjUuNjgyMzkgMCwyMDkuMzU2NDYgMCwyNTUuOTkwNTYiCiAgICAgaWQ9InBhdGgzOC03IgogICAgIHN0eWxlPSJmaWxsOnVybCgjbGluZWFyR3JhZGllbnQyOTYyKSIgLz4KICA8cGF0aAogICAgIGQ9Im0gMzgzLjk5MDk0LDI1NS45OTQzMyBjIDAsNzAuNjkwMDMgLTU3LjMwNzQxLDEyNy45OTcxNyAtMTI3Ljk5Nzc1LDEyNy45OTcxNyAtNzAuNjkwMzQsMCAtMTI3Ljk5NzczLC01Ny4zMDcxNCAtMTI3Ljk5NzczLC0xMjcuOTk3MTcgMCwtNzAuNjkwMDIgNTcuMzA3MzksLTEyNy45OTcxNiAxMjcuOTk3NzMsLTEyNy45OTcxNiA3MC42OTAzNCwwIDEyNy45OTc3NSw1Ny4zMDcxNCAxMjcuOTk3NzUsMTI3Ljk5NzE2IgogICAgIGZpbGw9IiNmZmZmZmYiCiAgICAgaWQ9InBhdGg0MCIgLz4KICA8cGF0aAogICAgIGQ9Im0gMzU5Ljk5MTU4LDI1NS45OTQzMyBjIDAsNTcuNDM1NjUgLTQ2LjU2MjQ5LDEwMy45OTc5NCAtMTAzLjk5ODM5LDEwMy45OTc5NCAtNTcuNDM1OSwwIC0xMDMuOTk4NCwtNDYuNTYyMjkgLTEwMy45OTg0LC0xMDMuOTk3OTQgMCwtNTcuNDM1NjQgNDYuNTYyNSwtMTAzLjk5NzkzIDEwMy45OTg0LC0xMDMuOTk3OTMgNTcuNDM1OSwwIDEwMy45OTgzOSw0Ni41NjIyOSAxMDMuOTk4MzksMTAzLjk5NzkzIgogICAgIGlkPSJwYXRoNDItNSIKICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50MjY4OCkiIC8+Cjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/clickhouse.js b/frontend/crawlab-ui/src/assets/js/svg/clickhouse.js
new file mode 100644
index 00000000..4c5cec2b
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/clickhouse.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyBoZWlnaHQ9IjIyMjIiIHZpZXdCb3g9IjAgMCA5IDgiIHdpZHRoPSIyNTAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Im0wIDdoMXYxaC0xeiIgZmlsbD0iI2YwMCIvPjxwYXRoIGQ9Im0wIDBoMXY3aC0xem0yIDBoMXY4aC0xem0yIDBoMXY4aC0xem0yIDBoMXY4aC0xem0yIDMuMjVoMXYxLjVoLTF6IiBmaWxsPSIjZmMwIi8+PC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/code.js b/frontend/crawlab-ui/src/assets/js/svg/code.js
new file mode 100644
index 00000000..241ee55b
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/code.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktY29kZSIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNNS44NTQgNC44NTRhLjUuNSAwIDEgMC0uNzA4LS43MDhsLTMuNSAzLjVhLjUuNSAwIDAgMCAwIC43MDhsMy41IDMuNWEuNS41IDAgMCAwIC43MDgtLjcwOEwyLjcwNyA4bDMuMTQ3LTMuMTQ2em00LjI5MiAwYS41LjUgMCAwIDEgLjcwOC0uNzA4bDMuNSAzLjVhLjUuNSAwIDAgMSAwIC43MDhsLTMuNSAzLjVhLjUuNSAwIDAgMS0uNzA4LS43MDhMMTMuMjkzIDhsLTMuMTQ3LTMuMTQ2eiIvPgo8L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/colly.js b/frontend/crawlab-ui/src/assets/js/svg/colly.js
new file mode 100644
index 00000000..55f34b2f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/colly.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjQ2MHB4IiBoZWlnaHQ9IjQ2MHB4IiBzdHlsZT0ic2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbjsgdGV4dC1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uOyBpbWFnZS1yZW5kZXJpbmc6b3B0aW1pemVRdWFsaXR5OyBmaWxsLXJ1bGU6ZXZlbm9kZDsgY2xpcC1ydWxlOmV2ZW5vZGQiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KPGc+PHBhdGggc3R5bGU9Im9wYWNpdHk6MC45OTciIGZpbGw9IiMwYjg3NzgiIGQ9Ik0gMTYyLjUsMTA1LjUgQyAyMTcuOTg1LDEwMC43NDMgMjYwLjMxOCwxMjEuNzQzIDI4OS41LDE2OC41QyAyODIuMDkxLDE3Mi45NTYgMjc0LjU5MSwxNzcuMjg5IDI2NywxODEuNUMgMjM4LjUxMywxMzguMTA3IDE5OS4wMTMsMTIzLjI3MyAxNDguNSwxMzdDIDEwNi41OTcsMTUzLjc5MiA4NC45MzAyLDE4NC43OTIgODMuNSwyMzBDIDg1LjMzMTYsMjc1LjMzNSAxMDcuMzMyLDMwNi4zMzUgMTQ5LjUsMzIzQyAxOTEuMDc0LDMzNC4yMzcgMjI2LjU3NCwzMjQuNDAzIDI1NiwyOTMuNUMgMjU5LjY2NywyODguMTY3IDI2My4zMzMsMjgyLjgzMyAyNjcsMjc3LjVDIDI3NC41LDI4MS42NTUgMjgxLjgzNCwyODUuOTg4IDI4OSwyOTAuNUMgMjY5LjY3OCwzMjUuMDcyIDI0MC4xNzgsMzQ1LjkwNSAyMDAuNSwzNTNDIDE0My43MDcsMzU5LjA5NiAxMDAuNTQsMzM3LjkzIDcxLDI4OS41QyA1MS42ODUzLDI0OS41NjcgNTEuNjg1MywyMDkuNTY3IDcxLDE2OS41QyA5Mi4xMjM2LDEzNC42OTQgMTIyLjYyNCwxMTMuMzYxIDE2Mi41LDEwNS41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjAuOTk4IiBmaWxsPSIjMGI4NDc2IiBkPSJNIDMyOS41LDE3MS41IEMgMzMxLjgzMywxNzEuNSAzMzQuMTY3LDE3MS41IDMzNi41LDE3MS41QyAzMzYuNSwxOTkuMTY3IDMzNi41LDIyNi44MzMgMzM2LjUsMjU0LjVDIDMzNC4xNjcsMjU0LjUgMzMxLjgzMywyNTQuNSAzMjkuNSwyNTQuNUMgMzI5LjUsMjI2LjgzMyAzMjkuNSwxOTkuMTY3IDMyOS41LDE3MS41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjAuOTY2IiBmaWxsPSIjMGI4YTdiIiBkPSJNIDM1Mi41LDE3MS41IEMgMzU0LjgzMywxNzEuNSAzNTcuMTY3LDE3MS41IDM1OS41LDE3MS41QyAzNTkuNSwxOTkuMTY3IDM1OS41LDIyNi44MzMgMzU5LjUsMjU0LjVDIDM1Ny4xNjcsMjU0LjUgMzU0LjgzMywyNTQuNSAzNTIuNSwyNTQuNUMgMzUyLjUsMjI2LjgzMyAzNTIuNSwxOTkuMTY3IDM1Mi41LDE3MS41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjAuOTg5IiBmaWxsPSIjMGI3ZjcwIiBkPSJNIDI4NC41LDIwOC41IEMgMjk4LjY2NSwyMDUuNjY2IDMwOC44MzEsMjEwLjY2NiAzMTUsMjIzLjVDIDMxNy45NzYsMjQ3LjUyMyAzMDcuNDc2LDI1Ny42OSAyODMuNSwyNTRDIDI3MC40NjgsMjQ2LjU3IDI2Ni4zMDEsMjM1LjczNiAyNzEsMjIxLjVDIDI3NC4wNzYsMjE1LjU4OSAyNzguNTc2LDIxMS4yNTUgMjg0LjUsMjA4LjUgWiBNIDI4OS41LDIxNS41IEMgMzA2LjAyMSwyMTYuNTYzIDMxMS41MjEsMjI0Ljg5NiAzMDYsMjQwLjVDIDI5Ny42MjYsMjQ5LjM4MiAyODguOTYsMjQ5LjcxNSAyODAsMjQxLjVDIDI3NC4zNzMsMjI5LjU0MyAyNzcuNTQsMjIwLjg3NiAyODkuNSwyMTUuNSBaIi8+PC9nPgo8Zz48cGF0aCBzdHlsZT0ib3BhY2l0eTowLjI1OSIgZmlsbD0iIzA0MmYyOSIgZD0iTSAzNzYuNSwyMDkuNSBDIDM3My42MTIsMjA4Ljk5IDM3MS4yNzksMjA5LjY1NiAzNjkuNSwyMTEuNUMgMzY4LjU5NiwyMTAuNzkxIDM2OC4yNjMsMjA5Ljc5MSAzNjguNSwyMDguNUMgMzcxLjM4NSwyMDguMTk0IDM3NC4wNTIsMjA4LjUyNyAzNzYuNSwyMDkuNSBaIi8+PC9nPgo8Zz48cGF0aCBzdHlsZT0ib3BhY2l0eTowLjI2NiIgZmlsbD0iIzA0MmUyOSIgZD0iTSA0MDQuNSwyMDkuNSBDIDQwNi45NDgsMjA4LjUyNyA0MDkuNjE1LDIwOC4xOTQgNDEyLjUsMjA4LjVDIDQxMi43MzcsMjA5Ljc5MSA0MTIuNDA0LDIxMC43OTEgNDExLjUsMjExLjVDIDQwOS43MjEsMjA5LjY1NiA0MDcuMzg4LDIwOC45OSA0MDQuNSwyMDkuNSBaIi8+PC9nPgo8Zz48cGF0aCBzdHlsZT0ib3BhY2l0eTowLjk5MiIgZmlsbD0iIzBiN2Y3MSIgZD0iTSAzNzYuNSwyMDkuNSBDIDM4MS4xNTEsMjIwLjgwNiAzODUuODE3LDIzMi4xNCAzOTAuNSwyNDMuNUMgMzk1LjM2NSwyMzIuMjM4IDQwMC4wMzEsMjIwLjkwNCA0MDQuNSwyMDkuNUMgNDA3LjM4OCwyMDguOTkgNDA5LjcyMSwyMDkuNjU2IDQxMS41LDIxMS41QyA0MDMuMDg1LDIzNC43NDcgMzk0LjA4NSwyNTcuNzQ3IDM4NC41LDI4MC41QyAzODEuNjE1LDI4MC44MDYgMzc4Ljk0OCwyODAuNDczIDM3Ni41LDI3OS41QyAzNzkuODE5LDI3MS4yMTkgMzgzLjE1MywyNjIuODg1IDM4Ni41LDI1NC41QyAzODAuNTQ2LDI0MC4yNjIgMzc0Ljg3OSwyMjUuOTI5IDM2OS41LDIxMS41QyAzNzEuMjc5LDIwOS42NTYgMzczLjYxMiwyMDguOTkgMzc2LjUsMjA5LjUgWiIvPjwvZz4KPGc+PHBhdGggc3R5bGU9Im9wYWNpdHk6MC40OTgiIGZpbGw9IiMwNzM5MzMiIGQ9Ik0gMzc2LjUsMjc5LjUgQyAzNzguOTQ4LDI4MC40NzMgMzgxLjYxNSwyODAuODA2IDM4NC41LDI4MC41QyAzODEuNjQ1LDI4MS44MDMgMzc4LjY0NSwyODEuODAzIDM3NS41LDI4MC41QyAzNzUuNjI0LDI3OS44OTMgMzc1Ljk1NywyNzkuNTYgMzc2LjUsMjc5LjUgWiIvPjwvZz4KPC9zdmc+Cg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/crawlee.js b/frontend/crawlab-ui/src/assets/js/svg/crawlee.js
new file mode 100644
index 00000000..3ae89f66
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/crawlee.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIiIGhlaWdodD0iNDIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGcgY2xpcC1wYXRoPSJ1cmwoI2EpIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPgogICAgPHBhdGggZD0iTTM5Ljg0MyAzNC44MWgtMjkuNGMtLjQyIDAtLjY4My0uNDc0LS40NzMtLjg1NWw3LjM1LTEzLjI0IDcuMzUxLTEzLjIzOGEuNTM3LjUzNyAwIDAgMSAuOTQ3IDBsNC43MjggOC41MTdhNi41MjEgNi41MjEgMCAwIDAgMS41NyAxMi44NDhjMS43NjUgMCAzLjM2OS0uNyA0LjU0Mi0xLjg0M2wzLjg2MSA2Ljk1NmMuMjEuMzc4LS4wNTMuODU0LS40NzMuODU0aC0uMDAzWiIgZmlsbD0idXJsKCNiKSIgc3Ryb2tlPSJ1cmwoI2MpIiBzdHJva2Utd2lkdGg9IjEuMDM5Ii8+CiAgICA8cGF0aCBkPSJNMzcuODU1IDI1LjAxN2E2LjUxOSA2LjUxOSAwIDAgMS01LjkzOCAzLjgyNSA2LjUxOCA2LjUxOCAwIDAgMS02LjUyLTYuNTIgNi41MTggNi41MTggMCAwIDEgOS4zNDMtNS44NzgiIHN0cm9rZT0idXJsKCNkKSIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPC9nPgogIDxkZWZzPgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJiIiB4MT0iNDAuMzkzIiB5MT0iNy4xOTMiIHgyPSIxMi45MTIiIHkyPSIzNy41NDEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIuMDAxIiBzdG9wLWNvbG9yPSIjRkZCMjAwIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjA4MSIgc3RvcC1jb2xvcj0iI0ZGQjEwMCIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii4xNTYiIHN0b3AtY29sb3I9IiNGRkFGMDIiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuMjI1IiBzdG9wLWNvbG9yPSIjRkVBQjA0Ii8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjI5MSIgc3RvcC1jb2xvcj0iI0ZEQTYwNiIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii4zNTMiIHN0b3AtY29sb3I9IiNGQ0EwMEEiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuNDEzIiBzdG9wLWNvbG9yPSIjRkI5ODBFIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjQ3MSIgc3RvcC1jb2xvcj0iI0ZBOTAxMyIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii41MjkiIHN0b3AtY29sb3I9IiNGOTg2MTgiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuNTg3IiBzdG9wLWNvbG9yPSIjRjc3QjFFIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjY0NyIgc3RvcC1jb2xvcj0iI0Y1NkYyNCIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii43MDkiIHN0b3AtY29sb3I9IiNGMzYzMkIiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuNzc0IiBzdG9wLWNvbG9yPSIjRjI1NTMyIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjg0NCIgc3RvcC1jb2xvcj0iI0VGNDczQSIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii45MTgiIHN0b3AtY29sb3I9IiNFRDM4NDIiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuOTk5IiBzdG9wLWNvbG9yPSIjRUIyODRCIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJjIiB4MT0iNDAuMzkzIiB5MT0iNy4xOTMiIHgyPSIxMi45MTIiIHkyPSIzNy41NDEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KICAgICAgPHN0b3Agb2Zmc2V0PSIuMDAxIiBzdG9wLWNvbG9yPSIjRkZCMjAwIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjA4MSIgc3RvcC1jb2xvcj0iI0ZGQjEwMCIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii4xNTYiIHN0b3AtY29sb3I9IiNGRkFGMDIiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuMjI1IiBzdG9wLWNvbG9yPSIjRkVBQjA0Ii8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjI5MSIgc3RvcC1jb2xvcj0iI0ZEQTYwNiIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii4zNTMiIHN0b3AtY29sb3I9IiNGQ0EwMEEiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuNDEzIiBzdG9wLWNvbG9yPSIjRkI5ODBFIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjQ3MSIgc3RvcC1jb2xvcj0iI0ZBOTAxMyIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii41MjkiIHN0b3AtY29sb3I9IiNGOTg2MTgiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuNTg3IiBzdG9wLWNvbG9yPSIjRjc3QjFFIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjY0NyIgc3RvcC1jb2xvcj0iI0Y1NkYyNCIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii43MDkiIHN0b3AtY29sb3I9IiNGMzYzMkIiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuNzc0IiBzdG9wLWNvbG9yPSIjRjI1NTMyIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjg0NCIgc3RvcC1jb2xvcj0iI0VGNDczQSIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii45MTgiIHN0b3AtY29sb3I9IiNFRDM4NDIiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuOTk5IiBzdG9wLWNvbG9yPSIjRUIyODRCIi8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50IGlkPSJkIiB4MT0iMzcuODU1IiB5MT0iMjIuMzIzIiB4Mj0iMjUuMzk4IiB5Mj0iMjIuMzIzIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CiAgICAgIDxzdG9wIG9mZnNldD0iLjAwMSIgc3RvcC1jb2xvcj0iI0ZGQjIwMCIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii4wODEiIHN0b3AtY29sb3I9IiNGRkIxMDAiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuMTU2IiBzdG9wLWNvbG9yPSIjRkZBRjAyIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjIyNSIgc3RvcC1jb2xvcj0iI0ZFQUIwNCIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii4yOTEiIHN0b3AtY29sb3I9IiNGREE2MDYiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuMzUzIiBzdG9wLWNvbG9yPSIjRkNBMDBBIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjQxMyIgc3RvcC1jb2xvcj0iI0ZCOTgwRSIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii40NzEiIHN0b3AtY29sb3I9IiNGQTkwMTMiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuNTI5IiBzdG9wLWNvbG9yPSIjRjk4NjE4Ii8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjU4NyIgc3RvcC1jb2xvcj0iI0Y3N0IxRSIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii42NDciIHN0b3AtY29sb3I9IiNGNTZGMjQiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuNzA5IiBzdG9wLWNvbG9yPSIjRjM2MzJCIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjc3NCIgc3RvcC1jb2xvcj0iI0YyNTUzMiIvPgogICAgICA8c3RvcCBvZmZzZXQ9Ii44NDQiIHN0b3AtY29sb3I9IiNFRjQ3M0EiLz4KICAgICAgPHN0b3Agb2Zmc2V0PSIuOTE4IiBzdG9wLWNvbG9yPSIjRUQzODQyIi8+CiAgICAgIDxzdG9wIG9mZnNldD0iLjk5OSIgc3RvcC1jb2xvcj0iI0VCMjg0QiIvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxjbGlwUGF0aCBpZD0iYSI+CiAgICAgIDxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik05LjQxOCA2LjcxNGgzMS40NXYyOC41NzFIOS40MTh6Ii8+CiAgICA8L2NsaXBQYXRoPgogIDwvZGVmcz4KPC9zdmc+Cg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/db2.js b/frontend/crawlab-ui/src/assets/js/svg/db2.js
new file mode 100644
index 00000000..8b449e5a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/db2.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyBjbGFzcz0ic3ZnLWljb24iIHN0eWxlPSJ3aWR0aDogMWVtOyBoZWlnaHQ6IDFlbTt2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO2ZpbGw6IGN1cnJlbnRDb2xvcjtvdmVyZmxvdzogaGlkZGVuOyIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik0yMzMuMyA4OTQuMlY2NDcuM2g4MC41YzgyLjcgMCAxMjMuOSA0MC4yIDEyMy45IDEyMC41IDAgMzguMS0xMS41IDY4LjktMzQuOCA5MS45LTIzLjMgMjMtNTMuMSAzNC43LTg5LjYgMzQuN2gtODB2LTAuMnogbTQ5LjctMjA0djE2MC45aDI3LjFjMjMuNiAwIDQyLTcuNSA1NS41LTIyLjMgMTMuNS0xNSAyMC4yLTM1IDIwLjItNjAuNCAwLTI0LjUtNy4xLTQzLjctMjEtNTcuNS0xNC4xLTEzLjgtMzIuMi0yMC42LTU0LjctMjAuNkgyODN2LTAuMXpNNDc3IDg5NC4yVjY0Ny4zaDgyLjdjMjUuMyAwIDQ0LjkgNS4xIDU4LjcgMTUuMyAxMy44IDEwLjIgMjAuNSAyNC4zIDIwLjUgNDIuNCAwIDEzLjQtNC4zIDI1LjItMTIuOCAzNS4yLTguNSAxMC0xOS4yIDE3LTMyLjIgMjAuOXYwLjdjMTYuMiAyLjIgMjkuMyA4LjUgMzkuMyAxOS4yIDkuOSAxMC43IDE0LjkgMjQgMTQuOSA0MCAwIDIyLjUtNy41IDQwLjMtMjIuNiA1My42LTE1LjEgMTMuMy0zNS42IDE5LjktNjEuNiAxOS45SDQ3N3YtMC4zeiBtNDkuNi0yMDcuNnY2MS4xaDIzLjZjMTEuNCAwIDIwLjItMi45IDI2LjgtOC43IDYuNi01LjggOS44LTEzLjggOS44LTI0LjIgMC0xOC45LTEzLTI4LjQtMzkuMS0yOC40bC0yMS4xIDAuMnogbTAgMTAwLjV2NjcuN2gyOS41YzEyLjUgMCAyMi4zLTMuMSAyOS4zLTkuMlM1OTYgODMwLjkgNTk2IDgyMGMwLTEwLjQtMy41LTE4LjUtMTAuNi0yNC4zLTcuMS01LjgtMTctOC43LTMwLTguN2wtMjguOCAwLjF6TTczMi42IDg1MS41aDk4Ljh2NDIuOUg2NzYuNVY4NzZjMC0xMi45IDIuMi0yNC43IDYuNy0zNS4yczEwLjYtMjAuNiAxOC40LTI5LjhjNy45LTkuMiAxOS45LTE5LjkgMzYuNC0zMi4zIDE1LjEtMTEuOSAyNS41LTIyLjUgMzEuNi0zMS42IDYuMS05LjIgOS0xOC45IDktMjkuMyAwLTIyLjMtMTEuNC0zMy4zLTM0LjMtMzMuMy0yMCAwLTM5LjEgOC41LTU3LjQgMjUuNXYtNDZjMjAuNC0xNCA0My4xLTIwLjkgNjguNi0yMC45IDIzLjMgMCA0MS43IDYuMyA1NS4yIDE4LjkgMTMuNSAxMi42IDIwLjQgMjkuOSAyMC40IDUyLjEgMCAxMi40LTEuOSAyMy41LTUuOSAzMy4zLTMuOCA5LjktOS42IDE5LjItMTcgMjguMS03LjQgOC43LTE5LjIgMTkuMi0zNS42IDMxLjUtMTUuNyAxMS45LTI2LjMgMjEuMS0zMS43IDI3LjYtNS40IDYuNS04LjMgMTEuOS04LjMgMTYuOXoiICAvPjxwYXRoIGQ9Ik04Mi4xIDE3MS42aDE3My4zdjI2SDgyLjF6TTI4MCAxNzEuNnYyNmgyNDcuMXMtMjUuOC0yNi01OC4yLTI2SDI4MHpNNTc2LjMgMTcxLjZ2MjYuMmgxNDkuOWwtOC44LTI2LjJ6TTk3My4zIDE3MS42SDgzMy42bC04LjggMjYuMmgxNDguNXpNODIuMSAyMjIuNmgxNzMuM3YyNkg4Mi4xek0yODAgMjQ4LjdoMjc2LjhzLTIuNi0yMC42LTktMjZIMjgwdjI2ek01NzYuMyAyNDguN0g3NDNsLTcuNS0yNi4xSDU3Ni4zek05NzMuMyAyNDguN3YtMjYuMUg4MTUuN2wtNy43IDI2LjF6TTEzMi40IDI3Mmg3NXYyNmgtNzV6TTMzMC41IDI3Mmg3NXYyNmgtNzV6TTQ3OS4zIDI5OGg3NXM1LjEtMTMuOCA1LjEtMjZoLTgwLjJ2MjZoMC4xek02MjYuOCAyNzJ2MjZoMTMzLjFsLTguOC0yNnpNNzkxLjEgMjk4aDEzNC42di0yNkg4MDB6TTEzMi40IDMyMi43aDc1djI2aC03NXpNMzMwLjUgMzQ4LjdoMTkyLjdzMTUuNi0xMy44IDIwLjctMjZIMzMwLjV2MjZ6TTc3NS42IDM0NC44bC02LjQtMjIuMUg2MjYuOHYyNmg3NXYtMTVsNS4yIDE1aDEzOC41bDUtMTV2MTVoNzUuMnYtMjZINzgzLjF6TTEzMi40IDM3My40aDc1djI2aC03NXpNMzMwLjUgMzk5LjZoMjEzLjRjLTUuMS0xMi40LTIwLjctMjYtMjAuNy0yNkgzMzAuNXYyNnpNNjI2LjggMzczLjRoNzV2MjZoLTc1ek04MzcuNiAzNzMuNEg3MTUuOWw5LjIgMjYuMmgxMDMuNnpNODQ5LjMgMzczLjRoNzV2MjZoLTc1ek0xMzIuNCA0MjQuMmg3NXYyNmgtNzV6TTMzMC41IDQyNC4yaDc1djI2aC03NXpNNTU0LjMgNDI0LjJoLTc1djI2aDgwLjJjLTAuMS0xMi4zLTUuMi0yNi01LjItMjZ6TTYyNi44IDQyNC4yaDc1djI2aC03NXpNODIwLjggNDI0LjJoLTg4bDkuMSAyNi4xaDY5Ljl6TTg0OS4zIDQyNC4yaDc1djI2aC03NXpNODMuNCA0NzMuNmgxNzMuM3YyNkg4My40ek01NTcgNDczLjZIMjgwdjI2aDI2Ny44YzUuMi01LjQgOS4yLTI2IDkuMi0yNnpNNTc3LjcgNDczLjZoMTI0LjF2MjZINTc3Ljd6TTgwMi41IDQ3My42aC01Mi45bDkuMiAyNmgzNC43ek04NDkuMyA0NzMuNmgxMjUuNXYyNkg4NDkuM3pNODMuNCA1MjQuNmgxNzMuM3YyNkg4My40ek01MjcuMiA1MjQuNkgyODB2MjZoMTg4LjljMzMuNi0wLjEgNTguMy0yNiA1OC4zLTI2ek01NzcuNyA1MjQuNmgxMjQuMXYyNkg1NzcuN3pNNzc2LjcgNTUwLjVsOS4zLTI1LjloLTE5LjdsOS4zIDI1Ljl6TTg0OS4zIDUyNC42aDEyNS41djI2SDg0OS4zeiIgIC8+PC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/devops.js b/frontend/crawlab-ui/src/assets/js/svg/devops.js
new file mode 100644
index 00000000..8fec2c3a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/devops.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyBpZD0iZjQzMzc1MDYtNWQ5NS00ZTgwLWI3Y2EtNjg0OThjNmUwMDhlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxOCIgaGVpZ2h0PSIxOCIgdmlld0JveD0iMCAwIDE4IDE4Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImJhNDIwMjc3LTcwMGUtNDJjYy05ZGU5LTUzODhhNWMxNmU1NCIgeDE9IjkiIHkxPSIxNi45NyIgeDI9IjkiIHkyPSIxLjAzIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMDA3OGQ0IiAvPjxzdG9wIG9mZnNldD0iMC4xNiIgc3RvcC1jb2xvcj0iIzEzODBkYSIgLz48c3RvcCBvZmZzZXQ9IjAuNTMiIHN0b3AtY29sb3I9IiMzYzkxZTUiIC8+PHN0b3Agb2Zmc2V0PSIwLjgyIiBzdG9wLWNvbG9yPSIjNTU5Y2VjIiAvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzVlYTBlZiIgLz48L2xpbmVhckdyYWRpZW50PjwvZGVmcz48dGl0bGU+SWNvbi1kZXZvcHMtMjYxPC90aXRsZT48cGF0aCBpZD0iYTkxZjBjYTQtOGZiNy00MDE5LTljMDktMGE1MmUyYzA1NzU0IiBkPSJNMTcsNHY5Ljc0bC00LDMuMjgtNi4yLTIuMjZWMTdMMy4yOSwxMi40MWwxMC4yMy44VjQuNDRabS0zLjQxLjQ5TDcuODUsMVYzLjI5TDIuNTgsNC44NCwxLDYuODd2NC42MWwyLjI2LDFWNi41N1oiIGZpbGw9InVybCgjYmE0MjAyNzctNzAwZS00MmNjLTlkZTktNTM4OGE1YzE2ZTU0KSIgLz48L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/dingtalk.js b/frontend/crawlab-ui/src/assets/js/svg/dingtalk.js
new file mode 100644
index 00000000..9d21de29
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/dingtalk.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPgo8c3ZnIGZpbGw9IiMwMDAwMDAiIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBjbGFzcz0iaWNvbiI+CiAgPHBhdGggZD0iTTU3My43IDI1Mi41QzQyMi41IDE5Ny40IDIwMS4zIDk2LjcgMjAxLjMgOTYuN2MtMTUuNy00LjEtMTcuOSAxMS4xLTE3LjkgMTEuMS01IDYxLjEgMzMuNiAxNjAuNSA1My42IDE4Mi44IDE5LjkgMjIuMyAzMTkuMSAxMTMuNyAzMTkuMSAxMTMuN1MzMjYgMzU3LjkgMjcwLjUgMzQxLjljLTU1LjYtMTYtMzcuOSAxNy44LTM3LjkgMTcuOCAxMS40IDYxLjcgNjQuOSAxMzEuOCAxMDcuMiAxMzguNCA0Mi4yIDYuNiAyMjAuMSA0IDIyMC4xIDRzLTM1LjUgNC4xLTkzLjIgMTEuOWMtNDIuNyA1LjgtOTcgMTIuNS0xMTEuMSAxNy44LTMzLjEgMTIuNSAyNCA2Mi42IDI0IDYyLjYgODQuNyA3Ni44IDEyOS43IDUwLjUgMTI5LjcgNTAuNSAzMy4zLTEwLjcgNjEuNC0xOC41IDg1LjItMjQuMkw1NjUgNzQzLjFoODQuNkw2MDMgOTI4bDIwNS4zLTI3MS45SDcwMC44bDIyLjMtMzguN2MuMy41LjQuOC40LjhTNzk5LjggNDk2LjEgODI5IDQzMy44bC42LTFoLS4xYzUtMTAuOCA4LjYtMTkuNyAxMC0yNS44IDE3LTcxLjMtMTE0LjUtOTkuNC0yNjUuOC0xNTQuNXoiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/drission-page.js b/frontend/crawlab-ui/src/assets/js/svg/drission-page.js
new file mode 100644
index 00000000..e81fc5f9
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/drission-page.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjIxOCIgaGVpZ2h0PSIxNzkiPgo8cGF0aCBkPSJNMCAwIEMxLjE5NzE2MTQxIC0wLjAxMjkzNTk0IDIuMzk0MzIyODEgLTAuMDI1ODcxODkgMy42Mjc3NjE4NCAtMC4wMzkxOTk4MyBDNC45Mzc5Njc5OSAtMC4wNDgxNzI5MSA2LjI0ODE3NDEzIC0wLjA1NzE0NiA3LjU5ODA4MzUgLTAuMDY2MzkwOTkgQzguOTk0NTQ1MzIgLTAuMDc5MjA4ODggMTAuMzkxMDA0MjUgLTAuMDkyMzQ0NSAxMS43ODc0NjAzMyAtMC4xMDU3NzM5MyBDMjQuNTIyMTI4NDIgLTAuMjIxNTUyMDQgMzcuMjU3NTY4MjcgLTAuMjc4MjkzMzkgNDkuOTkyNjY0MzQgLTAuMzIyNTQxMjQgQzU5LjQwMTE3NTMgLTAuMzU1Nzg0ODIgNjguODA4NzIzODggLTAuNDE1MzY1NzEgNzguMjE2ODIxOTEgLTAuNTEwMDUyMjYgQzg0LjgzODYxNTgzIC0wLjU3NDQzNzk2IDkxLjQ2MDE0NzggLTAuNjA4MDg1OTEgOTguMDgyMjQyNzMgLTAuNjE5MjkwNzcgQzEwMi4wMzQ0NTk2MyAtMC42MjY4NDEwOCAxMDUuOTg1NTEyMzcgLTAuNjQ3OTMyNTQgMTA5LjkzNzM4NTU2IC0wLjcwMjMxODE5IEMxMTMuNjU3NzE0NzUgLTAuNzUyOTE1NjIgMTE3LjM3NjI2ODU5IC0wLjc2NTMxNjI5IDEyMS4wOTY4ODc1OSAtMC43NDkwNjczMSBDMTIzLjEwNDE0NjI1IC0wLjc0OTk1NDYxIDEyNS4xMTEyNjY0MyAtMC43OTA0NzQ5NiAxMjcuMTE4MDg3NzcgLTAuODMyMzgyMiBDMTMzLjM3NDE2NzE5IC0wLjc2Nzk2NzcgMTM3LjI0NTMwMTM5IC0wLjUwMTM1NTEzIDE0MS44MjY5OTU4NSAzLjk5ODAwMTEgQzE0Mi45NzE0MjIwNSA1LjM1ODMzOTgxIDE0NC4wODAwNTA5MiA2Ljc0OTkwOTMzIDE0NS4xNDM5ODE5MyA4LjE3NDA4NzUyIEMxNDguODcxMDY1NjQgMTIuNzgzMzY2MTUgMTUyLjU5NTIwNjQ1IDE2LjYwNzY5NjQ1IDE1Ny4zNDkyOTg0OCAyMC4xNDk0OTg5NCBDMTY5LjU0OTIxNzIgMjkuMzExNDc3NjggMTgxLjY4MjExNDk3IDM5LjM2NjI3MDMzIDE4NC4yOTA2ODM3NSA1NS4xMTg5ODk5NCBDMTg1Ljg1OTMyOTcxIDY4LjgyNjMwMTc5IDE4NS4xNjU3NTQzNSA4Mi42NDE1ODEwNyAxODQuNDM3MDcyNzUgOTYuMzgzNDM4MTEgQzE4NC4yMTEwOTMxMyAxMDEuMjI3NDc2MTkgMTg0LjExNTk2MTc5IDEwNi4wNzAxNjAwNyAxODQuMDI2ODcyNjMgMTEwLjkxODM3MDI1IEMxODMuODQxOTYwMTYgMTIwLjk0NTgyNTg1IDE4My41NzQwODg1MiAxMzAuOTcyNzcwNzUgMTgzLjA3NDE1NzcxIDE0MC45ODk3NjEzNSBDMTgzLjAyNTAzMjM1IDE0Mi4zNDMyNjk0MiAxODMuMDI1MDMyMzUgMTQyLjM0MzI2OTQyIDE4Mi45NzQ5MTQ1NSAxNDMuNzI0MTIxMDkgQzE4Mi40MDkxMDA3OCAxNTMuNTk3NjkzODUgMTc4LjUxODEzMDU5IDE2MS41ODUxODExIDE3MS4xNDM5ODE5MyAxNjguMTc0MDg3NTIgQzE2Mi4wNDg5NTM1IDE3NC4zNTU3MjU1MSAxNTMuMzc1MTc3ODQgMTc1LjA0MTMzMTM3IDE0Mi43MzM4MjU2OCAxNzQuOTUwNDU0NzEgQzE0MS4xMTYwNjQzNiAxNzQuOTU4NjAzMDIgMTM5LjQ5ODMxNDE4IDE3NC45NjkyOTYyIDEzNy44ODA1ODQ3MiAxNzQuOTgyMzE1MDYgQzEzMy41MjA4NDI0MSAxNzUuMDA5NDc1NDIgMTI5LjE2MTk1OTg3IDE3NC45OTkxNDQwNiAxMjQuODAyMjA0MTMgMTc0Ljk4MDA0MDU1IEMxMjAuMjE5NDEyMTcgMTc0Ljk2NDc0OTUgMTE1LjYzNjY4OTE5IDE3NC45Nzg5NjU1MSAxMTEuMDUzODk0MDQgMTc0Ljk4ODM1NzU0IEMxMDMuMzYwODA4NDMgMTc0Ljk5OTI0NDQ4IDk1LjY2ODA2OTQ5IDE3NC45ODQ5NjQyNSA4Ny45NzUwMzY2MiAxNzQuOTU2MzE0MDkgQzc5LjEwNjU0NTA5IDE3NC45MjM2NjU3NiA3MC4yMzg4ODE0NSAxNzQuOTM0MjQ0ODMgNjEuMzcwNDA5OTcgMTc0Ljk2NzI3MjUyIEM1My43MjgzNTg1MiAxNzQuOTk0NTgzNDIgNDYuMDg2NTU0OTMgMTc0Ljk5ODMwMjI5IDM4LjQ0NDQ3MDQxIDE3NC45ODI2NDA1IEMzMy44OTIzMzY2NSAxNzQuOTczMzMxNjQgMjkuMzQwNTkwODIgMTc0Ljk3MTkxMzA4IDI0Ljc4ODQ4MjY3IDE3NC45OTE4NTk0NCBDMjAuNTA2NzI5MzEgMTc1LjAwOTMxMzg0IDE2LjIyNjAzMzQgMTc0Ljk5Njk1MDkgMTEuOTQ0MzkzMTYgMTc0Ljk2MjQ5MzkgQzEwLjM4MjAzNTU1IDE3NC45NTQ2MjQ2NSA4LjgxOTU4NzQ2IDE3NC45NTY3Mjc3NCA3LjI1NzI2NyAxNzQuOTcwMDY5ODkgQy0zLjc0MTY3MDM2IDE3NS4wNTUyNzIxNCAtMTIuOTI0NjE0MjcgMTczLjU0MDU5MDk4IC0yMS4zNTI0NjI3NyAxNjUuOTE1NDAxNDYgQy0yOS4zNTMwMjE3NiAxNTcuNzIwMjIxNzMgLTMwLjMwMzAzNTE2IDE0OS42NzI1NzU4NiAtMzAuMjYwMDcwOCAxMzguNjU4NzA2NjcgQy0zMC4yNjY5MzQwNSAxMzcuNDIzNTM4MDYgLTMwLjI3Mzc5NzMgMTM2LjE4ODM2OTQ1IC0zMC4yODA4Njg1MyAxMzQuOTE1NzcxNDggQy0zMC4yOTU5NzQyNyAxMzEuNTQ3NzkxNzYgLTMwLjI5NzY0NzE0IDEyOC4xODAzNjgzMiAtMzAuMjkxOTI2MTUgMTI0LjgxMjM5NjI5IEMtMzAuMjg4ODIyNTMgMTIxLjk5MTk3NjgxIC0zMC4yOTQ5Mzk3MSAxMTkuMTcxNjA2NzQgLTMwLjMwMDk2NDg5IDExNi4zNTExOTQzMiBDLTMwLjMxNDk4MTg4IDEwOS42OTMyMzk0NSAtMzAuMzEzNDcyNDUgMTAzLjAzNTQxMjk3IC0zMC4zMDIwNjI5OSA5Ni4zNzc0NTY2NyBDLTMwLjI5MDU4NDAxIDg5LjUyNzkyODEyIC0zMC4zMDQ2NTk4NSA4Mi42Nzg3OTEyNCAtMzAuMzMxNDU5NDYgNzUuODI5MzE0MjkgQy0zMC4zNTM2Njk2OCA2OS45MzAwOTQ4MyAtMzAuMzYwMjQxOTMgNjQuMDMwOTk3ODMgLTMwLjM1NDM4MzA1IDU4LjEzMTczOTU2IEMtMzAuMzUxMDI0OTkgNTQuNjE2NDY4OSAtMzAuMzUzMzUxMjMgNTEuMTAxNDQ5MjMgLTMwLjM3MDYzNDA4IDQ3LjU4NjIxNDA3IEMtMzAuMzg5MDgwNDkgNDMuNjYzNjA5NTEgLTMwLjM3NDg2MTQxIDM5Ljc0MTQ4MDc3IC0zMC4zNTc3MjcwNSAzNS44MTg4NjI5MiBDLTMwLjM2NzM5NTAyIDM0LjY2Njg5NDIzIC0zMC4zNzcwNjI5OSAzMy41MTQ5MjU1NCAtMzAuMzg3MDIzOTMgMzIuMzI4MDQ4NzEgQy0zMC4yOTY1NDYwMyAyMi40MTE2NjIzMSAtMjcuNjA3NTUwMDYgMTUuMjU2OTY0NzQgLTIwLjg1NjAxODA3IDcuOTY3MDU2MjcgQy0xNC42NTMwNDY0MSAyLjQwNjE4OTExIC04LjIxNTc3NDY2IDAuMDc0NzY4NTIgMCAwIFogIiBmaWxsPSIjRjNGMURFIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMi44NTYwMTgwNjY0MDYyNSwxLjgyNTkxMjQ3NTU4NTkzNzUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzEuMTk3MTYxNDEgLTAuMDEyOTM1OTQgMi4zOTQzMjI4MSAtMC4wMjU4NzE4OSAzLjYyNzc2MTg0IC0wLjAzOTE5OTgzIEM0LjkzNzk2Nzk5IC0wLjA0ODE3MjkxIDYuMjQ4MTc0MTMgLTAuMDU3MTQ2IDcuNTk4MDgzNSAtMC4wNjYzOTA5OSBDOC45OTQ1NDUzMiAtMC4wNzkyMDg4OCAxMC4zOTEwMDQyNSAtMC4wOTIzNDQ1IDExLjc4NzQ2MDMzIC0wLjEwNTc3MzkzIEMyNC41MjIxMjg0MiAtMC4yMjE1NTIwNCAzNy4yNTc1NjgyNyAtMC4yNzgyOTMzOSA0OS45OTI2NjQzNCAtMC4zMjI1NDEyNCBDNTkuNDAxMTc1MyAtMC4zNTU3ODQ4MiA2OC44MDg3MjM4OCAtMC40MTUzNjU3MSA3OC4yMTY4MjE5MSAtMC41MTAwNTIyNiBDODQuODM4NjE1ODMgLTAuNTc0NDM3OTYgOTEuNDYwMTQ3OCAtMC42MDgwODU5MSA5OC4wODIyNDI3MyAtMC42MTkyOTA3NyBDMTAyLjAzNDQ1OTYzIC0wLjYyNjg0MTA4IDEwNS45ODU1MTIzNyAtMC42NDc5MzI1NCAxMDkuOTM3Mzg1NTYgLTAuNzAyMzE4MTkgQzExMy42NTc3MTQ3NSAtMC43NTI5MTU2MiAxMTcuMzc2MjY4NTkgLTAuNzY1MzE2MjkgMTIxLjA5Njg4NzU5IC0wLjc0OTA2NzMxIEMxMjMuMTA0MTQ2MjUgLTAuNzQ5OTU0NjEgMTI1LjExMTI2NjQzIC0wLjc5MDQ3NDk2IDEyNy4xMTgwODc3NyAtMC44MzIzODIyIEMxMzMuMzc0MTY3MTkgLTAuNzY3OTY3NyAxMzcuMjQ1MzAxMzkgLTAuNTAxMzU1MTMgMTQxLjgyNjk5NTg1IDMuOTk4MDAxMSBDMTQyLjk3MTQyMjA1IDUuMzU4MzM5ODEgMTQ0LjA4MDA1MDkyIDYuNzQ5OTA5MzMgMTQ1LjE0Mzk4MTkzIDguMTc0MDg3NTIgQzE0OC44NzEwNjU2NCAxMi43ODMzNjYxNSAxNTIuNTk1MjA2NDUgMTYuNjA3Njk2NDUgMTU3LjM0OTI5ODQ4IDIwLjE0OTQ5ODk0IEMxNjkuNTQ5MjE3MiAyOS4zMTE0Nzc2OCAxODEuNjgyMTE0OTcgMzkuMzY2MjcwMzMgMTg0LjI5MDY4Mzc1IDU1LjExODk4OTk0IEMxODUuODU5MzI5NzEgNjguODI2MzAxNzkgMTg1LjE2NTc1NDM1IDgyLjY0MTU4MTA3IDE4NC40MzcwNzI3NSA5Ni4zODM0MzgxMSBDMTg0LjIxMTA5MzEzIDEwMS4yMjc0NzYxOSAxODQuMTE1OTYxNzkgMTA2LjA3MDE2MDA3IDE4NC4wMjY4NzI2MyAxMTAuOTE4MzcwMjUgQzE4My44NDE5NjAxNiAxMjAuOTQ1ODI1ODUgMTgzLjU3NDA4ODUyIDEzMC45NzI3NzA3NSAxODMuMDc0MTU3NzEgMTQwLjk4OTc2MTM1IEMxODMuMDI1MDMyMzUgMTQyLjM0MzI2OTQyIDE4My4wMjUwMzIzNSAxNDIuMzQzMjY5NDIgMTgyLjk3NDkxNDU1IDE0My43MjQxMjEwOSBDMTgyLjQwOTEwMDc4IDE1My41OTc2OTM4NSAxNzguNTE4MTMwNTkgMTYxLjU4NTE4MTEgMTcxLjE0Mzk4MTkzIDE2OC4xNzQwODc1MiBDMTYyLjA0ODk1MzUgMTc0LjM1NTcyNTUxIDE1My4zNzUxNzc4NCAxNzUuMDQxMzMxMzcgMTQyLjczMzgyNTY4IDE3NC45NTA0NTQ3MSBDMTQxLjExNjA2NDM2IDE3NC45NTg2MDMwMiAxMzkuNDk4MzE0MTggMTc0Ljk2OTI5NjIgMTM3Ljg4MDU4NDcyIDE3NC45ODIzMTUwNiBDMTMzLjUyMDg0MjQxIDE3NS4wMDk0NzU0MiAxMjkuMTYxOTU5ODcgMTc0Ljk5OTE0NDA2IDEyNC44MDIyMDQxMyAxNzQuOTgwMDQwNTUgQzEyMC4yMTk0MTIxNyAxNzQuOTY0NzQ5NSAxMTUuNjM2Njg5MTkgMTc0Ljk3ODk2NTUxIDExMS4wNTM4OTQwNCAxNzQuOTg4MzU3NTQgQzEwMy4zNjA4MDg0MyAxNzQuOTk5MjQ0NDggOTUuNjY4MDY5NDkgMTc0Ljk4NDk2NDI1IDg3Ljk3NTAzNjYyIDE3NC45NTYzMTQwOSBDNzkuMTA2NTQ1MDkgMTc0LjkyMzY2NTc2IDcwLjIzODg4MTQ1IDE3NC45MzQyNDQ4MyA2MS4zNzA0MDk5NyAxNzQuOTY3MjcyNTIgQzUzLjcyODM1ODUyIDE3NC45OTQ1ODM0MiA0Ni4wODY1NTQ5MyAxNzQuOTk4MzAyMjkgMzguNDQ0NDcwNDEgMTc0Ljk4MjY0MDUgQzMzLjg5MjMzNjY1IDE3NC45NzMzMzE2NCAyOS4zNDA1OTA4MiAxNzQuOTcxOTEzMDggMjQuNzg4NDgyNjcgMTc0Ljk5MTg1OTQ0IEMyMC41MDY3MjkzMSAxNzUuMDA5MzEzODQgMTYuMjI2MDMzNCAxNzQuOTk2OTUwOSAxMS45NDQzOTMxNiAxNzQuOTYyNDkzOSBDMTAuMzgyMDM1NTUgMTc0Ljk1NDYyNDY1IDguODE5NTg3NDYgMTc0Ljk1NjcyNzc0IDcuMjU3MjY3IDE3NC45NzAwNjk4OSBDLTMuNzQxNjcwMzYgMTc1LjA1NTI3MjE0IC0xMi45MjQ2MTQyNyAxNzMuNTQwNTkwOTggLTIxLjM1MjQ2Mjc3IDE2NS45MTU0MDE0NiBDLTI5LjM1MzAyMTc2IDE1Ny43MjAyMjE3MyAtMzAuMzAzMDM1MTYgMTQ5LjY3MjU3NTg2IC0zMC4yNjAwNzA4IDEzOC42NTg3MDY2NyBDLTMwLjI2NjkzNDA1IDEzNy40MjM1MzgwNiAtMzAuMjczNzk3MyAxMzYuMTg4MzY5NDUgLTMwLjI4MDg2ODUzIDEzNC45MTU3NzE0OCBDLTMwLjI5NTk3NDI3IDEzMS41NDc3OTE3NiAtMzAuMjk3NjQ3MTQgMTI4LjE4MDM2ODMyIC0zMC4yOTE5MjYxNSAxMjQuODEyMzk2MjkgQy0zMC4yODg4MjI1MyAxMjEuOTkxOTc2ODEgLTMwLjI5NDkzOTcxIDExOS4xNzE2MDY3NCAtMzAuMzAwOTY0ODkgMTE2LjM1MTE5NDMyIEMtMzAuMzE0OTgxODggMTA5LjY5MzIzOTQ1IC0zMC4zMTM0NzI0NSAxMDMuMDM1NDEyOTcgLTMwLjMwMjA2Mjk5IDk2LjM3NzQ1NjY3IEMtMzAuMjkwNTg0MDEgODkuNTI3OTI4MTIgLTMwLjMwNDY1OTg1IDgyLjY3ODc5MTI0IC0zMC4zMzE0NTk0NiA3NS44MjkzMTQyOSBDLTMwLjM1MzY2OTY4IDY5LjkzMDA5NDgzIC0zMC4zNjAyNDE5MyA2NC4wMzA5OTc4MyAtMzAuMzU0MzgzMDUgNTguMTMxNzM5NTYgQy0zMC4zNTEwMjQ5OSA1NC42MTY0Njg5IC0zMC4zNTMzNTEyMyA1MS4xMDE0NDkyMyAtMzAuMzcwNjM0MDggNDcuNTg2MjE0MDcgQy0zMC4zODkwODA0OSA0My42NjM2MDk1MSAtMzAuMzc0ODYxNDEgMzkuNzQxNDgwNzcgLTMwLjM1NzcyNzA1IDM1LjgxODg2MjkyIEMtMzAuMzY3Mzk1MDIgMzQuNjY2ODk0MjMgLTMwLjM3NzA2Mjk5IDMzLjUxNDkyNTU0IC0zMC4zODcwMjM5MyAzMi4zMjgwNDg3MSBDLTMwLjI5NjU0NjAzIDIyLjQxMTY2MjMxIC0yNy42MDc1NTAwNiAxNS4yNTY5NjQ3NCAtMjAuODU2MDE4MDcgNy45NjcwNTYyNyBDLTE0LjY1MzA0NjQxIDIuNDA2MTg5MTEgLTguMjE1Nzc0NjYgMC4wNzQ3Njg1MiAwIDAgWiBNLTIxLjM4MzM2MTgyIDE0LjkxNjI3NTAyIEMtMjYuMDAxNjQ2MTUgMjEuOTk2ODI3NjcgLTI2LjI5MzUyNjQgMjcuODc1MzE0NTggLTI2LjI2MDA3MDggMzYuMTc3MDE3MjEgQy0yNi4yNjY5MzQwNSAzNy4zNjk4MTI5MyAtMjYuMjczNzk3MyAzOC41NjI2MDg2NCAtMjYuMjgwODY4NTMgMzkuNzkxNTQ5NjggQy0yNi4yOTU5ODgwNiA0My4wNDgwODE3OSAtMjYuMjk3NjQyMzkgNDYuMzA0MjUzMTYgLTI2LjI5MTkyNjE1IDQ5LjU2MDgwNTMyIEMtMjYuMjg4ODIzNjggNTIuMjg5MDI5NjEgLTI2LjI5NDkzODg3IDU1LjAxNzIwMjc2IC0yNi4zMDA5NjQ4OSA1Ny43NDU0MTk3NCBDLTI2LjMxNDk5MDQ5IDY0LjE4OTEzMzk2IC0yNi4zMTM0NjY5OCA3MC42MzI3MTU1NiAtMjYuMzAyMDYyOTkgNzcuMDc2NDMxMjcgQy0yNi4yOTA2MDAxNSA4My42OTkyNTc2NiAtMjYuMzA0NjMwNjQgOTAuMzIxNjc4MjEgLTI2LjMzMTQ1OTQ2IDk2Ljk0NDQ1MTA5IEMtMjYuMzUzNzAxMjUgMTAyLjY1MDkwMjg1IC0yNi4zNjAyMzQzOSAxMDguMzU3MjI4MDIgLTI2LjM1NDM4MzA1IDExNC4wNjM3MTk5OSBDLTI2LjM1MTAzMDQxIDExNy40NjI4MTg2MSAtMjYuMzUzMzEzNjggMTIwLjg2MTY1NzU4IC0yNi4zNzA2MzQwOCAxMjQuMjYwNzE5MyBDLTI2LjM4NTU5MTEyIDEyOC4wNTg2NDE3MiAtMjYuMzc0ODI5ODEgMTMxLjg1NTY4MzkxIC0yNi4zNTc3MjcwNSAxMzUuNjUzNTc5NzEgQy0yNi4zNjczOTUwMiAxMzYuNzYyNTI1OTQgLTI2LjM3NzA2Mjk5IDEzNy44NzE0NzIxNyAtMjYuMzg3MDIzOTMgMTM5LjAxNDAyMjgzIEMtMjYuMzAyMjI1NCAxNDguMDUzOTAxMjggLTI0LjcxMzUzNTAyIDE1Ni4zNzgzMzcyNCAtMTguNDgxMDE4MDcgMTYzLjIzNjU4NzUyIEMtMTUuNTkwODM0NiAxNjYuMDM3NzM2OSAtMTIuNjk0MDYxNDUgMTY3LjA0NDI5NzI5IC04Ljg1NjAxODA3IDE2OC4xNzQwODc1MiBDLTguODUxOTA5MTggMTY3LjQ1ODY1Nzg0IC04Ljg0NzgwMDI5IDE2Ni43NDMyMjgxNSAtOC44NDM1NjY4OSAxNjYuMDA2MTE4NzcgQy04LjgxMDQzNTUgMTYyLjcyNzk2NjI3IC04LjczOTc2ODg2IDE1OS40NTE2MjQyNSAtOC42Njg1MTgwNyAxNTYuMTc0MDg3NTIgQy04LjY2MjcxNzI5IDE1NS4wNDg3MzU5NiAtOC42NTY5MTY1IDE1My45MjMzODQ0IC04LjY1MDkzOTk0IDE1Mi43NjM5MzEyNyBDLTguNDY3NjY2NDkgMTQ1Ljc5OTU0MDIzIC03LjgxMTU2Mjc2IDE0MC4zNzUxNDI1NCAtMi44NTYwMTgwNyAxMzUuMTc0MDg3NTIgQy0xLjk0MDc4MzY5IDEzNC43MjAzMzc1MiAtMS4wMjU1NDkzMiAxMzQuMjY2NTg3NTIgLTAuMDgyNTgwNTcgMTMzLjc5OTA4NzUyIEMyLjMyMjU1NjAxIDEzMi40MTU1Nzk4MiAyLjMyMjU1NjAxIDEzMi40MTU1Nzk4MiAyLjYxNzYxNDc1IDEzMC4xMzExMTg3NyBDMi44MTk0NjU1IDEyNy40NjA0NzgwMyAyLjgyNTU0NjkgMTI0Ljg1MTQ5NjEzIDIuNzY4OTgxOTMgMTIyLjE3NDA4NzUyIEMyLjc4ODMxNzg3IDEyMS4yNTg4NTMxNSAyLjgwNzY1MzgxIDEyMC4zNDM2MTg3NyAyLjgyNzU3NTY4IDExOS40MDA2NTAwMiBDMi44MDkwOTQ3NyAxMTYuODc5ODUzODkgMi43MjM5MDQ5NSAxMTQuNjI4MDk4OTMgMi4xNDM5ODE5MyAxMTIuMTc0MDg3NTIgQy0wLjE4MDM2NDEzIDExMC4xMDI3OTAyOSAtMC4xODAzNjQxMyAxMTAuMTAyNzkwMjkgLTIuODU2MDE4MDcgMTA5LjE3NDA4NzUyIEMtNC45MzI2NDE2NiAxMDUuNTE1Mjc0NTMgLTUuMjg5NTY1MTIgMTAyLjMzNjEzOTIyIC00Ljg1NjAxODA3IDk4LjE3NDA4NzUyIEMtMy4xMzY1NzA0MSA5NC4zOTEzMDI2NyAtMS43MDEyNjU1NCA5Mi42ODE3NTY1IDIuMTQzOTgxOTMgOTEuMTc0MDg3NTIgQzYuNTU0ODM2MTcgOTAuOTQxMTE5ODcgMTAuMTI1MzY5MzcgOTEuMzg0NjYyNzkgMTMuNjQzOTgxOTMgOTQuMTExNTg3NTIgQzE2LjQ2NDE0MjYzIDk3Ljk4OTMwODQ5IDE2LjIyNjc1NzA3IDEwMC4zMzcwOTg5IDE1LjY1OTYwNjkzIDEwNC45Nzg3NzUwMiBDMTQuOTUwNzI1NTggMTA3Ljk5Njg5MTExIDEzLjYyMDYwNjc1IDEwOS4zNzI5MDU4NCAxMS4xNDM5ODE5MyAxMTEuMTc0MDg3NTIgQzEwLjE1Mzk4MTkzIDExMS42NjkwODc1MiAxMC4xNTM5ODE5MyAxMTEuNjY5MDg3NTIgOS4xNDM5ODE5MyAxMTIuMTc0MDg3NTIgQzkuMTQzOTgxOTMgMTE3LjQ1NDA4NzUyIDkuMTQzOTgxOTMgMTIyLjczNDA4NzUyIDkuMTQzOTgxOTMgMTI4LjE3NDA4NzUyIEMxMS4zMzkxMTY2NiAxMjUuMjUwNzM4MDQgMTEuMzM5MTE2NjYgMTI1LjI1MDczODA0IDEzLjQ2ODIwMDY4IDEyMi4yNzk1NTYyNyBDMTkuOTc0Njg3MzQgMTEzLjEyNTczMTIxIDI3LjkwOTMyNzI1IDEwNi40MzA3MDIyOSAzOS4xNDM5ODE5MyAxMDQuMTc0MDg3NTIgQzQxLjQ3OTkxODI4IDEwNC4wNzYyMjk2NiA0My44MTg0NTg1OSAxMDQuMDM1ODkyMjQgNDYuMTU2NDMzMTEgMTA0LjAyODgyMzg1IEM0Ni44NTQ1NzEyMyAxMDQuMDI1NDY1MjQgNDcuNTUyNzA5MzUgMTA0LjAyMjEwNjYzIDQ4LjI3MjAwMzE3IDEwNC4wMTg2NDYyNCBDNTAuNTc3MDAzODcgMTA0LjAwOTE1ODg4IDUyLjg4MTkzMTY4IDEwNC4wMDcxOTM5MSA1NS4xODY5NTA2OCAxMDQuMDA2MTE4NzcgQzU2Ljc5NjM1MTQxIDEwNC4wMDI5MDM0OCA1OC40MDU3NTE4NSAxMDMuOTk5NTM5MiA2MC4wMTUxNTE5OCAxMDMuOTk2MDMyNzEgQzYzLjM5MDIxNDgxIDEwMy45OTAwNDYxNSA2Ni43NjUyNTIwNiAxMDMuOTg4MTkyMjEgNzAuMTQwMzE5ODIgMTAzLjk4ODc4NDc5IEM3NC40NDYyNzk1OSAxMDMuOTg4ODg2MTIgNzguNzUyMDczMzYgMTAzLjk3NTI2MjQ0IDgzLjA1Nzk5Mzg5IDEwMy45NTgwMTI1OCBDODYuMzg0MTg4MjIgMTAzLjk0Njg0MTIyIDg5LjcxMDM0MzE3IDEwMy45NDQ4NjcxOSA5My4wMzY1NTQzNCAxMDMuOTQ1Mzk0NTIgQzk0LjYyMjgzMjA1IDEwMy45NDQyMjEwNCA5Ni4yMDkxMTEgMTAzLjkzOTgzNTA3IDk3Ljc5NTM3MDEgMTAzLjkzMjA2MjE1IEMxMTEuOTAxMjgzODggMTAzLjg2OTQzOTAzIDEyNC43OTA3ODk1MyAxMDQuNzc4Njg0NDEgMTM1LjUxODk4MTkzIDExNS4wNDkwODc1MiBDMTM4Ljc1MzYzMDE0IDExOC40OTMyNjc2IDE0MS4wMzUzNTc0MSAxMjEuOTU2ODM4NDggMTQzLjE0Mzk4MTkzIDEyNi4xNzQwODc1MiBDMTQzLjQ3Mzk4MTkzIDEyNi4xNzQwODc1MiAxNDMuODAzOTgxOTMgMTI2LjE3NDA4NzUyIDE0NC4xNDM5ODE5MyAxMjYuMTc0MDg3NTIgQzE0NC4yNzg4NzU4NSAxMjMuOTA0ODcyMTYgMTQ0LjM3NTkyMTg5IDEyMS42MzMzODA4MyAxNDQuNDU2NDgxOTMgMTE5LjM2MTU4NzUyIEMxNDQuNTE0NDg5NzUgMTE4LjA5NzAxNzIxIDE0NC41NzI0OTc1NiAxMTYuODMyNDQ2OSAxNDQuNjMyMjYzMTggMTE1LjUyOTU1NjI3IEMxNDQuMzkwNTYzOTYgMTEzLjg2ODU5OTI0IDE0NC4zOTA1NjM5NiAxMTMuODY4NTk5MjQgMTQ0LjE0Mzk4MTkzIDExMi4xNzQwODc1MiBDMTQxLjc4NTgyMjc5IDExMC4zMDI2NjE3NSAxNDEuNzg1ODIyNzkgMTEwLjMwMjY2MTc1IDEzOS4xNDM5ODE5MyAxMDkuMTc0MDg3NTIgQzEzNy4wNjczNTgzNCAxMDUuNTE1Mjc0NTMgMTM2LjcxMDQzNDg4IDEwMi4zMzYxMzkyMiAxMzcuMTQzOTgxOTMgOTguMTc0MDg3NTIgQzEzOC44NjM0Mjk1OSA5NC4zOTEzMDI2NyAxNDAuMjk4NzM0NDYgOTIuNjgxNzU2NSAxNDQuMTQzOTgxOTMgOTEuMTc0MDg3NTIgQzE0OC41NTQ4MzYxNyA5MC45NDExMTk4NyAxNTIuMTI1MzY5MzcgOTEuMzg0NjYyNzkgMTU1LjY0Mzk4MTkzIDk0LjExMTU4NzUyIEMxNTguNDY0MTQyNjMgOTcuOTg5MzA4NDkgMTU4LjIyNjc1NzA3IDEwMC4zMzcwOTg5IDE1Ny42NTk2MDY5MyAxMDQuOTc4Nzc1MDIgQzE1Ni45NTA3MjU1OCAxMDcuOTk2ODkxMTEgMTU1LjYyMDYwNjc1IDEwOS4zNzI5MDU4NCAxNTMuMTQzOTgxOTMgMTExLjE3NDA4NzUyIEMxNTIuMTUzOTgxOTMgMTExLjY2OTA4NzUyIDE1Mi4xNTM5ODE5MyAxMTEuNjY5MDg3NTIgMTUxLjE0Mzk4MTkzIDExMi4xNzQwODc1MiBDMTUwLjkxMDY5MTA3IDExNS40MDIwMjU0OSAxNTAuNzY5MzA1MTkgMTE4LjYyNzc5OTg1IDE1MC42NDM5ODE5MyAxMjEuODYxNTg3NTIgQzE1MC41NzY5NTA2OCAxMjIuNzczNTk5MjQgMTUwLjUwOTkxOTQzIDEyMy42ODU2MTA5NiAxNTAuNDQwODU2OTMgMTI0LjYyNTI1OTQgQzE1MC4zNjE1ODAzIDEyNy4zNTAzOTM3MyAxNTAuMzkyMjE5NjQgMTI5LjU0OTE0NjQ2IDE1MS4xNDM5ODE5MyAxMzIuMTc0MDg3NTIgQzE1My4wMzQ0NjY0MiAxMzMuODg1OTkwMjggMTU0LjkyMDY3MzA3IDEzNC45MDkxMjM5MSAxNTcuMTgzNzc2ODYgMTM2LjA3MzI1NzQ1IEMxNjAuNDAyNDQ1OTQgMTM3Ljg4MDgyNzM3IDE2MS4wOTAwMzgzOCAxMzkuNjc2NzMxNzggMTYyLjE0Mzk4MTkzIDE0My4xNzQwODc1MiBDMTYyLjYxMzE2MTE1IDE0Ny4yOTg0MzE0NyAxNjIuNzQ1OTc5ODYgMTUxLjQwMTAxNTUzIDE2Mi44MzE0ODE5MyAxNTUuNTQ5MDg3NTIgQzE2Mi44NjU2NDIwOSAxNTYuNjY2NzA0NzEgMTYyLjg5OTgwMjI1IDE1Ny43ODQzMjE5IDE2Mi45MzQ5OTc1NiAxNTguOTM1ODA2MjcgQzE2My4wMTcxNDI2NiAxNjEuNjgxNzk5NzUgMTYzLjA4NjQwMzIyIDE2NC40Mjc0ODczNSAxNjMuMTQzOTgxOTMgMTY3LjE3NDA4NzUyIEMxNjYuODgxNTQ5MDQgMTY1Ljc2NzIyODkzIDE2OS4xMjU1NzU2NSAxNjQuNTc4NzQ0OTUgMTcxLjc2ODk4MTkzIDE2MS41NDkwODc1MiBDMTcyLjMzODc0NzU2IDE2MC45MTc0NDY5IDE3Mi45MDg1MTMxOCAxNjAuMjg1ODA2MjcgMTczLjQ5NTU0NDQzIDE1OS42MzUwMjUwMiBDMTc3Ljg3NTQ4MzkzIDE1My4wOTYyNTI3OCAxNzguNTc4NDUxNzYgMTQ3LjAwNzA4MjQ2IDE3OC40ODQ1NTgxMSAxMzkuMzYxNTg3NTIgQzE3OC40ODQ5OTYxOSAxMzguNDE0MzIyOTcgMTc4LjQ4NTQzNDI3IDEzNy40NjcwNTg0MSAxNzguNDg1ODg1NjIgMTM2LjQ5MTA4ODg3IEMxNzguNDgzODA5MDYgMTMzLjM4NTIwNjE2IDE3OC40NjA1MzE1NCAxMzAuMjc5ODc1IDE3OC40MzY5NTA2OCAxMjcuMTc0MDg3NTIgQzE3OC40MzEzNTA3IDEyNS4wMTA3NjA0NSAxNzguNDI3MDgyNSAxMjIuODQ3NDI5NTQgMTc4LjQyNDA4NzUyIDEyMC42ODQwOTcyOSBDMTc4LjQxMjY3MDQ3IDExNS4wMDkwNDIwNiAxNzguMzgzMjMwMjUgMTA5LjMzNDIxOTY2IDE3OC4zNDk5NzU1OSAxMDMuNjU5MjU1OTggQzE3OC4zMTkyMDY5OCA5Ny44NjA4NDY0NCAxNzguMzA1NTY3NjkgOTIuMDYyMzk5MDQgMTc4LjI5MDQ2NjMxIDg2LjI2MzkzMTI3IEMxNzguMjU4MzU3NTggNzQuOTAwNTQ4MTIgMTc4LjIwNzE5OTIzIDYzLjUzNzMzNzIzIDE3OC4xNDM5ODE5MyA1Mi4xNzQwODc1MiBDMTc3LjUyNDY2Nzk3IDUyLjE1OTUwNSAxNzYuOTA1MzU0IDUyLjE0NDkyMjQ5IDE3Ni4yNjcyNzI5NSA1Mi4xMjk4OTgwNyBDMTczLjQxMzEwNDIgNTIuMDU0NjEzMzUgMTcwLjU2MDAwNDQ0IDUxLjk1ODIyNDM5IDE2Ny43MDY0ODE5MyA1MS44NjE1ODc1MiBDMTY2LjczMjU5NTIxIDUxLjgzOTAyODkzIDE2NS43NTg3MDg1IDUxLjgxNjQ3MDM0IDE2NC43NTUzMTAwNiA1MS43OTMyMjgxNSBDMTU2LjgwMTYyMzgxIDUxLjUwMDYxNzYyIDE0OS4yMDUzMjUxNyA1MC4zMjMyMzczMSAxNDIuNzY4OTgxOTMgNDUuMjk5MDg3NTIgQzE0Mi4xNDI0OTc1NiA0NC44MjcyOTA2NSAxNDEuNTE2MDEzMTggNDQuMzU1NDkzNzcgMTQwLjg3MDU0NDQzIDQzLjg2OTQwMDAyIEMxMzkuMTQzOTgxOTMgNDIuMTc0MDg3NTIgMTM5LjE0Mzk4MTkzIDQyLjE3NDA4NzUyIDEzOC4yMTcxNDc4MyAzOS42MDM2MDcxOCBDMTM2LjQzMTEwMjY1IDM3LjAxMzAzNjIzIDEzNi40MzExMDI2NSAzNy4wMTMwMzYyMyAxMzQuMDM2NzY3OTYgMzYuNzE5NTAzNCBDMTMxLjA5NTk2ODQ4IDM2LjU1NDgxNTk2IDEyOC4xOTE5MTY4NyAzNi41NTY3Mjc2MiAxMjUuMjQ2NzY1MTQgMzYuNjA2NDYwNTcgQzEyNC4xMjQ2NjY0NCAzNi42MDU3MzA0NCAxMjMuMDAyNTY3NzUgMzYuNjA1MDAwMzEgMTIxLjg0NjQ2NjA2IDM2LjYwNDI0ODA1IEMxMTguMTI4NzQ0MDcgMzYuNjA3NzM4MDggMTE0LjQxMjI2Njk1IDM2LjY0NjY2ODkzIDExMC42OTQ3NjMxOCAzNi42ODU4MDYyNyBDMTA4LjEyMDcyOTQyIDM2LjY5NTEyOTI5IDEwNS41NDY2ODY3OSAzNi43MDIyNDc4MSAxMDIuOTcyNjQwOTkgMzYuNzA3MjQ0ODcgQzk2Ljg4NDQ5OTQ1IDM2LjcyNDQwMDMgOTAuNzk2ODczNDggMzYuNzYzODI2MjcgODQuNzA4OTI2MzggMzYuODEzOTkyMzIgQzc3Ljc3Nzk3NTk1IDM2Ljg2OTg2NTk3IDcwLjg0NzAwNDE3IDM2Ljg5NzM2NzE3IDYzLjkxNTg4NDYxIDM2LjkyMjQ2OTUgQzQ5LjY1ODM0Mjc2IDM2Ljk3NDc0MjMgMzUuNDAxMTc4MjcgMzcuMDYyNjk2ODIgMjEuMTQzOTgxOTMgMzcuMTc0MDg3NTIgQzIxLjE0Mzk4MTkzIDM1LjE5NDA4NzUyIDIxLjE0Mzk4MTkzIDMzLjIxNDA4NzUyIDIxLjE0Mzk4MTkzIDMxLjE3NDA4NzUyIEM1Ny43NzM5ODE5MyAzMS4xNzQwODc1MiA5NC40MDM5ODE5MyAzMS4xNzQwODc1MiAxMzIuMTQzOTgxOTMgMzEuMTc0MDg3NTIgQzEzMS44MTM5ODE5MyAyMi4yNjQwODc1MiAxMzEuNDgzOTgxOTMgMTMuMzU0MDg3NTIgMTMxLjE0Mzk4MTkzIDQuMTc0MDg3NTIgQzExMy40NTQwOTEwNCA0LjA2MTA1MTk3IDk1Ljc2NDUxMDIgMy45NjkwMTc0NCA3OC4wNzQzNTc5OSAzLjkxNTAyNjY2IEM2OS44NTk4MDM4NyAzLjg4OTI4NzIgNjEuNjQ1NDkwNzIgMy44NTQyMTg3MSA1My40MzEwOTEzMSAzLjc5Njg5MDI2IEM0Ni4yNjgxODY0MSAzLjc0NjkyNDIgMzkuMTA1NDU2MDkgMy43MTQ3NjU1OCAzMS45NDIzODE3NCAzLjcwMzY0NTA1IEMyOC4xNTI0MDkxNyAzLjY5NzE0ODQ1IDI0LjM2MjkzNTg3IDMuNjgxOTcwMDcgMjAuNTczMTIzOTMgMy42NDU0MzcyNCBDMTYuOTk5MjUwNTUgMy42MTEyNjgxIDEzLjQyNjA3NzczIDMuNjAxMDc1OTkgOS44NTIwNTY1IDMuNjA4NTQ5MTIgQzcuOTI4NzQwMzcgMy42MDY1MzcyOCA2LjAwNTQ5ODk2IDMuNTc5NzUyMTEgNC4wODIzODIyIDMuNTUxOTg2NjkgQy02Ljc4OTE0MTYgMy42MTYwNDUzMiAtMTQuMjg1NzkzMjggNi41ODA1NTU3MiAtMjEuMzgzMzYxODIgMTQuOTE2Mjc1MDIgWiAiIGZpbGw9IiMwNzA0MDUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMyLjg1NjAxODA2NjQwNjI1LDEuODI1OTEyNDc1NTg1OTM3NSkiLz4KPHBhdGggZD0iTTAgMCBDMC43MTI2ODAzNiAtMC4wMDgxNzc0OSAxLjQyNTM2MDcyIC0wLjAxNjM1NDk4IDIuMTU5NjM3NDUgLTAuMDI0NzgwMjcgQzQuNTIwNTYwNDYgLTAuMDQ5NjA1NTYgNi44ODE0MjMwNSAtMC4wNjYxNTA3NCA5LjI0MjQzMTY0IC0wLjA4MDU2NjQxIEMxMC40NTcwMDEzNyAtMC4wODg1NTE5MiAxMC40NTcwMDEzNyAtMC4wODg1NTE5MiAxMS42OTYxMDc4NiAtMC4wOTY2OTg3NiBDMTUuOTg5MjkwMTMgLTAuMTIzNDI5MTMgMjAuMjgyNDQzNDYgLTAuMTQyNzk2NzYgMjQuNTc1NjgzNTkgLTAuMTU3MjI2NTYgQzI4LjExMTEwNjAxIC0wLjE3MDUxNTE5IDMxLjY0NTk0NDMgLTAuMTk4MjcyNzQgMzUuMTgxMTUyMzQgLTAuMjM5MjU3ODEgQzM5LjQ2NDA5Mzc2IC0wLjI4ODkwMzc5IDQzLjc0NjUwOTIyIC0wLjMxMzM4NzQyIDQ4LjAyOTcyNjAzIC0wLjMyMDQ1MzY0IEM0OS42NTI2NTIwMiAtMC4zMjcxMTc5NyA1MS4yNzU1Njg1MyAtMC4zNDIyMTI0MiA1Mi44OTgzMjg3OCAtMC4zNjYzNDQ0NSBDNjUuNTk3MTA4MjMgLTAuNTQ0Mjc4ODIgNzYuNjQ3NTEzNzIgMS4wODY4NDcwNSA4Ni4yMTUwODc4OSAxMC4yMzU4Mzk4NCBDOTMuMDgyNTU2OTcgMTcuOTE1NzU1MDQgOTYuMzA2MzM5NzcgMjUuMDQ5MTU4MTQgOTYuMTYwNDAwMzkgMzUuNDM1MDU4NTkgQzk2LjE1NzU3MDUgMzYuMjAzNDMwNDggOTYuMTU0NzQwNiAzNi45NzE4MDIzNyA5Ni4xNTE4MjQ5NSAzNy43NjM0NTgyNSBDOTYuMTQwNzAxIDQwLjE5ODQyNjMgOTYuMTE1NjExNDkgNDIuNjMzMDE1NzEgOTYuMDkwMDg3ODkgNDUuMDY3ODcxMDkgQzk2LjA4MDA0OCA0Ni43MjY3MTk5MiA5Ni4wNzA5MjM5MiA0OC4zODU1NzQ1NSA5Ni4wNjI3NDQxNCA1MC4wNDQ0MzM1OSBDOTYuMDQyNDk1NDQgNTQuMDk0MTczMTggOTYuMDAzMzMzMzUgNTguMTQzMTAyMjggOTUuOTY1MDg3ODkgNjIuMTkyODcxMDkgQzkwLjAyNTA4Nzg5IDYyLjE5Mjg3MTA5IDg0LjA4NTA4Nzg5IDYyLjE5Mjg3MTA5IDc3Ljk2NTA4Nzg5IDYyLjE5Mjg3MTA5IEM3Ny45MzkzMDY2NCA2MC4xNjkwNDI5NyA3Ny45MTM1MjUzOSA1OC4xNDUyMTQ4NCA3Ny44ODY5NjI4OSA1Ni4wNjAwNTg1OSBDNzcuODQ1MjE3NjUgNTQuMDg0MTE3MzYgNzcuODAzMzMxODkgNTIuMTA4MTc2NDcgNzcuNzU3NTY4MzYgNTAuMTMyMzI0MjIgQzc3LjcyOTQ3MTY2IDQ4Ljc2Njg4OTQ1IDc3LjcwODIwMTEyIDQ3LjQwMTI5NTQ2IDc3LjY5NDA5MTggNDYuMDM1NjQ0NTMgQzc3LjY3MjUwODA3IDQ0LjA2NDMzMDcxIDc3LjYyMzczMzMgNDIuMDkzMzc0NzkgNzcuNTc0NDYyODkgNDAuMTIyNTU4NTkgQzc3LjU0MzA0MTk5IDM4LjM0NzI3NzgzIDc3LjU0MzA0MTk5IDM4LjM0NzI3NzgzIDc3LjUxMDk4NjMzIDM2LjUzNjEzMjgxIEM3Ni44NjU1MDg3NCAzMi41ODMwMTU2IDc1LjMwMDY0NzE1IDMwLjM5NzQzNTEgNzIuOTY1MDg3ODkgMjcuMTkyODcxMDkgQzcyLjk2NTA4Nzg5IDI2LjUzMjg3MTA5IDcyLjk2NTA4Nzg5IDI1Ljg3Mjg3MTA5IDcyLjk2NTA4Nzg5IDI1LjE5Mjg3MTA5IEM2Ny43Mzc4NTgyNSAyMy4wNzM3MjM5NCA2My43ODY3OTQ4NiAyMi4wNjA0MTk0OSA1OC4xMTAzNTE1NiAyMi4wMTU4NjkxNCBDNTcuNDE3Mjk0MTYgMjIuMDA5Mjk3OTQgNTYuNzI0MjM2NzYgMjIuMDAyNzI2NzUgNTYuMDEwMTc3NjEgMjEuOTk1OTU2NDIgQzUzLjcxNzgwMjgxIDIxLjk3NjI1NDY0IDUxLjQyNTQ5MzQxIDIxLjk2NDU2Nzk1IDQ5LjEzMzA1NjY0IDIxLjk1NDU4OTg0IEM0OC4zNDg5NTIyNyAyMS45NTA1MTQzMiA0Ny41NjQ4NDc5MSAyMS45NDY0Mzg3OSA0Ni43NTY5ODI4IDIxLjk0MjIzOTc2IEM0Mi42MDY4NzE0NCAyMS45MjEzOTI0OSAzOC40NTY3OTMgMjEuOTA3MDg0ODkgMzQuMzA2NjQwNjIgMjEuODk3NzA1MDggQzMwLjAyNTk3MzMxIDIxLjg4NjY0MjM1IDI1Ljc0NTgyNjA2IDIxLjg1MjI0MDY2IDIxLjQ2NTM0MjUyIDIxLjgxMjUzMzM4IEMxOC4xNjg0MzIxMSAyMS43ODYzNTY4NCAxNC44NzE2NDU0NiAyMS43NzgwMTIzNiAxMS41NzQ2NDAyNyAyMS43NzQ0MjM2IEM5Ljk5NzAxNzQxIDIxLjc2OTU2MDQyIDguNDE5Mzk5OTcgMjEuNzU3OTU0IDYuODQxODc4ODkgMjEuNzM5Mzg1NiBDNC42MzAxNzIwNSAyMS43MTQ5NzAyMyAyLjQxOTgwODg3IDIxLjcxNTk2MjY1IDAuMjA4MDA3ODEgMjEuNzIyOTAwMzkgQy0xLjA0ODY5MjE3IDIxLjcxNzAzNDE1IC0yLjMwNTM5MjE1IDIxLjcxMTE2NzkxIC0zLjYwMDE3Mzk1IDIxLjcwNTEyMzkgQy04Ljg1MDA3OTEyIDIyLjQ1MDYzMjQxIC0xMS45MDE1MjA5NyAyNC45MDYzMTg5OSAtMTUuMzQ3NDEyMTEgMjguODE3ODcxMDkgQy0xOC4wODg0OTk0MyAzMi42NzU2OTc2OSAtMTguMjA3Mjc3NjIgMzQuOTk5MTY2NTMgLTE4LjM1MTMxODM2IDM5LjczMTkzMzU5IEMtMTguMzkzMjEyODkgNDEuMDMzODg2NzIgLTE4LjQzNTEwNzQyIDQyLjMzNTgzOTg0IC0xOC40NzgyNzE0OCA0My42NzcyNDYwOSBDLTE4LjUxODA4NTEgNDUuMDU3NDUxNTQgLTE4LjU1Nzc5NzMzIDQ2LjQzNzY1OTkyIC0xOC41OTc0MTIxMSA0Ny44MTc4NzEwOSBDLTE4LjY0MDYwNjE4IDQ5LjIwNTkwNTI0IC0xOC42ODQyMjA3NiA1MC41OTM5MjYzNyAtMTguNzI4MjcxNDggNTEuOTgxOTMzNTkgQy0xOC44MzQ2MzE0NSA1NS4zODU0NTIzOCAtMTguOTM1MzAxMjkgNTguNzg5MTQ4MSAtMTkuMDM0OTEyMTEgNjIuMTkyODcxMDkgQy0yMy45ODQ5MTIxMSA2Mi4xOTI4NzEwOSAtMjguOTM0OTEyMTEgNjIuMTkyODcxMDkgLTM0LjAzNDkxMjExIDYyLjE5Mjg3MTA5IEMtMzQuMTI3MjcyOTggNTcuMzcyMTQ2NjMgLTM0LjIwNjc0MDkzIDUyLjU1NDU1MDM4IC0zNC4yNTQ2Mzg2NyA0Ny43MzM2NDI1OCBDLTM0LjI3NDYyNTQzIDQ2LjA5NzQ2ODQ2IC0zNC4zMDE4MjQ1NCA0NC40NjEzNjQ1OCAtMzQuMzM2NjY5OTIgNDIuODI1NDM5NDUgQy0zNC42OTUwMDAxOCAyNS41NTcwODI3NyAtMzQuNjk1MDAwMTggMjUuNTU3MDgyNzcgLTMwLjAzNDkxMjExIDE4LjE5Mjg3MTA5IEMtMjkuNDU3NDEyMTEgMTcuMTYxNjIxMDkgLTI4Ljg3OTkxMjExIDE2LjEzMDM3MTA5IC0yOC4yODQ5MTIxMSAxNS4wNjc4NzEwOSBDLTIwLjk4NzQ0OTMzIDUuNzQzMzM1MzIgLTExLjkwODU5NTYxIDAuMTE4NzY1NTYgMCAwIFogIiBmaWxsPSIjRkRGREZEIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3OS4wMzQ5MTIxMDkzNzUsMTA5LjgwNzEyODkwNjI1KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjk5IDAgMS45OCAwIDMgMCBDMy4wMTQ1ODI1MiAwLjU1MzQ5MTIxIDMuMDI5MTY1MDQgMS4xMDY5ODI0MiAzLjA0NDE4OTQ1IDEuNjc3MjQ2MDkgQzMuMTU5ODc5NTIgMjEuNTAwODI5NDEgMy4xNTk4Nzk1MiAyMS41MDA4Mjk0MSAxMyAzOCBDMTguNjgwODM5NjMgNDIuMDYxNTM0ODggMjMuODI5ODY1OCA0NS4wMTcwMjQ2MSAzMC44ODY3MTg3NSA0NS4zMTY0MDYyNSBDMzEuODI0NTExNzIgNDUuMzU4MzAwNzggMzIuNzYyMzA0NjkgNDUuNDAwMTk1MzEgMzMuNzI4NTE1NjIgNDUuNDQzMzU5MzggQzM1LjE5MzIxMjg5IDQ1LjUwMjMzMzk4IDM1LjE5MzIxMjg5IDQ1LjUwMjMzMzk4IDM2LjY4NzUgNDUuNTYyNSBDMzguMTY5NTk5NjEgNDUuNjI3Mjc1MzkgMzguMTY5NTk5NjEgNDUuNjI3Mjc1MzkgMzkuNjgxNjQwNjIgNDUuNjkzMzU5MzggQzQyLjEyMDk1NTQ2IDQ1Ljc5OTQxNjU0IDQ0LjU2MDM3MTEzIDQ1LjkwMTQ3NzE0IDQ3IDQ2IEM0NyA0Ni42NiA0NyA0Ny4zMiA0NyA0OCBDNDMuNTIwOTk3NTEgNDguMDg4MjUzNjEgNDAuMDQyMjg4ODMgNDguMTQwODA2MzUgMzYuNTYyNSA0OC4xODc1IEMzNS41ODg2MTMyOCA0OC4yMTI2MzY3MiAzNC42MTQ3MjY1NiA0OC4yMzc3NzM0NCAzMy42MTEzMjgxMiA0OC4yNjM2NzE4OCBDMjUuNDg4NDI0MzYgNDguMzQ1MTcyNTggMTguMTUwODM1NjQgNDcuMjUyODc5ODIgMTEuNjI1IDQyLjA2MjUgQzEwLjk5ODUxNTYyIDQxLjU3OTEwMTU2IDEwLjM3MjAzMTI1IDQxLjA5NTcwMzEyIDkuNzI2NTYyNSA0MC41OTc2NTYyNSBDNy44NTU0NzY5NyAzOC44NjYyNjcxNSA2LjQwMTc2MTE2IDM3LjEyODQ0MTAzIDUgMzUgQzUgMzQuMzQgNSAzMy42OCA1IDMzIEMtMzIuOTUgMzMgLTcwLjkgMzMgLTExMCAzMyBDLTExMSAyOSAtMTExIDI5IC0xMTAgMjcgQy03My4zNyAyNyAtMzYuNzQgMjcgMSAyNyBDMC41MDUgMjUuNTE1IDAuNTA1IDI1LjUxNSAwIDI0IEMtMC4wODIzOTE2OSAyMi4xNTM4OTQ2OCAtMC4xMDc0MzM1OSAyMC4zMDQ5NDgzNiAtMC4wOTc2NTYyNSAxOC40NTcwMzEyNSBDLTAuMDkyODIyMjcgMTYuODQ3MzE0NDUgLTAuMDkyODIyMjcgMTYuODQ3MzE0NDUgLTAuMDg3ODkwNjIgMTUuMjA1MDc4MTIgQy0wLjA3NTMyMjI3IDEzLjUyNTc1MTk1IC0wLjA3NTMyMjI3IDEzLjUyNTc1MTk1IC0wLjA2MjUgMTEuODEyNSBDLTAuMDU3OTg4MjggMTAuNjgxMzQ3NjYgLTAuMDUzNDc2NTYgOS41NTAxOTUzMSAtMC4wNDg4MjgxMiA4LjM4NDc2NTYyIEMtMC4wMzcwMzQ5OCA1LjU4OTc5MDczIC0wLjAyMDU3NjUgMi43OTQ5MjE4IDAgMCBaICIgZmlsbD0iI0NCQ0FCQyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTY0LDYpIi8+CjxwYXRoIGQ9Ik0wIDAgQzUuMDkwNDc4ODIgNC4wNDkyNDQ1MSA5LjQ0MjA1MDE5IDguNTc3NDcyMjIgMTMuNzk0OTIxODggMTMuMzg4NDI3NzMgQzE5LjM5MDk0MTQgMTkuNTIzNTIxMjcgMjUuMjkyODc4NjEgMjUuMzQ1NDEwMSAzMS4yMDgyNTE5NSAzMS4xNzA4OTg0NCBDMzIuMzM0OTMyODYgMzIuMjg1MTMxODQgMzIuMzM0OTMyODYgMzIuMjg1MTMxODQgMzMuNDg0Mzc1IDMzLjQyMTg3NSBDMzQuMTYyMDk5NjEgMzQuMDg3OTk4MDUgMzQuODM5ODI0MjIgMzQuNzU0MTIxMDkgMzUuNTM4MDg1OTQgMzUuNDQwNDI5NjkgQzM3IDM3IDM3IDM3IDM3IDM4IEMyNS44MzQ4NzI2IDM4LjU4Mjg2NjI4IDE2Ljc1ODkyNTYxIDM5LjAzNzk3MzU0IDcuOTQ5MjE4NzUgMzEuMTY3OTY4NzUgQy0wLjE3NjgxMjE0IDIyLjYzODE1OTkyIC0wLjM1MTQxNjA2IDE0LjU4MTc1NTk4IC0wLjEwNTQ2ODc1IDMuNDM3NSBDLTAuMDcwNjY0MDYgMi4zMDMxMjUgLTAuMDM1ODU5MzggMS4xNjg3NSAwIDAgWiAiIGZpbGw9IiM5RkM2NTMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE3MSwxMCkiLz4KPHBhdGggZD0iTTAgMCBDMzguMjggMCA3Ni41NiAwIDExNiAwIEMxMTYgMS4zMiAxMTYgMi42NCAxMTYgNCBDNzcuNzIgNCAzOS40NCA0IDAgNCBDMCAyLjY4IDAgMS4zNiAwIDAgWiAiIGZpbGw9IiMyNTI0MjEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU0LDg1KSIvPgo8cGF0aCBkPSJNMCAwIEMzNi45NiAwIDczLjkyIDAgMTEyIDAgQzExMi40OTUgMS45OCAxMTIuNDk1IDEuOTggMTEzIDQgQzc1LjcxIDQgMzguNDIgNCAwIDQgQzAgMi42OCAwIDEuMzYgMCAwIFogIiBmaWxsPSIjMjUyNDIxIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1NCwzNCkiLz4KPHBhdGggZD0iTTAgMCBDMy44MzU3MTQ2NyAwLjA0MzgzNjc0IDYuODc3MDA1MzYgMC4xODQ3ODQ0NCAxMC4yNSAyLjE4NzUgQzEyLjU0NTg3NjM0IDUuNjMxMzE0NTEgMTIuMzY2NjUyODUgNy4xOTk5OTA5OSAxMS43MTQ4NDM3NSAxMS4xOTE0MDYyNSBDMTEuMDM3MTM2OTMgMTQuMTAxNTU5MDYgMTAuMjA0MzA3NjIgMTUuOTA3NDc0NDQgOC4yNSAxOC4xODc1IEMyLjYyMTQyOTI0IDIxLjUxODY5NDk0IC0yLjMxNTg2OTc4IDIxLjUzNzA0MDI3IC04Ljc1IDIxLjE4NzUgQy03LjA0MTE3ODc5IDAuMDUyMDc5NzIgLTcuMDQxMTc4NzkgMC4wNTIwNzk3MiAwIDAgWiAiIGZpbGw9IiMwNDA0MDMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU5Ljc1LDQ4LjgxMjUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAgMTEuMjIgMCAyMi40NCAwIDM0IEMtMy42MyAzNCAtNy4yNiAzNCAtMTEgMzQgQy0xMS4xMDgwNzc2MiAyOS43OTA5NzcwNCAtMTEuMTg3MzY4NCAyNS41ODQ3Mzc5OCAtMTEuMjUgMjEuMzc1IEMtMTEuMjgzNTE1NjIgMjAuMTg1MTk1MzEgLTExLjMxNzAzMTI1IDE4Ljk5NTM5MDYyIC0xMS4zNTE1NjI1IDE3Ljc2OTUzMTI1IEMtMTEuNDc0NTI5MjMgNi43NjQwMDg5NCAtMTEuNDc0NTI5MjMgNi43NjQwMDg5NCAtNyAyIEMtNC4zMjY1NTQ4NiAwLjIxNzcwMzI0IC0zLjI2MjI5NjQxIDAgMCAwIFogIiBmaWxsPSIjMDBFRUZCIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0MCwxMzgpIi8+CjxwYXRoIGQ9Ik0wIDAgQzguMzA2NDQzNDYgMi4zNzMyNjk1NiA4LjMwNjQ0MzQ2IDIuMzczMjY5NTYgMTAuMzMzNDk2MDkgNS45MzExNTIzNCBDMTEuODc2NzkzNTIgMTAuNzIxNTkyNzkgMTEuNDM5Mjg4OTcgMTUuODM5NTU0NjkgMTEuMzEyNSAyMC44MTI1IEMxMS4zMDI4MzIwMyAyMS45Njk0MzM1OSAxMS4yOTMxNjQwNiAyMy4xMjYzNjcxOSAxMS4yODMyMDMxMiAyNC4zMTgzNTkzOCBDMTEuMTY4MjI0NTIgMzIuODMxNzc1NDggMTEuMTY4MjI0NTIgMzIuODMxNzc1NDggMTAgMzQgQzYuNyAzNCAzLjQgMzQgMCAzNCBDMCAyMi43OCAwIDExLjU2IDAgMCBaICIgZmlsbD0iIzAwRUVGQiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTgwLDEzOCkiLz4KPHBhdGggZD0iTTAgMCBDMS44NzUgMSAxLjg3NSAxIDMgMyBDMy43NTc1NzU3NiA5LjgxODE4MTgyIDMuNzU3NTc1NzYgOS44MTgxODE4MiAxLjQzNzUgMTMuNDM3NSBDLTEuNzY4MTU5MjcgMTUuNDkyNDA5NzkgLTQuMjExNzQ0OTcgMTUuODY0NzA1MTggLTggMTYgQy0xMC43OTU3MTUwMiAxNS4wNDM1NzExOCAtMTEuNjc2NDk0ODkgMTQuNjQ3MDEwMjIgLTEzIDEyIEMtMTMuNDI1NzY4NzEgOC4xNjgwODE2MiAtMTMuMzAzMjEzMTUgNi40NzA1MDMxNyAtMTEuMTg3NSAzLjE4NzUgQy03Ljc3MDMxMzQ5IC0wLjIyOTY4NjUxIC00LjY3NTQ3ODM1IC0wLjM4OTYyMzIgMCAwIFogIiBmaWxsPSIjMEUwRTBEIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNTIsNTUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzEuMzIgMCAyLjY0IDAgNCAwIEM0IDAuNjYgNCAxLjMyIDQgMiBDNC43MjE4NzUgMS42NDkzNzUgNS40NDM3NSAxLjI5ODc1IDYuMTg3NSAwLjkzNzUgQzkuMzEyMjcwNSAtMC4xMDQwOTAxNyAxMC44ODE2MTk5NiAwLjEwOTAzNDI4IDE0IDEgQzE0LjIyNTc3NDAyIDYuNDE4NTc2NiAxMy40NjY3MDExMyAxMC43NzQ4NzcyMSAxMiAxNiBDMTAuNjggMTUuNjcgOS4zNiAxNS4zNCA4IDE1IEM4LjMzIDExLjM3IDguNjYgNy43NCA5IDQgQzcuMzUgNCA1LjcgNCA0IDQgQzMuNjcgNy42MyAzLjM0IDExLjI2IDMgMTUgQzEuMzUgMTUgLTAuMyAxNSAtMiAxNSBDLTEuNDc0NjIyNzYgOS45Njk3OTI0IC0wLjg5NDA1MjE0IDQuOTgxMTQ3NjEgMCAwIFogIiBmaWxsPSIjMDYwNjA1IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNTksNTUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuNjYgMS4zMiAxLjMyIDIuNjQgMiA0IEMtMC45NyAzLjY3IC0zLjk0IDMuMzQgLTcgMyBDLTYuNjcgMy42NiAtNi4zNCA0LjMyIC02IDUgQy0zLjY4NTkzNzggNS43MzA3NTY0OCAtMS4zNTE3MTEzMSA2LjQwMTM4MjU4IDEgNyBDMi4zMTI4NjIxMSAxMS40MTU5OTA3MyAyLjMxMjg2MjExIDExLjQxNTk5MDczIDEuMDYyNSAxMy43NSBDLTIuMDY0ODk5NTQgMTUuNjQ1MzkzNjYgLTUuMzcyMDQ4NzQgMTUuOTcwNzQyMzMgLTkgMTYgQy05Ljk5IDE1LjY3IC0xMC45OCAxNS4zNCAtMTIgMTUgQy0xMiAxNC4wMSAtMTIgMTMuMDIgLTEyIDEyIEMtOS4wMyAxMi4zMyAtNi4wNiAxMi42NiAtMyAxMyBDLTMuMzMgMTIuMzQgLTMuNjYgMTEuNjggLTQgMTEgQy02LjMxNDA2MjIgMTAuMjY5MjQzNTIgLTguNjQ4Mjg4NjkgOS41OTg2MTc0MiAtMTEgOSBDLTExIDYuNjkgLTExIDQuMzggLTExIDIgQy03LjM2NTc2NzkxIC0wLjMyNTkwODU0IC00LjIyOTExNjY5IC0wLjE2MjY1ODMzIDAgMCBaICIgZmlsbD0iIzBDMEMwQiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTI2LDU1KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjY2IDEuMzIgMS4zMiAyLjY0IDIgNCBDLTAuOTcgMy42NyAtMy45NCAzLjM0IC03IDMgQy02LjY3IDMuNjYgLTYuMzQgNC4zMiAtNiA1IEMtMy42ODU5Mzc4IDUuNzMwNzU2NDggLTEuMzUxNzExMzEgNi40MDEzODI1OCAxIDcgQzIuMzEyODYyMTEgMTEuNDE1OTkwNzMgMi4zMTI4NjIxMSAxMS40MTU5OTA3MyAxLjA2MjUgMTMuNzUgQy0yLjA2NDg5OTU0IDE1LjY0NTM5MzY2IC01LjM3MjA0ODc0IDE1Ljk3MDc0MjMzIC05IDE2IEMtOS45OSAxNS42NyAtMTAuOTggMTUuMzQgLTEyIDE1IEMtMTIgMTQuMDEgLTEyIDEzLjAyIC0xMiAxMiBDLTkuMDMgMTIuMzMgLTYuMDYgMTIuNjYgLTMgMTMgQy0zLjMzIDEyLjM0IC0zLjY2IDExLjY4IC00IDExIEMtNi4zMTQwNjIyIDEwLjI2OTI0MzUyIC04LjY0ODI4ODY5IDkuNTk4NjE3NDIgLTExIDkgQy0xMSA2LjY5IC0xMSA0LjM4IC0xMSAyIEMtNy4zNjU3Njc5MSAtMC4zMjU5MDg1NCAtNC4yMjkxMTY2OSAtMC4xNjI2NTgzMyAwIDAgWiAiIGZpbGw9IiMwQzBDMEIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDExMCw1NSkiLz4KPHBhdGggZD0iTTAgMCBDMy40Mzc1IDAuODEyNSAzLjQzNzUgMC44MTI1IDUuNDM3NSAzLjgxMjUgQzUuMjUgNy4zNzUgNS4yNSA3LjM3NSA0LjQzNzUgMTAuODEyNSBDMS40Mzc1IDEyLjgxMjUgMS40Mzc1IDEyLjgxMjUgLTIuMTI1IDEyLjYyNSBDLTUuNTYyNSAxMS44MTI1IC01LjU2MjUgMTEuODEyNSAtNy41NjI1IDguODEyNSBDLTcuMzc1IDUuMjUgLTcuMzc1IDUuMjUgLTYuNTYyNSAxLjgxMjUgQy0zLjU2MjUgLTAuMTg3NSAtMy41NjI1IC0wLjE4NzUgMCAwIFogIiBmaWxsPSIjMDBFM0YwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxODEuNTYyNSw5Ny4xODc1KSIvPgo8cGF0aCBkPSJNMCAwIEMzLjQzNzUgMC44MTI1IDMuNDM3NSAwLjgxMjUgNS40Mzc1IDMuODEyNSBDNS4yNSA3LjM3NSA1LjI1IDcuMzc1IDQuNDM3NSAxMC44MTI1IEMxLjQzNzUgMTIuODEyNSAxLjQzNzUgMTIuODEyNSAtMi4xMjUgMTIuNjI1IEMtNS41NjI1IDExLjgxMjUgLTUuNTYyNSAxMS44MTI1IC03LjU2MjUgOC44MTI1IEMtNy4zNzUgNS4yNSAtNy4zNzUgNS4yNSAtNi41NjI1IDEuODEyNSBDLTMuNTYyNSAtMC4xODc1IC0zLjU2MjUgLTAuMTg3NSAwIDAgWiAiIGZpbGw9IiMwMEUzRjAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDM5LjU2MjUsOTcuMTg3NSkiLz4KPHBhdGggZD0iTTAgMCBDMC43MjE4NzUgMC4xODU2MjUgMS40NDM3NSAwLjM3MTI1IDIuMTg3NSAwLjU2MjUgQzQuOTg3ODU4NDUgMS4xNjc1MjQyOSA0Ljk4Nzg1ODQ1IDEuMTY3NTI0MjkgNy42ODc1IDAuNDM3NSBDOC44MzIxODc1IDAuMjIwOTM3NSA4LjgzMjE4NzUgMC4yMjA5Mzc1IDEwIDAgQzEwLjY2IDAuNjYgMTEuMzIgMS4zMiAxMiAyIEMxMS42MjUgNS4xMjUgMTEuNjI1IDUuMTI1IDExIDggQzkuNjggOCA4LjM2IDggNyA4IEM3IDYuNjggNyA1LjM2IDcgNCBDNi4wMSA0IDUuMDIgNCA0IDQgQzMuNjcgNy42MyAzLjM0IDExLjI2IDMgMTUgQzEuMzUgMTQuNjcgLTAuMyAxNC4zNCAtMiAxNCBDLTEuODU5MjM2ODUgMTIuMDQxMjk1NyAtMS43MTIyMzI0IDEwLjA4MzAzOTA3IC0xLjU2MjUgOC4xMjUgQy0xLjQ4MTI4OTA2IDcuMDM0NDUzMTIgLTEuNDAwMDc4MTIgNS45NDM5MDYyNSAtMS4zMTY0MDYyNSA0LjgyMDMxMjUgQy0xIDIgLTEgMiAwIDAgWiAiIGZpbGw9IiMwQTBBMDkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDc2LDU1KSIvPgo8cGF0aCBkPSJNMCAwIEMxLjgxMjUgMS4xODc1IDEuODEyNSAxLjE4NzUgMyAzIEMzLjQzMzQwMTQgNS44ODkzNDI2NCAzLjQyNTI1Nzg0IDcuMzUwOTIyMjQgMS44MTI1IDkuODEyNSBDLTAuNjQ5MDc3NzYgMTEuNDI1MjU3ODQgLTIuMTEwNjU3MzYgMTEuNDMzNDAxNCAtNSAxMSBDLTYuODEyNSA5LjgxMjUgLTYuODEyNSA5LjgxMjUgLTggOCBDLTguNDMzNDAxNCA1LjExMDY1NzM2IC04LjQyNTI1Nzg0IDMuNjQ5MDc3NzYgLTYuODEyNSAxLjE4NzUgQy00LjM1MDkyMjI0IC0wLjQyNTI1Nzg0IC0yLjg4OTM0MjY0IC0wLjQzMzQwMTQgMCAwIFogIiBmaWxsPSIjMDBFNUYyIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMzUsMTQ5KSIvPgo8cGF0aCBkPSJNMCAwIEMxLjgxMjUgMS4xODc1IDEuODEyNSAxLjE4NzUgMyAzIEMzLjQzMzQwMTQgNS44ODkzNDI2NCAzLjQyNTI1Nzg0IDcuMzUwOTIyMjQgMS44MTI1IDkuODEyNSBDLTAuNjQ5MDc3NzYgMTEuNDI1MjU3ODQgLTIuMTEwNjU3MzYgMTEuNDMzNDAxNCAtNSAxMSBDLTYuODEyNSA5LjgxMjUgLTYuODEyNSA5LjgxMjUgLTggOCBDLTguNDMzNDAxNCA1LjExMDY1NzM2IC04LjQyNTI1Nzg0IDMuNjQ5MDc3NzYgLTYuODEyNSAxLjE4NzUgQy00LjM1MDkyMjI0IC0wLjQyNTI1Nzg0IC0yLjg4OTM0MjY0IC0wLjQzMzQwMTQgMCAwIFogIiBmaWxsPSIjMDBFNUYyIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg4OSwxNDkpIi8+CjxwYXRoIGQ9Ik0wIDAgQzIuMzEgMCA0LjYyIDAgNyAwIEM2Ljg1ODkyMjUzIDEuNDU4ODAxMDkgNi43MTE5NzU4NSAyLjkxNzAzNTUxIDYuNTYyNSA0LjM3NSBDNi40ODEyODkwNiA1LjE4NzEwOTM3IDYuNDAwMDc4MTIgNS45OTkyMTg3NSA2LjMxNjQwNjI1IDYuODM1OTM3NSBDNiA5IDYgOSA1IDExIEMzLjAyIDExIDEuMDQgMTEgLTEgMTEgQy0xLjE0Mjg1NzE0IDMuNTcxNDI4NTcgLTEuMTQyODU3MTQgMy41NzE0Mjg1NyAwIDAgWiAiIGZpbGw9IiNFNUUzRDEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU5LDU0KSIvPgo8cGF0aCBkPSJNMCAwIEMtMC4xNDE4MzY5NiAxLjc1MDMyODQ0IC0wLjI4ODYzNDI5IDMuNTAwMjU1MzQgLTAuNDM3NSA1LjI1IEMtMC41MTg3MTA5NCA2LjIyNDUzMTI1IC0wLjU5OTkyMTg4IDcuMTk5MDYyNSAtMC42ODM1OTM3NSA4LjIwMzEyNSBDLTAuOTUwNzQ1ODQgMTAuNTY0NjE3NTMgLTEuMzUzNzE5NjggMTIuNzE5MDEwNjUgLTIgMTUgQy0zLjY1IDE1IC01LjMgMTUgLTcgMTUgQy02LjY3NzkzNDk0IDEyLjMzMTQ2MDkzIC02LjM0NDU3MDA0IDkuNjY1NjMyMSAtNiA3IEMtNS44NTU2MjUgNS44NjU2MjUgLTUuNzExMjUgNC43MzEyNSAtNS41NjI1IDMuNTYyNSBDLTUuMzc2ODc1IDIuNzE2ODc1IC01LjE5MTI1IDEuODcxMjUgLTUgMSBDLTMgMCAtMyAwIDAgMCBaICIgZmlsbD0iIzA0MDQwNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTM3LDU1KSIvPgo8cGF0aCBkPSJNMCAwIEMtMC42NiA0Ljk1IC0xLjMyIDkuOSAtMiAxNSBDLTMuOTggMTUgLTUuOTYgMTUgLTggMTUgQy03LjU1MjAyNjYgMTIuNjg1NDcwNzggLTcuMDkxMzM1MzkgMTAuMzczMzk5NjEgLTYuNjI1IDguMDYyNSBDLTYuMzY5NzY1NjIgNi43NzQ3MjY1NiAtNi4xMTQ1MzEyNSA1LjQ4Njk1MzEyIC01Ljg1MTU2MjUgNC4xNjAxNTYyNSBDLTUuNTcwNTQ2ODggMy4xMTczMDQ2OSAtNS4yODk1MzEyNSAyLjA3NDQ1MzEyIC01IDEgQy0zIDAgLTMgMCAwIDAgWiAiIGZpbGw9IiMwNDA0MDQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDk2LDU1KSIvPgo8cGF0aCBkPSJNMCAwIEMyLjE4NzUgMC4zMTI1IDIuMTg3NSAwLjMxMjUgNCAxIEM1LjEyNSA1Ljc1IDUuMTI1IDUuNzUgNCA4IEMyLjY4IDggMS4zNiA4IDAgOCBDMCA2LjY4IDAgNS4zNiAwIDQgQy0wLjk5IDMuNjcgLTEuOTggMy4zNCAtMyAzIEMtMS43NSAxLjQzNzUgLTEuNzUgMS40Mzc1IDAgMCBaICIgZmlsbD0iIzAwMDAwMCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoODMsNTUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzEuMzIgMCAyLjY0IDAgNCAwIEMzLjY3IDIuNjQgMy4zNCA1LjI4IDMgOCBDMS4zNSA4IC0wLjMgOCAtMiA4IEMtMS4xMjUgMS4xMjUgLTEuMTI1IDEuMTI1IDAgMCBaICIgZmlsbD0iI0RFREJDQSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTQ2LDU5KSIvPgo8cGF0aCBkPSJNMCAwIEMyLjEyNSAwLjM3NSAyLjEyNSAwLjM3NSA0IDEgQzMuNjcgMi4zMiAzLjM0IDMuNjQgMyA1IEMxLjY4IDUuMzMgMC4zNiA1LjY2IC0xIDYgQy0xLjYyNSA0LjEyNSAtMS42MjUgNC4xMjUgLTIgMiBDLTEuMzQgMS4zNCAtMC42OCAwLjY4IDAgMCBaICIgZmlsbD0iIzFBMTkxNyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTM1LDQ4KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjk5IDAuMzMgMS45OCAwLjY2IDMgMSBDMyAyLjMyIDMgMy42NCAzIDUgQzEuNjggNS4zMyAwLjM2IDUuNjYgLTEgNiBDLTEuNjI1IDQuMTI1IC0xLjYyNSA0LjEyNSAtMiAyIEMtMS4zNCAxLjM0IC0wLjY4IDAuNjggMCAwIFogIiBmaWxsPSIjMDMwMzAzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5NCw0OCkiLz4KPHBhdGggZD0iTTAgMCBDMi45NyAwLjMzIDUuOTQgMC42NiA5IDEgQzQuODIwOTE5MzYgMy4wODk1NDAzMiA0LjE5NTg5NDI3IDMuMTY1NTI2MTkgMCAyIEMwIDEuMzQgMCAwLjY4IDAgMCBaICIgZmlsbD0iI0NCQzhCOCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTE5LDU4KSIvPgo8cGF0aCBkPSJNMCAwIEMyLjk3IDAuMzMgNS45NCAwLjY2IDkgMSBDNC44MjA5MTkzNiAzLjA4OTU0MDMyIDQuMTk1ODk0MjcgMy4xNjU1MjYxOSAwIDIgQzAgMS4zNCAwIDAuNjggMCAwIFogIiBmaWxsPSIjQ0JDOEI4IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDMsNTgpIi8+Cjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/elasticsearch.js b/frontend/crawlab-ui/src/assets/js/svg/elasticsearch.js
new file mode 100644
index 00000000..8dd05fef
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/elasticsearch.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzIyOTI3MDE4MDgwIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjUyNzUiCiAgICAgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPgogIDxwYXRoCiAgICBkPSJNNDIuNjY2NjY3IDUxMmMwIDQ0LjI4OCA2LjIyOTMzMyA4Ny4wNCAxNi44MTA2NjYgMTI4SDY4Mi42NjY2NjdhMTI4IDEyOCAwIDEgMCAwLTI1Nkg1OS40NzczMzNDNDguODk2IDQyNC45NiA0Mi42NjY2NjcgNDY3LjcxMiA0Mi42NjY2NjcgNTEyIgogICAgZmlsbD0iIzM1MzUzNSIgcC1pZD0iNTI3NiI+PC9wYXRoPgogIDxwYXRoCiAgICBkPSJNOTI5LjYyMTMzMyAyMTkuNTYyNjY3YzguMjc3MzMzLTkuMDg4IDE2LjI5ODY2Ny0xOC40NzQ2NjcgMjMuOTc4NjY3LTI4LjA3NDY2N0E1MTAuNzIgNTEwLjcyIDAgMCAwIDU1NC42NjY2NjcgMEMzNjMuODYxMzMzIDAgMTk3LjgwMjY2NyAxMDQuNTc2IDEwOS42OTYgMjU5LjMyOGg3MjcuMDRjMzUuMDcyIDAgNjkuMTYyNjY3LTEzLjg2NjY2NyA5Mi44ODUzMzMtMzkuNzY1MzMzIgogICAgZmlsbD0iI0ZGQzEwMCIgcC1pZD0iNTI3NyI+PC9wYXRoPgogIDxwYXRoCiAgICBkPSJNODM5LjYzNzMzMyA3NjhIMTExLjcwMTMzM0MyMDAuMzIgOTIwLjg3NDY2NyAzNjUuMjI2NjY3IDEwMjQgNTU0LjY2NjY2NyAxMDI0YzE2MS4zNjUzMzMgMCAzMDUuMDI0LTc0Ljc1MiAzOTguODQ4LTE5MS4zNmE1ODAuOTQ5MzMzIDU4MC45NDkzMzMgMCAwIDAtMjAuNTIyNjY3LTI0LjIzNDY2N2MtMjMuNTA5MzMzLTI2LjM2OC01OC4wMjY2NjctNDAuNDA1MzMzLTkzLjM1NDY2Ny00MC40MDUzMzMiCiAgICBmaWxsPSIjMDBDMkIzIiBwLWlkPSI1Mjc4Ij48L3BhdGg+Cjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/gmail.js b/frontend/crawlab-ui/src/assets/js/svg/gmail.js
new file mode 100644
index 00000000..1a118a4e
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/gmail.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjUyIDQyIDg4IDY2Ij4KPHBhdGggZmlsbD0iIzQyODVmNCIgZD0iTTU4IDEwOGgxNFY3NEw1MiA1OXY0M2MwIDMuMzIgMi42OSA2IDYgNiIvPgo8cGF0aCBmaWxsPSIjMzRhODUzIiBkPSJNMTIwIDEwOGgxNGMzLjMyIDAgNi0yLjY5IDYtNlY1OWwtMjAgMTUiLz4KPHBhdGggZmlsbD0iI2ZiYmMwNCIgZD0iTTEyMCA0OHYyNmwyMC0xNXYtOGMwLTcuNDItOC40Ny0xMS42NS0xNC40LTcuMiIvPgo8cGF0aCBmaWxsPSIjZWE0MzM1IiBkPSJNNzIgNzRWNDhsMjQgMTggMjQtMTh2MjZMOTYgOTIiLz4KPHBhdGggZmlsbD0iI2M1MjIxZiIgZD0iTTUyIDUxdjhsMjAgMTVWNDhsLTUuNi00LjJjLTUuOTQtNC40NS0xNC40LS4yMi0xNC40IDcuMiIvPgo8L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/go.js b/frontend/crawlab-ui/src/assets/js/svg/go.js
new file mode 100644
index 00000000..23d289d6
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/go.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTYiIGhlaWdodD0iMjU2IiB2aWV3Qm94PSIwIDAgMjU2IDI1NiI+CiAgPGcgZmlsbD0ibm9uZSI+CiAgICA8cmVjdCB3aWR0aD0iMjU2IiBoZWlnaHQ9IjI1NiIgZmlsbD0iIzAwQjRFMCIgcng9IjYwIi8+CiAgICA8cGF0aCBmaWxsPSIjZmZmIgogICAgICAgICAgZD0iTTQwLjUgMTEzLjIzNGMtLjQgMC0uNS0uMi0uMy0uNWwyLjEtMi43Yy4yLS4zLjctLjUgMS4xLS41aDM1LjdjLjQgMCAuNS4zLjMuNmwtMS43IDIuNmMtLjIuMy0uNy42LTEgLjZ6bS0xNS4xIDkuMmMtLjQgMC0uNS0uMi0uMy0uNWwyLjEtMi43Yy4yLS4zLjctLjUgMS4xLS41aDQ1LjZjLjQgMCAuNi4zLjUuNmwtLjggMi40Yy0uMS40LS41LjYtLjkuNnptMjQuMiA5LjJjLS40IDAtLjUtLjMtLjMtLjZsMS40LTIuNWMuMi0uMy42LS42IDEtLjZoMjBjLjQgMCAuNi4zLjYuN2wtLjIgMi40YzAgLjQtLjQuNy0uNy43em0xMDMuOC0yMC4yYy02LjMgMS42LTEwLjYgMi44LTE2LjggNC40Yy0xLjUuNC0xLjYuNS0yLjktMWMtMS41LTEuNy0yLjYtMi44LTQuNy0zLjhjLTYuMy0zLjEtMTIuNC0yLjItMTguMSAxLjVjLTYuOCA0LjQtMTAuMyAxMC45LTEwLjIgMTljLjEgOCA1LjYgMTQuNiAxMy41IDE1LjdjNi44LjkgMTIuNS0xLjUgMTctNi42Yy45LTEuMSAxLjctMi4zIDIuNy0zLjdoLTE5LjNjLTIuMSAwLTIuNi0xLjMtMS45LTNjMS4zLTMuMSAzLjctOC4zIDUuMS0xMC45Yy4zLS42IDEtMS42IDIuNS0xLjZoMzYuNGMtLjIgMi43LS4yIDUuNC0uNiA4LjFjLTEuMSA3LjItMy44IDEzLjgtOC4yIDE5LjZjLTcuMiA5LjUtMTYuNiAxNS40LTI4LjUgMTdjLTkuOCAxLjMtMTguOS0uNi0yNi45LTYuNmMtNy40LTUuNi0xMS42LTEzLTEyLjctMjIuMmMtMS4zLTEwLjkgMS45LTIwLjcgOC41LTI5LjNjNy4xLTkuMyAxNi41LTE1LjIgMjgtMTcuM2M5LjQtMS43IDE4LjQtLjYgMjYuNSA0LjljNS4zIDMuNSA5LjEgOC4zIDExLjYgMTQuMWMuNi45LjIgMS40LTEgMS43Ii8+CiAgICA8cGF0aCBmaWxsPSIjZmZmIgogICAgICAgICAgZD0iTTE4Ni41IDE2Ni43MzRjLTkuMS0uMi0xNy40LTIuOC0yNC40LTguOGMtNS45LTUuMS05LjYtMTEuNi0xMC44LTE5LjNjLTEuOC0xMS4zIDEuMy0yMS4zIDguMS0zMC4yYzcuMy05LjYgMTYuMS0xNC42IDI4LTE2LjdjMTAuMi0xLjggMTkuOC0uOCAyOC41IDUuMWM3LjkgNS40IDEyLjggMTIuNyAxNC4xIDIyLjNjMS43IDEzLjUtMi4yIDI0LjUtMTEuNSAzMy45Yy02LjYgNi43LTE0LjcgMTAuOS0yNCAxMi44Yy0yLjcuNS01LjQuNi04IC45bTIzLjgtNDAuNGMtLjEtMS4zLS4xLTIuMy0uMy0zLjNjLTEuOC05LjktMTAuOS0xNS41LTIwLjQtMTMuM2MtOS4zIDIuMS0xNS4zIDgtMTcuNSAxNy40Yy0xLjggNy44IDIgMTUuNyA5LjIgMTguOWM1LjUgMi40IDExIDIuMSAxNi4zLS42YzcuOS00LjEgMTIuMi0xMC41IDEyLjctMTkuMSIvPgogIDwvZz4KPC9zdmc+Cg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/hive.js b/frontend/crawlab-ui/src/assets/js/svg/hive.js
new file mode 100644
index 00000000..678fcde6
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/hive.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNjQiIHdpZHRoPSI2NCI+PGcgdHJhbnNmb3JtPSJtYXRyaXgoLjA3Mjc5NCAwIDAgLjA3Mjc5NCAtLjAwMDQwNyAuNjM0MTUzKSIgZmlsbC1ydWxlPSJldmVub2RkIj48cGF0aCBkPSJNNTAzLjEyNyAxMTkuNzUyYy03LjctMjEuMTE1LTI3Mi45Mi0xMjQuMjItMjc2LjMtMTEyLjktNDguNyAxMi42OTUtNjguNTIgNjguODI2LTEwMi4wMiAxMDQuMzhsLTc0LjM5My00LjI2Yy0zMi43NyA0Mi4xNDItNDkuOTY3IDg1LjItMzguMjU4IDEyOS45NSA0Mi4wMDIgNTYuOTA2IDkwLjc2IDEwNS4zMyAxMjEuMTUgMTc2LjggMi40MDIgMzMuNjkyIDE0NS44MiAzLjUzMyAxNzYuNTYtMy4xMzYtNDEuOTkyIDMwLjA2LTc4LjU2IDc2LjY1LTYyLjg0NiAyMTAuODQgMTQuMzQ2IDYzLjAxNCAyNC4xNiAxMzMuMzcgMTUxLjQgMjA0LjY0IDE2Ljc1IDkuMzgyIDUxLjQwNyAyMC4yMDcgNzIuODM4IDI4LjA5OCAyMC40OTUgOS40IDQ0LjQ2IDEzLjI2NCAxMTIuMDctNy40MTIgMzkuMTI0LTE2Ljg2MyA4MS4zNjUtMjcuMDIyIDExOS42NS00My44NDRsLTQ2LjAxNyAyLjE2Yy02My4zNyAxLjM4LTExMi4zIDYuMTA2LTEyNy4zOC0xMS42bC01OC4zMi0xMDAuNjggMzQtNjYuMDRjNDcuMDU2IDQuODI2IDYyLjY3NSA0Mi45ODYgMTA0LjE1IDU3LjUxOGw0OC44ODUtMzYuMjE1YzE0MiA4My44MTYgMTk4LjQ4LTUzLjEyIDIxNC42Ny0xNTkuNzctMS43MjgtNDMuMzkyLTkzLjk1MiAxMy42LTg4LjM2Mi02LjY4IDIuMTY2LTQ2LjY0NC0zNS44NTQtMTA3LjY3LTYwLjQyLTE1NS4yMmwyOC40My0xMTBjMTIuOS0xMS41LTU5LjcyLTEzMy44Ni0xMTkuMDItMTQ5LjEyLTUyLjAzLTEzLjQtMTMwLjQ2IDUyLjQ5Mi0xMzAuNDYgNTIuNDkyeiIgZmlsbD0iI2ZkZWUyMSIvPjxnIGZpbGw9IiNjOGMwMzciPjxwYXRoIGQ9Ik03MzYuMzg3IDM1My41NWMuNSAxMS44MTYuNDUyIDI2LjE4NyAxLjU5NCAzNS4xNS0uMTk3IDYuODM4LTUuMzIzIDcuMS05LjU2NCA4LjUybDI3LjYzIDEwLjEyYzUuNDg2IDkuMjMgOS44OTQgMTguNDYzIDE0LjM0NyAyNy42OTQgNS4xNjUgMjIuNzA4IDEuMyAyMy4zNTctMi4xMjUgMjUuMDMtMTAuMDI3LjEwMi0yMC4wMjUuMTItMjkuMzU4LTEuODY0IDQuMTggMi4yMDcgNS4xIDMuODE1IDUuNTQzIDYuNjQyLjgxNiA1LjIxMi0yLjU0IDEyLjMzLTguMDY3IDE5LjE4OCA4LjE3MiA0LjUzNCAyMy4wNyA4Ljk3IDM0LjE0IDEzLjE4bDEyLjYyLTI4LjA5M2MtNS43MTUtNDEuMjctMjMuMDQ1LTc5LjMtNDYuNzYtMTE1LjU3em01MS41NyAxMzguNDNjMTUuMzI0IDYuMzg3IDM2LjQ5NSA0LjUxNSA2NS44NDgtMTAuNjEzIDQuMzY1LTMuMTA0IDguNDMtMS4yNS42MiAzLjg1OC0zOC41IDM0LjU0Ny01Ni45MjcgMTcuMjY1LTY2LjQ2NyA2Ljc1NXoiLz48cGF0aCBkPSJNODU2Ljc0NyA0NzYuODRjNC41NTIgOC43MTYgMS43OTQgMTUuODA1LjI2NiAyMy4xNjctNC43NTMgMzcuNDUtMTYuNDIzIDY3LjA1Ni0zOS4zMiA5Ny45OTQtNzQuNDc4IDExOS43NC0xNDkuNjcgNDQuODE3LTIwOS42Mi0zLjk0NmwtMjQuNzk4IDYyLjEzN2MtMS4zMTYgNS40NDYtNi44IDkuNjg4IDMxLjkzNyAyNi43MzhsMzkuNDUyLTI3Ljg2N2MxNTAuNDYgMTE0LjQ4IDI1Ny4wOC0xNjAuMjQgMjAyLjA4LTE3OC4yMnptLTIzMC41MyAxMDdjLTEwLjE1LjcyNC00MC4zNiAxMC45OTctNDEuNzA2IDE2Ljk0NmwxMy45MDItMjAuNzEyem0tMTkuNzQtMTI2Ljk3YzEuODYgMCAxMC43ODggMi4zOTcgOS44MyAxLjg2NC0xLjM3LS43NjMtLjE1MyAxMi41MiAyLjY1NyAxOS40NGwtMTAuODkzIDIzLjdjMjAuODY1LTI0LjA2MyA1My44NTgtMjIuNDYyIDg0LjQ4Ni0yNS41NjRsLTE0LjYxMi05LjA1NGMyLjUzOC04LjQ2Ni0uNDUtMTUuMTY1LTEuODYtMjIuMzY4em01MS4yNy03MS4zN2MtMjIuMzg3IDYuODYzLTQzLjgxNiAxOC4wNzUtNTguMjAzIDQxLjk3NiAxMC45NzYtMzkuMzk1IDEzLjIxMy0zNy45IDE2Ljc1Ny0zOS44NDYgMTUuMzUtNi44NjUgMjcuOTIyLTIuNTg2IDQxLjQ0Ni0yLjEzeiIvPjwvZz48cGF0aCBkPSJNNTM4LjE5NyA4NDcuMjVjLTI3LjA3IDI5Ljg3LTg3LjQ3LTIuMjYtMTM3LjYzLTE4LjY0LTEyNy40Ni04MS4wNS0xNTIuMzYtMTU3LjM1LTE1NC4xLTIzMi4yLTYuNTUzLTEwNy4yMiAyNi4zNy0xNjkuNTIgNjguMDE0LTE4NC4yNy0yNy41NiA1My4xMy00MC40IDE0OC41LTI3LjYzIDIxOS40MiAxMC4zMTUgMzkuNDU1IDEwLjUzIDEwNiA3Ni41NDUgMTQxLjYyIDMyLjMyNyAxOC4yIDIzLjU3IDMyLjM2OCA0NS40MyA0OS40MTMgMjMuOTU1IDE4LjY4IDkwLjc0NyAzNi40MzcgMTI5LjM2IDI0LjY1OHptNDAuNDQtNDk1LjVjLTQ1LjgzLTY1LTExMC4zLTg5LjM4Ny0xODIuOTgtOTIuMDUzIDE0LjU2OC00Ljc1IDI5LjEzNi03LjIyMiA0My43MDQtMTQuMjQ2IDMuNTY3LTMuNzQ4IDIuNC0xMC4zNDQgMS4wNjMtMTcuMDQyLTY5Ljk3LTE4LjI3LTExNC4xMy00MC44NS0xNzAuMDMtNjEuNzhsMTUwLjkgMzYuMjE1YzEwMS45IDMuODcyIDkzLjE1OCAyOS4wMzIgMTU3LjM0IDE0OC45em00OS4wNy0zMS4zNWMtMzMuODctNDguMzUtNjYuNzItMTA3LjAzLTExMS4zLTE0NC4zNS0xMDcuNjQtNDguNTg0LTIxNC4xMi04NC42NjYtMzM4LjktMTE3LjVsMzkuODI3LTUxLjIxNmMxMzIuNDIgMzAuMjI3IDI1Ni44IDgwLjgwOCAzNjguMiAxNjQuMiAxOC44NSA0Ny42NiAzMS40MiA5NS4zODMgNDIuMTczIDE0OC44N3ptNjguMjgtODUuNDRzLTE4Ljk4OC00Mi42LTI4LjIzMy01OC44NjZjLTIxLjQ0Ni0yMy43NjYtMzIuMy02OC4wNTQtNzYuMjEzLTg4LjkyMiAxMy44OTIgMy43NSAyMy40LS43MTggNTEuNzYgMjQuOTkybDQ0LjY3IDg0LjczNHptMjMuNjIgNzMuOGM0LjM4LTM2LjkwOCAxMi42OTUtOTYuMSAzLjE1Mi0xMTkuNUw2NDIuNzIgODIuNjNjLTQuNDMtNC4xMjMtNy42Mi05LjcyLTExLjEtMTQuMDg0IDM3LjYgOS43NDQgNzYuODYgMzUuODk0IDEyOS44IDEzOS4xM3oiIGZpbGw9IiNmY2Y2YTAiLz48cGF0aCBkPSJNNTYxLjQ4NyAzMTMuMTJjLTE0Ljk4LTExLjgtMjguMzMtNTMuNS01MS4wMy02Mi4xNi0yMS0xLjc1OC0yOC43MTUtOC4zNjgtNTYuMzQ0LTIuNzQgMTAuMDItNS4wMiAxOS40ODItMTEuNDQ2IDMwLjEzNC0xNC44ODUgNy4yMTUtMS43MDMgMTMuOTgzLjE4NCAyMC44NjcgMS4xMzMgMS43LS44MDQgMi44MTQtMS44MDMgMi4zOTgtMy4zMTMtMjcuODEyLTE3LjE4Ny04NC44NjYtMTcuNDMyLTEyMy4zOC0yNy4yNDMgNDQuNyAxLjQ5NiA5NC4xNTYtLjk4IDEyNy43NSA5LjggMjcuMDEzIDIzLjk3MyAzNS43ODggNjkuMTIgNDkuNjA1IDk5LjQxNXptODEuOS0yMDEuMDkzYzYuMi41NjMgMzkuNTc0IDUzLjM1MyA0MS4xNDIgNjIuMTM3IDIuNzc1IDE5LjIwMiA5LjYyIDQwLjcyOCAxMS40NiA2MC44Mi01LjQ5Ny0xOS4yNy0xMS40ODYtMzguNTM3LTE4Ljk3NC01Ny44MDYtMi4yLTUuMzA2LTcuNzI0LTE3LjMwMi0yMy4xMDctMzQuMjctNy4xNjMtMTEuOTYtOC43LTIxLjM2LTEwLjUyLTMwLjg4em0xMTMuMSAyODcuNTIzSDc0Mi4ybDE1Ljc4IDQuMTQyem0tOTcuNDItMTQuMjVjLTE0LjM1OC0xLjc2NS0yOS4xLTItNDMuNDQ4IDEuMTU2LTUuODUyIDcuODYtNi4yNTUgMTUuODU2LTguODEzIDIzLjI0NSAxNy40NTQtMTkuMjM2IDI0LjM4LTIwLjQxMiA1Mi4yNi0yNC40em0xOTUuODIgOTkuMjdjLTggNi4zNzctOC43ODggMTIuMzgtMzIuNDEzIDE5LjU3Mi0xNy4yMjggNC4wNzQtMjYuOTYtMi44My0zNS4wNy0xMi4xMTYgMTIuMjk0IDMuNTIyIDE0LjI2IDEyLjggNDcuMjI1IDIuMzk3em0tNzEuNiA0Yy01Ljc1IDE1LjgxMi0xMS45NSAzMS44LTE4LjIgNDYuNi0xNC44ODMgMTcuNDU0LTguNTQgNy41MDgtMzAuODIgMzQuNjE3IDcuMTMtMTAuOTk4IDE2LjQtMjEuNTY2IDIxLjI1NC0zMy4wMiAzLjQtNy41NDIgNy42NTMtMTUuNTQzIDkuNTMyLTIyLjI0NC01LjkwNy0yLjk1LTE4LTMuMTgzLTE5LjY0LTIuMTI1LTE5LjgwOCAxMS42ODMtMjMuMzgyIDI0LjA0Ny0zNS4wNiAzNi4wODYgOC40NjQtMTMuNTMgMTUuNjkzLTI4LjI5OCAyNS41MDUtNDAuNDc2IDEuMTE4LTEuNTggOC4zODMtMS44ODYgMTIuODUtMi44Mi02Ljk0LTEuMi0xOS43ODYtMy42ODUtMjAuODItMy41NzItMTIuOTIyIDMuMTA1LTIwLjE2MyAxNC4zNi0yOS43NTYgMjEuODM2IDYuOTk0LTEwLjA5OCAxMy40MzYtMjAuNjEyIDIyLjMxNy0yOS4yOTIuODc4LTEuMDIgMjQuODgzIDEuMDgyIDM3LjMyNCA2LjA0N2wuMjQ3IDQuOTUgNy4wNjMgMi44NSA1LjMxNC0xMy4zMTR6bS0xNTYuNzUgOTUuMzNjLTMuMTg4LTEuMS0zMS4zNS00LjctMzEuMzUtMy44Ni0xMC44MTMgNy42OTYtMTIuNDUzIDE1LjA2LTEyLjIyIDIxLjQzNiAxOC0xNy42OTcgMTcuMzItMTcuNzQ3IDQzLjU3Mi0xNy41NzV6bS0yMS40LTEyNy4zczIwLjMyMi0yLjk2MyAxOS45MjYtMS4wNjVjLTEuNDcyIDcuMDUgMi40ODggMjQuMjMzIDIuOTIzIDIxLjgzNmwtMTEuNDI0IDkuMzJjMjMuOTI2LTEyLjAzIDQ5LjExMy05LjYxMiA3NC42NTYtMTEuNDUgMCAwLTIxLjUwNy01LjkzOC0yMC4xOTItNi4xMjUgNC43OTgtLjY4LTEuMjc3LTIyLjQzLTIuOTIzLTIyLjkgNC4xLTEuMzg1IDguMDg3LTIuNDMgMTIuMjItMy40NjItMzAuNzU2LTguMDQ4LTQ4LjU0LTMuMzA2LTc1LjE4OCAxMy44NDd6Ii8+PHBhdGggZD0iTTc4MS4wMjcgNDcxLjc4bC00LjUxNyAyLjI2M2MtLjY0LTEzLjIzLTE0Ljg1LTEyLjI2LTI2LjgzNC0xMi4zODJsLTkuODMtMi4zOTdjMyAyLjc2NiAxMi4yMyAyLjU1IDguNTAyIDguNTItMy4xMzcgMS42NzctNC41OCA4Ljg3LTYuMzc2IDE0LjkxMmwtNS4zMTQuMjY2IDI4LjQyOCAxMS4xODQgNi4xLjkzMiAxNC42MTItNy44NTZ6Ii8+PHBhdGggZD0iTTY0Ni4zMjcgNDYyLjg2Yy0yLjI4Ny00LjUyNS0xMy43OTQtMy44ODItMTYuMjc1LS45NTUtMi4wMTYgMi4zOC0uMTI1IDE3LjY0MiAyLjQ2IDE1LjA2OCAzLjczNy0zLjI4NSA4LjYxOC00LjY2IDE0LjQ4LTQuMzk0Ljc0LTIuODIuNDc3LTUuODY1LS42NjQtOS43MnptMTEwLjM1IDIyLjc0YzEuMDgyLS44IDMuMzctOC40NDMgMi42My0xMC43MzMtMS41MTItNS40MDMtMTIuMjEyLTQuMjY2LTEyLjIxMi00LjI2Ni0yLjYgMS41MzYtNC45IDkuOTYtMy45NDUgMTIuNzQuNDU1IDIuMzUzIDExLjggMy4xMDMgMTMuNTI2IDIuMjZ6IiBmaWxsPSIjZmZmIi8+PHBhdGggZD0iTTI5OS4xMDcgNzQ5LjY0bDYuNzYuNjZjNDAuMDcgMTkuNjc1IDEwNi4wNSA0OC44NDcgMjAxLjU3IDMzLjE3bDE0Ljk4OCAyMy41ODRjLTQyLjA2NyAyMC40NDItODcuMzEyIDE1LjczOC0xMjkuNyAxNy4xNzJ6bS01Ny41My0xNTUuNTRsMjAuMDczIDg0LjI2OGM1My43NCAzMy41OTMgMTQ1LjU0IDcyLjA3IDIyMi4wNiA2OC45NjVsLTE2LjktMzguOGMtMTU5LjczLTMzLjkyLTE3My4yNC03Ni44LTIyNS4yMi0xMTQuNDR6bTQ5Ljk1LTE3Mi42YzkuOCA2NS4zMDIgMjIuOTA3IDExNC44IDc5LjcgMTU2LjI4IDM0LjgwNCAyNS4wMyA2OS4zNSA0OS45NTQgMTExLjM3IDcwLjUyNCAwIDAtMy42ODQgMTkuMTctNi43NjMgMTguODMtMTI1LjA1LTEzLjktMjE2LjYtMTE3LjU4LTIyNy43LTE2NC45NCAxMC43NC0zNi4zMyAyNi4xNS01OS4wMiA0My40LTgwLjY5M3ptNjAuMS0zN2MxNi42NjQgNzIuMzc2IDU2LjM5NyAxNDUuNjggOTUuOSAyMTIuMTggMTQuNDc3IDE4LjM3IDE4LjI2NiAyNi40NzUgNDAuNTggMzcuNjYgMzAuMTYgOS41NSA1MS40ODYgNy4xMTIgNzMuMzc3IDUuNjc2LTUuOTk4LTExLjEwMi0xMS4zMy0yMi43MDYtMTguMjE3LTMzLjE0LTQ5LjAxOC0zOC43NjUtMjYuMzY0LTc0LjA2LTEzLjM0NC05Ni40MDYtMjcuMjU2LTYuODg3LTYzLjM3LTIxLjUyNS02OC4zNDUtNDAuNzc4LTgtNjIuMjctMy45MzctODIuMDI1IDQuMDctMTE0LTM2LjkgNy43ODItNzQuMyAxNS40OTQtMTE0IDI4LjgyeiIvPjxwYXRoIGQ9Ik0yMTYuMjM3LjA1OGMtMTIuMTMgNy45MjUtMjMuODE4IDE5LjI1Ni0zNi40IDMyLjEzNS0yMC44NDMgMjEuNTc1LTM0LjcgNDIuNjItNTUuOTE1IDU4LjIyNC00LjI0MyAzLjY2LTE2LjE1IDkuNTUtMzEuMTIgMTAuNDI1LTcuMDUyLjM1Mi0xMS42NDYgMS41Mi0yNC42MDQtLjEwOC0xMS40MjgtNi4xODgtMjIuMjg1LTIuMTY0LTMyLjg4IDEwLjUtMTEuNjIgMTYuNy0yNi4zMzQgNDguNDM3LTMyLjQ1MyA2OC40LTEyLjYgNTAuOTM3IDE5LjU0IDkyLjkwNSA1MC4zNzQgMTI1LjI1IDI3LjQzIDI2Ljc5NiA0My4yNDMgNDMuNTQyIDU0LjUwNSA2OC4xMDcgOC41MjcgMTUuOTMzIDE0Ljg0NCAzNy44IDIxLjY4MyA1My4xMyAyLjQ2NiA0Ljg2IDEuOTUzIDQuODM1IDguNiA2LjMgMTQuMzMzIDMuMDYgMzQuMjE1IDMuMDgzIDUxLjkxNSA0LjYwNCA3LjY1OC4xMDcgMTguMTc1LS4xNzggMjguMTQtMS4yMTcgMTMuNzMtMi41OTMgMjkuODYzLTUuMTMzIDQzLjM4NC05LjggMTMuMjEzLTMuMjU0IDI0Ljk4NS03Ljc2IDM1LjU5Ny0xMS45MDctMS4zNyA0LjY0NC0xMS40NzggOS4xMTUtMTUuMjY4IDE1LjAwMi0zNS45NyA1MS41LTQ1LjgyMyA5Ny4zMDgtMzkuNTYgMTY5LjYgMy41NDIgMzIuMDQyIDEwLjg5NiA1OC42MDUgMjEuOTkyIDg4Ljk5NyA1LjA3NyAxMy45MDggMTUuODgyIDM1LjkzNCAyNi4yMzYgNTAuNTY1IDMwLjggNDMuNTA2IDk5LjY3MiA5OS4zNzQgMTk1LjU2IDEyMC44OCAxNi43MyAyLjI4NyAzNS43MTUgMS4wNjcgNTMuNTctMy42ODggNDcuMzI2LTE0LjM0NiAxNDMuNzgtNDguMjc1IDE0My43OC00OC4yNzVzLTg1LjYyIDcuMDgzLTEyNC44MyAzLjIwNmMtOS4wNzgtMS40Mi0xOS4wOC0xLjktMjUuNDA1LTguMDg3LTEuMDYtMS4zNy00LjkxNC05LjEzMi0yLjQyNi05LjIwMiAzLjM5NS0uMDk1IDEzLjE0Mi00LjE0IDI4LjItNS40ODItMzIuMTMtMy40Ni0zMS42Ny0zLjQxOC0zNC4wMTYtOS4yNy0zLjY0Ny05LjA4NS05LjIzLTIxLjUwMi0xNC45NzctMzIuMzY3IDE0LjExOCAxLjIgNDUuMzc2IDIuOTQ2IDU1Ljk4NC04LjAwMyAwIDAtMTguNDk3IDIuMTM2LTM0Ljg0My4yMTgtNS41MDctLjY0Ni0xNC45LTMuOTk1LTE3LjctNS4wMy03LjMzOC0yLjg1OC0xMy4zOC0zLjc0Ni0xNC43ODgtNS42LTIuNTQ4LTYuNDg2LTQuMjc1LTguNjY3LTcuMzUtMTcuMzU2LTQuMTY2LTExLjUwNC00LjQ5Ni0yNC4zMzctNS4zLTM2LjA4MyAxMC42OTMgMTMuMTggMjQuMzE4IDI0LjI3NSA0Mi4zNTYgMjkuOTQuMjMtLjQ4OCAyMy40IDEwLjA3MiA0MC4yMjYgNC42NDJsMy41LTEuMTI1YzAgLjMtMTEuMTQ4Ljk3Ny0xNS4yMzctMS4wNDctMzQuMjItMTQuNTQ1LTM5LjM3LTI3LjI3Ni00NC44OTUtMzMuNmwtMTQuNzc3LTIyLjEzM2M0LjQ5NC04Ljk2IDcuMDM1LTkuMyAxMy05LjM4NCAxOCAyIDI1Ljg0OCAzLjU0IDM2LjgwNy44OTYgNy40MDUgMTUuMjQgOS40MDYgMzAuNTcgMjYuMjY1IDQxLjY2MiA1Ni4xNjYgMTYuNjcyIDY4LjM3LTQuOTUgODEuOTczLTI0LjA1NyA0MC40MTIgMjkuNjU4IDEwNi4yIDM4LjgwMyAxNTEuOS41MTUgNTguMDg2LTY3LjIxMiA3Ni40NzYtMTczLjE3IDcxLjMyNS0xNzkuMjItNy4yNTQtMTIuMzA3LTE2Ljc3Mi0yNS0yNC45NDUtMjMuMjQ1LTI5LjEzIDcuOTUyLTM5Ljg3IDIyLjczLTY4LjczNSAxOS4zNiAzLjQzOC0uMTk0IDkuMjItLjI4NyA5LjI2My0uNjE1IDIuMjI0LTI0LjQ0LS4xOC0zNi4yNjctMS4yNTItMzguMjk3LTguNzYtMTkuMzE3LTIwLjI2LTM5LjY0Ni0yOC4yNzgtNTQuODgtMi4wNjctMy4xNi04LjEzNy0yNy4xNjUtMTguMzA4LTM2LjY0OC00LjM2Mi0zLjcyNS0xNS4wNC0xMy41NDYtMTUuMDQtMTMuNTQ2bC0uOTA1IDEwLjQ0czQuMTc0LjYzIDUuNzYyIDcuMDk3YzYuMDIyIDI0LjUxNyAzNi42ODcgODIuNDg1IDM4LjkzOCA4NS4yNjMgMTAuODY4IDE3LjU0NSAxLjEwNSAzOS40IDkuMzY3IDUxLjY2My43OTQgMS41NTggMTYuODU3LS4xMzcgMjkuMzA3LjYyOCAyMC40NzMtNC40MDMgMTkuNi0xMy40MjYgMzcuMjk0LTE0Ljc4MiAxMS45MTMtLjkxMyAxMy4xMDggMjEuNDYgMTIuOTUgMjMuMTk2LTIuMjIgMjQuNzA0LTkuNzM1IDUzLjQyNy0yMS4yMzYgNzkuNjU4LTIzLjkxMiA0Ni4wNy01MC43IDg3LjQ0Ni04OC42NyA5My4yMTYtNDYuMzI2IDguMDk1LTcwLjQtMTIuMTU4LTk1LjUxMi0yNS4wNTVsLTkuNjA3IDguMTkzYy0zMi42MzcgMzIuNS03MS40IDI5LjYxNC04Ny4yNC0xMi44NS03LjktMTYuNDczLTE4LjE3Mi0yNi41Ny0yNi45NzYtNDAuN2wtNDYuNjg4IDMzLjYyN2MtMy44ODYgNy45Ny04LjY2NSAyMC41NC0xNC40NyAzNC41OC00LjAzNiA5Ljc2LTcuNDIgMjYuNTI1LTcuMiA0MC40NTYtNS45ODUgMTAuMTk1IDIwLjczIDUxLjU0MiAzNy42IDc2LjY5MmwxNC42IDIxLjE5M2MzLjM0OCA4LjY4MyAxMC42OCAxNi4zNSAxMS4xMjQgMTcuMjg0IDMxLjMzNyA0MC44NzItMzkuNTMgMzEuNDYtNTQuMTY3IDI5LTI4Ljg3Ni00LjYtNTcuMTItMTYuNjYzLTgzLjY3LTMyLjc5MmwtNC42MDUtMi44NDhjLTMxLjU1LTE5Ljg2Ni02MC41OC00NS4yODMtODUuMjY0LTcwLjQyMy0xNC43MTgtMTYuNjY2LTI4LjczMi01MC44MjctMzkuMDgzLTc1LjA0NS0xNS41MTMtNTguMjI2LTM3Ljc3Ni0xNTkuMTUgMjIuNTMyLTIzNS42MyAzLjgyMy00LjM3MiA3Ljk0OC0xMS42NSAxMS40Ni0xMy4xIDE3LjkxOC0xMi4xMiAzNy4zNzUtMjAuMzggNTguMjk4LTI0Ljk2NmwtMi4xMTItMTMuNTYzYy0xMC40NSAyLjM3NC00NS42MzMgMTUuMjQzLTU1LjU4IDIwLjYzLTIyLjYyMyA2LjU1OC00MS4zODggMTMuNDA2LTcwLjIyIDIwLjYyNS05LjM3MiAxLjE0Ny0xOC41OTIgMS4xNjctMjcuNTgtLjA1My0yMC43NC0yLjgxOC01Ni4wOTQtLjMtNTguMzUtMi4zOTYtMTQuMDQ0LTE5LjU4NS0xNy45LTU0LjY3Ni0zMC43MDMtNzMuNDU2bC0uMTUtLjE4Ny0uMTYyLS4xNzhjLTcuNDctMTAuMDQtMTYuMTc3LTE4LjIzLTI0Ljc1Mi0yNi43NS0zMC43MDgtMzAuNS01Ny02MC02NC4zNDgtOTEuMjctMS44NS05LjIzLTYuNjE0LTE4LjgtNC4wODItNDYuMWwuMDQ3LS4xMzQuMDQ0LS4xMzRjNy4zODgtMjUuNTEzIDE5LjQwNy00Ni4zIDM5LjgwNi02Ny4xNTMgMjEuMjI4LjM0MyA0Mi4yNC44IDU4LjYwNyAzLjkzNiA3LjUgMS4yNTYgMjMuMTI0IDMuMjk2IDM5LjI1MiA5LjMwNCA0MC44MjIgMTUuMjA3IDk0Ljc2IDQwLjIgOTQuNzYgNDAuMi00MC40ODgtMjIuMjM2LTg1Ljc2LTUxLjExNS0xMTQuMzUtNTYuOTQzLTQuMjU0LS42MDItNi44NjgtMi41NDMtOC4xMy01LjkzIDQzLTI1LjI2NCA1MC44NjUtNTUgNzkuMTQ3LTgxLjY4NiAxMi45MjUtNS42MjYgMTcuOTY1LTguNDk4IDI4Ljk0Ny05LjY5MiAxMDEuNTcgMTYuMDYgMTY1LjUgNTYuNTE1IDIxNi4xIDgzLjU2NiAyMC41MyAxMS4xNjQgMzkuMiAxOS42MDYgNTcuMDE1IDI5Ljc3MyAxNS42MjQgNS4xNCA2Mi43NyA0MC43MzQgNzYuNTcgNTkuOTI1IDE0LjAxNyAyOS41OTUgMjQuMjUgNjEuNTYzIDMzLjU4NSA5Mi40IDYuNjkzIDMxLjM3NSAxMi41NjQgNDQuMTAyIDEyLjU2NCA0NC4xMDJzLTUuNjg4LTI2LjI0My00Ljc0LTMwLjk1OGM1Ljg5OCAyLjIwNCAxOS44NCA2LjU1NyAyNS42MiA1Ljg4IDAgMC0yNS44MTgtMTMuMzQ2LTI5LjItMjUuMTQtMTAuODU2LTM3Ljg0Ny0yMS43NDQtOTYuNzY1LTI0LjY4LTk5LjgzNy04LjI3NC0xMC40LTQyLjc3Ny0zNi44MTYtNjMuOTQtNDkuMDMtOC4wMDYtNC42Mi0xMi4zMzMtNy40ODctMTIuNjctOS41MTggNi43Ni02LjgzIDE1LjEtMTUuODY1IDIyLjU5OC0yMS42MDYgNy4xNzctNS41MDMgMTMuNy0xMS43IDIzLjcxMy0xNS40MjMgNDMuOTYzLTE5LjggNjkuMSA3LjYxOCA3NS4wNjYgMi4wNjYgMCAwLTkuNDU4LTEwLjg0LTUuMjU3LTkuMDU2IDQuMzAyIDIuMzMgMTguMzIzIDUuMDc4IDE5Ljg2MiA2LjUgMTYuMDIzIDEyLjUzNCA1Ny45MTMgNTguMzQ0IDgzLjE2IDEwNi42OCA2LjA2NCAxMS44NCA4LjUzIDE5LjYzNiA1LjcxNiAzMy44OTMtMi44MiAxNC4yNy01LjAwMiAyMi4xMTctOC4wNzggMzEuNjQ3LTIuNzc4IDYuMzctMTguNDk4IDQ5Ljk4OC0xOC40MzcgNTUuODQ4LTMuMTY3IDIzLjkzMiAxMC4yNjQgNTMuODkzIDEwLjI2NCA1My44OTMuMTI3LTguMTMzLS41LTEyLjQ2Ni4yMzctMTguMjgybC45MDQtMTAuMzkzcy0uNTczLTIuNzY0LS40Ny0zLjg2Yy42ODItNy4xODcgMi40NDUtMTMuMjMgMi45NjItMTcuMzQ1IDUuMDQ4LTMxLjI1NSAxMy44MjItNTMuNzU1IDIzLjc3NS04MS4xODIgMi45NTgtNi45MjIgNi44MjgtMTAuNzcgNi42My0xNi4wNC4xNjMtOS4zNTItOC4yMDUtMjEuOTA0LTE0LjI1Mi0zNC4xNjMtNi4xLTEyLjQtMTMuNC0yNi4yMDItMjIuOTYtNDAuNzYtMjEuODItMzEuMzc2LTQwLjQyLTU1LjkyNS03NC40OC03MS4zLTkuNTMtNC4xODItNDcuMDMtOC4yNy01OS45OC01Ljg0LTE1LjcgMy4yNzMtMjkuMjIgNi41LTQwIDEzLjM5OC0xNi45NSAxMC44NTQtMzAuMjcgMjcuNjYtNDUuOSAzNy41Ni0zNC41Ni0xNy4yODItNTEuMjQtMzAuMjE2LTU0LjM1LTMyLTIwLjU0LTExLjAwNi00NS4yLTIzLjc3OC03MS43My0zNS45LTEyLjcyLTExLjg2Mi05MS43LTQwLjIzMy0xNjQuMDQtNDUuODkyem00NDIuMDggNjQ0LjZjLTIxLjMtMTYuNjUtMzkuMjMtMzMuOC01MS4wNS01MS40NC0zLjg1OCAyMC43NTgtMTcuODY0IDM1LjU0Mi0yOC42ODggNTAuMDgzLTIuMTU1IDMuNC0zLjcwOCA4LjA2IDYuOTAyIDIyLjg4IDIuODYgMy45NDggMTMuMjA3IDQuNjIzIDIwLjE5MiA0LjI2LTcuMTA2LTUuMzcyLTE3LjkxOC0xMS4wNTMtMTkuNjYtMTUuOTc3IDEyLjUgOC41IDI0LjA3NiAxMC45NTcgMzQuNTM4IDkuNTg2IDIuNC0uMjcgNS4zNi0yLjgwNCA3LjY1NS02LjcwNiA0LjY2NC0xMC4wMiA4LjMxNS0xMi40MzMgMTIuMDA1LTE1LjEzbDguNTAyIDEwLjY1eiIgc3Ryb2tlPSIjMDAwIiBzdHJva2Utd2lkdGg9Ii4xMTQiLz48L2c+PC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/image-broken.js b/frontend/crawlab-ui/src/assets/js/svg/image-broken.js
new file mode 100644
index 00000000..c1a59669
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/image-broken.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIj48c2NyaXB0IHhtbG5zPSIiIGlkPSJhcmdlbnQteC1leHRlbnNpb24iIGRhdGEtZXh0ZW5zaW9uLWlkPSJhamNpY2psa2lib2xiZWFhYWdlamZobm9mb2dvY2djaiIvPgogIDxwYXRoIGQ9Ik0yMiAzSDJ2MThoMjB2LTJoLTJ2LTJoMnYtMmgtMnYtMmgydi0yaC0yVjloMlY3aC0yVjVoMlYzem0tMiA0djJoLTJ2MmgydjJoLTJ2MmgydjJoLTJ2Mkg0VjVoMTR2Mmgyem0tNiAyaC0ydjJoLTJ2Mkg4djJINnYyaDJ2LTJoMnYtMmgydi0yaDJ2Mmgydi0yaC0yVjl6TTYgN2gydjJINlY3eiIgZmlsbD0iIzAwMDAwMCIvPgo8L3N2Zz4K`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/image.js b/frontend/crawlab-ui/src/assets/js/svg/image.js
new file mode 100644
index 00000000..54da2a52
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/image.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktZmlsZS1pbWFnZSI+PHBhdGggZD0iTTguMDAyIDUuNWExLjUgMS41IDAgMSAxLTMgMCAxLjUgMS41IDAgMCAxIDMgMHoiLz48cGF0aCBkPSJNMTIgMEg0YTIgMiAwIDAgMC0yIDJ2MTJhMiAyIDAgMCAwIDIgMmg4YTIgMiAwIDAgMCAyLTJWMmEyIDIgMCAwIDAtMi0yek0zIDJhMSAxIDAgMCAxIDEtMWg4YTEgMSAwIDAgMSAxIDF2OGwtMi4wODMtMi4wODNhLjUuNSAwIDAgMC0uNzYuMDYzTDggMTEgNS44MzUgOS43YS41LjUgMCAwIDAtLjYxMS4wNzZMMyAxMlYyeiIvPjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/journal-code.js b/frontend/crawlab-ui/src/assets/js/svg/journal-code.js
new file mode 100644
index 00000000..20fe0f60
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/journal-code.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktam91cm5hbC1jb2RlIiB2aWV3Qm94PSIwIDAgMTYgMTYiPgogIDxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTguNjQ2IDUuNjQ2YS41LjUgMCAwIDEgLjcwOCAwbDIgMmEuNS41IDAgMCAxIDAgLjcwOGwtMiAyYS41LjUgMCAwIDEtLjcwOC0uNzA4TDEwLjI5MyA4IDguNjQ2IDYuMzU0YS41LjUgMCAwIDEgMC0uNzA4em0tMS4yOTIgMGEuNS41IDAgMCAwLS43MDggMGwtMiAyYS41LjUgMCAwIDAgMCAuNzA4bDIgMmEuNS41IDAgMCAwIC43MDgtLjcwOEw1LjcwNyA4bDEuNjQ3LTEuNjQ2YS41LjUgMCAwIDAgMC0uNzA4eiIvPgogIDxwYXRoIGQ9Ik0zIDBoMTBhMiAyIDAgMCAxIDIgMnYxMmEyIDIgMCAwIDEtMiAySDNhMiAyIDAgMCAxLTItMnYtMWgxdjFhMSAxIDAgMCAwIDEgMWgxMGExIDEgMCAwIDAgMS0xVjJhMSAxIDAgMCAwLTEtMUgzYTEgMSAwIDAgMC0xIDF2MUgxVjJhMiAyIDAgMCAxIDItMnoiLz4KICA8cGF0aCBkPSJNMSA1di0uNWEuNS41IDAgMCAxIDEgMFY1aC41YS41LjUgMCAwIDEgMCAxaC0yYS41LjUgMCAwIDEgMC0xSDF6bTAgM3YtLjVhLjUuNSAwIDAgMSAxIDBWOGguNWEuNS41IDAgMCAxIDAgMWgtMmEuNS41IDAgMCAxIDAtMUgxem0wIDN2LS41YS41LjUgMCAwIDEgMSAwdi41aC41YS41LjUgMCAwIDEgMCAxaC0yYS41LjUgMCAwIDEgMC0xSDF6Ii8+Cjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/journal-text.js b/frontend/crawlab-ui/src/assets/js/svg/journal-text.js
new file mode 100644
index 00000000..c51a94dd
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/journal-text.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktam91cm5hbC10ZXh0IiB2aWV3Qm94PSIwIDAgMTYgMTYiPgogIDxwYXRoIGQ9Ik01IDEwLjVhLjUuNSAwIDAgMSAuNS0uNWgyYS41LjUgMCAwIDEgMCAxaC0yYS41LjUgMCAwIDEtLjUtLjV6bTAtMmEuNS41IDAgMCAxIC41LS41aDVhLjUuNSAwIDAgMSAwIDFoLTVhLjUuNSAwIDAgMS0uNS0uNXptMC0yYS41LjUgMCAwIDEgLjUtLjVoNWEuNS41IDAgMCAxIDAgMWgtNWEuNS41IDAgMCAxLS41LS41em0wLTJhLjUuNSAwIDAgMSAuNS0uNWg1YS41LjUgMCAwIDEgMCAxaC01YS41LjUgMCAwIDEtLjUtLjV6Ii8+CiAgPHBhdGggZD0iTTMgMGgxMGEyIDIgMCAwIDEgMiAydjEyYTIgMiAwIDAgMS0yIDJIM2EyIDIgMCAwIDEtMi0ydi0xaDF2MWExIDEgMCAwIDAgMSAxaDEwYTEgMSAwIDAgMCAxLTFWMmExIDEgMCAwIDAtMS0xSDNhMSAxIDAgMCAwLTEgMXYxSDFWMmEyIDIgMCAwIDEgMi0yeiIvPgogIDxwYXRoIGQ9Ik0xIDV2LS41YS41LjUgMCAwIDEgMSAwVjVoLjVhLjUuNSAwIDAgMSAwIDFoLTJhLjUuNSAwIDAgMSAwLTFIMXptMCAzdi0uNWEuNS41IDAgMCAxIDEgMFY4aC41YS41LjUgMCAwIDEgMCAxaC0yYS41LjUgMCAwIDEgMC0xSDF6bTAgM3YtLjVhLjUuNSAwIDAgMSAxIDB2LjVoLjVhLjUuNSAwIDAgMSAwIDFoLTJhLjUuNSAwIDAgMSAwLTFIMXoiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/justify.js b/frontend/crawlab-ui/src/assets/js/svg/justify.js
new file mode 100644
index 00000000..7bcda738
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/justify.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktanVzdGlmeSIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yIDEyLjVhLjUuNSAwIDAgMSAuNS0uNWgxMWEuNS41IDAgMCAxIDAgMWgtMTFhLjUuNSAwIDAgMS0uNS0uNXptMC0zYS41LjUgMCAwIDEgLjUtLjVoMTFhLjUuNSAwIDAgMSAwIDFoLTExYS41LjUgMCAwIDEtLjUtLjV6bTAtM2EuNS41IDAgMCAxIC41LS41aDExYS41LjUgMCAwIDEgMCAxaC0xMWEuNS41IDAgMCAxLS41LS41em0wLTNhLjUuNSAwIDAgMSAuNS0uNWgxMWEuNS41IDAgMCAxIDAgMWgtMTFhLjUuNSAwIDAgMS0uNS0uNXoiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/kafka.js b/frontend/crawlab-ui/src/assets/js/svg/kafka.js
new file mode 100644
index 00000000..975507c2
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/kafka.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzIyOTI3MzczNzQxIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijk2MDkiCiAgICAgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPgogIDxwYXRoCiAgICBkPSJNNjkzLjgxNDQzMiA1NjcuMDg4MjUzYTEzMy4wMDUyOTYgMTMzLjAwNTI5NiAwIDAgMC0xMDAuMDgxOTI2IDQ1LjQ4MTYwNGwtNjIuNzExMTgxLTQ0LjQwOTc1NWM2LjY1NTA2NC0xOC4zNDk0MiAxMC40OTQ1MjQtMzguMDEwNjU1IDEwLjQ5NDUyNC01OC42MTU3NTcgMC0yMC4yNjkxNS0zLjY3OTQ4My0zOS41OTQ0MzItMTAuMTEwNTc4LTU3LjY4Nzg4N2w2Mi41ODMxOTktNDMuOTI5ODIzYTEzMi45NzMzMDEgMTMyLjk3MzMwMSAwIDAgMCA5OS44NzM5NTYgNDUuMjA5NjQzYzczLjU4OTY1MSAwIDEzMy40ODUyMjktNTkuODQ3NTg0IDEzMy40ODUyMjgtMTMzLjQ4NTIyOSAwLTczLjYyMTY0Ny01OS44OTU1NzctMTMzLjQ4NTIyOS0xMzMuNDg1MjI4LTEzMy40ODUyMjlhMTMzLjYxMzIxMSAxMzMuNjEzMjExIDAgMCAwLTEyNy45NTAwMDcgMTcxLjQxNTg5NWwtNjIuNjE1MTk1IDQzLjk0NTgyYTE3Mi41MDM3NDIgMTcyLjUwMzc0MiAwIDAgMC0xMDYuNzM2OTktNjEuOTkxMjgydi03NS40NjEzODlhMTMzLjcwOTE5NyAxMzMuNzA5MTk3IDAgMCAwIDEwNi4wMDEwOTMtMTMwLjU4OTYzNUM1MDIuNTYxMzI3IDU5Ljg0NzU4NCA0NDIuNjY1NzUgMCAzNjkuMDc2MDk5IDBzLTEzMy40MzcyMzUgNTkuODQ3NTg0LTEzMy40MzcyMzYgMTMzLjQ4NTIyOWExMzMuNTk3MjEzIDEzMy41OTcyMTMgMCAwIDAgMTAzLjYxNzQyOSAxMjkuOTgxNzIxdjc2LjQ1MzI0OWExNzIuNTY3NzMzIDE3Mi41Njc3MzMgMCAwIDAtMTQyLjUyMzk1NyAxNjkuNjI0MTQ2IDE3Mi41Njc3MzMgMTcyLjU2NzczMyAwIDAgMCAxNDMuNzcxNzgyIDE2OS44MzIxMTh2ODAuNzA4NjVhMTMzLjY2MTIwNCAxMzMuNjYxMjA0IDAgMCAwLTEwNC44MTcyNiAxMzAuMjg1Njc5QzIzNS42Mzg4NjMgOTY0LjAwODQzNiAyOTUuNDg2NDQ3IDEwMjMuODU2MDIgMzY5LjA3NjA5OSAxMDIzLjg1NjAyczEzMy40ODUyMjktNTkuODQ3NTg0IDEzMy40ODUyMjgtMTMzLjQ4NTIyOGExMzMuNjEzMjExIDEzMy42MTMyMTEgMCAwIDAtMTA0LjgxNzI2LTEzMC4yODU2Nzl2LTgwLjcwODY1YTE3Mi40Mzk3NTEgMTcyLjQzOTc1MSAwIDAgMCAxMDQuOTQ1MjQyLTYxLjA0NzQxNmw2My4xNDMxMjEgNDQuNjk3NzE1YTEzMi44NzczMTQgMTMyLjg3NzMxNCAwIDAgMC01LjQ1NTIzMyAzNy41NDY3MmMwIDczLjU4OTY1MSA1OS44OTU1NzcgMTMzLjQ4NTIyOSAxMzMuNDg1MjI5IDEzMy40ODUyMjhzMTMzLjQ4NTIyOS01OS44NDc1ODQgMTMzLjQ4NTIyOC0xMzMuNDg1MjI4LTU5Ljk0MzU3LTEzMy40ODUyMjktMTMzLjUzMzIyMi0xMzMuNDg1MjI5eiBtMC0zMTIuMTE2MTA4YTY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEgNjQuNzEwOSA2NC43MjY4OTcgNjQuNzkwODg5IDY0Ljc5MDg4OSAwIDAgMS02NC43MTA5IDY0LjcxMDkgNjQuODIyODg0IDY0LjgyMjg4NCAwIDAgMS02NC43MTA5LTY0LjcxMDkgNjQuODIyODg0IDY0LjgyMjg4NCAwIDAgMSA2NC43MTA5LTY0LjcxMDl6TTMwNC4zNjUxOTkgMTMzLjUwMTIyNmE2NC43OTA4ODkgNjQuNzkwODg5IDAgMCAxIDY0LjcxMDktNjQuNzEwOSA2NC43OTA4ODkgNjQuNzkwODg5IDAgMCAxIDY0LjcyNjg5NyA2NC43MTA5IDY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEtNjQuNzI2ODk3IDY0LjcxMDkgNjQuNzkwODg5IDY0Ljc5MDg4OSAwIDAgMS02NC43MTA5LTY0LjcxMDl6IG0xMjkuNDIxOCA3NTYuODg1NTYzYTY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEtNjQuNzEwOSA2NC43MTA5IDY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEtNjQuNzEwOS02NC43MTA5IDY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEgNjQuNzEwOS02NC43MTA5IDY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEgNjQuNzI2ODk3IDY0LjcxMDl6IG0tNjQuNzEwOS0yOTAuNTY3MTM5YTkwLjM1NTI5NCA5MC4zNTUyOTQgMCAwIDEtOTAuMjU5MzA4LTkwLjI1OTMwNyA5MC4zNTUyOTQgOTAuMzU1Mjk0IDAgMCAxIDkwLjI1OTMwOC05MC4yNzUzMDUgOTAuMzU1Mjk0IDkwLjM1NTI5NCAwIDAgMSA5MC4yNzUzMDUgOTAuMjc1MzA1IDkwLjM1NTI5NCA5MC4zNTUyOTQgMCAwIDEtOTAuMjc1MzA1IDkwLjI1OTMwN3ogbTMyNC43MzgzMzMgMTY1LjQ4MDczYTY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEtNjQuNzEwOS02NC43MTA5IDY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEgNjQuNzEwOS02NC43MTA5IDY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEgNjQuNzEwOSA2NC43MTA5IDY0Ljc5MDg4OSA2NC43OTA4ODkgMCAwIDEtNjQuNzEwOSA2NC43MTA5eiIKICAgIGZpbGw9IiMwMDAwMDAiIHAtaWQ9Ijk2MTAiPjwvcGF0aD4KPC9zdmc+Cg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/lark.js b/frontend/crawlab-ui/src/assets/js/svg/lark.js
new file mode 100644
index 00000000..ac8d3295
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/lark.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPgo8c3ZnIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDQ4IDQ4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cmVjdCB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuMDEiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MS4wNzIxIDUuOTk0MDhMMy4zMTExOSAxNi41MTg3TDEyLjM4NjEgMjUuODEyNkwyMC44MDAzIDI1Ljk1OTRMMzAuNDgzMiAxNi41MTg3QzMwLjIyNzEgMTUuOTk0MyAzMC4wOTkgMTUuNTU1MiAzMC4wOTkgMTUuMjAxM0MzMC4wOTkgMTQuNDA3NCAzMC40MTA4IDEzLjc3ODYgMzAuODk1MiAxMy4zMzNDMzEuNzI0NiAxMi41NyAzMi43MjI2IDEyLjQ1NTggMzMuODg5NCAxMi45OTA1TDQxLjA3MjEgNS45OTQwOFoiIGZpbGw9IiMwMDAwMDAiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00Mi4xMDI4IDYuNzI4NDlMMzEuNTc4MiA0NC40ODk0TDIyLjI4NDQgMzUuNDE0NUwyMi4xMzc1IDI3LjAwMDNMMzEuNTEyMiAxNy40ODE3QzMyLjAyMDIgMTcuODQ1NSAzMi41NzUgMTguMDEwNiAzMy4xNzY2IDE3Ljk3NjlDMzQuMDc5MSAxNy45MjY0IDM0LjY2MjEgMTcuMzgxNCAzNC45MzU2IDE3LjA2MDNDMzUuMjA5MSAxNi43MzkzIDM1LjUzIDE2LjIwNTIgMzUuNTAzMyAxNS40MTE0QzM1LjQ4NTQgMTQuODgyMiAzNS4zMTE2IDE0LjM5NDEgMzQuOTgyIDEzLjk0NzNMNDIuMTAyOCA2LjcyODQ5WiIgZmlsbD0iIzAwMDAwMCIvPgo8L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/link.js b/frontend/crawlab-ui/src/assets/js/svg/link.js
new file mode 100644
index 00000000..1a259416
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/link.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktbGluayIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNNi4zNTQgNS41SDRhMyAzIDAgMCAwIDAgNmgzYTMgMyAwIDAgMCAyLjgzLTRIOWMtLjA4NiAwLS4xNy4wMS0uMjUuMDMxQTIgMiAwIDAgMSA3IDEwLjVINGEyIDIgMCAxIDEgMC00aDEuNTM1Yy4yMTgtLjM3Ni40OTUtLjcxNC44Mi0xeiIvPgogIDxwYXRoIGQ9Ik05IDUuNWEzIDMgMCAwIDAtMi44MyA0aDEuMDk4QTIgMiAwIDAgMSA5IDYuNWgzYTIgMiAwIDEgMSAwIDRoLTEuNTM1YTQuMDIgNC4wMiAwIDAgMS0uODIgMUgxMmEzIDMgMCAxIDAgMC02SDl6Ii8+Cjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/list-ol.js b/frontend/crawlab-ui/src/assets/js/svg/list-ol.js
new file mode 100644
index 00000000..d8576034
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/list-ol.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktbGlzdC1vbCIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik01IDExLjVhLjUuNSAwIDAgMSAuNS0uNWg5YS41LjUgMCAwIDEgMCAxaC05YS41LjUgMCAwIDEtLjUtLjV6bTAtNGEuNS41IDAgMCAxIC41LS41aDlhLjUuNSAwIDAgMSAwIDFoLTlhLjUuNSAwIDAgMS0uNS0uNXptMC00YS41LjUgMCAwIDEgLjUtLjVoOWEuNS41IDAgMCAxIDAgMWgtOWEuNS41IDAgMCAxLS41LS41eiIvPgogIDxwYXRoIGQ9Ik0xLjcxMyAxMS44NjV2LS40NzRIMmMuMjE3IDAgLjM2My0uMTM3LjM2My0uMzE3IDAtLjE4NS0uMTU4LS4zMS0uMzYxLS4zMS0uMjIzIDAtLjM2Ny4xNTItLjM3My4zMWgtLjU5Yy4wMTYtLjQ2Ny4zNzMtLjc4Ny45ODYtLjc4Ny41ODgtLjAwMi45NTQuMjkxLjk1Ny43MDNhLjU5NS41OTUgMCAwIDEtLjQ5Mi41OTR2LjAzM2EuNjE1LjYxNSAwIDAgMSAuNTY5LjYzMWMuMDAzLjUzMy0uNTAyLjgtMS4wNTEuOC0uNjU2IDAtMS0uMzctMS4wMDgtLjc5NGguNTgyYy4wMDguMTc4LjE4Ni4zMDYuNDIyLjMwOS4yNTQgMCAuNDI0LS4xNDUuNDIyLS4zNS0uMDAyLS4xOTUtLjE1NS0uMzQ4LS40MTQtLjM0OGgtLjN6bS0uMDA0LTQuNjk5aC0uNjA0di0uMDM1YzAtLjQwOC4yOTUtLjg0NC45NTgtLjg0NC41ODMgMCAuOTYuMzI2Ljk2Ljc1NiAwIC4zODktLjI1Ny42MTctLjQ3Ni44NDhsLS41MzcuNTcydi4wM2gxLjA1NFY5SDEuMTQzdi0uMzk1bC45NTctLjk5Yy4xMzgtLjE0Mi4yOTMtLjMwNC4yOTMtLjUwOCAwLS4xOC0uMTQ3LS4zMi0uMzQyLS4zMmEuMzMuMzMgMCAwIDAtLjM0Mi4zMzh2LjA0MXpNMi41NjQgNWgtLjYzNVYyLjkyNGgtLjAzMWwtLjU5OC40MnYtLjU2N2wuNjI5LS40NDNoLjYzNVY1eiIvPgo8L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/list-ul.js b/frontend/crawlab-ui/src/assets/js/svg/list-ul.js
new file mode 100644
index 00000000..049bfe54
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/list-ul.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktbGlzdC11bCIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik01IDExLjVhLjUuNSAwIDAgMSAuNS0uNWg5YS41LjUgMCAwIDEgMCAxaC05YS41LjUgMCAwIDEtLjUtLjV6bTAtNGEuNS41IDAgMCAxIC41LS41aDlhLjUuNSAwIDAgMSAwIDFoLTlhLjUuNSAwIDAgMS0uNS0uNXptMC00YS41LjUgMCAwIDEgLjUtLjVoOWEuNS41IDAgMCAxIDAgMWgtOWEuNS41IDAgMCAxLS41LS41em0tMyAxYTEgMSAwIDEgMCAwLTIgMSAxIDAgMCAwIDAgMnptMCA0YTEgMSAwIDEgMCAwLTIgMSAxIDAgMCAwIDAgMnptMCA0YTEgMSAwIDEgMCAwLTIgMSAxIDAgMCAwIDAgMnoiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/logo-icon-white.js b/frontend/crawlab-ui/src/assets/js/svg/logo-icon-white.js
new file mode 100644
index 00000000..d462f7de
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/logo-icon-white.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZwogIHZlcnNpb249IjEuMCIKICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgd2lkdGg9IjUzLjAzNTE1NjI1IgogIGhlaWdodD0iNTMuMDM1MTU2MjUiCj4KICA8cmVjdAogICAgeD0iMCIKICAgIHk9IjAiCiAgICB3aWR0aD0iMTAwJSIKICAgIGhlaWdodD0iMTAwJSIKICAgIGZpbGw9InRyYW5zcGFyZW50IgogICAgZmlsbC1vcGFjaXR5PSIwIgogIC8+CiAgPHN2ZwogICAgdmVyc2lvbj0iMS4wIgogICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgICB4PSIwIgogICAgeT0iMCIKICAgIHdpZHRoPSIxMDAlIgogICAgaGVpZ2h0PSIxMDAlIgogICAgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgbWVldCIKICAgIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIKICA+CiAgICA8ZyBmaWxsPSIjZmZmIiBjbGFzcz0ibmV3aW5pdGlhbHN2Zy1nIG5ld2luaXRpYWxzdmciPgogICAgICA8ZyBjbGFzcz0idHAtbmFtZSI+CiAgICAgICAgPGcgY2xhc3M9InRwLWdyYXBoIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLCAwKSIgbWFzaz0idXJsKCNlZDIzYWQxNy05ZGYzLTRmNzktYTIxNC04NDk2Mjc5OWEzOTkpIj4KICAgICAgICAgIDxyZWN0IGZpbGw9IiNmZmYiIHg9IjAiIHk9IjAiIHdpZHRoPSI1My4wMzUxNTYyNSIgaGVpZ2h0PSI1My4wMzUxNTYyNSIgcng9IjI2LjUxNzU3ODEyNSIvPgogICAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOS4wMDc1Nzc4OTYxMTgxNjQsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIiBmaWxsPSIjZmZmIj4KICAgICAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgICAgZD0iTTM2Ljk1LTI3LjcxTDM2Ljk1LTI3LjcxTDMxLjgwLTI3LjcxUTI5Ljg2LTM1LjY2IDIwLjg0LTM1Ljg4TDIwLjg0LTM1Ljg4UTguMzgtMzUuMjMgNy45NS0xOS45OEw3Ljk1LTE5Ljk4UTcuOTUtMy42NSAyMS4wNS0zLjY1TDIxLjA1LTMuNjVRMzAuMDgtMy44NyAzMi4yMy0xNC42MUwzMi4yMy0xNC42MUwzNy4zOC0xNC42MVEzNS4wMiAwLjIxIDE5Ljk4IDAuNjRMMTkuOTggMC42NFEyLjc5IDAgMi4zNi0xOS43N0wyLjM2LTE5Ljc3UTMuMjItMzkuNTMgMjAuODQtNDAuMzlMMjAuODQtNDAuMzlRMzQuODAtMzkuOTYgMzYuOTUtMjcuNzFaIgogICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIuMzYzMjgxMjUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1Ny4wMzUxNTYyNSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNOS4wMi0xNi43Nkw5LjAyLTE2Ljc2TDkuMDIgMEw0LjA4IDBMNC4wOC0yOC43OUw4LjgxLTI4Ljc5TDguODEtMjMuODVRMTIuMDMtMjkuNjUgMTYuOTctMjkuNjVMMTYuOTctMjkuNjVRMTcuNDAtMjkuNjUgMTcuODMtMjkuNDNMMTcuODMtMjkuNDNRMTguMDUtMjkuNDMgMTguMjYtMjkuNDNMMTguMjYtMjkuNDNMMTguMjYtMjQuMjhMMTYuMzMtMjQuMjhROS40NS0yMy44NSA5LjAyLTE2Ljc2WiIKICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC00LjA4MjAzMTI1LCA0MC4zOTA2MjUpIi8+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzUuMjE0ODQzNzUsIDApIj4KICAgICAgICAgIDxnIGZpbGw9IiNmZmYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgICAgZD0iTTcuOTUtMTkuOThMNy45NS0xOS45OEwzLjQ0LTE5Ljk4UTMuNjUtMjkuNjUgMTUuMjUtMjkuNjVMMTUuMjUtMjkuNjVRMjUuNTctMjkuMjIgMjUuNzgtMjEuNDhMMjUuNzgtMjEuNDhMMjUuNzgtNC45NFEyNS43OC0zLjIyIDI3LjUwLTMuMjJMMjcuNTAtMy4yMlEyNy43MS0zLjIyIDI4LjE0LTMuMjJMMjguMTQtMy4yMlEyOC43OS0zLjQ0IDI5LjIyLTMuNDRMMjkuMjItMy40NEwyOS4yMiAwUTI4Ljc5IDAgMjguMTQgMEwyOC4xNCAwUTI3LjA3IDAuMjEgMjYuNDMgMC4yMUwyNi40MyAwLjIxUTIxLjI3IDAuMjEgMjEuNDgtMy44N0wyMS40OC0zLjg3UTE3LjE5IDAuNDMgMTAuOTYgMC40M0wxMC45NiAwLjQzUTIuMzYgMCAxLjkzLTcuNTJMMS45My03LjUyUTIuMTUtMTUuMjUgMTAuNzQtMTYuNTRMMTAuNzQtMTYuNTRMMTguNjktMTcuNDBRMjEuMjctMTcuODMgMjEuMjctMjAuODRMMjEuMjctMjAuODRRMjEuMjctMjUuNTcgMTQuMzktMjUuNTdMMTQuMzktMjUuNTdROC4zOC0yNS41NyA3Ljk1LTE5Ljk4Wk0yMS4wNS05Ljg4TDIxLjA1LTkuODhMMjEuMDUtMTQuNjFRMTkuMzQtMTMuNTQgMTMuMTEtMTIuNjhMMTMuMTEtMTIuNjhRNy4wOS0xMi4yNSA3LjA5LTguMTZMNy4wOS04LjE2UTcuMDktMy4yMiAxMi4wMy0zLjIyTDEyLjAzLTMuMjJRMjAuNDEtMy44NyAyMS4wNS05Ljg4WiIKICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xLjkzMzU5Mzc1LCA0MC4zOTA2MjUpIi8+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTA2LjUsIDApIj4KICAgICAgICAgIDxnIGZpbGw9IiNmZmYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgICAgZD0iTTE5Ljc3LTIyLjM0TDE5LjU1LTIyLjM0TDE0LjE4IDBMOS4wMiAwTDAuODYtMjguNzlMNi4wMi0yOC43OUwxMS4zOS02LjAyTDExLjYwLTYuMDJMMTcuMTktMjguNzlMMjIuNTYtMjguNzlMMjguMTQtNi4wMkwyOC4zNi02LjAyTDMzLjk1LTI4Ljc5TDM4Ljg5LTI4Ljc5TDMwLjUxIDBMMjUuNTcgMEwxOS43Ny0yMi4zNFoiCiAgICAgICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMC44NTkzNzUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDguNTI3MzQzNzUsIDApIj4KICAgICAgICAgIDxnIGZpbGw9IiNmZmYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgICAgICA8cGF0aCBkPSJNMy42NS0zOS4zMkw4LjU5LTM5LjMyTDguNTkgMEwzLjY1IDBMMy42NS0zOS4zMloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zLjY1MjM0Mzc1LCA0MC4zOTA2MjUpIi8+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTU3LjQ2ODc1LCAwKSI+CiAgICAgICAgICA8ZyBmaWxsPSIjZmZmIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDYpIj4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJzY2FsZSgxKSI+CiAgICAgICAgICAgICAgPHBhdGgKICAgICAgICAgICAgICAgIGQ9Ik03Ljk1LTE5Ljk4TDcuOTUtMTkuOThMMy40NC0xOS45OFEzLjY1LTI5LjY1IDE1LjI1LTI5LjY1TDE1LjI1LTI5LjY1UTI1LjU3LTI5LjIyIDI1Ljc4LTIxLjQ4TDI1Ljc4LTIxLjQ4TDI1Ljc4LTQuOTRRMjUuNzgtMy4yMiAyNy41MC0zLjIyTDI3LjUwLTMuMjJRMjcuNzEtMy4yMiAyOC4xNC0zLjIyTDI4LjE0LTMuMjJRMjguNzktMy40NCAyOS4yMi0zLjQ0TDI5LjIyLTMuNDRMMjkuMjIgMFEyOC43OSAwIDI4LjE0IDBMMjguMTQgMFEyNy4wNyAwLjIxIDI2LjQzIDAuMjFMMjYuNDMgMC4yMVEyMS4yNyAwLjIxIDIxLjQ4LTMuODdMMjEuNDgtMy44N1ExNy4xOSAwLjQzIDEwLjk2IDAuNDNMMTAuOTYgMC40M1EyLjM2IDAgMS45My03LjUyTDEuOTMtNy41MlEyLjE1LTE1LjI1IDEwLjc0LTE2LjU0TDEwLjc0LTE2LjU0TDE4LjY5LTE3LjQwUTIxLjI3LTE3LjgzIDIxLjI3LTIwLjg0TDIxLjI3LTIwLjg0UTIxLjI3LTI1LjU3IDE0LjM5LTI1LjU3TDE0LjM5LTI1LjU3UTguMzgtMjUuNTcgNy45NS0xOS45OFpNMjEuMDUtOS44OEwyMS4wNS05Ljg4TDIxLjA1LTE0LjYxUTE5LjM0LTEzLjU0IDEzLjExLTEyLjY4TDEzLjExLTEyLjY4UTcuMDktMTIuMjUgNy4wOS04LjE2TDcuMDktOC4xNlE3LjA5LTMuMjIgMTIuMDMtMy4yMkwxMi4wMy0zLjIyUTIwLjQxLTMuODcgMjEuMDUtOS44OFoiCiAgICAgICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMS45MzM1OTM3NSwgNDAuMzkwNjI1KSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE4OC43NTM5MDYyNSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNNy43My0zLjQ0TDcuNzMgMEwzLjIyIDBMMy4yMi0zOS4zMkw3Ljk1LTM5LjMyTDcuOTUtMjUuMTRMOC4xNi0yNS4xNFExMC43NC0yOS40MyAxNi4zMy0yOS42NUwxNi4zMy0yOS42NVEyNy43MS0yOS4wMCAyOC4zNi0xNS4yNUwyOC4zNi0xNS4yNVEyNy43MSAwIDE1LjY4IDAuNDNMMTUuNjggMC40M1ExMC4zMSAwLjQzIDcuOTUtMy40NEw3Ljk1LTMuNDRMNy43My0zLjQ0Wk0yMy40Mi0xNC4zOUwyMy40Mi0xNC4zOVEyMy42My0yNS4xNCAxNS40Ny0yNS4xNEwxNS40Ny0yNS4xNFE3LjczLTI1LjE0IDcuNzMtMTIuODlMNy43My0xMi44OVE3Ljk1LTMuODcgMTUuNjgtMy4yMkwxNS42OC0zLjIyUTIzLjIwLTMuNDQgMjMuNDItMTQuMzlaIgogICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMuMjIyNjU2MjUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgPC9nPgogICAgPC9nPgogICAgPG1hc2sgaWQ9ImVkMjNhZDE3LTlkZjMtNGY3OS1hMjE0LTg0OTYyNzk5YTM5OSI+CiAgICAgIDxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSI1My4wMzUxNTYyNSIgaGVpZ2h0PSI1My4wMzUxNTYyNSIgcng9IjI2LjUxNzU3ODEyNSIgZmlsbD0id2hpdGUiLz4KICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOS4wMDc1Nzc4OTYxMTgxNjQsNikiIGZpbGw9ImJsYWNrIj4KICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgIGQ9Ik0zNi45NS0yNy43MUwzNi45NS0yNy43MUwzMS44MC0yNy43MVEyOS44Ni0zNS42NiAyMC44NC0zNS44OEwyMC44NC0zNS44OFE4LjM4LTM1LjIzIDcuOTUtMTkuOThMNy45NS0xOS45OFE3Ljk1LTMuNjUgMjEuMDUtMy42NUwyMS4wNS0zLjY1UTMwLjA4LTMuODcgMzIuMjMtMTQuNjFMMzIuMjMtMTQuNjFMMzcuMzgtMTQuNjFRMzUuMDIgMC4yMSAxOS45OCAwLjY0TDE5Ljk4IDAuNjRRMi43OSAwIDIuMzYtMTkuNzdMMi4zNi0xOS43N1EzLjIyLTM5LjUzIDIwLjg0LTQwLjM5TDIwLjg0LTQwLjM5UTM0LjgwLTM5Ljk2IDM2Ljk1LTI3LjcxWiIKICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIuMzYzMjgxMjUsIDQwLjM5MDYyNSkiLz4KICAgICAgICA8L2c+CiAgICAgIDwvZz4KICAgIDwvbWFzaz4KICAgIDxkZWZzIHYtZ3JhPSJvZCIvPgogIDwvc3ZnPgo8L3N2Zz4KCgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/logo-icon.js b/frontend/crawlab-ui/src/assets/js/svg/logo-icon.js
new file mode 100644
index 00000000..3a8e2cb5
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/logo-icon.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZwogIHZlcnNpb249IjEuMCIKICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgd2lkdGg9IjUzLjAzNTE1NjI1IgogIGhlaWdodD0iNTMuMDM1MTU2MjUiCj4KICA8cmVjdAogICAgeD0iMCIKICAgIHk9IjAiCiAgICB3aWR0aD0iMTAwJSIKICAgIGhlaWdodD0iMTAwJSIKICAgIGZpbGw9InRyYW5zcGFyZW50IgogICAgZmlsbC1vcGFjaXR5PSIwIgogIC8+CiAgPHN2ZwogICAgdmVyc2lvbj0iMS4wIgogICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgICB4PSIwIgogICAgeT0iMCIKICAgIHdpZHRoPSIxMDAlIgogICAgaGVpZ2h0PSIxMDAlIgogICAgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgbWVldCIKICAgIGNvbG9yLWludGVycG9sYXRpb24tZmlsdGVycz0ic1JHQiIKICA+CiAgICA8ZyBmaWxsPSIjNDA5ZWZmIiBjbGFzcz0ibmV3aW5pdGlhbHN2Zy1nIG5ld2luaXRpYWxzdmciPgogICAgICA8ZyBjbGFzcz0idHAtbmFtZSI+CiAgICAgICAgPGcgY2xhc3M9InRwLWdyYXBoIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLCAwKSIgbWFzaz0idXJsKCNlZDIzYWQxNy05ZGYzLTRmNzktYTIxNC04NDk2Mjc5OWEzOTkpIj4KICAgICAgICAgIDxyZWN0IGZpbGw9IiM0MDllZmYiIHg9IjAiIHk9IjAiIHdpZHRoPSI1My4wMzUxNTYyNSIgaGVpZ2h0PSI1My4wMzUxNTYyNSIgcng9IjI2LjUxNzU3ODEyNSIvPgogICAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOS4wMDc1Nzc4OTYxMTgxNjQsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIiBmaWxsPSIjZmZmIj4KICAgICAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgICAgZD0iTTM2Ljk1LTI3LjcxTDM2Ljk1LTI3LjcxTDMxLjgwLTI3LjcxUTI5Ljg2LTM1LjY2IDIwLjg0LTM1Ljg4TDIwLjg0LTM1Ljg4UTguMzgtMzUuMjMgNy45NS0xOS45OEw3Ljk1LTE5Ljk4UTcuOTUtMy42NSAyMS4wNS0zLjY1TDIxLjA1LTMuNjVRMzAuMDgtMy44NyAzMi4yMy0xNC42MUwzMi4yMy0xNC42MUwzNy4zOC0xNC42MVEzNS4wMiAwLjIxIDE5Ljk4IDAuNjRMMTkuOTggMC42NFEyLjc5IDAgMi4zNi0xOS43N0wyLjM2LTE5Ljc3UTMuMjItMzkuNTMgMjAuODQtNDAuMzlMMjAuODQtNDAuMzlRMzQuODAtMzkuOTYgMzYuOTUtMjcuNzFaIgogICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIuMzYzMjgxMjUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1Ny4wMzUxNTYyNSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iIzQwOWVmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNOS4wMi0xNi43Nkw5LjAyLTE2Ljc2TDkuMDIgMEw0LjA4IDBMNC4wOC0yOC43OUw4LjgxLTI4Ljc5TDguODEtMjMuODVRMTIuMDMtMjkuNjUgMTYuOTctMjkuNjVMMTYuOTctMjkuNjVRMTcuNDAtMjkuNjUgMTcuODMtMjkuNDNMMTcuODMtMjkuNDNRMTguMDUtMjkuNDMgMTguMjYtMjkuNDNMMTguMjYtMjkuNDNMMTguMjYtMjQuMjhMMTYuMzMtMjQuMjhROS40NS0yMy44NSA5LjAyLTE2Ljc2WiIKICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC00LjA4MjAzMTI1LCA0MC4zOTA2MjUpIi8+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzUuMjE0ODQzNzUsIDApIj4KICAgICAgICAgIDxnIGZpbGw9IiM0MDllZmYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgICAgZD0iTTcuOTUtMTkuOThMNy45NS0xOS45OEwzLjQ0LTE5Ljk4UTMuNjUtMjkuNjUgMTUuMjUtMjkuNjVMMTUuMjUtMjkuNjVRMjUuNTctMjkuMjIgMjUuNzgtMjEuNDhMMjUuNzgtMjEuNDhMMjUuNzgtNC45NFEyNS43OC0zLjIyIDI3LjUwLTMuMjJMMjcuNTAtMy4yMlEyNy43MS0zLjIyIDI4LjE0LTMuMjJMMjguMTQtMy4yMlEyOC43OS0zLjQ0IDI5LjIyLTMuNDRMMjkuMjItMy40NEwyOS4yMiAwUTI4Ljc5IDAgMjguMTQgMEwyOC4xNCAwUTI3LjA3IDAuMjEgMjYuNDMgMC4yMUwyNi40MyAwLjIxUTIxLjI3IDAuMjEgMjEuNDgtMy44N0wyMS40OC0zLjg3UTE3LjE5IDAuNDMgMTAuOTYgMC40M0wxMC45NiAwLjQzUTIuMzYgMCAxLjkzLTcuNTJMMS45My03LjUyUTIuMTUtMTUuMjUgMTAuNzQtMTYuNTRMMTAuNzQtMTYuNTRMMTguNjktMTcuNDBRMjEuMjctMTcuODMgMjEuMjctMjAuODRMMjEuMjctMjAuODRRMjEuMjctMjUuNTcgMTQuMzktMjUuNTdMMTQuMzktMjUuNTdROC4zOC0yNS41NyA3Ljk1LTE5Ljk4Wk0yMS4wNS05Ljg4TDIxLjA1LTkuODhMMjEuMDUtMTQuNjFRMTkuMzQtMTMuNTQgMTMuMTEtMTIuNjhMMTMuMTEtMTIuNjhRNy4wOS0xMi4yNSA3LjA5LTguMTZMNy4wOS04LjE2UTcuMDktMy4yMiAxMi4wMy0zLjIyTDEyLjAzLTMuMjJRMjAuNDEtMy44NyAyMS4wNS05Ljg4WiIKICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xLjkzMzU5Mzc1LCA0MC4zOTA2MjUpIi8+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTA2LjUsIDApIj4KICAgICAgICAgIDxnIGZpbGw9IiM0MDllZmYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgICAgZD0iTTE5Ljc3LTIyLjM0TDE5LjU1LTIyLjM0TDE0LjE4IDBMOS4wMiAwTDAuODYtMjguNzlMNi4wMi0yOC43OUwxMS4zOS02LjAyTDExLjYwLTYuMDJMMTcuMTktMjguNzlMMjIuNTYtMjguNzlMMjguMTQtNi4wMkwyOC4zNi02LjAyTDMzLjk1LTI4Ljc5TDM4Ljg5LTI4Ljc5TDMwLjUxIDBMMjUuNTcgMEwxOS43Ny0yMi4zNFoiCiAgICAgICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMC44NTkzNzUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDguNTI3MzQzNzUsIDApIj4KICAgICAgICAgIDxnIGZpbGw9IiM0MDllZmYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgICAgICA8cGF0aCBkPSJNMy42NS0zOS4zMkw4LjU5LTM5LjMyTDguNTkgMEwzLjY1IDBMMy42NS0zOS4zMloiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0zLjY1MjM0Mzc1LCA0MC4zOTA2MjUpIi8+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTU3LjQ2ODc1LCAwKSI+CiAgICAgICAgICA8ZyBmaWxsPSIjNDA5ZWZmIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDYpIj4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJzY2FsZSgxKSI+CiAgICAgICAgICAgICAgPHBhdGgKICAgICAgICAgICAgICAgIGQ9Ik03Ljk1LTE5Ljk4TDcuOTUtMTkuOThMMy40NC0xOS45OFEzLjY1LTI5LjY1IDE1LjI1LTI5LjY1TDE1LjI1LTI5LjY1UTI1LjU3LTI5LjIyIDI1Ljc4LTIxLjQ4TDI1Ljc4LTIxLjQ4TDI1Ljc4LTQuOTRRMjUuNzgtMy4yMiAyNy41MC0zLjIyTDI3LjUwLTMuMjJRMjcuNzEtMy4yMiAyOC4xNC0zLjIyTDI4LjE0LTMuMjJRMjguNzktMy40NCAyOS4yMi0zLjQ0TDI5LjIyLTMuNDRMMjkuMjIgMFEyOC43OSAwIDI4LjE0IDBMMjguMTQgMFEyNy4wNyAwLjIxIDI2LjQzIDAuMjFMMjYuNDMgMC4yMVEyMS4yNyAwLjIxIDIxLjQ4LTMuODdMMjEuNDgtMy44N1ExNy4xOSAwLjQzIDEwLjk2IDAuNDNMMTAuOTYgMC40M1EyLjM2IDAgMS45My03LjUyTDEuOTMtNy41MlEyLjE1LTE1LjI1IDEwLjc0LTE2LjU0TDEwLjc0LTE2LjU0TDE4LjY5LTE3LjQwUTIxLjI3LTE3LjgzIDIxLjI3LTIwLjg0TDIxLjI3LTIwLjg0UTIxLjI3LTI1LjU3IDE0LjM5LTI1LjU3TDE0LjM5LTI1LjU3UTguMzgtMjUuNTcgNy45NS0xOS45OFpNMjEuMDUtOS44OEwyMS4wNS05Ljg4TDIxLjA1LTE0LjYxUTE5LjM0LTEzLjU0IDEzLjExLTEyLjY4TDEzLjExLTEyLjY4UTcuMDktMTIuMjUgNy4wOS04LjE2TDcuMDktOC4xNlE3LjA5LTMuMjIgMTIuMDMtMy4yMkwxMi4wMy0zLjIyUTIwLjQxLTMuODcgMjEuMDUtOS44OFoiCiAgICAgICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMS45MzM1OTM3NSwgNDAuMzkwNjI1KSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE4OC43NTM5MDYyNSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iIzQwOWVmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNNy43My0zLjQ0TDcuNzMgMEwzLjIyIDBMMy4yMi0zOS4zMkw3Ljk1LTM5LjMyTDcuOTUtMjUuMTRMOC4xNi0yNS4xNFExMC43NC0yOS40MyAxNi4zMy0yOS42NUwxNi4zMy0yOS42NVEyNy43MS0yOS4wMCAyOC4zNi0xNS4yNUwyOC4zNi0xNS4yNVEyNy43MSAwIDE1LjY4IDAuNDNMMTUuNjggMC40M1ExMC4zMSAwLjQzIDcuOTUtMy40NEw3Ljk1LTMuNDRMNy43My0zLjQ0Wk0yMy40Mi0xNC4zOUwyMy40Mi0xNC4zOVEyMy42My0yNS4xNCAxNS40Ny0yNS4xNEwxNS40Ny0yNS4xNFE3LjczLTI1LjE0IDcuNzMtMTIuODlMNy43My0xMi44OVE3Ljk1LTMuODcgMTUuNjgtMy4yMkwxNS42OC0zLjIyUTIzLjIwLTMuNDQgMjMuNDItMTQuMzlaIgogICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMuMjIyNjU2MjUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgPC9nPgogICAgPC9nPgogICAgPG1hc2sgaWQ9ImVkMjNhZDE3LTlkZjMtNGY3OS1hMjE0LTg0OTYyNzk5YTM5OSI+CiAgICAgIDxyZWN0IHg9IjAiIHk9IjAiIHdpZHRoPSI1My4wMzUxNTYyNSIgaGVpZ2h0PSI1My4wMzUxNTYyNSIgcng9IjI2LjUxNzU3ODEyNSIgZmlsbD0id2hpdGUiLz4KICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOS4wMDc1Nzc4OTYxMTgxNjQsNikiIGZpbGw9ImJsYWNrIj4KICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgIGQ9Ik0zNi45NS0yNy43MUwzNi45NS0yNy43MUwzMS44MC0yNy43MVEyOS44Ni0zNS42NiAyMC44NC0zNS44OEwyMC44NC0zNS44OFE4LjM4LTM1LjIzIDcuOTUtMTkuOThMNy45NS0xOS45OFE3Ljk1LTMuNjUgMjEuMDUtMy42NUwyMS4wNS0zLjY1UTMwLjA4LTMuODcgMzIuMjMtMTQuNjFMMzIuMjMtMTQuNjFMMzcuMzgtMTQuNjFRMzUuMDIgMC4yMSAxOS45OCAwLjY0TDE5Ljk4IDAuNjRRMi43OSAwIDIuMzYtMTkuNzdMMi4zNi0xOS43N1EzLjIyLTM5LjUzIDIwLjg0LTQwLjM5TDIwLjg0LTQwLjM5UTM0LjgwLTM5Ljk2IDM2Ljk1LTI3LjcxWiIKICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIuMzYzMjgxMjUsIDQwLjM5MDYyNSkiLz4KICAgICAgICA8L2c+CiAgICAgIDwvZz4KICAgIDwvbWFzaz4KICAgIDxkZWZzIHYtZ3JhPSJvZCIvPgogIDwvc3ZnPgo8L3N2Zz4KCgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/logo-main.js b/frontend/crawlab-ui/src/assets/js/svg/logo-main.js
new file mode 100644
index 00000000..ec95b766
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/logo-main.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZwogIHZlcnNpb249IjEuMCIKICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgd2lkdGg9IjIyMCIKICBoZWlnaHQ9IjU2Igo+CiAgPHJlY3QKICAgIHg9IjAiCiAgICB5PSIwIgogICAgd2lkdGg9IjEwMCUiCiAgICBoZWlnaHQ9IjEwMCUiCiAgICBmaWxsPSJ0cmFuc3BhcmVudCIKICAgIGZpbGwtb3BhY2l0eT0iMSIKICAvPgogIDxzdmcKICAgIHZlcnNpb249IjEuMCIKICAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIgogICAgeD0iMCIKICAgIHk9IjAiCiAgICB3aWR0aD0iMTAwJSIKICAgIGhlaWdodD0iMTAwJSIKICAgIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIG1lZXQiCiAgICBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiCiAgPgogICAgPGRlZnM+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTIiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZmE3MWNkIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjOWI1OWI2Ii8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTMiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZjlkNDIzIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZjgzNjAwIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjMDA2NGQyIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMWNiMGY2Ii8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTUiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZjAwOTc4Ii8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjM2Y1MWIxIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTYiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjNzg3M2Y1Ii8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZWM3N2FiIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZjlkNDIzIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZTE0ZmFkIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTgiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjMDA5ZWZkIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMmFmNTk4Ii8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTkiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZmZjYzAwIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDBiMTQwIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iMTAwIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIwJSI+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Q1MTAwNyIvPgogICAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZmODE3NyIvPgogICAgICA8L2xpbmVhckdyYWRpZW50PgogICAgICA8bGluZWFyR3JhZGllbnQgaWQ9IjEwMiIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNhMmI2ZGYiLz4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMwYzM0ODMiLz4KICAgICAgPC9saW5lYXJHcmFkaWVudD4KICAgICAgPGxpbmVhckdyYWRpZW50IGlkPSIxMDMiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjN2FjNWQ4Ii8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZWVhMmEyIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iMTA0IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIwJSI+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwZWNiYyIvPgogICAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwN2FkZiIvPgogICAgICA8L2xpbmVhckdyYWRpZW50PgogICAgICA8bGluZWFyR3JhZGllbnQgaWQ9IjEwNSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNiODg3NDYiLz4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZGY1YTYiLz4KICAgICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDwvZGVmcz4KICAgIDxnIGZpbGw9IiM0MDllZmYiIGNsYXNzPSJuZXdpbml0aWFsc3ZnLWcgbmV3aW5pdGlhbHN2ZyI+CiAgICAgIDxnIGNsYXNzPSJ0cC1uYW1lIj4KICAgICAgICA8ZyBjbGFzcz0idHAtZ3JhcGgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsIDApIiBtYXNrPSJ1cmwoI2VkMjNhZDE3LTlkZjMtNGY3OS1hMjE0LTg0OTYyNzk5YTM5OSkiPgogICAgICAgICAgPHJlY3QgZmlsbD0iIzQwOWVmZiIgeD0iMCIgeT0iMCIgd2lkdGg9IjUzLjAzNTE1NjI1IiBoZWlnaHQ9IjUzLjAzNTE1NjI1IiByeD0iMjYuNTE3NTc4MTI1Ii8+CiAgICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5LjAwNzU3Nzg5NjExODE2NCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiIGZpbGw9IiNmZmYiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNMzYuOTUtMjcuNzFMMzYuOTUtMjcuNzFMMzEuODAtMjcuNzFRMjkuODYtMzUuNjYgMjAuODQtMzUuODhMMjAuODQtMzUuODhROC4zOC0zNS4yMyA3Ljk1LTE5Ljk4TDcuOTUtMTkuOThRNy45NS0zLjY1IDIxLjA1LTMuNjVMMjEuMDUtMy42NVEzMC4wOC0zLjg3IDMyLjIzLTE0LjYxTDMyLjIzLTE0LjYxTDM3LjM4LTE0LjYxUTM1LjAyIDAuMjEgMTkuOTggMC42NEwxOS45OCAwLjY0UTIuNzkgMCAyLjM2LTE5Ljc3TDIuMzYtMTkuNzdRMy4yMi0zOS41MyAyMC44NC00MC4zOUwyMC44NC00MC4zOVEzNC44MC0zOS45NiAzNi45NS0yNy43MVoiCiAgICAgICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMi4zNjMyODEyNSwgNDAuMzkwNjI1KSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU3LjAzNTE1NjI1LCAwKSI+CiAgICAgICAgICA8ZyBmaWxsPSIjNDA5ZWZmIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDYpIj4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJzY2FsZSgxKSI+CiAgICAgICAgICAgICAgPHBhdGgKICAgICAgICAgICAgICAgIGQ9Ik05LjAyLTE2Ljc2TDkuMDItMTYuNzZMOS4wMiAwTDQuMDggMEw0LjA4LTI4Ljc5TDguODEtMjguNzlMOC44MS0yMy44NVExMi4wMy0yOS42NSAxNi45Ny0yOS42NUwxNi45Ny0yOS42NVExNy40MC0yOS42NSAxNy44My0yOS40M0wxNy44My0yOS40M1ExOC4wNS0yOS40MyAxOC4yNi0yOS40M0wxOC4yNi0yOS40M0wxOC4yNi0yNC4yOEwxNi4zMy0yNC4yOFE5LjQ1LTIzLjg1IDkuMDItMTYuNzZaIgogICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQuMDgyMDMxMjUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3NS4yMTQ4NDM3NSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iIzQwOWVmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNNy45NS0xOS45OEw3Ljk1LTE5Ljk4TDMuNDQtMTkuOThRMy42NS0yOS42NSAxNS4yNS0yOS42NUwxNS4yNS0yOS42NVEyNS41Ny0yOS4yMiAyNS43OC0yMS40OEwyNS43OC0yMS40OEwyNS43OC00Ljk0UTI1Ljc4LTMuMjIgMjcuNTAtMy4yMkwyNy41MC0zLjIyUTI3LjcxLTMuMjIgMjguMTQtMy4yMkwyOC4xNC0zLjIyUTI4Ljc5LTMuNDQgMjkuMjItMy40NEwyOS4yMi0zLjQ0TDI5LjIyIDBRMjguNzkgMCAyOC4xNCAwTDI4LjE0IDBRMjcuMDcgMC4yMSAyNi40MyAwLjIxTDI2LjQzIDAuMjFRMjEuMjcgMC4yMSAyMS40OC0zLjg3TDIxLjQ4LTMuODdRMTcuMTkgMC40MyAxMC45NiAwLjQzTDEwLjk2IDAuNDNRMi4zNiAwIDEuOTMtNy41MkwxLjkzLTcuNTJRMi4xNS0xNS4yNSAxMC43NC0xNi41NEwxMC43NC0xNi41NEwxOC42OS0xNy40MFEyMS4yNy0xNy44MyAyMS4yNy0yMC44NEwyMS4yNy0yMC44NFEyMS4yNy0yNS41NyAxNC4zOS0yNS41N0wxNC4zOS0yNS41N1E4LjM4LTI1LjU3IDcuOTUtMTkuOThaTTIxLjA1LTkuODhMMjEuMDUtOS44OEwyMS4wNS0xNC42MVExOS4zNC0xMy41NCAxMy4xMS0xMi42OEwxMy4xMS0xMi42OFE3LjA5LTEyLjI1IDcuMDktOC4xNkw3LjA5LTguMTZRNy4wOS0zLjIyIDEyLjAzLTMuMjJMMTIuMDMtMy4yMlEyMC40MS0zLjg3IDIxLjA1LTkuODhaIgogICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEuOTMzNTkzNzUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDYuNSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iIzQwOWVmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNMTkuNzctMjIuMzRMMTkuNTUtMjIuMzRMMTQuMTggMEw5LjAyIDBMMC44Ni0yOC43OUw2LjAyLTI4Ljc5TDExLjM5LTYuMDJMMTEuNjAtNi4wMkwxNy4xOS0yOC43OUwyMi41Ni0yOC43OUwyOC4xNC02LjAyTDI4LjM2LTYuMDJMMzMuOTUtMjguNzlMMzguODktMjguNzlMMzAuNTEgMEwyNS41NyAwTDE5Ljc3LTIyLjM0WiIKICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0wLjg1OTM3NSwgNDAuMzkwNjI1KSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE0OC41MjczNDM3NSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iIzQwOWVmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoIGQ9Ik0zLjY1LTM5LjMyTDguNTktMzkuMzJMOC41OSAwTDMuNjUgMEwzLjY1LTM5LjMyWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMuNjUyMzQzNzUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNTcuNDY4NzUsIDApIj4KICAgICAgICAgIDxnIGZpbGw9IiM0MDllZmYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgICAgZD0iTTcuOTUtMTkuOThMNy45NS0xOS45OEwzLjQ0LTE5Ljk4UTMuNjUtMjkuNjUgMTUuMjUtMjkuNjVMMTUuMjUtMjkuNjVRMjUuNTctMjkuMjIgMjUuNzgtMjEuNDhMMjUuNzgtMjEuNDhMMjUuNzgtNC45NFEyNS43OC0zLjIyIDI3LjUwLTMuMjJMMjcuNTAtMy4yMlEyNy43MS0zLjIyIDI4LjE0LTMuMjJMMjguMTQtMy4yMlEyOC43OS0zLjQ0IDI5LjIyLTMuNDRMMjkuMjItMy40NEwyOS4yMiAwUTI4Ljc5IDAgMjguMTQgMEwyOC4xNCAwUTI3LjA3IDAuMjEgMjYuNDMgMC4yMUwyNi40MyAwLjIxUTIxLjI3IDAuMjEgMjEuNDgtMy44N0wyMS40OC0zLjg3UTE3LjE5IDAuNDMgMTAuOTYgMC40M0wxMC45NiAwLjQzUTIuMzYgMCAxLjkzLTcuNTJMMS45My03LjUyUTIuMTUtMTUuMjUgMTAuNzQtMTYuNTRMMTAuNzQtMTYuNTRMMTguNjktMTcuNDBRMjEuMjctMTcuODMgMjEuMjctMjAuODRMMjEuMjctMjAuODRRMjEuMjctMjUuNTcgMTQuMzktMjUuNTdMMTQuMzktMjUuNTdROC4zOC0yNS41NyA3Ljk1LTE5Ljk4Wk0yMS4wNS05Ljg4TDIxLjA1LTkuODhMMjEuMDUtMTQuNjFRMTkuMzQtMTMuNTQgMTMuMTEtMTIuNjhMMTMuMTEtMTIuNjhRNy4wOS0xMi4yNSA3LjA5LTguMTZMNy4wOS04LjE2UTcuMDktMy4yMiAxMi4wMy0zLjIyTDEyLjAzLTMuMjJRMjAuNDEtMy44NyAyMS4wNS05Ljg4WiIKICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xLjkzMzU5Mzc1LCA0MC4zOTA2MjUpIi8+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTg4Ljc1MzkwNjI1LCAwKSI+CiAgICAgICAgICA8ZyBmaWxsPSIjNDA5ZWZmIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDYpIj4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJzY2FsZSgxKSI+CiAgICAgICAgICAgICAgPHBhdGgKICAgICAgICAgICAgICAgIGQ9Ik03LjczLTMuNDRMNy43MyAwTDMuMjIgMEwzLjIyLTM5LjMyTDcuOTUtMzkuMzJMNy45NS0yNS4xNEw4LjE2LTI1LjE0UTEwLjc0LTI5LjQzIDE2LjMzLTI5LjY1TDE2LjMzLTI5LjY1UTI3LjcxLTI5LjAwIDI4LjM2LTE1LjI1TDI4LjM2LTE1LjI1UTI3LjcxIDAgMTUuNjggMC40M0wxNS42OCAwLjQzUTEwLjMxIDAuNDMgNy45NS0zLjQ0TDcuOTUtMy40NEw3LjczLTMuNDRaTTIzLjQyLTE0LjM5TDIzLjQyLTE0LjM5UTIzLjYzLTI1LjE0IDE1LjQ3LTI1LjE0TDE1LjQ3LTI1LjE0UTcuNzMtMjUuMTQgNy43My0xMi44OUw3LjczLTEyLjg5UTcuOTUtMy44NyAxNS42OC0zLjIyTDE1LjY4LTMuMjJRMjMuMjAtMy40NCAyMy40Mi0xNC4zOVoiCiAgICAgICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMy4yMjI2NTYyNSwgNDAuMzkwNjI1KSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICA8L2c+CiAgICA8L2c+CiAgICA8bWFzayBpZD0iZWQyM2FkMTctOWRmMy00Zjc5LWEyMTQtODQ5NjI3OTlhMzk5Ij4KICAgICAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjUzLjAzNTE1NjI1IiBoZWlnaHQ9IjUzLjAzNTE1NjI1IiByeD0iMjYuNTE3NTc4MTI1IiBmaWxsPSJ3aGl0ZSIvPgogICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5LjAwNzU3Nzg5NjExODE2NCw2KSIgZmlsbD0iYmxhY2siPgogICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgPHBhdGgKICAgICAgICAgICAgZD0iTTM2Ljk1LTI3LjcxTDM2Ljk1LTI3LjcxTDMxLjgwLTI3LjcxUTI5Ljg2LTM1LjY2IDIwLjg0LTM1Ljg4TDIwLjg0LTM1Ljg4UTguMzgtMzUuMjMgNy45NS0xOS45OEw3Ljk1LTE5Ljk4UTcuOTUtMy42NSAyMS4wNS0zLjY1TDIxLjA1LTMuNjVRMzAuMDgtMy44NyAzMi4yMy0xNC42MUwzMi4yMy0xNC42MUwzNy4zOC0xNC42MVEzNS4wMiAwLjIxIDE5Ljk4IDAuNjRMMTkuOTggMC42NFEyLjc5IDAgMi4zNi0xOS43N0wyLjM2LTE5Ljc3UTMuMjItMzkuNTMgMjAuODQtNDAuMzlMMjAuODQtNDAuMzlRMzQuODAtMzkuOTYgMzYuOTUtMjcuNzFaIgogICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMi4zNjMyODEyNSwgNDAuMzkwNjI1KSIvPgogICAgICAgIDwvZz4KICAgICAgPC9nPgogICAgPC9tYXNrPgogICAgPGRlZnMgdi1ncmE9Im9kIi8+CiAgPC9zdmc+Cjwvc3ZnPgoKCg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/logo-white.js b/frontend/crawlab-ui/src/assets/js/svg/logo-white.js
new file mode 100644
index 00000000..3abddd30
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/logo-white.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZwogIHZlcnNpb249IjEuMCIKICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiCiAgd2lkdGg9IjIyMCIKICBoZWlnaHQ9IjU2Igo+CiAgPHJlY3QKICAgIHg9IjAiCiAgICB5PSIwIgogICAgd2lkdGg9IjEwMCUiCiAgICBoZWlnaHQ9IjEwMCUiCiAgICBmaWxsPSJ0cmFuc3BhcmVudCIKICAgIGZpbGwtb3BhY2l0eT0iMSIKICAvPgogIDxzdmcKICAgIHZlcnNpb249IjEuMCIKICAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIgogICAgeD0iMCIKICAgIHk9IjAiCiAgICB3aWR0aD0iMTAwJSIKICAgIGhlaWdodD0iMTAwJSIKICAgIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIG1lZXQiCiAgICBjb2xvci1pbnRlcnBvbGF0aW9uLWZpbHRlcnM9InNSR0IiCiAgPgogICAgPGRlZnM+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTIiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZmE3MWNkIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjOWI1OWI2Ii8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTMiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZjlkNDIzIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZjgzNjAwIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTQiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjMDA2NGQyIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMWNiMGY2Ii8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTUiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZjAwOTc4Ii8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjM2Y1MWIxIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTYiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjNzg3M2Y1Ii8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZWM3N2FiIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTciIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZjlkNDIzIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZTE0ZmFkIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTgiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjMDA5ZWZkIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMmFmNTk4Ii8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iOTkiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjZmZjYzAwIi8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDBiMTQwIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iMTAwIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIwJSI+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2Q1MTAwNyIvPgogICAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2ZmODE3NyIvPgogICAgICA8L2xpbmVhckdyYWRpZW50PgogICAgICA8bGluZWFyR3JhZGllbnQgaWQ9IjEwMiIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNhMmI2ZGYiLz4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMwYzM0ODMiLz4KICAgICAgPC9saW5lYXJHcmFkaWVudD4KICAgICAgPGxpbmVhckdyYWRpZW50IGlkPSIxMDMiIHgxPSIwJSIgeTE9IjAlIiB4Mj0iMTAwJSIgeTI9IjAlIj4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjN2FjNWQ4Ii8+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjZWVhMmEyIi8+CiAgICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICAgIDxsaW5lYXJHcmFkaWVudCBpZD0iMTA0IiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIwJSI+CiAgICAgICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwZWNiYyIvPgogICAgICAgIDxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwN2FkZiIvPgogICAgICA8L2xpbmVhckdyYWRpZW50PgogICAgICA8bGluZWFyR3JhZGllbnQgaWQ9IjEwNSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgICAgIDxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNiODg3NDYiLz4KICAgICAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNmZGY1YTYiLz4KICAgICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDwvZGVmcz4KICAgIDxnIGZpbGw9IiNmZmYiIGNsYXNzPSJuZXdpbml0aWFsc3ZnLWcgbmV3aW5pdGlhbHN2ZyI+CiAgICAgIDxnIGNsYXNzPSJ0cC1uYW1lIj4KICAgICAgICA8ZyBjbGFzcz0idHAtZ3JhcGgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsIDApIiBtYXNrPSJ1cmwoI2VkMjNhZDE3LTlkZjMtNGY3OS1hMjE0LTg0OTYyNzk5YTM5OSkiPgogICAgICAgICAgPHJlY3QgZmlsbD0iI2ZmZiIgeD0iMCIgeT0iMCIgd2lkdGg9IjUzLjAzNTE1NjI1IiBoZWlnaHQ9IjUzLjAzNTE1NjI1IiByeD0iMjYuNTE3NTc4MTI1Ii8+CiAgICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5LjAwNzU3Nzg5NjExODE2NCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiIGZpbGw9IiNmZmYiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNMzYuOTUtMjcuNzFMMzYuOTUtMjcuNzFMMzEuODAtMjcuNzFRMjkuODYtMzUuNjYgMjAuODQtMzUuODhMMjAuODQtMzUuODhROC4zOC0zNS4yMyA3Ljk1LTE5Ljk4TDcuOTUtMTkuOThRNy45NS0zLjY1IDIxLjA1LTMuNjVMMjEuMDUtMy42NVEzMC4wOC0zLjg3IDMyLjIzLTE0LjYxTDMyLjIzLTE0LjYxTDM3LjM4LTE0LjYxUTM1LjAyIDAuMjEgMTkuOTggMC42NEwxOS45OCAwLjY0UTIuNzkgMCAyLjM2LTE5Ljc3TDIuMzYtMTkuNzdRMy4yMi0zOS41MyAyMC44NC00MC4zOUwyMC44NC00MC4zOVEzNC44MC0zOS45NiAzNi45NS0yNy43MVoiCiAgICAgICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMi4zNjMyODEyNSwgNDAuMzkwNjI1KSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU3LjAzNTE1NjI1LCAwKSI+CiAgICAgICAgICA8ZyBmaWxsPSIjZmZmIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDYpIj4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJzY2FsZSgxKSI+CiAgICAgICAgICAgICAgPHBhdGgKICAgICAgICAgICAgICAgIGQ9Ik05LjAyLTE2Ljc2TDkuMDItMTYuNzZMOS4wMiAwTDQuMDggMEw0LjA4LTI4Ljc5TDguODEtMjguNzlMOC44MS0yMy44NVExMi4wMy0yOS42NSAxNi45Ny0yOS42NUwxNi45Ny0yOS42NVExNy40MC0yOS42NSAxNy44My0yOS40M0wxNy44My0yOS40M1ExOC4wNS0yOS40MyAxOC4yNi0yOS40M0wxOC4yNi0yOS40M0wxOC4yNi0yNC4yOEwxNi4zMy0yNC4yOFE5LjQ1LTIzLjg1IDkuMDItMTYuNzZaIgogICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTQuMDgyMDMxMjUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3NS4yMTQ4NDM3NSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNNy45NS0xOS45OEw3Ljk1LTE5Ljk4TDMuNDQtMTkuOThRMy42NS0yOS42NSAxNS4yNS0yOS42NUwxNS4yNS0yOS42NVEyNS41Ny0yOS4yMiAyNS43OC0yMS40OEwyNS43OC0yMS40OEwyNS43OC00Ljk0UTI1Ljc4LTMuMjIgMjcuNTAtMy4yMkwyNy41MC0zLjIyUTI3LjcxLTMuMjIgMjguMTQtMy4yMkwyOC4xNC0zLjIyUTI4Ljc5LTMuNDQgMjkuMjItMy40NEwyOS4yMi0zLjQ0TDI5LjIyIDBRMjguNzkgMCAyOC4xNCAwTDI4LjE0IDBRMjcuMDcgMC4yMSAyNi40MyAwLjIxTDI2LjQzIDAuMjFRMjEuMjcgMC4yMSAyMS40OC0zLjg3TDIxLjQ4LTMuODdRMTcuMTkgMC40MyAxMC45NiAwLjQzTDEwLjk2IDAuNDNRMi4zNiAwIDEuOTMtNy41MkwxLjkzLTcuNTJRMi4xNS0xNS4yNSAxMC43NC0xNi41NEwxMC43NC0xNi41NEwxOC42OS0xNy40MFEyMS4yNy0xNy44MyAyMS4yNy0yMC44NEwyMS4yNy0yMC44NFEyMS4yNy0yNS41NyAxNC4zOS0yNS41N0wxNC4zOS0yNS41N1E4LjM4LTI1LjU3IDcuOTUtMTkuOThaTTIxLjA1LTkuODhMMjEuMDUtOS44OEwyMS4wNS0xNC42MVExOS4zNC0xMy41NCAxMy4xMS0xMi42OEwxMy4xMS0xMi42OFE3LjA5LTEyLjI1IDcuMDktOC4xNkw3LjA5LTguMTZRNy4wOS0zLjIyIDEyLjAzLTMuMjJMMTIuMDMtMy4yMlEyMC40MS0zLjg3IDIxLjA1LTkuODhaIgogICAgICAgICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTEuOTMzNTkzNzUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMDYuNSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoCiAgICAgICAgICAgICAgICBkPSJNMTkuNzctMjIuMzRMMTkuNTUtMjIuMzRMMTQuMTggMEw5LjAyIDBMMC44Ni0yOC43OUw2LjAyLTI4Ljc5TDExLjM5LTYuMDJMMTEuNjAtNi4wMkwxNy4xOS0yOC43OUwyMi41Ni0yOC43OUwyOC4xNC02LjAyTDI4LjM2LTYuMDJMMzMuOTUtMjguNzlMMzguODktMjguNzlMMzAuNTEgMEwyNS41NyAwTDE5Ljc3LTIyLjM0WiIKICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0wLjg1OTM3NSwgNDAuMzkwNjI1KSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICAgIDxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE0OC41MjczNDM3NSwgMCkiPgogICAgICAgICAgPGcgZmlsbD0iI2ZmZiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCw2KSI+CiAgICAgICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgICAgIDxwYXRoIGQ9Ik0zLjY1LTM5LjMyTDguNTktMzkuMzJMOC41OSAwTDMuNjUgMEwzLjY1LTM5LjMyWiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTMuNjUyMzQzNzUsIDQwLjM5MDYyNSkiLz4KICAgICAgICAgICAgPC9nPgogICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNTcuNDY4NzUsIDApIj4KICAgICAgICAgIDxnIGZpbGw9IiNmZmYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsNikiPgogICAgICAgICAgICA8ZyB0cmFuc2Zvcm09InNjYWxlKDEpIj4KICAgICAgICAgICAgICA8cGF0aAogICAgICAgICAgICAgICAgZD0iTTcuOTUtMTkuOThMNy45NS0xOS45OEwzLjQ0LTE5Ljk4UTMuNjUtMjkuNjUgMTUuMjUtMjkuNjVMMTUuMjUtMjkuNjVRMjUuNTctMjkuMjIgMjUuNzgtMjEuNDhMMjUuNzgtMjEuNDhMMjUuNzgtNC45NFEyNS43OC0zLjIyIDI3LjUwLTMuMjJMMjcuNTAtMy4yMlEyNy43MS0zLjIyIDI4LjE0LTMuMjJMMjguMTQtMy4yMlEyOC43OS0zLjQ0IDI5LjIyLTMuNDRMMjkuMjItMy40NEwyOS4yMiAwUTI4Ljc5IDAgMjguMTQgMEwyOC4xNCAwUTI3LjA3IDAuMjEgMjYuNDMgMC4yMUwyNi40MyAwLjIxUTIxLjI3IDAuMjEgMjEuNDgtMy44N0wyMS40OC0zLjg3UTE3LjE5IDAuNDMgMTAuOTYgMC40M0wxMC45NiAwLjQzUTIuMzYgMCAxLjkzLTcuNTJMMS45My03LjUyUTIuMTUtMTUuMjUgMTAuNzQtMTYuNTRMMTAuNzQtMTYuNTRMMTguNjktMTcuNDBRMjEuMjctMTcuODMgMjEuMjctMjAuODRMMjEuMjctMjAuODRRMjEuMjctMjUuNTcgMTQuMzktMjUuNTdMMTQuMzktMjUuNTdROC4zOC0yNS41NyA3Ljk1LTE5Ljk4Wk0yMS4wNS05Ljg4TDIxLjA1LTkuODhMMjEuMDUtMTQuNjFRMTkuMzQtMTMuNTQgMTMuMTEtMTIuNjhMMTMuMTEtMTIuNjhRNy4wOS0xMi4yNSA3LjA5LTguMTZMNy4wOS04LjE2UTcuMDktMy4yMiAxMi4wMy0zLjIyTDEyLjAzLTMuMjJRMjAuNDEtMy44NyAyMS4wNS05Ljg4WiIKICAgICAgICAgICAgICAgIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xLjkzMzU5Mzc1LCA0MC4zOTA2MjUpIi8+CiAgICAgICAgICAgIDwvZz4KICAgICAgICAgIDwvZz4KICAgICAgICA8L2c+CiAgICAgICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTg4Ljc1MzkwNjI1LCAwKSI+CiAgICAgICAgICA8ZyBmaWxsPSIjZmZmIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDYpIj4KICAgICAgICAgICAgPGcgdHJhbnNmb3JtPSJzY2FsZSgxKSI+CiAgICAgICAgICAgICAgPHBhdGgKICAgICAgICAgICAgICAgIGQ9Ik03LjczLTMuNDRMNy43MyAwTDMuMjIgMEwzLjIyLTM5LjMyTDcuOTUtMzkuMzJMNy45NS0yNS4xNEw4LjE2LTI1LjE0UTEwLjc0LTI5LjQzIDE2LjMzLTI5LjY1TDE2LjMzLTI5LjY1UTI3LjcxLTI5LjAwIDI4LjM2LTE1LjI1TDI4LjM2LTE1LjI1UTI3LjcxIDAgMTUuNjggMC40M0wxNS42OCAwLjQzUTEwLjMxIDAuNDMgNy45NS0zLjQ0TDcuOTUtMy40NEw3LjczLTMuNDRaTTIzLjQyLTE0LjM5TDIzLjQyLTE0LjM5UTIzLjYzLTI1LjE0IDE1LjQ3LTI1LjE0TDE1LjQ3LTI1LjE0UTcuNzMtMjUuMTQgNy43My0xMi44OUw3LjczLTEyLjg5UTcuOTUtMy44NyAxNS42OC0zLjIyTDE1LjY4LTMuMjJRMjMuMjAtMy40NCAyMy40Mi0xNC4zOVoiCiAgICAgICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMy4yMjI2NTYyNSwgNDAuMzkwNjI1KSIvPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgICA8L2c+CiAgICA8L2c+CiAgICA8bWFzayBpZD0iZWQyM2FkMTctOWRmMy00Zjc5LWEyMTQtODQ5NjI3OTlhMzk5Ij4KICAgICAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjUzLjAzNTE1NjI1IiBoZWlnaHQ9IjUzLjAzNTE1NjI1IiByeD0iMjYuNTE3NTc4MTI1IiBmaWxsPSJ3aGl0ZSIvPgogICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5LjAwNzU3Nzg5NjExODE2NCw2KSIgZmlsbD0iYmxhY2siPgogICAgICAgIDxnIHRyYW5zZm9ybT0ic2NhbGUoMSkiPgogICAgICAgICAgPHBhdGgKICAgICAgICAgICAgZD0iTTM2Ljk1LTI3LjcxTDM2Ljk1LTI3LjcxTDMxLjgwLTI3LjcxUTI5Ljg2LTM1LjY2IDIwLjg0LTM1Ljg4TDIwLjg0LTM1Ljg4UTguMzgtMzUuMjMgNy45NS0xOS45OEw3Ljk1LTE5Ljk4UTcuOTUtMy42NSAyMS4wNS0zLjY1TDIxLjA1LTMuNjVRMzAuMDgtMy44NyAzMi4yMy0xNC42MUwzMi4yMy0xNC42MUwzNy4zOC0xNC42MVEzNS4wMiAwLjIxIDE5Ljk4IDAuNjRMMTkuOTggMC42NFEyLjc5IDAgMi4zNi0xOS43N0wyLjM2LTE5Ljc3UTMuMjItMzkuNTMgMjAuODQtNDAuMzlMMjAuODQtNDAuMzlRMzQuODAtMzkuOTYgMzYuOTUtMjcuNzFaIgogICAgICAgICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMi4zNjMyODEyNSwgNDAuMzkwNjI1KSIvPgogICAgICAgIDwvZz4KICAgICAgPC9nPgogICAgPC9tYXNrPgogICAgPGRlZnMgdi1ncmE9Im9kIi8+CiAgPC9zdmc+Cjwvc3ZnPgoKCg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/logo.js b/frontend/crawlab-ui/src/assets/js/svg/logo.js
new file mode 100644
index 00000000..33bffbda
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/logo.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGZpbGw9Im5vbmUiPgogICAgICAgIDxjaXJjbGUgY3g9IjE1MCIgY3k9IjE1MCIgcj0iMTMwIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjQwIiBzdHJva2U9IiM0MDllZmYiPgogICAgICAgIDwvY2lyY2xlPgogICAgICAgIDxjaXJjbGUgY3g9IjE1MCIgY3k9IjE1MCIgcj0iMTEwIiBmaWxsPSJ3aGl0ZSI+CiAgICAgICAgPC9jaXJjbGU+CiAgICAgICAgPGNpcmNsZSBjeD0iMTUwIiBjeT0iMTUwIiByPSI3MCIgZmlsbD0iIzQwOWVmZiI+CiAgICAgICAgPC9jaXJjbGU+CiAgICAgICAgPHBhdGggZD0iCiAgICAgICAgICAgIE0gMTUwLDE1MAogICAgICAgICAgICBMIDI4MCwyMjUKICAgICAgICAgICAgQSAxNTAsMTUwIDkwIDAgMCAyODAsNzUKICAgICAgICAgICAgIiBmaWxsPSIjNDA5ZWZmIj4KICAgICAgICA8L3BhdGg+CiAgICA8L2c+Cjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/maven.js b/frontend/crawlab-ui/src/assets/js/svg/maven.js
new file mode 100644
index 00000000..581d39e2
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/maven.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPgo8c3ZnIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDMyIDMyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImEiIHgxPSItNzI3NC44IiB5MT0iMjA4Mi41MDciIHgyPSItNzI0My44NzIiIHkyPSIyMDQ2LjM0MyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgtMC4wMjMsIC0wLjExNiwgLTAuMTE2LCAwLjAyMywgNzkuNjgsIC04ODkuNDg0KSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2Y2OTkyMyIvPjxzdG9wIG9mZnNldD0iMC4zMTIiIHN0b3AtY29sb3I9IiNmNzlhMjMiLz48c3RvcCBvZmZzZXQ9IjAuODM4IiBzdG9wLWNvbG9yPSIjZTk3ODI2Ii8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImIiIHgxPSItNzUwMy42ODkiIHkxPSIyMDg2LjU5NSIgeDI9Ii03MjgzLjAzNiIgeTI9IjIwODYuNTk1IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjAyMywgLTAuMTE2LCAtMC4xMTYsIDAuMDIzLCA3OS42OCwgLTg4OS40ODQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwLjMyMyIgc3RvcC1jb2xvcj0iIzllMjA2NCIvPjxzdG9wIG9mZnNldD0iMC42MyIgc3RvcC1jb2xvcj0iI2M5MjAzNyIvPjxzdG9wIG9mZnNldD0iMC43NTEiIHN0b3AtY29sb3I9IiNjZDIzMzUiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNlOTc4MjYiLz48L2xpbmVhckdyYWRpZW50PjxsaW5lYXJHcmFkaWVudCBpZD0iYyIgeDE9Ii03NDc3LjA1MyIgeTE9IjIwNjQuNDIiIHgyPSItNzM0NS41NjYiIHkyPSIyMDY0LjQyIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KC0wLjAyMywgLTAuMTE2LCAtMC4xMTYsIDAuMDIzLCA3OS42OCwgLTg4OS40ODQpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMjgyNjYyIi8+PHN0b3Agb2Zmc2V0PSIwLjA5NSIgc3RvcC1jb2xvcj0iIzY2MmU4ZCIvPjxzdG9wIG9mZnNldD0iMC43ODgiIHN0b3AtY29sb3I9IiM5ZjIwNjQiLz48c3RvcCBvZmZzZXQ9IjAuOTQ5IiBzdG9wLWNvbG9yPSIjY2QyMDMyIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImQiIHgxPSItNzQ5MS4yOTYiIHkxPSIyMDg4LjYzMyIgeDI9Ii03MjcwLjY0MyIgeTI9IjIwODguNjMzIiB4bGluazpocmVmPSIjYiIvPjxsaW5lYXJHcmFkaWVudCBpZD0iZSIgeDE9Ii03NDc1LjIwMyIgeTE9IjIwODUuNjEiIHgyPSItNzM1OS4yNDQiIHkyPSIyMDg1LjYxIiB4bGluazpocmVmPSIjYyIvPjxsaW5lYXJHcmFkaWVudCBpZD0iZiIgeDE9Ii03NDkxLjI5NiIgeTE9IjIwNjUuNzk5IiB4Mj0iLTcyNzAuNjQzIiB5Mj0iMjA2NS43OTkiIHhsaW5rOmhyZWY9IiNiIi8+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iLTc1MDQuOTg0IiB5MT0iMjA2Ni45MDUiIHgyPSItNzI4NC4zMzEiIHkyPSIyMDY2LjkwNSIgeGxpbms6aHJlZj0iI2IiLz48bGluZWFyR3JhZGllbnQgaWQ9ImgiIHgxPSItNzQ5MS4yOTYiIHkxPSIyMDU4Ljk1OSIgeDI9Ii03MjcwLjY0MyIgeTI9IjIwNTguOTU5IiB4bGluazpocmVmPSIjYiIvPjxsaW5lYXJHcmFkaWVudCBpZD0iaSIgeDE9Ii03NDkxLjI5NiIgeTE9IjIwNTkuNzYyIiB4Mj0iLTcyNzAuNjQzIiB5Mj0iMjA1OS43NjIiIHhsaW5rOmhyZWY9IiNiIi8+PGxpbmVhckdyYWRpZW50IGlkPSJqIiB4MT0iLTgzMDcuNjMyIiB5MT0iMjA0MC4wMDgiIHgyPSItODI3Ni43MDQiIHkyPSIyMDAzLjg0NCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjA3NCwgLTAuMTEyLCAtMC4xMTIsIC0wLjA3NCwgODY0LjA4MywgLTc3NS41MSkiIHhsaW5rOmhyZWY9IiNhIi8+PGxpbmVhckdyYWRpZW50IGlkPSJrIiB4MT0iLTg1MzYuNTIxIiB5MT0iMjA0NC4wOTYiIHgyPSItODMxNS44NjgiIHkyPSIyMDQ0LjA5NiIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjA3NCwgLTAuMTEyLCAtMC4xMTIsIC0wLjA3NCwgODY0LjA4MywgLTc3NS41MSkiIHhsaW5rOmhyZWY9IiNiIi8+PGxpbmVhckdyYWRpZW50IGlkPSJsIiB4MT0iLTg1MDkuODg1IiB5MT0iMjAyMS45MjEiIHgyPSItODM3OC4zOTciIHkyPSIyMDIxLjkyMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjA3NCwgLTAuMTEyLCAtMC4xMTIsIC0wLjA3NCwgODY0LjA4MywgLTc3NS41MSkiIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJtIiB4MT0iLTg1MjQuMTI4IiB5MT0iMjA0Ni4xMzQiIHgyPSItODMwMy40NzUiIHkyPSIyMDQ2LjEzNCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjA3NCwgLTAuMTEyLCAtMC4xMTIsIC0wLjA3NCwgODY0LjA4MywgLTc3NS41MSkiIHhsaW5rOmhyZWY9IiNiIi8+PGxpbmVhckdyYWRpZW50IGlkPSJuIiB4MT0iLTg1MDguMDM1IiB5MT0iMjA0My4xMTEiIHgyPSItODM5Mi4wNzYiIHkyPSIyMDQzLjExMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjA3NCwgLTAuMTEyLCAtMC4xMTIsIC0wLjA3NCwgODY0LjA4MywgLTc3NS41MSkiIHhsaW5rOmhyZWY9IiNjIi8+PGxpbmVhckdyYWRpZW50IGlkPSJvIiB4MT0iLTg1MjQuMTI4IiB5MT0iMjAyMy4zIiB4Mj0iLTgzMDMuNDc1IiB5Mj0iMjAyMy4zIiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuMDc0LCAtMC4xMTIsIC0wLjExMiwgLTAuMDc0LCA4NjQuMDgzLCAtNzc1LjUxKSIgeGxpbms6aHJlZj0iI2IiLz48bGluZWFyR3JhZGllbnQgaWQ9InAiIHgxPSItODUzNy44MTYiIHkxPSIyMDI0LjQwNyIgeDI9Ii04MzE3LjE2MyIgeTI9IjIwMjQuNDA3IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDAuMDc0LCAtMC4xMTIsIC0wLjExMiwgLTAuMDc0LCA4NjQuMDgzLCAtNzc1LjUxKSIgeGxpbms6aHJlZj0iI2IiLz48bGluZWFyR3JhZGllbnQgaWQ9InEiIHgxPSItODUyNC4xMjgiIHkxPSIyMDE2LjQ2IiB4Mj0iLTgzMDMuNDc1IiB5Mj0iMjAxNi40NiIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjA3NCwgLTAuMTEyLCAtMC4xMTIsIC0wLjA3NCwgODY0LjA4MywgLTc3NS41MSkiIHhsaW5rOmhyZWY9IiNiIi8+PGxpbmVhckdyYWRpZW50IGlkPSJyIiB4MT0iLTg1MjQuMTI4IiB5MT0iMjAxNy4yNjMiIHgyPSItODMwMy40NzUiIHkyPSIyMDE3LjI2MyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgwLjA3NCwgLTAuMTEyLCAtMC4xMTIsIC0wLjA3NCwgODY0LjA4MywgLTc3NS41MSkiIHhsaW5rOmhyZWY9IiNiIi8+PC9kZWZzPjx0aXRsZT5maWxlX3R5cGVfbWF2ZW48L3RpdGxlPjxwYXRoIGQ9Ik01Ljk3NiwyLjUzM2E5LjUzOSw5LjUzOSwwLDAsMC0uNCwyLjY1NWwxLjQyNy42OTFhMTcuOTI4LDE3LjkyOCwwLDAsMSwuMS0yLjU0MWMuMDA1LS4wNTguMDExLS4wOTIuMDExLS4wOTIsMCwuMDMxLS4wMDkuMDYxLS4wMTEuMDkyQTE1LjkzOSwxNS45MzksMCwwLDAsNy4xLDUuODQ1YTI3Ljc5LDI3Ljc5LDAsMCwwLDIuMTE0LTJBMy4wNDEsMy4wNDEsMCwwLDAsNy42MjEsMi4xNDRTNi4zNTcsMS42MzQsNS45NzYsMi41MzNaIiBzdHlsZT0iZmlsbDp1cmwoI2EpIi8+PHBhdGggZD0iTTkuNjg4LDkuNzIyQS4wMTguMDE4LDAsMCwwLDkuNyw5LjcwOWwtLjA4LjA3MWMwLC4wMDctLjAwOS4wMTMtLjAxLjAxOVoiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMTAuNTYyLDEyLjE5Yy0uMDM4LjA0Ni0uMDgzLjA5MS0uMTI4LjEzNkMxMC40NzgsMTIuMjgyLDEwLjUyMywxMi4yMzYsMTAuNTYyLDEyLjE5WiIgc3R5bGU9ImZpbGw6bm9uZSIvPjxwYXRoIGQ9Ik05Ljc0MSwxOS43NzZjMC0uMDIxLS4wMTQtLjA0NC0uMDE4LS4wNjUtLjEyMi0uNDQ1LS4yNC0uODc2LS4zNTUtMS4zLS4xMjUtLjQ3NC0uMjQ0LS45MzktLjM1OS0xLjM5MS0uMTE4LS40NzMtLjIzMS0uOTM3LS4zMzYtMS4zODQtLjExMi0uNDcyLS4yMTUtLjkyNS0uMzExLTEuMzY2LS4wNzctLjM1OS0uMTUyLS43MDctLjIyMS0xLjA0Ny0uMDI0LS4xMTUtLjA0NS0uMjI1LS4wNjYtLjMzNi0uMDQyLS4yMjItLjA4Ni0uNDM2LS4xMjYtLjY0N3MtLjA3My0uMzg2LS4xMDktLjU3NWMtLjAxMS0uMDY0LS4wMjItLjEyOC0uMDM0LS4xODZMNy44LDExLjQ0NGwtLjA2OS4wNjItLjE0Mi0uMDdjMCwuMDE1LjAwNS4wMzIuMDEuMDQ3LjA0Ny4yODUuMS41NzMuMTUyLjg2My4wMy4xNjQuMDYzLjMzMi4wOTMuNS4wODguNDY1LjE4MS45MjYuMjgyLDEuMzkzcy4yLjk0LjMwOCwxLjQuMjE0LjkxNC4zMjQsMS4zNjguMjIyLjkuMzM4LDEuMzQ2Yy4xMTkuNDY2LjI0LjkyNS4zNjUsMS4zNzQuMDI2LjEuMDU1LjIwNi4wODUuMy4xLjM2Ni4yLjcyMS4zLDEuMDczbC4yMjkuMTE1LjA3Ni0uMDY4YS4wNjkuMDY5LDAsMCwwLS4wMDktLjAyOUM5Ljk5MywyMC42NjgsOS44NjUsMjAuMjE1LDkuNzQxLDE5Ljc3NloiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMTAuOTI0LDEzLjQ5NWMtLjA2Ny4wNzMtLjEzNi4xNDItLjIwNi4yMTFoMGMuMDM2LS4wMzMuMDczLS4wNzIuMTA3LS4xMDhBMS4yMTIsMS4yMTIsMCwwLDAsMTAuOTI0LDEzLjQ5NVoiIHN0eWxlPSJmaWxsOiNiZTIwMmUiLz48cGF0aCBkPSJNMTAuOTI0LDEzLjQ5NWMtLjA2Ny4wNzMtLjEzNi4xNDItLjIwNi4yMTFoMGMuMDM2LS4wMzMuMDczLS4wNzIuMTA3LS4xMDhBMS4yMTIsMS4yMTIsMCwwLDAsMTAuOTI0LDEzLjQ5NVoiIHN0eWxlPSJmaWxsOiNiZTIwMmU7b3BhY2l0eTowLjM0OTk5OTk5NDAzOTUzNTU7aXNvbGF0aW9uOmlzb2xhdGUiLz48cGF0aCBkPSJNMTAuNCwxMi4zNTZoMGMuMDEyLS4wMDkuMDIxLS4wMjEuMDMyLS4wMy4wNDUtLjA0NS4wOS0uMDkuMTI4LS4xMzYtLjA1LjA1NS0uMTA3LjEwOC0uMTYxLjE2NloiIHN0eWxlPSJmaWxsOiNiZTIwMmUiLz48cGF0aCBkPSJNMTAuNCwxMi4zNTZoMGMuMDEyLS4wMDkuMDIxLS4wMjEuMDMyLS4wMy4wNDUtLjA0NS4wOS0uMDkuMTI4LS4xMzYtLjA1LjA1NS0uMTA3LjEwOC0uMTYxLjE2NloiIHN0eWxlPSJmaWxsOiNiZTIwMmU7b3BhY2l0eTowLjM0OTk5OTk5NDAzOTUzNTU7aXNvbGF0aW9uOmlzb2xhdGUiLz48cGF0aCBkPSJNNy4zOCwxMC4wOTJxLS4xLS43MTItLjE4LTEuNGMtLjA1NS0uNDc3LS4xLS45NDUtLjEzMS0xLjQwOCwwLS4wMjgtLjAwNS0uMDU2LDAtLjA4MUM3LjAzMiw2Ljc1MSw3LjAxLDYuMzEsNyw1Ljg3OEw1LjU3NSw1LjE5MWMwLC4wODYtLjAxLjE3NS0uMDEzLjI2OS0uMDE0LjM0MS0uMDI1LjctLjAyOCwxLjA4NSwwLC40MywwLC44NzguMDA4LDEuMzQ3LjAxLjQzMi4wMjguODgyLjA1MSwxLjM0LjAyMy4zOTIuMDQ5Ljc4OC4wODEsMS4xOTIsMCwuMDE3LDAsLjAyOCwwLC4wNDZsMS45MDYuOTY1QzcuNTE3LDEwLjk4NCw3LjQ0NywxMC41MzYsNy4zOCwxMC4wOTJaIiBzdHlsZT0iZmlsbDp1cmwoI2IpIi8+PHBhdGggZD0iTTEwLjIsMjEuMzYyYy4xLjM1Ni4yMTIuNzIzLjMyOCwxLjA5NSwwLDAsMCwuMDExLDAsLjAxNS4wMTYuMDU0LjAyOS4xMDUuMDQ5LjE1Ny4wNzYuMjUxLjE0OS40NzQuMzA3Ljk4NGExLjkyNywxLjkyNywwLDAsMSwxLjA5NC4yNjIsMS41NjMsMS41NjMsMCwwLDAtMS4wNDEtLjU2NiwzLjM0OSwzLjM0OSwwLDAsMCwxLjgyNy0yLjk3N2MtLjAxMi0uMTA2LS4wMjYtLjIxNi0uMDQ3LS4zMjZhMS4zNDcsMS4zNDcsMCwwLDEtLjc2NiwxLjE1MmwwLDAsMCwwYTMuNzY2LDMuNzY2LDAsMCwwLC42MS0yLjM3M2MtLjAxNS0uMTkzLS4wNC0uNC0uMDc1LS42MTlhMy4wNTYsMy4wNTYsMCwwLDEtMS43NzIsMi40NjJsLS41NzguNTMyQzEwLjE1NywyMS4yMjcsMTAuMTc1LDIxLjI5MywxMC4yLDIxLjM2MloiIHN0eWxlPSJmaWxsOnVybCgjYykiLz48cGF0aCBkPSJNOS40NTMsMTkuNzM1Yy0uMTIxLS40NTItLjI0Mi0uOTExLS4zNjUtMS4zNzQtLjExNi0uNDQ0LS4yMjctLjg5Mi0uMzM4LTEuMzQ2cy0uMjE4LS45MTEtLjMyNC0xLjM2OC0uMjEtLjkzNC0uMzA4LTEuNC0uMTktLjkzMS0uMjgyLTEuMzkzYy0uMDMzLS4xNjgtLjA2My0uMzMyLS4wOTMtLjUtLjA1My0uMjg2LS4xLS41NzQtLjE1Mi0uODYzLDAtLjAxNS0uMDA1LS4wMzItLjAxLS4wNDdsLTEuODk1LS45NjdhLjY3OS42NzksMCwwLDEsLjAwNy4wOTFjLjAzOC40MjkuMDc1Ljg2NS4xMjYsMS4zcy4xMDYuODkuMTcyLDEuMzQxYy4wNTYuMzguMTE2Ljc1Ny4xNzksMS4xMzguMDEzLjA3NS4wMjguMTU0LjA0MS4yMjguMDg3LjQ3MS4xODEuOTI2LjI4MiwxLjM1MS4xMTMuNDgyLjIzNS45MzQuMzU3LDEuMzU2LjA4Mi4yNzYuMTYzLjU0Mi4yNDYuNzk0LjA3Mi4yMTIuMTUuNDI1LjIyNS42MzUuMTgxLjQ5My4zODIuOTcxLjYsMS40MzhsMS45MTUuOTdjLS4xLS4zNTItLjItLjcxLS4zLTEuMDczQzkuNTA4LDE5Ljk0MSw5LjQ4MiwxOS44NCw5LjQ1MywxOS43MzVaIiBzdHlsZT0iZmlsbDp1cmwoI2QpIi8+PHBhdGggZD0iTTcuOTQ3LDIwLjIwOGExNC44NiwxNC44NiwwLDAsMCwuODgxLDEuNjI3Yy4wMTEuMDE2LjAyMi4wMzguMDMzLjA1NGEzLjg2NCwzLjg2NCwwLDAsMC0xLjQ2LS4xMTMsNS4zNyw1LjM3LDAsMCwxLDIuMzUzLDEuMzcxLDIuNDY4LDIuNDY4LDAsMCwxLTEuMTEyLjU1OCwyLjQ2LDIuNDYsMCwwLDAsMS4yMi0uMTA4LDMsMywwLDAsMC0uNzQyLDEuNDQ3LDMuMDg0LDMuMDg0LDAsMCwxLDEuMjA1LTEuMjc2Yy41NzUsMS45MDYsMS4yNjcsMy45NzIsMi4wMzQsNi4xNGEuNTc3LjU3NywwLDAsMCwuMDktLjU0MWMtLjE0MS0uMzg5LTEuMDU0LTIuOTY4LTIuMDk1LTYuNDY4LS4wMy0uMS0uMDU2LS4yLS4wODktLjMtLjAwOS0uMDI5LS4wMTYtLjA1NC0uMDI1LS4wODRxLS4xNi0uNTUtLjMyNi0xLjEzMmMtLjAyNC0uMDktLjA1LS4xNzQtLjA3NC0uMjY1bDAsMC0xLjkxNS0uOTdBLjM1Ny4zNTcsMCwwLDAsNy45NDcsMjAuMjA4WiIgc3R5bGU9ImZpbGw6dXJsKCNlKSIvPjxwYXRoIGQ9Ik03LjgwNiwxMS40NzZjLjAxMS4wNjQuMDIzLjEyMS4wMzQuMTg2LjAzNy4xOS4wNy4zODIuMTA5LjU3NS4wNC4yMTEuMDg0LjQyNi4xMjYuNjQ3LjAyMS4xMTEuMDQyLjIyMi4wNjYuMzM2LjA2OS4zNC4xNDQuNjg4LjIyMSwxLjA0Ny4xLjQ0MS4yLjg5NC4zMTEsMS4zNjYuMS40NDYuMjE4LjkxMS4zMzYsMS4zODQuMTE1LjQ1MS4yMzEuOTEzLjM1OSwxLjM5MS4xMTIuNDIzLjIzMy44NTguMzU1LDEuMywwLC4wMjEuMDE0LjA0NC4wMTguMDY1LjEyMy40MzkuMjUyLjg5Mi4zODYsMS4zNTNhLjA2OS4wNjksMCwwLDAsLjAwOS4wMjlsLjU3OC0uNTMyYy0uMDE1LDAtLjAyNi4wMTMtLjA0NS4wMjFhNS41NDcsNS41NDcsMCwwLDAsMS40MDgtMy4xMTQsNy40Nyw3LjQ3LDAsMCwwLS4wMTgtMS43MzcsMTEuOTI1LDExLjkyNSwwLDAsMC0uMjc4LTEuNDg2Yy0uMTE0LS40NTgtLjI1NC0uOTQ0LS40MjUtMS40NTZhMi44OTEsMi44OTEsMCwwLDEtLjQ0MS42MzdsLS4xLjExYy0uMDMzLjAzNy0uMDcuMDY5LS4xMDcuMTA4aDBhMy4zNzcsMy4zNzcsMCwwLDAsLjQxNi0yLjUwNywzLjE3NiwzLjE3NiwwLDAsMS0uNTcyLjk5MWMtLjAzOC4wNDYtLjA4My4wOTEtLjEyOC4xMzYtLjAxMi4wMDktLjAyMS4wMjEtLjAzNi4wMzNoMGEzLjUyLDMuNTIsMCwwLDAsLjM3MS0uODE0LDEuNTA5LDEuNTA5LDAsMCwwLC4wNDMtLjE3Yy4wMi0uMDg3LjAzNC0uMTc1LjA0Ny0uMjYzLjAwNS0uMDU4LjAxNC0uMTEyLjAxOS0uMTcxYTIuNDYxLDIuNDYxLDAsMCwwLDAtLjQxM2MwLS4wNDYsMC0uMDg3LS4wMTEtLjEzLS4wMTEtLjA2NC0uMDE5LS4xMjQtLjAzMS0uMTgyLS4wNDYtLjI2Ny0uMS0uNS0uMTQ5LS43LS4wMjYtLjEtLjA1LS4xOTItLjA3Ni0uMjc1LS4wMTItLjAzMy0uMDE4LS4wNjUtLjAzLS4xLS4wMzEtLjA5MS0uMDU2LS4xNzUtLjA4NC0uMjQ1YTIuNDUyLDIuNDUyLDAsMCwwLS4xMDUtLjI0MmgwYS41MzIuNTMyLDAsMCwxLS4wNDIuMTE1LDMuNzQ5LDMuNzQ5LDAsMCwxLS42NS45MzRsLjUzMy0uNDg3TDkuNjg3LDkuN2EuMDE4LjAxOCwwLDAsMC0uMDA5LjAxM0ExLjA4MiwxLjA4MiwwLDAsMCw5LjYsOS44YzAtLjAwNy4wMDktLjAxMy4wMS0uMDE5TDcuNzksMTEuNDQ2QzcuODA1LDExLjQ1OSw3LjgwNywxMS40NjksNy44MDYsMTEuNDc2WiIgc3R5bGU9ImZpbGw6dXJsKCNmKSIvPjxwYXRoIGQ9Ik03LjEsNS44NDVjLjAyMi40LjA1Ny44NDguMSwxLjM0NGEuNDg1LjQ4NSwwLDAsMCwuMDA5LjA3OGMuMDQxLjQzMy4wOTQuOS4xNjIsMS40MDkuMDU4LjQzOS4xMjIuOS4yLDEuNC4wNjUuNDMzLjE0NS44ODYuMjI3LDEuMzY3bDEuODItMS42NjdhMi45ODcsMi45ODcsMCwwLDAsLjQ1Ny0xLjY0N2MwLS4xNDMtLjAxMS0uMjkzLS4wMjItLjQ0N0ExNC4zMjQsMTQuMzI0LDAsMCwwLDkuODk1LDYuMjVhMTIuNDYsMTIuNDYsMCwwLDAtLjI3MS0xLjI4Nyw2LjgyNSw2LjgyNSwwLDAsMC0uMjA5LS42NTNjLS4wNjUtLjE2My0uMTMzLS4zMTItLjItLjQ1NEEyOC44LDI4LjgsMCwwLDEsNy4xLDUuODQ1WiIgc3R5bGU9ImZpbGw6dXJsKCNnKSIvPjxwYXRoIGQ9Ik0xMC44MjcsMTMuNmMtLjAzMy4wMzctLjA3LjA2OS0uMTA3LjEwOGgwQTEuMzkxLDEuMzkxLDAsMCwxLDEwLjgyNywxMy42WiIgc3R5bGU9ImZpbGw6I2JlMjAyZSIvPjxwYXRoIGQ9Ik0xMC44MjcsMTMuNmMtLjAzMy4wMzctLjA3LjA2OS0uMTA3LjEwOGgwQTEuMzkxLDEuMzkxLDAsMCwxLDEwLjgyNywxMy42WiIgc3R5bGU9ImZpbGw6I2JlMjAyZTtvcGFjaXR5OjAuMzQ5OTk5OTk0MDM5NTM1NTtpc29sYXRpb246aXNvbGF0ZSIvPjxwYXRoIGQ9Ik0xMC44MjcsMTMuNmMtLjAzMy4wMzctLjA3LjA2OS0uMTA3LjEwOGgwQTEuMzkxLDEuMzkxLDAsMCwxLDEwLjgyNywxMy42WiIgc3R5bGU9ImZpbGw6dXJsKCNoKSIvPjxwYXRoIGQ9Ik0xMC40LDEyLjM1OWMuMDEyLS4wMDkuMDIxLS4wMjEuMDM2LS4wMzMtLjAxMi4wMDktLjAyMS4wMjEtLjAzNi4wMzNaIiBzdHlsZT0iZmlsbDojYmUyMDJlIi8+PHBhdGggZD0iTTEwLjQsMTIuMzU5Yy4wMTItLjAwOS4wMjEtLjAyMS4wMzYtLjAzMy0uMDEyLjAwOS0uMDIxLjAyMS0uMDM2LjAzM1oiIHN0eWxlPSJmaWxsOiNiZTIwMmU7b3BhY2l0eTowLjM0OTk5OTk5NDAzOTUzNTU7aXNvbGF0aW9uOmlzb2xhdGUiLz48cGF0aCBkPSJNMTAuNCwxMi4zNTljLjAxMi0uMDA5LjAyMS0uMDIxLjAzNi0uMDMzLS4wMTIuMDA5LS4wMjEuMDIxLS4wMzYuMDMzWiIgc3R5bGU9ImZpbGw6dXJsKCNpKSIvPjxwYXRoIGQ9Ik0yNC42MjcsMi44OTFBMTAuNzg5LDEwLjc4OSwwLDAsMCwyMi4yLDQuNzEzbC42LDEuNjlhMjAuMjc4LDIwLjI3OCwwLDAsMSwyLjEtMS45NjVjLjA1MS0uMDQyLjA4Mi0uMDY2LjA4Mi0uMDY2LS4wMjYuMDI0LS4wNTYuMDQyLS4wODIuMDY2YTE4LjAyOCwxOC4wMjgsMCwwLDAtMiwyLjAxMywzMS40MzIsMzEuNDMyLDAsMCwwLDMuMjg4LjA3MiwzLjQ0LDMuNDQsMCwwLDAsLjA3MS0yLjYzOVMyNS42NDcsMi40NywyNC42MjcsMi44OTFaIiBzdHlsZT0iZmlsbDp1cmwoI2opIi8+PHBhdGggZD0iTTIxLjksMTEuNjI3YS4wMi4wMiwwLDAsMCwuMDE3LDBsLS4xMjEtLjAwN2MtLjAwNiwwLS4wMTcsMC0uMDIzLjAwOFoiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMjAuNjQ1LDE0LjMwOGMtLjA2OC4wMDctLjE0LjAwNy0uMjEyLjAwOEMyMC41MDYsMTQuMzE1LDIwLjU3OCwxNC4zMTUsMjAuNjQ1LDE0LjMwOFoiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMTMuOTU4LDE5Ljc2M2MuMDE0LS4wMi4wMjMtLjA0Ni4wMzctLjA2Ny4yNTUtLjQ1Ni41LS45Ljc1LTEuMzMxLjI3Ni0uNDgxLjU1LS45NS44MTYtMS40LjI4LS40NzQuNTU5LS45MzguODI5LTEuMzgxLjI4NS0uNDY4LjU2MS0uOTE1LjgzNS0xLjM0Ni4yMjMtLjM1LjQzOS0uNjg5LjY1NC0xLjAxOC4wNzItLjExMS4xNDMtLjIxNy4yMTQtLjMyMy4xNDItLjIxMi4yNzgtLjQxOS40MTMtLjYyMS4xMjctLjE4Ni4yNDgtLjM2OC4zNjktLjU1LjA0Mi0uMDYuMDg0LS4xMjEuMTItLjE3NmwuMDIxLS4wMy0uMSwwLS4wNTgtLjE2OWMtLjAwOC4wMTUtLjAyMS4wMy0uMDI5LjA0Ni0uMTg5LjI2Ni0uMzc4LjUzOC0uNTYzLjgxNS0uMTA2LjE1Ni0uMjEzLjMxOC0uMzI1LjQ3OS0uMy40NDQtLjU5MS44ODktLjg3OSwxLjM0NnMtLjU4NC45MTgtLjg2NywxLjM3NS0uNTU0LjkwNS0uODI2LDEuMzU5LS41MzcuOS0uOCwxLjM1MmMtLjI3NC40Ny0uNTQyLjkzNS0uOCwxLjQtLjA1OS4xLS4xMTkuMjEtLjE3My4zMTMtLjIxLjM3NC0uNDEzLjczOC0uNjExLDEuMWwuMDkzLjI3NC4xMTYuMDA2YS4wNzguMDc4LDAsMCwwLC4wMTYtLjAzMUMxMy40NTIsMjAuNjgxLDEzLjcwOSwyMC4yMTQsMTMuOTU4LDE5Ljc2M1oiIHN0eWxlPSJmaWxsOm5vbmUiLz48cGF0aCBkPSJNMTkuOSwxNS42NDZjLS4xMTIuMDA2LS4yMjMuMDA2LS4zMzQuMDA2aDBjLjA1NSwwLC4xMTYsMCwuMTcyLDBBMS4zNzEsMS4zNzEsMCwwLDAsMTkuOSwxNS42NDZaIiBzdHlsZT0iZmlsbDojYmUyMDJlIi8+PHBhdGggZD0iTTE5LjksMTUuNjQ2Yy0uMTEyLjAwNi0uMjIzLjAwNi0uMzM0LjAwNmgwYy4wNTUsMCwuMTE2LDAsLjE3MiwwQTEuMzcxLDEuMzcxLDAsMCwwLDE5LjksMTUuNjQ2WiIgc3R5bGU9ImZpbGw6I2JlMjAyZTtvcGFjaXR5OjAuMzQ5OTk5OTk0MDM5NTM1NTtpc29sYXRpb246aXNvbGF0ZSIvPjxwYXRoIGQ9Ik0yMC4zODQsMTQuMzE0aDBjLjAxNiwwLC4wMzMsMCwuMDUsMCwuMDcyLDAsLjE0NCwwLC4yMTItLjAwOC0uMDg0LDAtLjE3MiwwLS4yNjEuMDA2WiIgc3R5bGU9ImZpbGw6I2JlMjAyZSIvPjxwYXRoIGQ9Ik0yMC4zODQsMTQuMzE0aDBjLjAxNiwwLC4wMzMsMCwuMDUsMCwuMDcyLDAsLjE0NCwwLC4yMTItLjAwOC0uMDg0LDAtLjE3MiwwLS4yNjEuMDA2WiIgc3R5bGU9ImZpbGw6I2JlMjAyZTtvcGFjaXR5OjAuMzQ5OTk5OTk0MDM5NTM1NTtpc29sYXRpb246aXNvbGF0ZSIvPjxwYXRoIGQ9Ik0xOS43NTEsMTAuMDkxcS40ODYtLjY1Mi45NjgtMS4yNzJjLjMzNC0uNDI4LjY3Mi0uODM4LDEuMDEzLTEuMjM3LjAyLS4wMjUuMDQxLS4wNDkuMDYtLjA2OS4zMzQtLjM4OS42NjYtLjc2MSwxLTEuMTE3bC0uNi0xLjY4NWMtLjA3MS4wNjctLjE0Ny4xMzQtLjIyNC4yMDUtLjI4My4yNjMtLjU3OS41NDctLjg4NC44NTEtLjM0NC4zNDMtLjcuNzA3LTEuMDY0LDEuMDkxLS4zMzUuMzU2LS42NzguNzMyLTEuMDIzLDEuMTE5LS4yOTMuMzM0LS41ODcuNjczLS44ODIsMS4wMjRsLS4wMzQuMDM5Ljc2NywyLjI5MUMxOS4xNTMsMTAuOTE4LDE5LjQ1MiwxMC41LDE5Ljc1MSwxMC4wOTFaIiBzdHlsZT0iZmlsbDp1cmwoI2spIi8+PHBhdGggZD0iTTEzLjA2MywyMS40Yy0uMi4zNy0uNC43NS0uNjA1LDEuMTQyLDAsLjAwNS0uMDA3LjAxLS4wMDguMDE1LS4wMy4wNTctLjA2LjEwOC0uMDg1LjE2NS0uMTM4LjI2Mi0uMjU2LjUtLjUzNCwxLjAzNmEyLjE4LDIuMTgsMCwwLDEsLjY3MywxLjA4LDEuNzY4LDEuNzY4LDAsMCwwLS4zODgtMS4yODJjMS41NTguMywyLjk3My4xLDMuODM2LS45NDUuMDc1LS4wOTQuMTUtLjE5NC4yMjEtLjNhMS41MjQsMS41MjQsMCwwLDEtMS41MzIuMzE4aDBBNC4yNiw0LjI2LDAsMCwwLDE3LjAxNywyMS4yYy4xNDEtLjE2OC4yODQtLjM1Mi40MzEtLjU1OGEzLjQ1NiwzLjQ1NiwwLDAsMS0zLjM4My41NzRsLS44ODgtLjAzMUMxMy4xMzksMjEuMjYxLDEzLjEsMjEuMzI4LDEzLjA2MywyMS40WiIgc3R5bGU9ImZpbGw6dXJsKCNsKSIvPjxwYXRoIGQ9Ik0xMy43NTgsMTkuNWMuMjYyLS40NjEuNTI5LS45MjYuOC0xLjQuMjYtLjQ1LjUyNS0uOS44LTEuMzUycy41NDktLjkwNi44MjYtMS4zNTkuNTczLS45MTkuODY3LTEuMzc1LjU4Ny0uOS44NzktMS4zNDZjLjEwNy0uMTYyLjIxMy0uMzE4LjMyNS0uNDc5LjE4NC0uMjcyLjM3NC0uNTQ0LjU2My0uODE1LjAwOC0uMDE1LjAyMS0uMDMuMDI5LS4wNDZsLS43NTctMi4yODRhLjc2OC43NjgsMCwwLDEtLjA2Ny4wNzljLS4zMS4zNzYtLjYyNi43NTYtLjkzNCwxLjE0OXMtLjYyMi44LS45MjcsMS4yMTdjLS4yNTcuMzUtLjUwOC43LS43NiwxLjA1OC0uMDQ5LjA3LS4xLjE0Ni0uMTQ4LjIxNy0uMy40NDktLjU5MS44ODktLjg0NywxLjMxMi0uMjkyLjQ3OC0uNTU0LjkzOS0uNzg5LDEuMzc1LS4xNTMuMjg4LS4zLjU2Ni0uNDMzLjgzNS0uMTExLjIyOC0uMjE3LjQ2MS0uMzIzLjY5LS4yNDYuNTQtLjQ2NCwxLjA4NS0uNjYxLDEuNjMzbC43NzEsMi4zYy4yLS4zNjUuNDA2LS43MjguNjExLTEuMUMxMy42MzgsMTkuNzEsMTMuNywxOS42MDgsMTMuNzU4LDE5LjVaIiBzdHlsZT0iZmlsbDp1cmwoI20pIi8+PHBhdGggZD0iTTEyLjE3LDE4LjY4NmExNi44MDgsMTYuODA4LDAsMCwwLS41ODMsMi4wMWMwLC4wMjItLjAxMy4wNDgtLjAxNi4wN2E0LjM3LDQuMzcsMCwwLDAtMS4wODYtMS4yNTEsNi4wNzQsNi4wNzQsMCwwLDEsLjgwNSwyLjk3MywyLjc5MiwyLjc5MiwwLDAsMS0xLjMzOS0uNDM1LDIuNzgyLDIuNzgyLDAsMCwwLDEuMDY4Ljg4MywzLjQsMy40LDAsMCwwLTEuNzQ3LjU3NSwzLjQ4OCwzLjQ4OCwwLDAsMSwxLjk4NC0uMDY5QzEwLjIsMjUuNDMyLDkuMTIsMjcuNjQ1LDguMDE1LDMwYS42NTIuNjUyLDAsMCwwLC41LS4zNjRjLjItLjQyNiwxLjUxLTMuMjI2LDMuNDUzLTYuODcyLjA1NC0uMS4xMTMtLjIwNS4xNjgtLjMxNC4wMTYtLjAzMS4wMy0uMDU3LjA0Ni0uMDg3cS4zMDgtLjU3LjYzNy0xLjE3Yy4wNTItLjA5Mi4xLS4xOC4xNTEtLjI3MnYtLjAwNWwtLjc3MS0yLjNBLjQuNCwwLDAsMCwxMi4xNywxOC42ODZaIiBzdHlsZT0iZmlsbDp1cmwoI24pIi8+PHBhdGggZD0iTTE4Ljk5NCwxMS41NDRjLS4wNDIuMDYtLjA3OC4xMTYtLjEyLjE3Ni0uMTIxLjE4Mi0uMjQ3LjM2My0uMzY5LjU1cy0uMjcxLjQwOS0uNDEzLjYyMWMtLjA3MS4xMDYtLjE0Mi4yMTItLjIxNC4zMjMtLjIxNS4zMjktLjQzMS42NjktLjY1NCwxLjAxOC0uMjc0LjQzMS0uNTUuODc4LS44MzUsMS4zNDYtLjI3LjQ0My0uNTQ5LjkwNi0uODI5LDEuMzgxLS4yNjYuNDU0LS41MzkuOTE5LS44MTYsMS40LS4yNDYuNDMtLjQ5NC44NzUtLjc1LDEuMzMxLS4wMTQuMDItLjAyMy4wNDYtLjAzNy4wNjctLjI0OS40NTEtLjUwNi45MTgtLjc2NSwxLjRhLjA3OC4wNzgsMCwwLDAtLjAxNi4wMzFsLjg4OC4wMzFjLS4wMTUtLjAwOC0uMDMyLS4wMS0uMDUzLS4wMTlhNi4yNzQsNi4yNzQsMCwwLDAsMy42MDctMS4zODlBOC40NDksOC40NDksMCwwLDAsMTguOTg2LDE4LjRhMTMuNDg3LDEzLjQ4NywwLDAsMCwuOTU2LTEuNDE3Yy4yNzItLjQ1OS41NDYtLjk2Mi44MTUtMS41MWEzLjI2OSwzLjI2OSwwLDAsMS0uODYxLjE2MmwtLjE2OC4wMDljLS4wNTYsMC0uMTExLDAtLjE3MiwwaDBhMy44MiwzLjgyLDAsMCwwLDIuMzI2LTEuNjg4LDMuNTkyLDMuNTkyLDAsMCwxLTEuMjQ4LjM0NGMtLjA2OC4wMDctLjE0LjAwNy0uMjEyLjAwOC0uMDE2LDAtLjAzMywwLS4wNTUsMGgwYTMuOTgxLDMuOTgxLDAsMCwwLC45NDUtLjM2LDEuNzA3LDEuNzA3LDAsMCwwLC4xNy0uMWMuMDg2LS4wNTQuMTY2LS4xMTQuMjQ3LS4xNzQuMDUxLS4wNDIuMS0uMDguMTUxLS4xMjJhMi43ODQsMi43ODQsMCwwLDAsLjMyNi0uMzM1Yy4wMzQtLjAzOS4wNjYtLjA3My4wOTQtLjExNC4wNDItLjA2LjA4My0uMTE1LjExOS0uMTcxLjE3NS0uMjUxLjMyLS40OC40MzktLjY4NC4wNTktLjEuMTEyLS4xOTQuMTU4LS4yODIuMDE2LS4wMzYuMDM3LS4wNjcuMDU0LS4xLjA0OC0uMS4wOTQtLjE4Ni4xMjctLjI2NGEyLjc3MywyLjc3MywwLDAsMCwuMTA3LS4yNzhoMGEuNi42LDAsMCwxLS4xMjUuMDU5LDQuMjQsNC4yNCwwLDAsMS0xLjI2NS4yMzZsLjgxNi4wMzEtLjgxNi0uMDMxYS4wMi4wMiwwLDAsMC0uMDE3LDAsMS4yMjQsMS4yMjQsMCwwLDAtLjEyOCwwYy4wMDYsMCwuMDE3LDAsLjAyMy0uMDA4bC0yLjc5LS4xQzE5LjAwNywxMS41MjksMTksMTEuNTM5LDE4Ljk5NCwxMS41NDRaIiBzdHlsZT0iZmlsbDp1cmwoI28pIi8+PHBhdGggZD0iTTIyLjksNi40NTJjLS4zLjMzOS0uNjI4LjcyOC0uOTg2LDEuMTY0YS41NDguNTQ4LDAsMCwwLS4wNTUuMDdjLS4zMTEuMzgxLS42MzguOC0uOTg5LDEuMjYzLS4zLjQtLjYyLjgyNC0uOTU0LDEuMjktLjI5Mi40LS41ODcuODI5LS45LDEuMjgxbDIuNzkuMWEzLjM3OSwzLjM3OSwwLDAsMCwxLjY3Ny0uOTYyYy4xMTEtLjExNy4yMjQtLjI0NC4zMzgtLjM3N0ExNi4yLDE2LjIsMCwwLDAsMjQuODI4LDlhMTQuMDkzLDE0LjA5MywwLDAsMCwuOC0xLjI1MSw3LjcyLDcuNzIsMCwwLDAsLjM1MS0uNjkxYy4wNzctLjE4My4xNDEtLjM1Ni4yLS41MjVBMzIuNTgsMzIuNTgsMCwwLDEsMjIuOSw2LjQ1MloiIHN0eWxlPSJmaWxsOnVybCgjcCkiLz48cGF0aCBkPSJNMTkuNzM4LDE1LjY1NWMtLjA1NiwwLS4xMTEsMC0uMTcyLDBoMEExLjU3MywxLjU3MywwLDAsMSwxOS43MzgsMTUuNjU1WiIgc3R5bGU9ImZpbGw6I2JlMjAyZSIvPjxwYXRoIGQ9Ik0xOS43MzgsMTUuNjU1Yy0uMDU2LDAtLjExMSwwLS4xNzIsMGgwQTEuNTczLDEuNTczLDAsMCwxLDE5LjczOCwxNS42NTVaIiBzdHlsZT0iZmlsbDojYmUyMDJlO29wYWNpdHk6MC4zNDk5OTk5OTQwMzk1MzU1O2lzb2xhdGlvbjppc29sYXRlIi8+PHBhdGggZD0iTTE5LjczOCwxNS42NTVjLS4wNTYsMC0uMTExLDAtLjE3MiwwaDBBMS41NzMsMS41NzMsMCwwLDEsMTkuNzM4LDE1LjY1NVoiIHN0eWxlPSJmaWxsOnVybCgjcSkiLz48cGF0aCBkPSJNMjAuMzc4LDE0LjMxM2MuMDE2LDAsLjAzMywwLC4wNTUsMC0uMDE2LDAtLjAzMywwLS4wNTUsMFoiIHN0eWxlPSJmaWxsOiNiZTIwMmUiLz48cGF0aCBkPSJNMjAuMzc4LDE0LjMxM2MuMDE2LDAsLjAzMywwLC4wNTUsMC0uMDE2LDAtLjAzMywwLS4wNTUsMFoiIHN0eWxlPSJmaWxsOiNiZTIwMmU7b3BhY2l0eTowLjM0OTk5OTk5NDAzOTUzNTU7aXNvbGF0aW9uOmlzb2xhdGUiLz48cGF0aCBkPSJNMjAuMzc4LDE0LjMxM2MuMDE2LDAsLjAzMywwLC4wNTUsMC0uMDE2LDAtLjAzMywwLS4wNTUsMFoiIHN0eWxlPSJmaWxsOnVybCgjcikiLz48L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/mongodb.js b/frontend/crawlab-ui/src/assets/js/svg/mongodb.js
new file mode 100644
index 00000000..c3ac619b
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/mongodb.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzIyOTI2ODkzNjA5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQyODUiCiAgICAgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPgogIDxwYXRoCiAgICBkPSJNNzMzLjAwNzM3IDQwNi4xNjg2MDNjLTUzLjg5OTk0Ny0yMzcuMjY5NzY4LTE4MS4wMzE4MjMtMzE1LjIzNjY5Mi0xOTQuNjk4ODEtMzQ1LjA2OTY2My0xNS4wMTA5ODUtMjEuMDI1OTc5LTMxLjI2NTk2OS02MS4wOTk5NC0zMS4yNjU5NjktNjEuMDk5OTQtMC4xIDAuNzk5OTk5LTAuMTYgMS4zMTE5OTktMC4yMjQgMi4wNzk5OTh2MC41NDM5OTloLTAuMDMyYTE1LjM3MDk4NSAxNS4zNzA5ODUgMCAwIDAtMC4xNiAxLjY2Mzk5OXYwLjYzOTk5OWgtMC4xYzAgMC40MTYtMC4xIDAuNzY3OTk5LTAuMSAxLjExOTk5OXYxLjExOTk5OWgtMC4xMjhhNS4zMDY5OTUgNS4zMDY5OTUgMCAwIDEtMC4xMjggMS4wNTU5OTl2MC44OTk5OTloLTAuMDk5OTk5YTIuNzU1OTk3IDIuNzU1OTk3IDAgMCAxLTAuMSAwLjg5OTk5OXYwLjg2Mzk5OWgtMC4xYTEwLjMwNDk5IDEwLjMwNDk5IDAgMCAxLTAuMSAxLjM3NTk5OXYwLjFjLTAuMTI4IDAuNzM1OTk5LTAuMjU2IDEuNDM5OTk5LTAuMzg0IDIuMTQzOTk4djAuMzUxOTk5aC0wLjFhMi45OTk5OTcgMi45OTk5OTcgMCAwIDAtMC4xMjggMC41MTJ2MC43Mjc5OTloLTAuMTI3OTk5djAuOTI3OTk5aC0wLjIyNHYwLjc3MmgtMC4yMjR2MC44OTk5OTloLTAuMTZ2MC43OTk5OTloLTAuMTZ2MC43MzU5OTloLTAuMjU1OTk5djAuNjA4aC0wLjE2djAuNzY3OTk5aC0wLjE2djAuNjA3OTk5aC0wLjIyNHYwLjU0NGgtMC4xMjh2MC42Mzk5OTloLTAuMTZjLTAuMDMyIDAuMDMyLTAuMDMyIDAuMTI4LTAuMDMyIDAuMTZ2MC40MTZoLTAuMTI3OTk5YTAuNzA1OTk5IDAuNzA1OTk5IDAgMCAwLTAuMDMyIDAuMjU1OTk5djAuMjU2aC0wLjFhMy41NjY5OTcgMy41NjY5OTcgMCAwIDAtMC4xIDAuNDE2Yy0wLjEyOCAwLjI4OC0wLjI4OCAwLjYwNzk5OS0wLjQxNiAwLjg5OTk5OXYwLjFjLTAuMSAwLjEtMC4xNiAwLjIyNC0wLjIyNCAwLjI4Nzk5OXYwLjM1MmgtMC4xNTk5OTl2MC4zNTJoLTAuMjI0djAuMzUxOTk5aC0wLjEyOHYwLjQxNmgtMC4yNTZ2MC42MDc5OTloLTAuMTZ2MC4xNmgtMC4xNnYwLjM1MmgtMC4xNTk5OTl2MC40Nzk5OTloLTAuMTZ2MC4zNTJoLTAuMjU2djAuNDhoLTAuMTZ2MC4zNTE5OTloLTAuMjI0djAuMzUyaC0wLjEyNzk5OXYwLjQxNmgtMC4yMjR2MC4zNTE5OTloLTAuMTZ2MC4yNTZoLTAuMTZ2MC4zNTJoLTAuMjU2djAuMzgzOTk5aC0wLjE2djAuMjU2aC0wLjIyMzk5OXYwLjM1MmgtMC4xNnYwLjQ3OTk5OWgtMC4yMjR2MC4xNmgtMC4xMjh2MC4zNTJoLTAuMjI4djAuMTU5OTk5aC0wLjE1OTk5OXYwLjQxNmgtMC4xNnYwLjE2aC0wLjE2djAuMzUyaC0wLjIydjAuMjQ3OTk5aC0wLjEyOGwtMC4xIDAuMTZ2MC4xNmgtMC4xYy0wLjAzMiAwLjEtMC4xIDAuMS0wLjEgMC4xNnYwLjAzMmgtMC4wMzE5OTlhMC42MTg5OTkgMC42MTg5OTkgMCAwIDEtMC4xNiAwLjI4OHYwLjEyNzk5OWgtMC4wMzJhNS4zMzU5OTUgNS4zMzU5OTUgMCAwIDAtMC41MTIgMC43Njh2MC4wMzJhMi40NTk5OTggMi40NTk5OTggMCAwIDAtMC4zODM5OTkgMC40MTU5OTl2MC4xaC0wLjAzMmMtMC4wMzIgMC4wMzItMC4xMjggMC4xLTAuMTI4IDAuMTI4djAuMTI4aC0wLjA5MmwtMC4xMjggMC4xMjh2MC4wMzJoLTAuMDMyYzAgMC4wMzItMC4xIDAuMS0wLjEyOCAwLjE2djAuMTU5OTk5aC0wLjEyOGwtMC4xIDAuMXYwLjFoLTAuMWMwIDAuMS0wLjEgMC4xLTAuMDk5OTk5IDAuMTI4djAuMTI4aC0wLjE2YTAuMTcyIDAuMTcyIDAgMCAxLTAuMSAwLjEyOHYwLjI1NmgtMC4xMjh2MC4xNTk5OTloLTAuMTZ2MC4yNTZoLTAuMTZ2MC4zNTJoLTAuMjI0di0wLjM1MmgtMC4yMjM5OTl2MC4xNmgtMC4xNnYwLjI1NmgtMC4yMjR2MC4zNTE5OTloLTAuMjI0djAuMTZoLTAuMTZ2MC4yNTZoLTAuMTU5OTk5djAuMTZoLTAuMTZ2MC4zNTJoLTAuMjR2MC4xNTk5OTloLTAuMTZ2MC4yNTZoLTAuMjI0djAuMTZoLTAuMTZ2MC4yMjRoLTAuMjIzOTk5djAuNDE1OTk5aC0wLjF2MC4xNmgtMC4yNTZ2MC4yMjRoLTAuMTZ2MC4xaC0wLjE2djAuMTZoLTAuMjIzOTk5djAuNDE1OTk5aC0wLjE2djAuMTZoLTAuMjI0djAuMTZoLTAuMTZ2MC4yNTZoLTAuMjI0djAuMTZoLTAuMjIzOTk5djAuMTU5OTk5aC0wLjE2djAuMTZoLTAuMTZ2MC40MTZoLTAuMTZ2MC4yMjRoLTAuMjU2djAuMTZoLTAuMTZ2MC4xNTk5OTloLTAuMjIzOTk5djAuMjU2aC0wLjE2djAuMTZoLTAuMjI0djAuMjg4aC0wLjE2djAuMTZoLTAuMjU2djAuMjU1OTk5aC0wLjA5OTk5OXYwLjE2aC0wLjE2djAuMTZoLTAuMjI0djAuMTZoLTAuMTZ2MC4yNTZoLTAuMjI0djAuMTU5OTk5aC0wLjEyOGEwLjE1OCAwLjE1OCAwIDAgMC0wLjAzMiAwLjF2MC4xaC0wLjA5OTk5OWwtMC4xNiAwLjE2YTAuODc1OTk5IDAuODc1OTk5IDAgMCAxLTAuMTYgMC4xMjh2MC4yNTZoLTAuMTZ2MC4yMjM5OTloLTAuMTZ2MC4xNmgtMC4xNnYwLjEzMmgtMC4xMjhsLTAuMTI3OTk5IDAuMTI4djAuMTI4aC0wLjFsLTAuMSAwLjF2MC4xMjhoLTAuMWEzLjg0NTk5NiAzLjg0NTk5NiAwIDAgMS0wLjYwOCAwLjY3MTk5OWMtMC4xIDAuMS0wLjM1MiAwLjI4OC0wLjUxMTk5OSAwLjQxNmExNS43NTg5ODUgMTUuNzU4OTg1IDAgMCAwLTEuNjYzOTk4IDEuMzc1OTk4IDcuODMzOTkyIDcuODMzOTkyIDAgMCAwLTAuOTI4IDAuNzM2djAuMDMyaC0wLjAzMTk5OWEyMzYuMzc5NzY5IDIzNi4zNzk3NjkgMCAwIDEtMi4wNzk5OTggMS42NjM5OTh2MC4wMzJjLTEuMDIzOTk5IDAuODYzOTk5LTIuMDE1OTk4IDEuNjYzOTk4LTMuMTY4OTk3IDIuNjU2OTk3di0wLjAzMmgtMC4xYy0yLjQzMjk5OCAyLjAxNTk5OC00Ljk5Mjk5NSA0LjI1Njk5Ni03LjkzNzk5MyA2Ljc4NDk5NHYwLjAzMmgtMC4wMDVjLTcuMTk5OTkzIDYuMzA0OTk0LTE1Ljc3ODk4NSAxNC40MzQ5ODYtMjUuMzgxOTc1IDI0LjIyODk3NmwtMC43OTk5OTkgMC43OTk5OTlDMzg0LjAzNDcxMSAxNDguOTI4ODU1IDI5Mi41NTg4IDI3NC4wNDM3MzIgMjgyLjkyNDgwOSA0NzYuMjYzNTM1YTQ0Mi4xMzg1NjggNDQyLjEzODU2OCAwIDAgMCAwLjI1NiA0OC44MTA5NTJ2MC4zODRjNC42NDA5OTUgNzkuNjY0OTIyIDI5LjY2OTk3MSAxNDcuNzExODU2IDYwLjk0MDk0MSAyMDIuOTU1ODAydjAuMDMyYTUzMC4wMTk0ODIgNTMwLjAxOTQ4MiAwIDAgMCAzOS41Mjg5NjEgNTkuOTUxOTQxdjAuMDMyYzQ3LjAxNzk1NCA2Mi4wOTM5MzkgOTUuMDI4OTA3IDk4Ljg2OTkwMyAxMDcuMjg2ODk1IDEwNy43OTk4OTUgMTguODE5OTgyIDQzLjY1Nzk1NyAxNy4wNTk5ODMgMTE4LjU4NTg4NCAxNy4wNTk5ODQgMTE4LjU4NTg4NGwyNy40OTM5NzMgOS4xODE5OTFzLTUuNTk5OTk1LTcyLjU5MTkyOSAyLjI3Mjk5OC0xMDcuNjM5ODk1YzIuNDMyOTk4LTEwLjk3Nzk4OSA4LjE5Mzk5Mi0yMC4zMjM5OCAxNC44ODI5ODUtMjguMjYxOTcyYTM1OC4yOTI2NSAzNTguMjkyNjUgMCAwIDAgMzQuMDIyOTY3LTI3LjUyNTk3M2MwLjc2Nzk5OS0wLjc5OTk5OSAxLjE4Mzk5OS0xLjUzNTk5OSAxLjg4Nzk5OC0yLjI5OTk5OCA2NC45MDk5MzctNjAuNTI0OTQxIDE4Ni4xNTI4MTgtMjA5LjU0OTc5NSAxNDQuNTc1ODU5LTQ1Mi4zMjI1NTh6IgogICAgZmlsbD0iIzQ3QTI0OCIgcC1pZD0iNDI4NiI+PC9wYXRoPgo8L3N2Zz4K`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/ms_teams.js b/frontend/crawlab-ui/src/assets/js/svg/ms_teams.js
new file mode 100644
index 00000000..6b203990
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/ms_teams.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMjI4LjgzMyAyMDczLjMzMyI+CiAgPHBhdGggZmlsbD0iIzUwNTlDOSIgZD0iTTE1NTQuNjM3LDc3Ny41aDU3NS43MTNjNTQuMzkxLDAsOTguNDgzLDQ0LjA5Miw5OC40ODMsOTguNDgzYzAsMCwwLDAsMCwwdjUyNC4zOTgJYzAsMTk5LjkwMS0xNjIuMDUxLDM2MS45NTItMzYxLjk1MiwzNjEuOTUyaDBoLTEuNzExYy0xOTkuOTAxLDAuMDI4LTM2MS45NzUtMTYyLTM2Mi4wMDQtMzYxLjkwMWMwLTAuMDE3LDAtMC4wMzQsMC0wLjA1MlY4MjguOTcxCUMxNTAzLjE2Nyw4MDAuNTQ0LDE1MjYuMjExLDc3Ny41LDE1NTQuNjM3LDc3Ny41TDE1NTQuNjM3LDc3Ny41eiIvPgogIDxjaXJjbGUgZmlsbD0iIzUwNTlDOSIgY3g9IjE5NDMuNzUiIGN5PSI0NDAuNTgzIiByPSIyMzMuMjUiLz4KICA8Y2lyY2xlIGZpbGw9IiM3QjgzRUIiIGN4PSIxMjE4LjA4MyIgY3k9IjMzNi45MTciIHI9IjMzNi45MTciLz4KICA8cGF0aCBmaWxsPSIjN0I4M0VCIiBkPSJNMTY2Ny4zMjMsNzc3LjVINzE3LjAxYy01My43NDMsMS4zMy05Ni4yNTcsNDUuOTMxLTk1LjAxLDk5LjY3NnY1OTguMTA1CWMtNy41MDUsMzIyLjUxOSwyNDcuNjU3LDU5MC4xNiw1NzAuMTY3LDU5OC4wNTNjMzIyLjUxLTcuODkzLDU3Ny42NzEtMjc1LjUzNCw1NzAuMTY3LTU5OC4wNTNWODc3LjE3NglDMTc2My41NzksODIzLjQzMSwxNzIxLjA2Niw3NzguODMsMTY2Ny4zMjMsNzc3LjV6Ii8+CiAgPHBhdGggb3BhY2l0eT0iLjEiIGQ9Ik0xMjQ0LDc3Ny41djgzOC4xNDVjLTAuMjU4LDM4LjQzNS0yMy41NDksNzIuOTY0LTU5LjA5LDg3LjU5OAljLTExLjMxNiw0Ljc4Ny0yMy40NzgsNy4yNTQtMzUuNzY1LDcuMjU3SDY2Ny42MTNjLTYuNzM4LTE3LjEwNS0xMi45NTgtMzQuMjEtMTguMTQyLTUxLjgzMwljLTE4LjE0NC01OS40NzctMjcuNDAyLTEyMS4zMDctMjcuNDcyLTE4My40OVY4NzcuMDJjLTEuMjQ2LTUzLjY1OSw0MS4xOTgtOTguMTksOTQuODU1LTk5LjUySDEyNDR6Ii8+CiAgPHBhdGggb3BhY2l0eT0iLjIiIGQ9Ik0xMTkyLjE2Nyw3NzcuNXY4ODkuOTc4Yy0wLjAwMiwxMi4yODctMi40NywyNC40NDktNy4yNTcsMzUuNzY1CWMtMTQuNjM0LDM1LjU0MS00OS4xNjMsNTguODMzLTg3LjU5OCw1OS4wOUg2OTEuOTc1Yy04LjgxMi0xNy4xMDUtMTcuMTA1LTM0LjIxLTI0LjM2Mi01MS44MzMJYy03LjI1Ny0xNy42MjMtMTIuOTU4LTM0LjIxLTE4LjE0Mi01MS44MzNjLTE4LjE0NC01OS40NzYtMjcuNDAyLTEyMS4zMDctMjcuNDcyLTE4My40OVY4NzcuMDIJYy0xLjI0Ni01My42NTksNDEuMTk4LTk4LjE5LDk0Ljg1NS05OS41MkgxMTkyLjE2N3oiLz4KICA8cGF0aCBvcGFjaXR5PSIuMiIgZD0iTTExOTIuMTY3LDc3Ny41djc4Ni4zMTJjLTAuMzk1LDUyLjIyMy00Mi42MzIsOTQuNDYtOTQuODU1LDk0Ljg1NWgtNDQ3Ljg0CWMtMTguMTQ0LTU5LjQ3Ni0yNy40MDItMTIxLjMwNy0yNy40NzItMTgzLjQ5Vjg3Ny4wMmMtMS4yNDYtNTMuNjU5LDQxLjE5OC05OC4xOSw5NC44NTUtOTkuNTJIMTE5Mi4xNjd6Ii8+CiAgPHBhdGggb3BhY2l0eT0iLjIiIGQ9Ik0xMTQwLjMzMyw3NzcuNXY3ODYuMzEyYy0wLjM5NSw1Mi4yMjMtNDIuNjMyLDk0LjQ2LTk0Ljg1NSw5NC44NTVINjQ5LjQ3MgljLTE4LjE0NC01OS40NzYtMjcuNDAyLTEyMS4zMDctMjcuNDcyLTE4My40OVY4NzcuMDJjLTEuMjQ2LTUzLjY1OSw0MS4xOTgtOTguMTksOTQuODU1LTk5LjUySDExNDAuMzMzeiIvPgogIDxwYXRoIG9wYWNpdHk9Ii4xIiBkPSJNMTI0NCw1MDkuNTIydjE2My4yNzVjLTguODEyLDAuNTE4LTE3LjEwNSwxLjAzNy0yNS45MTcsMS4wMzcJYy04LjgxMiwwLTE3LjEwNS0wLjUxOC0yNS45MTctMS4wMzdjLTE3LjQ5Ni0xLjE2MS0zNC44NDgtMy45MzctNTEuODMzLTguMjkzYy0xMDQuOTYzLTI0Ljg1Ny0xOTEuNjc5LTk4LjQ2OS0yMzMuMjUtMTk4LjAwMwljLTcuMTUzLTE2LjcxNS0xMi43MDYtMzQuMDcxLTE2LjU4Ny01MS44MzNoMjU4LjY0OEMxMjAxLjQ0OSw0MTQuODY2LDEyNDMuODAxLDQ1Ny4yMTcsMTI0NCw1MDkuNTIyeiIvPgogIDxwYXRoIG9wYWNpdHk9Ii4yIiBkPSJNMTE5Mi4xNjcsNTYxLjM1NXYxMTEuNDQyYy0xNy40OTYtMS4xNjEtMzQuODQ4LTMuOTM3LTUxLjgzMy04LjI5MwljLTEwNC45NjMtMjQuODU3LTE5MS42NzktOTguNDY5LTIzMy4yNS0xOTguMDAzaDE5MC4yMjhDMTE0OS42MTYsNDY2LjY5OSwxMTkxLjk2OCw1MDkuMDUxLDExOTIuMTY3LDU2MS4zNTV6Ii8+CiAgPHBhdGggb3BhY2l0eT0iLjIiIGQ9Ik0xMTkyLjE2Nyw1NjEuMzU1djExMS40NDJjLTE3LjQ5Ni0xLjE2MS0zNC44NDgtMy45MzctNTEuODMzLTguMjkzCWMtMTA0Ljk2My0yNC44NTctMTkxLjY3OS05OC40NjktMjMzLjI1LTE5OC4wMDNoMTkwLjIyOEMxMTQ5LjYxNiw0NjYuNjk5LDExOTEuOTY4LDUwOS4wNTEsMTE5Mi4xNjcsNTYxLjM1NXoiLz4KICA8cGF0aCBvcGFjaXR5PSIuMiIgZD0iTTExNDAuMzMzLDU2MS4zNTV2MTAzLjE0OGMtMTA0Ljk2My0yNC44NTctMTkxLjY3OS05OC40NjktMjMzLjI1LTE5OC4wMDMJaDEzOC4zOTVDMTA5Ny43ODMsNDY2LjY5OSwxMTQwLjEzNCw1MDkuMDUxLDExNDAuMzMzLDU2MS4zNTV6Ii8+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJhIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjE5OC4wOTkiIHkxPSIxNjgzLjA3MjYiIHgyPSI5NDIuMjM0NCIgeTI9IjM5NC4yNjA3IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDAgMjA3NS4zMzMzKSI+CiAgICA8c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiM1YTYyYzMiLz4KICAgIDxzdG9wIG9mZnNldD0iLjUiIHN0b3AtY29sb3I9IiM0ZDU1YmQiLz4KICAgIDxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzM5NDBhYiIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHBhdGggZmlsbD0idXJsKCNhKSIgZD0iTTk1LjAxLDQ2Ni41aDk1MC4zMTJjNTIuNDczLDAsOTUuMDEsNDIuNTM4LDk1LjAxLDk1LjAxdjk1MC4zMTJjMCw1Mi40NzMtNDIuNTM4LDk1LjAxLTk1LjAxLDk1LjAxCUg5NS4wMWMtNTIuNDczLDAtOTUuMDEtNDIuNTM4LTk1LjAxLTk1LjAxVjU2MS41MUMwLDUwOS4wMzgsNDIuNTM4LDQ2Ni41LDk1LjAxLDQ2Ni41eiIvPgogIDxwYXRoIGZpbGw9IiNGRkYiIGQ9Ik04MjAuMjExLDgyOC4xOTNINjMwLjI0MXY1MTcuMjk3SDUwOS4yMTFWODI4LjE5M0gzMjAuMTIzVjcyNy44NDRoNTAwLjA4OFY4MjguMTkzeiIvPgo8L3N2Zz4K`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/mssql.js b/frontend/crawlab-ui/src/assets/js/svg/mssql.js
new file mode 100644
index 00000000..cb1dc46f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/mssql.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzIyOTI3MzI2MjIyIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijg2NDEiCiAgICAgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPgogIDxwYXRoCiAgICBkPSJNNjEwLjk4NyAyMDMuMDkzQzU5Ny4zMzMgMTY4Ljk2IDU0Ny44NCA1NC42MTMgNTEwLjI5MyA0MC45NmMtNS4xMi0xLjcwNy0xMC4yNC0xLjcwNy0xNS4zNi0xLjcwNy01Mi45MDYgMC0xNzIuMzczIDU0LjYxNC0yMjYuOTg2IDkwLjQ1NC0zLjQxNCAxLjcwNi01LjEyIDYuODI2LTUuMTIgMTEuOTQ2IDEuNzA2IDUuMTIgNS4xMiA2LjgyNyAxMC4yNCA2LjgyNyA0Mi42NjYgMS43MDcgMTg5LjQ0IDguNTMzIDMyNy42OCA2Ni41NmgzLjQxM2MzLjQxMyAwIDUuMTItMS43MDcgNi44MjctMy40MTN2LTguNTM0ek01MDMuNDY3IDU2LjMyYzEwLjI0IDMuNDEzIDI1LjYgMjAuNDggMzkuMjUzIDQ2LjA4TDM4NS43MDcgODUuMzMzYzQyLjY2Ni0xNy4wNjYgODMuNjI2LTMwLjcyIDEwNy41Mi0zMC43MiA1LjEyIDAgOC41MzMgMS43MDcgMTAuMjQgMS43MDd6IG0xNS4zNiA1OS43MzNjLTM1Ljg0IDEzLjY1NC02Ni41NiAyNy4zMDctODAuMjE0IDMyLjQyNy0xMS45NDYtMjAuNDgtMjcuMzA2LTM1Ljg0LTQyLjY2Ni00Ni4wOGwxMjIuODggMTMuNjUzeiBtLTE2My44NC0xNy4wNjZjOC41MzMgMy40MTMgNDAuOTYgMTUuMzYgNjQuODUzIDQ3Ljc4Ni01NC42MTMtMTAuMjQtMTAwLjY5My0xMy42NTMtMTI5LjcwNy0xNS4zNiAxOC43NzQtOC41MzMgNDAuOTYtMjAuNDggNjQuODU0LTMyLjQyNnogbTEwNy41MiA1Ni4zMmMyMC40OC0xMC4yNCA1Ni4zMi0yMy44OTQgOTIuMTYtMzUuODQgMTEuOTQ2IDIyLjE4NiAyMy44OTMgNDcuNzg2IDM1Ljg0IDczLjM4Ni00NC4zNzQtMTUuMzYtODguNzQ3LTI5LjAxMy0xMjgtMzcuNTQ2eiIKICAgIGZpbGw9IiM1MTgyRTQiIHAtaWQ9Ijg2NDIiPjwvcGF0aD4KICA8cGF0aAogICAgZD0iTTY2NS42IDQxOC4xMzNsLTMuNDEzLTE1LjM2LTEzLjY1NCA4LjUzNGMtMS43MDYgMS43MDYtMTY3LjI1MyAxMTAuOTMzLTM4OS4xMiAyMzIuMTA2bC0xLjcwNiAxLjcwNy0xLjcwNyAxLjcwN2MtMjMuODkzIDI3LjMwNi0xMzkuOTQ3IDE2NS41NDYtMTE5LjQ2NyAyMTguNDUzIDIwLjQ4IDUxLjIgMTkyLjg1NCAxMjEuMTczIDMzNC41MDcgMTIxLjE3MyA0Mi42NjcgMCA4MS45Mi02LjgyNiAxMTQuMzQ3LTE4Ljc3MyAxNy4wNjYtNi44MjcgMzIuNDI2LTIwLjQ4IDQyLjY2Ni0zNy41NDcgMjcuMzA3LTU2LjMyIDg4Ljc0Ny0yMTMuMzMzIDM3LjU0Ny01MTJ6IG0tMTguNzczIDIwLjQ4YzguNTMzIDQ5LjQ5NCAxMy42NTMgOTMuODY3IDE1LjM2IDEzNi41MzQtMzcuNTQ3IDE3LjA2Ni03My4zODcgMzIuNDI2LTEwNy41MiA0Ni4wOCAyMC40OC01OS43MzQgMzcuNTQ2LTExNC4zNDcgNDcuNzg2LTE1My42IDIwLjQ4LTEzLjY1NCAzNC4xMzQtMjIuMTg3IDQ0LjM3NC0yOS4wMTR6TTU3OC41NiA0ODEuMjhjLTExLjk0NyA0MC45Ni0yNy4zMDcgOTIuMTYtNDcuNzg3IDE0OC40OC0yMi4xODYtMjIuMTg3LTQ0LjM3My00Ny43ODctNjQuODUzLTgwLjIxMyA0NC4zNzMtMjUuNiA4MS45Mi00OS40OTQgMTEyLjY0LTY4LjI2N3pNNDc0LjQ1MyA4MjIuNjEzYzMuNDE0IDEuNzA3IDguNTM0IDMuNDE0IDExLjk0NyA1LjEyIDQ2LjA4IDE4Ljc3NCA5Mi4xNiAyNy4zMDcgMTI2LjI5MyAzMi40MjctNjEuNDQgMzIuNDI3LTE0NS4wNjYgNTkuNzMzLTIwOC4yMTMgNzguNTA3IDI1LjYtMzIuNDI3IDQ3Ljc4Ny03My4zODcgNjkuOTczLTExNi4wNTR6IG0tMTAuMjQtMjUuNkM0MTkuODQgNzc0LjgyNyAzODQgNzQ3LjUyIDM1NC45ODcgNzE1LjA5M2M0Ni4wOC0xNS4zNiAxMDAuNjkzLTM0LjEzMyAxNjcuMjUzLTYxLjQ0LTE1LjM2IDQ3Ljc4Ny0zNS44NCA5Ny4yOC01OC4wMjcgMTQzLjM2eiBtNzUuMDk0LTEzMy4xMmMxOC43NzMgMTUuMzYgMzUuODQgMjUuNiA1MS4yIDM1Ljg0IDE4Ljc3MyAxMC4yNCAzNS44NCAxOC43NzQgNTEuMiAyMy44OTRDNTgzLjY4IDc1MC45MzMgNTMyLjQ4IDc3My4xMiA0ODYuNCA3OTEuODkzYzIwLjQ4LTQwLjk2IDM3LjU0Ny04NS4zMzMgNTIuOTA3LTEyOHpNNDUwLjU2IDU1OS43ODdjMTguNzczIDMyLjQyNiAzOS4yNTMgNTguMDI2IDYxLjQ0IDc4LjUwNi02NC44NTMgMjUuNi0xMTcuNzYgNDYuMDgtMTYyLjEzMyA1OS43MzQgMjIuMTg2LTM1Ljg0IDQ2LjA4LTc1LjA5NCA3My4zODYtMTIyLjg4IDguNTM0LTUuMTIgMTguNzc0LTEwLjI0IDI3LjMwNy0xNS4zNnogbS01OS43MzMgMzQuMTMzYy0yMC40OCAzNC4xMzMtMzkuMjU0IDY0Ljg1My01Ni4zMiA5Mi4xNi04LjUzNC0xNS4zNi0xNy4wNjctMzAuNzItMjMuODk0LTQ2LjA4IDI3LjMwNy0xNS4zNiA1NC42MTQtMzAuNzIgODAuMjE0LTQ2LjA4ek0yNzEuMzYgNjYwLjQ4YzguNTMzLTMuNDEzIDE1LjM2LTguNTMzIDIyLjE4Ny0xMS45NDcgOC41MzMgMTguNzc0IDE3LjA2NiAzNy41NDcgMjkuMDEzIDU0LjYxNCAwIDEuNzA2LTEuNzA3IDEuNzA2LTEuNzA3IDMuNDEzLTQ2LjA4IDEzLjY1My04MC4yMTMgMjIuMTg3LTEwNS44MTMgMjUuNiAxOC43NzMtMjUuNiAzOS4yNTMtNDkuNDkzIDU2LjMyLTcxLjY4eiBtLTg1LjMzMyAyMjEuODY3Yy0xNS4zNi0xMC4yNC0yNS42LTIwLjQ4LTI5LjAxNC0yNy4zMDctNi44MjYtMTcuMDY3IDEzLjY1NC01OC4wMjcgNDQuMzc0LTEwMi40IDIzLjg5My0zLjQxMyA1OC4wMjYtOC41MzMgMTA0LjEwNi0yMi4xODctNTguMDI2IDg4Ljc0Ny05Ny4yOCAxMzEuNDE0LTExOS40NjYgMTUxLjg5NHogbTE0OC40OC0xNjAuNDI3YzEuNzA2IDAgMy40MTMgMCAzLjQxMy0xLjcwNyAyOS4wMTMgMzUuODQgNjQuODUzIDY0Ljg1NCAxMDkuMjI3IDg3LjA0LTExNy43NiA0Ny43ODctMTkyLjg1NCA2OC4yNjctMjM3LjIyNyA3OC41MDcgMjUuNi0yNS42IDY2LjU2LTc1LjA5MyAxMjQuNTg3LTE2My44NHpNMzU4LjQgOTUwLjYxM2MtNTIuOTA3LTExLjk0Ni0xMDAuNjkzLTI5LjAxMy0xMzYuNTMzLTQ3Ljc4NiA0Ni4wOC0xMC4yNCAxMTkuNDY2LTMyLjQyNyAyMjguNjkzLTc1LjA5NC0yMy44OTMgNDYuMDgtNDkuNDkzIDg4Ljc0Ny03Ni44IDExNy43NmwtMTUuMzYgNS4xMnogbTI0OS4xNzMtMzIuNDI2Yy02LjgyNiAxMy42NTMtMTcuMDY2IDIyLjE4Ni0zMC43MiAyNy4zMDYtMzAuNzIgMTEuOTQ3LTY2LjU2IDE3LjA2Ny0xMDUuODEzIDE3LjA2Ny0yMy44OTMgMC00Ni4wOC0xLjcwNy02OS45NzMtNS4xMiA2Ni41Ni0xOC43NzMgMTYwLjQyNi00OS40OTMgMjI2Ljk4Ni04Ny4wNC02LjgyNiAyMi4xODctMTMuNjUzIDM3LjU0Ny0yMC40OCA0Ny43ODd6IG0zMC43Mi03NS4wOTRjLTM0LjEzMy0zLjQxMy04OC43NDYtMTEuOTQ2LTE0My4zNi0zNC4xMzMgNDcuNzg3LTIwLjQ4IDEwMi40LTQ0LjM3MyAxNjUuNTQ3LTczLjM4Ny02LjgyNyA0Mi42NjctMTMuNjUzIDc4LjUwNy0yMi4xODcgMTA3LjUyeiBtMjIuMTg3LTEzNC44MjZjLTMwLjcyLTEwLjI0LTczLjM4Ny0yOS4wMTQtMTE0LjM0Ny02My4xNDcgMzUuODQtMTUuMzYgNzMuMzg3LTMwLjcyIDExNi4wNTQtNDkuNDkzIDEuNzA2IDQwLjk2IDEuNzA2IDc4LjUwNi0xLjcwNyAxMTIuNjR6IG0tNTEuMi0zNjYuOTM0Yy0zNS44NC0yMy44OTMtNzMuMzg3LTM3LjU0Ni0xMDcuNTItNDYuMDgtNDkuNDkzIDM0LjEzNC0xMDAuNjkzIDgwLjIxNC0xNDEuNjUzIDE0Ni43NzQtOC41MzQgMjAuNDgtMTguNzc0IDQ0LjM3My0yOS4wMTQgNjYuNTYgNjEuNDQtNDYuMDggMTYwLjQyNy0xMTQuMzQ3IDI3OC4xODctMTY3LjI1NHogbTE4Ljc3My0xMjYuMjkzYy01Ni4zMi0xNy4wNjctMTEyLjY0LTMwLjcyLTE2My44NC00MC45Ni02NC44NTMgMjUuNi05My44NjYgNjkuOTczLTEwNS44MTMgOTcuMjggMTUuMzYtMS43MDcgNzEuNjgtMTAuMjQgMTM5Ljk0NyA1LjEyIDQ5LjQ5My0zMi40MjcgOTUuNTczLTUxLjIgMTI5LjcwNi02MS40NHogbS0yNTAuODggMTYzLjg0YzAgMS43MDcgMCA1LjEyLTEuNzA2IDguNTMzIDMwLjcyLTM5LjI1MyA2Ni41Ni03MS42OCAxMDAuNjkzLTk3LjI4LTUyLjkwNy04LjUzMy05Ny4yOC01LjEyLTExMi42NC0xLjcwNiAxMS45NDcgMjkuMDEzIDE3LjA2NyA1OS43MzMgMTMuNjUzIDkwLjQ1M3ogbTQyNC45Ni0xMDAuNjkzYy00NC4zNzMtMjAuNDgtOTMuODY2LTM3LjU0Ny0xNDEuNjUzLTUyLjkwNy0yNy4zMDcgNi44MjctODAuMjEzIDIzLjg5My0xMzQuODI3IDU4LjAyNyAzNC4xMzQgMTAuMjQgNzEuNjggMjUuNiAxMDUuODE0IDUxLjIgNTIuOTA2LTIzLjg5NCAxMTAuOTMzLTQ0LjM3NCAxNzAuNjY2LTU2LjMyeiBtODUuMzM0IDQyLjY2NmMtMTcuMDY3LTEwLjI0LTM1Ljg0LTIwLjQ4LTU2LjMyLTMwLjcyLTYzLjE0NyAxMS45NDctMTI2LjI5NCAzMi40MjctMTg0LjMyIDU2LjMyIDE3LjA2NiAxNS4zNiAzNC4xMzMgMzIuNDI3IDUxLjIgNTIuOTA3IDkwLjQ1My0zNy41NDcgMTYyLjEzMy01OS43MzMgMTg3LjczMy02OC4yNjcgMy40MTMtMS43MDYgNS4xMi02LjgyNiAxLjcwNy0xMC4yNHogbS01MzkuMzA3LTcxLjY4YzEzLjY1My0yNS42IDM5LjI1My01OC4wMjYgODUuMzMzLTgxLjkyLTcxLjY4LTEzLjY1My0xMzEuNDEzLTIzLjg5My0xNjAuNDI2LTI3LjMwNi01LjEyIDAtOC41MzQgNS4xMi0zLjQxNCA4LjUzMyAyOS4wMTQgMjUuNiA1OC4wMjcgNTkuNzMzIDc4LjUwNyAxMDAuNjkzeiIKICAgIGZpbGw9IiM1MTgyRTQiIHAtaWQ9Ijg2NDMiPjwvcGF0aD4KICA8cGF0aAogICAgZD0iTTYyOC4wNTMgMzU0Ljk4N0M0ODEuMjggNDE5Ljg0IDM2My41MiA1MDYuODggMzEwLjYxMyA1NDkuNTQ3Yy0yMC40OCAzNy41NDYtNDIuNjY2IDcxLjY4LTU4LjAyNiA5NS41NzMtMy40MTQgNS4xMiAzLjQxMyAxMC4yNCA4LjUzMyA2LjgyNyAxMjYuMjkzLTEwNy41MiAyOTAuMTMzLTE5MS4xNDcgNDE5Ljg0LTI0NS43Ni0xNy4wNjctMjAuNDgtMzUuODQtMzcuNTQ3LTUyLjkwNy01MS4yeiIKICAgIGZpbGw9IiM1MTgyRTQiIHAtaWQ9Ijg2NDQiPjwvcGF0aD4KPC9zdmc+Cg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/mysql.js b/frontend/crawlab-ui/src/assets/js/svg/mysql.js
new file mode 100644
index 00000000..83eaa5b6
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/mysql.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzIyOTI3MjM5ODYxIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjYyODUiCiAgICAgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPgogIDxwYXRoCiAgICBkPSJNNzgwLjMyMjEzMyAwSDI0My42OTQ5MzNDMTA5LjEyNDI2NyAwIDAgMTA5LjEwNzIgMCAyNDMuNjc3ODY3djUzNi42NDQyNjZDMCA5MTQuODkyOCAxMDkuMTI0MjY3IDEwMjQgMjQzLjY5NDkzMyAxMDI0aDUzNi42MTAxMzRDOTE0Ljg5MjggMTAyNCAxMDI0IDkxNC44OTI4IDEwMjQgNzgwLjMzOTJWMjQzLjY3Nzg2N0MxMDI0IDEwOS4xMDcyIDkxNC44OTI4IDAgNzgwLjMyMjEzMyAweiBtLTEyOC40Nzc4NjYgNjk5LjEzNmMtMzAuMTIyNjY3LTE3Ljc0OTMzMy0xNS45OTE0NjctNDAuMDM4NCA0LjQ1NDQtNDguNTIwNTMzIDIxLjUyMTA2Ny04Ljk3NzA2NyA1NC44MTgxMzMtMTIuNDU4NjY3IDEwNC40NjUwNjYtMTMuNTE2OGgwLjc2OGMtMTkuOTUwOTMzLTE5LjY2MDgtNTYuMDgxMDY3LTM3LjcxNzMzMy0xMzAuNDIzNDY2LTYzLjQxOTczNGEyNy41MTE0NjcgMjcuNTExNDY3IDAgMCAxLTEyLjU3ODEzNC04Ljg3NDY2NmMtMTUuMzc3MDY3LTE5LjMxOTQ2Ny0yMy40MzI1MzMtMzguMDU4NjY3LTMzLjQ2NzczMy03MS4zMDQ1MzQtMjIuNzY2OTMzLTc1LjY1NjUzMy0yOC4zNDc3MzMtODQuMzA5MzMzLTc1LjQzNDY2Ny0xMzYuMjYwMjY2LTU4Ljc3NzYtNjQuODAyMTMzLTEyOC44ODc0NjctOTYuMDY4MjY3LTIxMi4wMzYyNjYtOTQuODU2NTM0YTI3LjY5OTIgMjcuNjk5MiAwIDAgMS0xMy40NjU2LTMuMjU5NzMzIDc1Ny4zMTYyNjcgNzU3LjMxNjI2NyAwIDAgMC00Ni43OTY4LTIzLjQzMjUzM2MxLjIxMTczMyAxLjc5MiAyLjQ1NzYgMy41NjY5MzMgMy42ODY0IDUuMzQxODY2IDI2Ljg2MjkzMyAzOS4wOTk3MzMgMzUuMDg5MDY3IDU1LjI0NDggMjcuNTYyNjY2IDc3LjQ4MjY2Ny0zLjQxMzMzMyAxMC4xNTQ2NjcgMzAuMDAzMiA3Mi40NjUwNjcgNTcuMDM2OCA5My4wODE2IDguMTc0OTMzIDYuMjI5MzMzIDEyLjIwMjY2NyAxNi40NjkzMzMgMTAuNDc4OTM0IDI2LjU4OTg2Ny0xMC41ODEzMzMgNjEuNzY0MjY3LTEwLjQ3ODkzMyAxNDAuOTM2NTMzIDEuMDA2OTMzIDE4Ny4yNzI1MzMgMS45MTE0NjctNS4zOTMwNjcgMy44NC0xMS40Njg4IDUuOTU2MjY3LTE4LjA5MDY2NyAyLjk2OTYtOS43MTA5MzMgMTMuNDk5NzMzLTQ2Ljg4MjEzMyAxNS42NTAxMzMtNTMuMjY1MDY2IDEuMzE0MTMzLTMuOTc2NTMzIDIuNDc0NjY3LTcuMTY4IDMuODU3MDY3LTkuOTMyOCAxLjQ1MDY2Ny0yLjg1MDEzMyAxLjQ1MDY2Ny0yLjg1MDEzMyA0LjY5MzMzMy03LjM4OTg2NyA1Ljg1Mzg2Ny03LjE2OCA1Ljg1Mzg2Ny03LjE2OCAyNy45NTUyLTkuNjQyNjY3IDE3LjkzNzA2NyAxMC40MTA2NjcgMTcuOTM3MDY3IDEwLjQxMDY2NyAyMC41MTQxMzMgMTguMTQxODY3IDEuNTAxODY3IDUuMDY4OCAxNC4zNTMwNjcgODYuOTcxNzMzIDc2LjY0NjQgMTQ2LjYzNjggMTAuOTM5NzMzIDEwLjQ2MTg2NyA0NS4zMjkwNjcgNDkuODAwNTMzIDU4LjA3Nzg2NyA3MC42OTAxMzMtOS40Mzc4NjcgMTEuOTYzNzMzLTE0MS4xNDEzMzMtODguMDEyOC0xNjEuMjQ1ODY3LTExOS4wMDU4NjYtNS41MTI1MzMgMTUuNDI4MjY3LTEwLjgyMDI2NyAyNy41NjI2NjctMTYuNzA4MjY2IDM2Ljk0OTMzMy0xNy4xMTc4NjcgMjcuMTM2LTQ3Ljk0MDI2NyAzNS42NjkzMzMtNjguOTQ5MzM0IDUuNDk1NDY3LTMxLjYwNzQ2Ny00NS4xMDcyLTM3LjAwMDUzMy0xNTIuNTI0OC0yNC4yNTE3MzMtMjQxLjgwMDUzNC0zOS4xMTY4LTM4LjEwOTg2Ny03NC40NDQ4LTEwOS40MTQ0LTYyLjk1ODkzMy0xNDMuMzc3MDY2LTEuMTc3NiAzLjQ4MTYtOC4xNzQ5MzMtMTAuMjU3MDY3LTIwLjc1MzA2Ny0yOC42MjA4bC0yLjY0NTMzMy0zLjg1NzA2N2MtMTQuNjYwMjY3LTIxLjExMTQ2Ny0xNy44Njg4LTI1LjkyNDI2Ny0yMi44MTgxMzQtMzQuOTAxMzMzLTExLjAyNTA2Ny0xOS45MTY4LTE0LjgzMDkzMy0zNC42OTY1MzMtMi45ODY2NjYtNTAuODQxNiAyMS4wNjAyNjctMjguODQyNjY3IDU4LjE4MDI2Ny0xNy4zNzM4NjcgMTM2LjkwODggMjQuNTkzMDY2IDk2LjExOTQ2NyAwLjQ0MzczMyAxNzguNzkwNCAzOC40MzQxMzMgMjQ2LjQwODUzMyAxMTIuOTQ3MiA1My45MTM2IDU5LjQ0MzIgNjIuNDY0IDc0LjU2NDI2NyA4Ny4zOTg0IDE1Ny4zODg4IDYuODc3ODY3IDIyLjgxODEzMyAxMi4wNjYxMzMgMzUuODQgMTkuNDIxODY3IDQ2Ljg4MjEzNCA2OC44NjQgMjQuNDIyNCAxMTQuNTg1NiA0OS4wODM3MzMgMTQyLjU0MDggNzQuNTQ3MiAxMi4yMDI2NjcgMTEuMDI1MDY3IDIwLjkwNjY2NyAyMi4xNjk2IDI3LjAzMzYgMzMuNjM4NCAzLjU2NjkzMyA2LjgyNjY2NyA2LjQ1MTIgMTMuOTk0NjY3IDguNjUyOCAyMS40MDE2YTI3LjYxMzg2NyAyNy42MTM4NjcgMCAwIDEtMjUuNTgyOTM0IDM3LjkwNTA2NiAyMTM5LjMwNjY2NyAyMTM5LjMwNjY2NyAwIDAgMC02My42NDE2IDAuNzY4bDYuMzMxNzM0IDMuNzM3NmMxLjMxNDEzMyAwLjgzNjI2NyAxMDQuNzIxMDY3IDEzMC42OTY1MzMgMTExLjU0NzczMyAxNjAuODcwNC05NC44NTY1MzMtNzUuMzY2NC0xODIuMjU0OTMzLTE0MC40OTI4LTIxMi4zMDkzMzMtMTU4LjE5MDkzM3pNMzYyLjU0NzIgMzUxLjQwMjY2N2ExMzMuMTcxMiAxMzMuMTcxMiAwIDAgMC0yOC44NzY4LTQ1LjQzMTQ2NyAyNy41NDU2IDI3LjU0NTYgMCAwIDEgMzYuNjU5MiA4LjgyMzQ2N2M4LjI3NzMzMyAxMy43MjE2IDUuNDEwMTMzIDI4LjYyMDgtNy43ODI0IDM2LjYwOHoiCiAgICBmaWxsPSIjMDM1YjhhIiBwLWlkPSI2Mjg2Ij48L3BhdGg+Cjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/netease.js b/frontend/crawlab-ui/src/assets/js/svg/netease.js
new file mode 100644
index 00000000..76279464
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/netease.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPg0KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCA0OCA0OCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48ZGVmcz48c3R5bGU+LmF7ZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDt9PC9zdHlsZT48L2RlZnM+PHBhdGggY2xhc3M9ImEiIGQ9Ik0xNi41NjU1LDUuMzA3NnYyOC4zNzkiLz48cGF0aCBjbGFzcz0iYSIgZD0iTTkuNzY3OCwyMy40OUgyMy4wMjMiLz48cGF0aCBjbGFzcz0iYSIgZD0iTTI4LjIwNiwxMC4zMjA5VjM2Ljc0NTFINC44NCIvPjxwYXRoIGNsYXNzPSJhIiBkPSJNNC41LDM5LjQ2NDJWMTIuMDIyN0gyOC4xNjkxIi8+PHBhdGggY2xhc3M9ImEiIGQ9Ik0zNC4xNTMzLDQyLjY5MjRWMTIuOTUzOEEzLjU1NDUsMy41NTQ1LDAsMCwxLDM3LjM4MjEsOS44MUg0My41Ii8+PHBhdGggY2xhc3M9ImEiIGQ9Ik0zOC41NTgzLDIzLjgzaDMuOTkzNGMuNzUsMCwuOTQ3OS0xLjE1NDcuOTQ3OS0xLjg2OTJWNy44NTY0Ii8+PHBhdGggY2xhc3M9ImEiIGQ9Ik00My41LDI1LjM2djguNTgxNmEzLjIwNiwzLjIwNiwwLDAsMS0zLjA1ODgsMy4xNDM4SDM0LjE1MzciLz48cGF0aCBjbGFzcz0iYSIgZD0iTTM4LjU1ODMsMjMuODNINDIuNDNjLjc4NDksMCwxLjA3ODkuOTExOSwxLjA3LDEuNTI5NCIvPjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/oracle.js b/frontend/crawlab-ui/src/assets/js/svg/oracle.js
new file mode 100644
index 00000000..91d340ed
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/oracle.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IS0tIFVwbG9hZGVkIHRvOiBTVkcgUmVwbywgd3d3LnN2Z3JlcG8uY29tLCBHZW5lcmF0b3I6IFNWRyBSZXBvIE1peGVyIFRvb2xzIC0tPgo8c3ZnIHdpZHRoPSI4MDBweCIgaGVpZ2h0PSI4MDBweCIgdmlld0JveD0iMCAwIDI0IDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxwYXRoIGZpbGw9IiNGMDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTcuOTU3MzU5LDE4LjkxMjM2NjQgQzQuMTE2NzAyNTIsMTguOTEyMzY2NCAxLDE1LjgwMzQ1OCAxLDExLjk2MTczNzMgQzEsOC4xMjAwMDc3MyA0LjExNjcwMjUyLDUgNy45NTczNTksNSBMMTYuMDQzNzk0OCw1IEMxOS44ODU1MTU2LDUgMjMsOC4xMjAwMDc3MyAyMywxMS45NjE3MzczIEMyMywxNS44MDM0NTggMTkuODg1NTE1NiwxOC45MTIzNjY0IDE2LjA0Mzc5NDgsMTguOTEyMzY2NCBMNy45NTczNTksMTguOTEyMzY2NCBMNy45NTczNTksMTguOTEyMzY2NCBaIE0xNS44NjM5MTc2LDE2LjQ1ODU0ODggQzE4LjM1MjIwMSwxNi40NTg1NDg4IDIwLjM2NzQzOTcsMTQuNDQ4ODU4IDIwLjM2NzQzOTcsMTEuOTYxNzM3MyBDMjAuMzY3NDM5Nyw5LjQ3NDYwNTk1IDE4LjM1MjIwMSw3LjQ1MzgxOTM0IDE1Ljg2MzkxNzYsNy40NTM4MTkzNCBMOC4xMzYwODI0LDcuNDUzODE5MzQgQzUuNjQ4OTUyODUsNy40NTM4MTkzNCAzLjYzMjU1ODU1LDkuNDc0NjA1OTUgMy42MzI1NTg1NSwxMS45NjE3MzczIEMzLjYzMjU1ODU1LDE0LjQ0ODg1OCA1LjY0ODk1Mjg1LDE2LjQ1ODU0ODggOC4xMzYwODI0LDE2LjQ1ODU0ODggTDE1Ljg2MzkxNzYsMTYuNDU4NTQ4OCBMMTUuODYzOTE3NiwxNi40NTg1NDg4IFoiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/outlook.js b/frontend/crawlab-ui/src/assets/js/svg/outlook.js
new file mode 100644
index 00000000..0543f60a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/outlook.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMy4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxpdmVsbG9fMSIgeG1sbnM6eD0iaHR0cDovL25zLmFkb2JlLmNvbS9FeHRlbnNpYmlsaXR5LzEuMC8iIHhtbG5zOmk9Imh0dHA6Ly9ucy5hZG9iZS5jb20vQWRvYmVJbGx1c3RyYXRvci8xMC4wLyIgeG1sbnM6Z3JhcGg9Imh0dHA6Ly9ucy5hZG9iZS5jb20vR3JhcGhzLzEuMC8iIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMTgzMS4wODUgMTcwMy4zMzUiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDE4MzEuMDg1IDE3MDMuMzM1IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIGZpbGw9IiMwQTI3NjciIGQ9Ik0xODMxLjA4Myw4OTQuMjVjMC4xLTE0LjMxOC03LjI5OC0yNy42NDQtMTkuNTAzLTM1LjEzMWgtMC4yMTNsLTAuNzY3LTAuNDI2bC02MzQuNDkyLTM3NS41ODUgIGMtMi43NC0xLjg1MS01LjU4My0zLjU0My04LjUxNy01LjA2N2MtMjQuNDk4LTEyLjYzOS01My41OTktMTIuNjM5LTc4LjA5OCwwYy0yLjkzNCwxLjUyNS01Ljc3NywzLjIxNi04LjUxNyw1LjA2N0w0NDYuNDg2LDg1OC42OTMgIGwtMC43NjYsMC40MjZjLTE5LjM5MiwxMi4wNTktMjUuMzM3LDM3LjU1Ni0xMy4yNzgsNTYuOTQ4YzMuNTUzLDUuNzE0LDguNDQ3LDEwLjQ3NCwxNC4yNTcsMTMuODY4bDYzNC40OTIsMzc1LjU4NSAgYzIuNzQ5LDEuODM1LDUuNTkyLDMuNTI3LDguNTE3LDUuMDY4YzI0LjQ5OCwxMi42MzksNTMuNTk5LDEyLjYzOSw3OC4wOTgsMGMyLjkyNS0xLjU0MSw1Ljc2Ny0zLjIzMiw4LjUxNy01LjA2OGw2MzQuNDkyLTM3NS41ODUgIEMxODIzLjQ5LDkyMi41NDUsMTgzMS4yMjgsOTA4LjkyMywxODMxLjA4Myw4OTQuMjV6Ii8+DQo8cGF0aCBmaWxsPSIjMDM2NEI4IiBkPSJNNTIwLjQ1Myw2NDMuNDc3aDQxNi4zOHYzODEuNjc0aC00MTYuMzhWNjQzLjQ3N3ogTTE3NDUuOTE3LDI1NS41VjgwLjkwOCAgYzEtNDMuNjUyLTMzLjU1Mi03OS44NjItNzcuMjAzLTgwLjkwOEg1ODguMjA0QzU0NC41NTIsMS4wNDYsNTEwLDM3LjI1Niw1MTEsODAuOTA4VjI1NS41bDYzOC43NSwxNzAuMzMzTDE3NDUuOTE3LDI1NS41eiIvPg0KPHBhdGggZmlsbD0iIzAwNzhENCIgZD0iTTUxMSwyNTUuNWg0MjUuODMzdjM4My4yNUg1MTFWMjU1LjV6Ii8+DQo8cGF0aCBmaWxsPSIjMjhBOEVBIiBkPSJNMTM2Mi42NjcsMjU1LjVIOTM2LjgzM3YzODMuMjVMMTM2Mi42NjcsMTAyMmgzODMuMjVWNjM4Ljc1TDEzNjIuNjY3LDI1NS41eiIvPg0KPHBhdGggZmlsbD0iIzAwNzhENCIgZD0iTTkzNi44MzMsNjM4Ljc1aDQyNS44MzNWMTAyMkg5MzYuODMzVjYzOC43NXoiLz4NCjxwYXRoIGZpbGw9IiMwMzY0QjgiIGQ9Ik05MzYuODMzLDEwMjJoNDI1LjgzM3YzODMuMjVIOTM2LjgzM1YxMDIyeiIvPg0KPHBhdGggZmlsbD0iIzE0NDQ3RCIgZD0iTTUyMC40NTMsMTAyNS4xNTFoNDE2LjM4djM0Ni45NjloLTQxNi4zOFYxMDI1LjE1MXoiLz4NCjxwYXRoIGZpbGw9IiMwMDc4RDQiIGQ9Ik0xMzYyLjY2NywxMDIyaDM4My4yNXYzODMuMjVoLTM4My4yNVYxMDIyeiIvPg0KPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8xXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMTI4LjQ1ODQiIHkxPSI4MTEuMDgzMyIgeDI9IjExMjguNDU4NCIgeTI9IjEuOTk4MiIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAwIDE3MDUuMzMzNCkiPg0KCTxzdG9wIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzM1QjhGMSIvPg0KCTxzdG9wIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6IzI4QThFQSIvPg0KPC9saW5lYXJHcmFkaWVudD4NCjxwYXRoIGZpbGw9InVybCgjU1ZHSURfMV8pIiBkPSJNMTgxMS41OCw5MjcuNTkzbC0wLjgwOSwwLjQyNmwtNjM0LjQ5MiwzNTYuODQ4Yy0yLjc2OCwxLjcwMy01LjU3OCwzLjMyMS04LjUxNyw0Ljc2OSAgYy0xMC43NzcsNS4xMzItMjIuNDgxLDguMDI5LTM0LjQwNyw4LjUxN2wtMzQuNjYzLTIwLjI3Yy0yLjkyOS0xLjQ3LTUuNzczLTMuMTA1LTguNTE3LTQuODk3TDQ0Ny4xNjcsOTA2LjAwM2gtMC4yOTggIGwtMjEuMDM2LTExLjc1M3Y3MjIuMzg0YzAuMzI4LDQ4LjE5NiwzOS42NTMsODcuMDA2LDg3Ljg0OSw4Ni43aDEyMzAuOTE0YzAuNzI0LDAsMS4zNjMtMC4zNDEsMi4xMjktMC4zNDEgIGMxMC4xOC0wLjY1MSwyMC4yMTYtMi43NDUsMjkuODA4LTYuMjE3YzQuMTQ1LTEuNzU2LDguMTQ2LTMuODM1LDExLjk2Ni02LjIxN2MyLjg1My0xLjYxOCw3Ljc1LTUuMTUyLDcuNzUtNS4xNTIgIGMyMS44MTQtMTYuMTQyLDM0LjcyNi00MS42MzUsMzQuODMzLTY4Ljc3MlY4OTQuMjVDMTgzMS4wNjgsOTA4LjA2NywxODIzLjYxNiw5MjAuODA3LDE4MTEuNTgsOTI3LjU5M3oiLz4NCjxwYXRoIG9wYWNpdHk9IjAuNSIgZmlsbD0iIzBBMjc2NyIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0xNzk3LjAxNyw4OTEuMzk3djQ0LjI4N2wtNjYzLjQ0OCw0NTYuNzkxTDQ0Ni42OTksOTA2LjMwMSAgYzAtMC4yMzUtMC4xOTEtMC40MjYtMC40MjYtMC40MjZsMCwwbC02My4wMjMtMzcuODk5di0zMS45MzhsMjUuOTc2LTAuNDI2bDU0LjkzMiwzMS41MTJsMS4yNzcsMC40MjZsNC42ODQsMi45ODEgIGMwLDAsNjQ1LjU2MywzNjguMzQ2LDY0Ny4yNjcsMzY5LjE5N2wyNC42OTgsMTQuNDc4YzIuMTI5LTAuODUyLDQuMjU4LTEuNzAzLDYuODEzLTIuNTU1ICBjMS4yNzgtMC44NTIsNjQwLjg3OS0zNjAuNjgxLDY0MC44NzktMzYwLjY4MUwxNzk3LjAxNyw4OTEuMzk3eiIvPg0KPHBhdGggZmlsbD0iIzE0OTBERiIgZD0iTTE4MTEuNTgsOTI3LjU5M2wtMC44MDksMC40NjhsLTYzNC40OTIsMzU2Ljg0OGMtMi43NjgsMS43MDMtNS41NzgsMy4zMjEtOC41MTcsNC43NjkgIGMtMjQuNjQxLDEyLjAzOC01My40NTcsMTIuMDM4LTc4LjA5OCwwYy0yLjkxOC0xLjQ0NS01Ljc2LTMuMDM3LTguNTE3LTQuNzY5TDQ0Ni42NTcsOTI4LjA2MWwtMC43NjYtMC40NjggIGMtMTIuMjUtNi42NDItMTkuOTMtMTkuNDA5LTIwLjA1Ny0zMy4zNDN2NzIyLjM4NGMwLjMwNSw0OC4xODgsMzkuNjE2LDg3LjAwNCw4Ny44MDMsODYuN2MwLjAwMSwwLDAuMDAyLDAsMC4wMDQsMGgxMjI5LjYzNiAgYzQ4LjE4OCwwLjMwNyw4Ny41LTM4LjUwOSw4Ny44MDctODYuNjk2YzAtMC4wMDEsMC0wLjAwMiwwLTAuMDA0Vjg5NC4yNUMxODMxLjA2OCw5MDguMDY3LDE4MjMuNjE2LDkyMC44MDcsMTgxMS41OCw5MjcuNTkzeiIvPg0KPHBhdGggb3BhY2l0eT0iMC4xIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3ICAgICIgZD0iTTExODUuNTIsMTI3OS42MjlsLTkuNDk2LDUuMzIzYy0yLjc1MiwxLjc1Mi01LjU5NSwzLjM1OS04LjUxNyw0LjgxMiAgYy0xMC40NjIsNS4xMzUtMjEuODM4LDguMTQ2LTMzLjQ3LDguODU3bDI0MS40MDUsMjg1LjQ3OWw0MjEuMTA3LDEwMS40NzZjMTEuNTM5LTguNzE2LDIwLjcxNy0yMC4xNzgsMjYuNy0zMy4zNDNMMTE4NS41MiwxMjc5LjYyOSAgeiIvPg0KPHBhdGggb3BhY2l0eT0iMC4wNSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0xMjI4LjUyOSwxMjU1LjQ0MmwtNTIuNTA1LDI5LjUxYy0yLjc1MiwxLjc1Mi01LjU5NSwzLjM1OS04LjUxNyw0LjgxMiAgYy0xMC40NjIsNS4xMzUtMjEuODM4LDguMTQ2LTMzLjQ3LDguODU3bDExMy4xMDEsMzExLjgzOGw1NDkuNTM4LDc0Ljk4OWMyMS42NDktMTYuMjU0LDM0LjM5NC00MS43NDMsMzQuNDA3LTY4LjgxNXYtOS4zMjYgIEwxMjI4LjUyOSwxMjU1LjQ0MnoiLz4NCjxwYXRoIGZpbGw9IiMyOEE4RUEiIGQ9Ik01MTQuODMzLDE3MDMuMzMzaDEyMjguMzE2YzE4LjkwMSwwLjA5NiwzNy4zMzUtNS44NzQsNTIuNTktMTcuMDMzbC02OTcuMDg5LTQwOC4zMzEgIGMtMi45MjktMS40Ny01Ljc3My0zLjEwNS04LjUxNy00Ljg5N0w0NDcuMTI1LDkwNi4wODhoLTAuMjk4bC0yMC45OTMtMTEuODM4djcxOS45MTQgIEM0MjUuNzg2LDE2NjMuMzY0LDQ2NS42MzIsMTcwMy4yODYsNTE0LjgzMywxNzAzLjMzM0M1MTQuODMyLDE3MDMuMzMzLDUxNC44MzIsMTcwMy4zMzMsNTE0LjgzMywxNzAzLjMzM3oiLz4NCjxwYXRoIG9wYWNpdHk9IjAuMSIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAgICAiIGQ9Ik0xMDIyLDQxOC43MjJ2OTA4LjMwM2MtMC4wNzYsMzEuODQ2LTE5LjQ0LDYwLjQ3MS00OC45NzEsNzIuMzkyICBjLTkuMTQ4LDMuOTMxLTE5LDUuOTYtMjguOTU3LDUuOTYySDQyNS44MzNWMzgzLjI1SDUxMXYtNDIuNTgzaDQzMy4wNzNDOTg3LjA5MiwzNDAuODMsMTAyMS45MDcsMzc1LjcwMiwxMDIyLDQxOC43MjJ6Ii8+DQo8cGF0aCBvcGFjaXR5PSIwLjIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgICAgIiBkPSJNOTc5LjQxNyw0NjEuMzA1djkwOC4zMDJjMC4xMDcsMTAuMjg3LTIuMDc0LDIwLjQ2OS02LjM4OCwyOS44MDggIGMtMTEuODI2LDI5LjE0OS00MC4wODMsNDguMjczLTcxLjU0LDQ4LjQxN0g0MjUuODMzVjM4My4yNWg0NzUuNjU2YzEyLjM1Ni0wLjEyNCwyNC41MzMsMi45NTgsMzUuMzQ0LDguOTQzICBDOTYyLjkzNyw0MDUuMzQ0LDk3OS40MDcsNDMyLjA3Niw5NzkuNDE3LDQ2MS4zMDV6Ii8+DQo8cGF0aCBvcGFjaXR5PSIwLjIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgICAgIiBkPSJNOTc5LjQxNyw0NjEuMzA1djgyMy4xMzZjLTAuMjA4LDQzLTM0LjkyOCw3Ny44NTMtNzcuOTI3LDc4LjIyNUg0MjUuODMzVjM4My4yNSAgaDQ3NS42NTZjMTIuMzU2LTAuMTI0LDI0LjUzMywyLjk1OCwzNS4zNDQsOC45NDNDOTYyLjkzNyw0MDUuMzQ0LDk3OS40MDcsNDMyLjA3Niw5NzkuNDE3LDQ2MS4zMDV6Ii8+DQo8cGF0aCBvcGFjaXR5PSIwLjIiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgICAgIiBkPSJNOTM2LjgzMyw0NjEuMzA1djgyMy4xMzZjLTAuMDQ2LDQzLjA2Ny0zNC44NjEsNzguMDE1LTc3LjkyNyw3OC4yMjVINDI1LjgzMyAgVjM4My4yNWg0MzMuMDcyYzQzLjA2MiwwLjAyMyw3Ny45NTEsMzQuOTUxLDc3LjkyNyw3OC4wMTNDOTM2LjgzMyw0NjEuMjc3LDkzNi44MzMsNDYxLjI5MSw5MzYuODMzLDQ2MS4zMDV6Ii8+DQo8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzJfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjE2Mi43NDY5IiB5MT0iMTM4My4wNzQxIiB4Mj0iNzc0LjA4NjQiIHkyPSIzMjQuMjU5MiIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAwIDE3MDUuMzMzNCkiPg0KCTxzdG9wIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6IzE3ODREOSIvPg0KCTxzdG9wIG9mZnNldD0iMC41IiBzdHlsZT0ic3RvcC1jb2xvcjojMTA3QUQ1Ii8+DQoJPHN0b3Agb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojMEE2M0M5Ii8+DQo8L2xpbmVhckdyYWRpZW50Pg0KPHBhdGggZmlsbD0idXJsKCNTVkdJRF8yXykiIGQ9Ik03OC4wNTUsMzgzLjI1aDc4MC43MjNjNDMuMTA5LDAsNzguMDU1LDM0Ljk0Nyw3OC4wNTUsNzguMDU1djc4MC43MjMgIGMwLDQzLjEwOS0zNC45NDYsNzguMDU1LTc4LjA1NSw3OC4wNTVINzguMDU1Yy00My4xMDksMC03OC4wNTUtMzQuOTQ3LTc4LjA1NS03OC4wNTVWNDYxLjMwNSAgQzAsNDE4LjE5NywzNC45NDcsMzgzLjI1LDc4LjA1NSwzODMuMjV6Ii8+DQo8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMjQzLjk2LDcxMC42MzFjMTkuMjM4LTQwLjk4OCw1MC4yOS03NS4yODksODkuMTctOTguNDk1YzQzLjA1Ny0yNC42NTEsOTIuMDgxLTM2Ljk0LDE0MS42NzUtMzUuNTE1ICBjNDUuOTY1LTAuOTk3LDkxLjMyMSwxMC42NTUsMTMxLjExNCwzMy42ODNjMzcuNDE0LDIyLjMxMiw2Ny41NDcsNTUuMDA0LDg2Ljc0Miw5NC4xMDljMjAuOTA0LDQzLjA5LDMxLjMyMiw5MC41MTIsMzAuNDA1LDEzOC4zOTYgIGMxLjAxMyw1MC4wNDMtOS43MDYsOTkuNjI4LTMxLjI5OSwxNDQuNzgzYy0xOS42NTIsNDAuNTAzLTUwLjc0MSw3NC4zNi04OS40MjUsOTcuMzg4Yy00MS4zMjcsMjMuNzM0LTg4LjM2NywzNS42OTItMTM2LjAxMSwzNC41NzggIGMtNDYuOTQ3LDEuMTMzLTkzLjMwMy0xMC42NTEtMTM0LjAxLTM0LjA2N2MtMzcuNzM4LTIyLjM0MS02OC4yNDktNTUuMDctODcuODkyLTk0LjI4Yy0yMS4wMjgtNDIuNDY3LTMxLjU3LTg5LjM1NS0zMC43NDUtMTM2LjczNSAgQzIxMi44MDgsODA0Ljg1OSwyMjMuMTU4LDc1NS42ODYsMjQzLjk2LDcxMC42MzF6IE0zMzkuMDA2LDk0MS44NThjMTAuMjU3LDI1LjkxMiwyNy42NTEsNDguMzg1LDUwLjE2Myw2NC44MTIgIGMyMi45MywxNi4wMjYsNTAuMzg3LDI0LjI5NCw3OC4zNTMsMjMuNTkxYzI5Ljc4MywxLjE3OCw1OS4xNC03LjM3Miw4My42MzQtMjQuMzU4YzIyLjIyNy0xNi4zNzUsMzkuMTY0LTM4LjkwOSw0OC43MTUtNjQuODEyICBjMTAuNjc3LTI4LjkyOCwxNS45NDYtNTkuNTcyLDE1LjU0My05MC40MDRjMC4zMy0zMS4xMjctNC42MjMtNjIuMDg0LTE0LjY0OS05MS41NTRjLTguODU1LTI2LjYwNy0yNS4yNDYtNTAuMDY5LTQ3LjE4Mi02Ny41MzcgIGMtMjMuODgtMTcuNzktNTMuMTU4LTI2LjgxMy04Mi45MS0yNS41NWMtMjguNTcyLTAuNzQtNTYuNjQ0LDcuNTkzLTgwLjE4NCwyMy44MDRjLTIyLjg5MywxNi40OTYtNDAuNjE3LDM5LjE2OC01MS4xLDY1LjM2NSAgYy0yMy4yNTUsNjAuMDQ5LTIzLjM3NiwxMjYuNTk1LTAuMzQxLDE4Ni43MjhMMzM5LjAwNiw5NDEuODU4eiIvPg0KPHBhdGggZmlsbD0iIzUwRDlGRiIgZD0iTTEzNjIuNjY3LDI1NS41aDM4My4yNXYzODMuMjVoLTM4My4yNVYyNTUuNXoiLz4NCjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/pencil-fill.js b/frontend/crawlab-ui/src/assets/js/svg/pencil-fill.js
new file mode 100644
index 00000000..29ba3ee7
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/pencil-fill.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktcGVuY2lsLWZpbGwiIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZD0iTTEyLjg1NC4xNDZhLjUuNSAwIDAgMC0uNzA3IDBMMTAuNSAxLjc5MyAxNC4yMDcgNS41bDEuNjQ3LTEuNjQ2YS41LjUgMCAwIDAgMC0uNzA4bC0zLTN6bS42NDYgNi4wNjFMOS43OTMgMi41IDMuMjkzIDlIMy41YS41LjUgMCAwIDEgLjUuNXYuNWguNWEuNS41IDAgMCAxIC41LjV2LjVoLjVhLjUuNSAwIDAgMSAuNS41di41aC41YS41LjUgMCAwIDEgLjUuNXYuMjA3bDYuNS02LjV6bS03LjQ2OCA3LjQ2OEEuNS41IDAgMCAxIDYgMTMuNVYxM2gtLjVhLjUuNSAwIDAgMS0uNS0uNVYxMmgtLjVhLjUuNSAwIDAgMS0uNS0uNVYxMWgtLjVhLjUuNSAwIDAgMS0uNS0uNVYxMGgtLjVhLjQ5OS40OTkgMCAwIDEtLjE3NS0uMDMybC0uMTc5LjE3OGEuNS41IDAgMCAwLS4xMS4xNjhsLTIgNWEuNS41IDAgMCAwIC42NS42NWw1LTJhLjUuNSAwIDAgMCAuMTY4LS4xMWwuMTc4LS4xNzh6Ii8+Cjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/playwright.js b/frontend/crawlab-ui/src/assets/js/svg/playwright.js
new file mode 100644
index 00000000..3f1bae3f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/playwright.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9Ii0wLjUgLTAuNSAyNCAyNCIgaWQ9IlBsYXl3cmlnaHQtLVN0cmVhbWxpbmUtU3ZnLUxvZ29zLnN2ZyIgaGVpZ2h0PSIyNCIgd2lkdGg9IjI0Ij48ZGVzYz5QbGF5d3JpZ2h0IFN0cmVhbWxpbmUgSWNvbjogaHR0cHM6Ly9zdHJlYW1saW5laHEuY29tPC9kZXNjPjxwYXRoIGZpbGw9IiMyRDQ1NTIiIGQ9Ik03LjY2MjY4OTU4MzMzMzMzNCAxMi41OTQxNTMxMjVjLTAuODQwNjk3OTE2NjY2NjY2NyAwLjIzODYwMTA0MTY2NjY2NjY4IC0xLjM5MjI0MjcwODMzMzMzMzMgMC42NTY5MTM1NDE2NjY2NjY2IC0xLjc1NTU3MDgzMzMzMzMzMzUgMS4wNzQ5MTQ1ODMzMzMzMzMzIDAuMzQ3OTk0NzkxNjY2NjY2NjQgLTAuMzA0NTM0Mzc0OTk5OTk5OTcgMC44MTQxMjgxMjUgLTAuNTg0MDMyMjkxNjY2NjY2NyAxLjQ0MjkzODU0MTY2NjY2NyAtMC43NjIyODIyOTE2NjY2NjY3IDAuNjQzMTM3NSAtMC4xODIyNzUwMDAwMDAwMDAwMiAxLjE5MTgwNzI5MTY2NjY2NjcgLTAuMTgwOTU3MjkxNjY2NjY2NjYgMS42NDUxNzA4MzMzMzMzMzMyIC0wLjA5MzQ4NTQxNjY2NjY2NjY3di0wLjM1NDUxMTQ1ODMzMzMzMzM2Yy0wLjM4NjczNTQxNjY2NjY2NjcgLTAuMDM1MzYyNTAwMDAwMDAwMDA1IC0wLjgzMDEzMjI5MTY2NjY2NjcgLTAuMDA3MTg3NSAtMS4zMzI1Mzg1NDE2NjY2NjY2IDAuMTM1MzY0NTgzMzMzMzMzMzJabS0xLjc5NDAwMDAwMDAwMDAwMDMgLTIuOTgwMjAxMDQxNjY2NjY2NyAtMy4xMjIyMDIwODMzMzMzMzM3IDAuODIyNTYxNDU4MzMzMzMzNHMwLjA1NjkwMTA0MTY2NjY2NjY2NiAwLjA4MDM4MDIwODMzMzMzMzM0IDAuMTYyMjQ1ODMzMzMzMzMzMzQgMC4xODc2MTc3MDgzMzMzMzMzNmwyLjY0NzI1MjA4MzMzMzMzMzUgLTAuNjk3NTQ2ODc1MDAwMDAwMXMtMC4wMzc1MTg3NSAwLjQ4MzM4MzMzMzMzMzMzMzMzIC0wLjM2MzI4MDIwODMzMzMzMzM1IDAuOTE1NzgzMzMzMzMzMzMzNGMwLjYxNjIwODMzMzMzMzMzMzQgLTAuNDY2MjA1MjA4MzMzMzMzMzQgMC42NzU5ODQzNzUgLTEuMjI4NDE1NjI1IDAuNjc1OTg0Mzc1IC0xLjIyODQxNTYyNVptMi42MTM0OTQ3OTE2NjY2NjY3IDcuMzM3NzE4NzVDNC4wODgzOTM3NSAxOC4xMzUwOTI3MDgzMzMzMzUgMS43NjM3OTU3MjkxNjY2NjY4IDEzLjA0MzE1NjI1MDAwMDAwMSAxLjA2MDAwMDUyMDgzMzMzMzIgMTAuNDAwMjQwNjI1Yy0wLjMyNTE2MjUgLTEuMjE5OTU4MzMzMzMzMzMzMyAtMC40NjcxMDYwNDE2NjY2NjY3IC0yLjE0Mzg4NzUgLTAuNTA0OTM4NjQ1ODMzMzMzNCAtMi43NDAxMzg1NDE2NjY2NjY2IC0wLjAwNDA2NTcyOTE2NjY2NjY2NiAtMC4wNjE5MDgzMzMzMzMzMzMzNCAtMC4wMDIxODczOTU4MzMzMzMzMzMzIC0wLjExNDA4OTU4MzMzMzMzMzM0IDAuMDAyMTg5NzkxNjY2NjY2NjY2NyAtMC4xNjE5MTA0MTY2NjY2NjY2NyAtMC4yMjc5MjUyMDgzMzMzMzMzMiAwLjAxMzc1MjA4MzMzMzMzMzMzMyAtMC4zMzcwNDcyNzA4MzMzMzMzNiAwLjEzMjIyNjA0MTY2NjY2NjY1IC0wLjMxNDg0NjA0MTY2NjY2NjcgMC40NzQ2MTQ1ODMzMzMzMzM0IDAuMDM3ODMyNjA0MTY2NjY2NjY1IDAuNTk1OTE1NjI1IDAuMTc5Nzc4NTQxNjY2NjY2NjggMS41MTk3OTY4NzUgMC41MDQ5NDEwNDE2NjY2NjY2IDIuNzQwMTM4NTQxNjY2NjY2NkMxLjQ1MDgyODAyMDgzMzMzMzMgMTMuMzU1NDUzMTI1IDMuNzc1NzM3NTAwMDAwMDAwNCAxOC40NDczODk1ODMzMzMzMzMgOC4xNjk1NTIwODMzMzMzMzMgMTcuMjYzOTkxNjY2NjY2NjdjMC45NTYzNjg3NSAtMC4yNTc2NDc5MTY2NjY2NjY2NCAxLjY3NDg1NTIwODMzMzMzMzUgLTAuNzI2OTE5NzkxNjY2NjY2NiAyLjIxNDIwNTIwODMzMzMzMzUgLTEuMzI1OTczOTU4MzMzMzMzNCAtMC40OTcxMzU0MTY2NjY2NjY3IDAuNDQ5MDAzMTI1MDAwMDAwMDYgLTEuMTE5MzMzMzMzMzMzMzMzMyAwLjgwMjYwNDE2NjY2NjY2NjcgLTEuOTAxNTcyOTE2NjY2NjY2OCAxLjAxMzY1MzEyNVptMC44MjU2NzYwNDE2NjY2NjY2IC0xMC40NTQwMjcwODMzMzMzMzN2MC4zMTI3MDQxNjY2NjY2NjY2N2gxLjcyMzQxODc1Yy0wLjAzNTM2MjUwMDAwMDAwMDAwNSAtMC4xMTA3MTE0NTgzMzMzMzMzNSAtMC4wNzA5ODg1NDE2NjY2NjY2NyAtMC4yMTA0NzM5NTgzMzMzMzMzMiAtMC4xMDYzNTEwNDE2NjY2NjY2NyAtMC4zMTI3MDQxNjY2NjY2NjY2N2gtMS42MTcwNjc3MDgzMzMzMzM0WiIgc3Ryb2tlLXdpZHRoPSIxIj48L3BhdGg+PHBhdGggZmlsbD0iIzJENDU1MiIgZD0iTTExLjQxNjUyOTE2NjY2NjY2NyA5LjA3Mjg3NzA4MzMzMzMzM2MwLjc3NTA1MjA4MzMzMzMzMzMgMC4yMjAxMDUyMDgzMzMzMzMzMyAxLjE4NDk3OTE2NjY2NjY2NjcgMC43NjM1MDQxNjY2NjY2NjY3IDEuNDAxNjM0Mzc1IDEuMjQ0Mzk1ODMzMzMzMzMzNGwwLjg2NDE3NzA4MzMzMzMzMzQgMC4yNDU0MjkxNjY2NjY2NjY2N3MtMC4xMTc4NzUwMDAwMDAwMDAwMSAtMS42ODMwMDEwNDE2NjY2NjY4IC0xLjY0MDIxMTQ1ODMzMzMzMzQgLTIuMTE1NDI1Yy0xLjQyNDE1NTIwODMzMzMzMzMgLTAuNDA0NjMyMjkxNjY2NjY2NyAtMi4zMDA1NTEwNDE2NjY2NjY2IDAuNzkxMjk1ODMzMzMzMzMzMyAtMi40MDcxNDE2NjY2NjY2NjcgMC45NDYwNjY2NjY2NjY2NjY3IDAuNDE0Mjg3NTAwMDAwMDAwMDMgLTAuMjk1MTY2NjY2NjY2NjY2NyAxLjAxOTI1OTM3NSAtMC41MzY4MTA0MTY2NjY2NjY3IDEuNzgxNTQxNjY2NjY2NjY2OCAtMC4zMjA0NjY2NjY2NjY2NjY3Wm02Ljg3OTA4NDM3NTAwMDAwMSAxLjI1MjE1ODMzMzMzMzMzMzNjLTEuNDI1NDQ4OTU4MzMzMzMzNCAtMC40MDY0NTMxMjQ5OTk5OTk5NyAtMi4zMDE1MDkzNzQ5OTk5OTk3IDAuNzkxOTY2NjY2NjY2NjY2NyAtMi40MDY1NDI3MDgzMzMzMzM3IDAuOTQ0NTU3MjkxNjY2NjY2NyAwLjQxNDU5ODk1ODMzMzMzMzMgLTAuMjk0ODA3MjkxNjY2NjY2NjQgMS4wMTkyNTkzNzUgLTAuNTM2NTQ2ODc1IDEuNzgxMjA2MjQ5OTk5OTk5OSAtMC4zMTkyMjA4MzMzMzMzMzMzNCAwLjc3MzgwNjI1IDAuMjIwNDY0NTgzMzMzMzMzMzUgMS4xODMzOTc5MTY2NjY2NjY2IDAuNzYzMTkyNzA4MzMzMzMzNCAxLjQwMDcyMzk1ODMzMzMzMzMgMS4yNDQ0MTk3OTE2NjY2NjY2bDAuODY1NDIyOTE2NjY2NjY2NyAwLjI0NjMzOTU4MzMzMzMzMzMzcy0wLjExOTc5MTY2NjY2NjY2NjY3IC0xLjY4MzMzNjQ1ODMzMzMzMzIgLTEuNjQwODEwNDE2NjY2NjY2OCAtMi4xMTYwOTU4MzMzMzMzMzM1Wm0tMC44NTg1NzA4MzMzMzMzMzM0IDQuNDM3NTYyNDk5OTk5OTk5IC03LjE4ODkxMzU0MTY2NjY2NyAtMi4wMDk3NDQ3OTE2NjY2NjdzMC4wNzc4MTY2NjY2NjY2NjY2NiAwLjM5NDU2OTc5MTY2NjY2NjY3IDAuMzc2NDMzMzMzMzMzMzMzMzQgMC45MDU0ODEyNWw2LjA1MjczNzUgMS42OTIxMDUyMDgzMzMzMzM0YzAuNDk4MzA5Mzc1IC0wLjI4ODMxNDU4MzMzMzMzMzQgMC43NTk3NDI3MDgzMzMzMzMzIC0wLjU4Nzg0MTY2NjY2NjY2NjYgMC43NTk3NDI3MDgzMzMzMzMzIC0wLjU4Nzg0MTY2NjY2NjY2NjZaTTEyLjQ1Mjk0MjcwODMzMzMzMyAxOS4wODg1Mzg1NDE2NjY2N0M2Ljc2MDczMDIwODMzMzMzMyAxNy41NjI0MTY2NjY2NjY2NjcgNy40NDg4ODU0MTY2NjY2NjcgMTAuMzA5NzczOTU4MzMzMzM0IDguMzY5OTYzNTQxNjY2NjY2IDYuODcyOTk4OTU4MzMzMzMzNWMwLjM3OTIzNjQ1ODMzMzMzMzM2IC0xLjQxNjMyMDgzMzMzMzMzMzUgMC43NjkxMzQzNzUgLTIuNDY5MDI2MDQxNjY2NjY3IDEuMDkyNDUyMDgzMzMzMzMzMyAtMy4xNzQ2OTQ3OTE2NjY2NjY2IC0wLjE5MjkxMjUwMDAwMDAwMDAxIC0wLjAzOTY5ODk1ODMzMzMzMzMzIC0wLjM1MjY5MDYyNSAwLjA2MTkwODMzMzMzMzMzMzM0IC0wLjUxMDU3NjA0MTY2NjY2NjcgMC4zODI5OTc5MTY2NjY2NjY2NiAtMC4zNDMyOTg5NTgzMzMzMzMzNSAwLjY5NjMwMTA0MTY2NjY2NjYgLTAuNzgyMjg3NSAxLjgyOTk4NTQxNjY2NjY2NyAtMS4yMDcxNDA2MjUgMy40MTcwMzMzMzMzMzMzMzM0IC0wLjkyMDgxNDU4MzMzMzMzMzMgMy40MzY3MDMxMjUgLTEuNjA4OTkzNzUgMTAuNjg5MTA2MjUgNC4wODI5NzkxNjY2NjY2NjcgMTIuMjE1MjI4MTI1MDAwMDAxIDIuNjgyOTAyMDgzMzMzMzMzNSAwLjcxODc1IDQuNzcyOTU1MjA4MzMzMzMzIC0wLjM3MzY1NDE2NjY2NjY2NjczIDYuMzMwOTg5NTgzMzMzMzMzIC0yLjA4OTIxNDU4MzMzMzMzMzMgLTEuNDc4ODc2MDQxNjY2NjY2NiAxLjMzOTQ2MjUgLTMuMzY3MDMyMjkxNjY2NjY2NCAyLjA5MDQzNjQ1ODMzMzMzMzUgLTUuNzA1NzI1IDEuNDY0MTg5NTgzMzMzMzMzNFoiIHN0cm9rZS13aWR0aD0iMSI+PC9wYXRoPjxwYXRoIGZpbGw9IiNFMjU3NEMiIGQ9Ik05LjMwNzkwODMzMzMzMzMzNCAxNS4yNTIwNDI3MDgzMzMzMzNWMTMuNzg4NWwtNC4wNjYzNzYwNDE2NjY2NjY2IDEuMTUzMDY2NjY2NjY2NjY2N3MwLjMwMDQ2MTQ1ODMzMzMzMzMgLTEuNzQ1ODQzNzUwMDAwMDAwMSAyLjQyMTIwNTIwODMzMzMzMzMgLTIuMzQ3NDM3NTAwMDAwMDAwM2MwLjY0MzEzNzUgLTAuMTgyMjc1MDAwMDAwMDAwMDIgMS4xOTE4NzkxNjY2NjY2NjY4IC0wLjE4MTAyOTE2NjY2NjY2NjY5IDEuNjQ1MTcwODMzMzMzMzMzMiAtMC4wOTM0ODU0MTY2NjY2NjY2N1Y2LjQ5NzYxOTc5MTY2NjY2N2gyLjAzNjA1MTA0MTY2NjY2N2MtMC4yMjE2ODY0NTgzMzMzMzMzNCAtMC42ODUwNDA2MjUwMDAwMDAxIC0wLjQzNjEzNzUgLTEuMjEyNDM1NDE2NjY2NjY2NyAtMC42MTYyNTYyNTAwMDAwMDAxIC0xLjU3ODkwMjA4MzMzMzMzMzQgLTAuMjk3OTY5NzkxNjY2NjY2NyAtMC42MDY1NTMxMjUgLTAuNjAzNDE0NTgzMzMzMzMzNCAtMC4yMDQ0NjA0MTY2NjY2NjY2NyAtMS4yOTY4NjQ1ODMzMzMzMzM0IDAuMzc1NTIyOTE2NjY2NjY2NjUgLTAuNDg4NDE0NTgzMzMzMzMzNCAwLjQwODAzNDM3NSAtMS43MjI3NzE4NzUgMS4yNzg0NjQ1ODMzMzMzMzMyIC0zLjU4MDIzNzUgMS43NzkwMDIwODMzMzMzMzMzIC0xLjg1NzUxMzU0MTY2NjY2NjYgMC41MDA4NDg5NTgzMzMzMzMzIC0zLjM1OTE5NzkxNjY2NjY2NyAwLjM2ODAyMzk1ODMzMzMzMzM1IC0zLjk4NTc2NTgzMzMzMzMzMzcgMC4yNTk1MTY2NjY2NjY2NjY3IC0wLjg4ODI2MjM5NTgzMzMzMzQgLTAuMTUzMjM3NSAtMS4zNTI4NzE5NzkxNjY2NjY4IC0wLjM0ODMwNjI1IC0xLjMwOTQwNjc3MDgzMzMzMzUgMC4zMjczMTg3NTAwMDAwMDAwNSAwLjAzNzgyNTQxNjY2NjY2NjY3IDAuNTk1OTE1NjI1IDAuMTc5Nzc4NTQxNjY2NjY2NjggMS41MTk4Njg3NSAwLjUwNDkzODY0NTgzMzMzMzQgMi43NDAxMzg1NDE2NjY2NjY2QzEuNzYzODQzNjQ1ODMzMzMzMyAxMy4wNDI3OTY4NzUgNC4wODg3NTMxMjUgMTguMTM0NzMzMzMzMzMzMzM3IDguNDgyNTQzNzUgMTYuOTUxMzM1NDE2NjY2NjY2YzEuMTQ3NzIzOTU4MzMzMzMzNCAtMC4zMDkyNTQxNjY2NjY2NjY2NiAxLjk1NzgyNzA4MzMzMzMzMzMgLTAuOTIwNTAzMTI1IDIuNTE5MzYyNDk5OTk5OTk5NyAtMS42OTk2MDQxNjY2NjY2NjY4aC0xLjY5Mzk5NzkxNjY2NjY2Njh2MC4wMDAzMTE0NTgzMzMzMzMzMzMzNVpNMi43NDYyMjM5NTgzMzMzMzM3IDEwLjQzNjQ4OTU4MzMzMzMzNGwzLjEyMjUxMzU0MTY2NjY2NjcgLTAuODIyNTYxNDU4MzMzMzMzNHMtMC4wOTEwMTc3MDgzMzMzMzMzNCAxLjIwMTIyMjkxNjY2NjY2NjYgLTEuMjYxNTk3OTE2NjY2NjY2NyAxLjUwOTgwNjI1Yy0xLjE3MDg5MTY2NjY2NjY2NjcgMC4zMDgyNzE4NzUwMDAwMDAwMyAtMS44NjA5MTU2MjUgLTAuNjg3MjQ0NzkxNjY2NjY2NyAtMS44NjA5MTU2MjUgLTAuNjg3MjQ0NzkxNjY2NjY2N1oiIHN0cm9rZS13aWR0aD0iMSI+PC9wYXRoPjxwYXRoIGZpbGw9IiMyRUFEMzMiIGQ9Ik0yMS4wNTk0NDY4NzUwMDAwMDMgNi41NjY5NzkxNjY2NjY2NjdjLTAuODExNjYwNDE2NjY2NjY2NyAwLjE0MjI4ODU0MTY2NjY2NjY2IC0yLjc1ODkyMTg3NSAwLjMxOTU1NjI1MDAwMDAwMDA0IC01LjE2NTM5MjcwODMzMzMzMyAtMC4zMjU0NzM5NTgzMzMzMzMzNyAtMi40MDcxNDE2NjY2NjY2NjcgLTAuNjQ0NjQ2ODc1MDAwMDAwMSAtNC4wMDQyNTIwODMzMzMzMzMgLTEuNzcyMTI2MDQxNjY2NjY2NyAtNC42MzcwMTU2MjUwMDAwMDEgLTIuMzAyMDg0Mzc1IC0wLjg5NzAwMDAwMDAwMDAwMDEgLTAuNzUxMzA5Mzc1MDAwMDAwMSAtMS4yOTE1Njk3OTE2NjY2NjY5IC0xLjI3MzQ1NzI5MTY2NjY2NjcgLTEuNjc5ODg2NDU4MzMzMzMzNSAtMC40ODM2NzA4MzMzMzMzMzM0IC0wLjM0MzI5ODk1ODMzMzMzMzM1IDAuNjk2NTg4NTQxNjY2NjY2NyAtMC43ODIzMTE0NTgzMzMzMzM0IDEuODMwMjk2ODc1MDAwMDAwMiAtMS4yMDcyMzY0NTgzMzMzMzMzIDMuNDE3MzQ0NzkxNjY2NjY3IC0wLjkyMDc0MjcwODMzMzMzMzQgMy40MzY3MDMxMjUgLTEuNjA4ODk3OTE2NjY2NjY2NiAxMC42ODkxMDYyNSA0LjA4MzAwMzEyNSAxMi4yMTUyMDQxNjY2NjY2NjYgNS42OTA3MDMxMjUwMDAwMDEgMS41MjQ4MjgxMjUgOC43MjAzMDYyNSAtNS4xMDA0MTc3MDgzMzMzMzM1IDkuNjQxMTIwODMzMzMzMzM0IC04LjUzNzQzMjI5MTY2NjY2OSAwLjQyNDkyNTAwMDAwMDAwMDA1IC0xLjU4NjczNjQ1ODMzMzMzMzIgMC42MTEyNDg5NTgzMzMzMzM0IC0yLjc4ODI3MDgzMzMzMzMzMzMgMC42NjI1MTk3OTE2NjY2NjY2IC0zLjU2MzAxMTQ1ODMzMzMzMzQgMC4wNTg0NTgzMzMzMzMzMzMzMzQgLTAuODc3NjE3NzA4MzMzMzMzNCAtMC41NDQzNTcyOTE2NjY2NjY3IC0wLjYyMjg2ODc1MDAwMDAwMDEgLTEuNjk3MTEyNSAtMC40MjA4NzYwNDE2NjY2NjY2NFpNOS42MjMzOTE2NjY2NjY2NjcgOS40MTAzMDYyNXMwLjg5NzAwMDAwMDAwMDAwMDEgLTEuMzk1MDkzNzUwMDAwMDAwMiAyLjQxODM3ODEyNSAtMC45NjI2OTM3NTAwMDAwMDAxYzEuNTIyMzM2NDU4MzMzMzMzNCAwLjQzMjQyMzk1ODMzMzMzMzM2IDEuNjQwMjExNDU4MzMzMzMzNCAyLjExNTQ0ODk1ODMzMzMzNCAxLjY0MDIxMTQ1ODMzMzMzMzQgMi4xMTU0NDg5NTgzMzMzMzRsLTQuMDU4NTg5NTgzMzMzMzM0IC0xLjE1Mjc1NTIwODMzMzMzMzJaTTEzLjMzNzEyNSAxNS42NzA2NjY2NjY2NjY2NjdjLTIuNjc2MDAyMDgzMzMzMzMzIC0wLjc4Mzg5MjcwODMzMzMzMzQgLTMuMDg4NzA4MzMzMzMzMzMzNCAtMi45MTc3ODk1ODMzMzMzMzMgLTMuMDg4NzA4MzMzMzMzMzMzNCAtMi45MTc3ODk1ODMzMzMzMzNsNy4xODg1NzgxMjUgMi4wMDk4MTY2NjY2NjY2NjY2YzAgLTAuMDAwMzM1NDE2NjY2NjY2NjY2NyAtMS40NTEwMTI1IDEuNjgyMDQyNzA4MzMzMzMzNCAtNC4wOTk4Njk3OTE2NjY2NjcgMC45MDc5NzI5MTY2NjY2NjY3Wm0yLjU0MTU5NTgzMzMzMzMzMzUgLTQuMzg1MzgxMjUwMDAwMDAxczAuODk1NzU0MTY2NjY2NjY2NiAtMS4zOTQxMzU0MTY2NjY2NjY2IDIuNDE2ODQ0NzkxNjY2NjY2NyAtMC45NjA0ODk1ODMzMzMzMzM1YzEuNTIxMDQyNzA4MzMzMzMzMyAwLjQzMzA3MDgzMzMzMzMzMzM1IDEuNjQwODEwNDE2NjY2NjY2OCAyLjExNjA5NTgzMzMzMzMzMzUgMS42NDA4MTA0MTY2NjY2NjY4IDIuMTE2MDk1ODMzMzMzMzMzNWwtNC4wNTc2NTUyMDgzMzMzMzMgLTEuMTU1NjA2MjUwMDAwMDAwMloiIHN0cm9rZS13aWR0aD0iMSI+PC9wYXRoPjxwYXRoIGZpbGw9IiNENjUzNDgiIGQ9Ik03Ljg4Njk4NzUwMDAwMDAwMSAxNC4xOTE1MDMxMjUgNS4yNDE2MDQxNjY2NjY2NjcgMTQuOTQxMjU1MjA4MzMzMzM1czAuMjg3MzU2MjUwMDAwMDAwMDQgLTEuNjM3MDQ4OTU4MzMzMzMzNCAyLjIzNjEyNzA4MzMzMzMzMzMgLTIuMjg1NzkyNzA4MzMzMzMzbC0xLjQ5Nzk0Njg3NSAtNS42MjE1NTkzNzUgLTAuMTI5NDQ2ODc1MDAwMDAwMDIgMC4wMzkzMzk1ODMzMzMzMzMzNGMtMS44NTc0ODk1ODMzMzMzMzM0IDAuNTAwOTIwODMzMzMzMzMzNCAtMy4zNTkxNzM5NTgzMzMzMzMzIDAuMzY4MDIzOTU4MzMzMzMzMzUgLTMuOTg1NzM3MDgzMzMzMzMzIDAuMjU5NTE2NjY2NjY2NjY2NyAtMC44ODgyNTc2MDQxNjY2NjY4IC0wLjE1MzE2NTYyNSAtMS4zNTI4NjQ3OTE2NjY2NjY3IC0wLjM0ODMwNjI1IC0xLjMwOTQwNjc3MDgzMzMzMzUgMC4zMjczOTA2MjUwMDAwMDAwNSAwLjAzNzgzMjYwNDE2NjY2NjY2NSAwLjU5NTkxNTYyNSAwLjE3OTc3ODU0MTY2NjY2NjY4IDEuNTE5Nzk2ODc1IDAuNTA0OTM4NjQ1ODMzMzMzNCAyLjc0MDExNDU4MzMzMzMzMzQgMC43MDM0ODEzNTQxNjY2NjY3IDIuNjQyNTMyMjkxNjY2NjY3IDMuMDI4NDA1MjA4MzMzMzMzNSA3LjczNDQ2ODc1MDAwMDAwMDQgNy40MjIxNzE4NzUwMDAwMDEgNi41NTEwNzA4MzMzMzMzMzNsMC4xMjk0NDY4NzUwMDAwMDAwMiAtMC4wNDA2NTcyOTE2NjY2NjY2NjQgLTAuNzI0NzYzNTQxNjY2NjY2NyAtMi43MTkxNzUwMDAwMDAwMDAzWk0yLjc0NjI5NTgzMzMzMzMzMzQgMTAuNDM2NTM3NWwzLjEyMjUxMzU0MTY2NjY2NjcgLTAuODIyNjA5Mzc1cy0wLjA5MTAxNzcwODMzMzMzMzM0IDEuMjAxMjIyOTE2NjY2NjY2NiAtMS4yNjE1NzM5NTgzMzMzMzM0IDEuNTA5ODA2MjVjLTEuMTcwOTE1NjI1IDAuMzA4MjcxODc1MDAwMDAwMDMgLTEuODYwOTM5NTgzMzMzMzMzNSAtMC42ODcxOTY4NzUgLTEuODYwOTM5NTgzMzMzMzMzNSAtMC42ODcxOTY4NzVaIiBzdHJva2Utd2lkdGg9IjEiPjwvcGF0aD48cGF0aCBmaWxsPSIjMUQ4RDIyIiBkPSJtMTMuNDU3ODI3MDgzMzMzMzMzIDE1LjcwMDAxNTYyNSAtMC4xMjEwMzc1IC0wLjAyOTQyMDgzMzMzMzMzMzMzN2MtMi42NzU5NzgxMjUgLTAuNzgzODIwODMzMzMzMzMzMyAtMy4wODg3MDgzMzMzMzMzMzM0IC0yLjkxNzcxNzcwODMzMzMzMzQgLTMuMDg4NzA4MzMzMzMzMzMzNCAtMi45MTc3MTc3MDgzMzMzMzM0bDMuNzA2ODgxMjUwMDAwMDAwMyAxLjAzNjE1IDEuOTYyNDk4OTU4MzMzMzMzNCAtNy41NDEyNjg3NSAtMC4wMjM3NDI3MDgzMzMzMzMzMzQgLTAuMDA2MjUzMTI1MDAwMDAwMDAxYy0yLjQwNzE0MTY2NjY2NjY2NyAtMC42NDQ2NDY4NzUwMDAwMDAxIC00LjAwNDE1NjI1IC0xLjc3MjEyNjA0MTY2NjY2NjcgLTQuNjM3MDE1NjI1MDAwMDAxIC0yLjMwMjA4NDM3NSAtMC44OTcwMDAwMDAwMDAwMDAxIC0wLjc1MTMwOTM3NTAwMDAwMDEgLTEuMjkxNTY5NzkxNjY2NjY2OSAtMS4yNzM0NTcyOTE2NjY2NjY3IC0xLjY3OTg4NjQ1ODMzMzMzMzUgLTAuNDgzNjcwODMzMzMzMzMzNCAtMC4zNDI5NjM1NDE2NjY2NjY3IDAuNjk2NTg4NTQxNjY2NjY2NyAtMC43ODE5NzYwNDE2NjY2NjY3IDEuODMwMjk2ODc1MDAwMDAwMiAtMS4yMDY5MDEwNDE2NjY2NjY2IDMuNDE3MzQ0NzkxNjY2NjY3IC0wLjkyMDc0MjcwODMzMzMzMzQgMy40MzY3MDMxMjUgLTEuNjA4ODk3OTE2NjY2NjY2NiAxMC42ODkxMDYyNSA0LjA4MzAwMzEyNSAxMi4yMTUxNTYyNWwwLjExNjY1MzEyNTAwMDAwMDAxIDAuMDI2MjgyMjkxNjY2NjY2NjcgMC44ODgyNTUyMDgzMzMzMzM0IC0zLjQxNDUxNzcwODMzMzMzMzNaTTkuNjIzMzkxNjY2NjY2NjY3IDkuNDEwMzMwMjA4MzMzMzM1czAuODk3MDAwMDAwMDAwMDAwMSAtMS4zOTUxMTc3MDgzMzMzMzM1IDIuNDE4Mzc4MTI1IC0wLjk2MjcxNzcwODMzMzMzMzRjMS41MjIzMzY0NTgzMzMzMzM0IDAuNDMyNDIzOTU4MzMzMzMzMzYgMS42NDAyMTE0NTgzMzMzMzM0IDIuMTE1NDQ4OTU4MzMzMzM0IDEuNjQwMjExNDU4MzMzMzMzNCAyLjExNTQ0ODk1ODMzMzMzNGwtNC4wNTg1ODk1ODMzMzMzMzQgLTEuMTUyNzMxMjVaIiBzdHJva2Utd2lkdGg9IjEiPjwvcGF0aD48cGF0aCBmaWxsPSIjQzA0QjQxIiBkPSJtOC4wMjE3NzcwODMzMzMzMzMgMTQuMTUyOTU0MTY2NjY2NjY3IC0wLjcwOTQzMDIwODMzMzMzMzQgMC4yMDEzNDU4MzMzMzMzMzMzNmMwLjE2NzU4ODU0MTY2NjY2NjY3IDAuOTQ0ODIwODMzMzMzMzMzMyAwLjQ2Mjk5NDc5MTY2NjY2NjcgMS44NTE1NDc5MTY2NjY2NjY4IDAuOTI2Njg0Mzc1MDAwMDAwMSAyLjY1MjU3MDgzMzMzMzMzMzMgMC4wODA2OTE2NjY2NjY2NjY2NiAtMC4wMTc4MjUgMC4xNjA3MzY0NTgzMzMzMzMzNiAtMC4wMzMxMzQzNzUgMC4yNDI5Mzc1IC0wLjA1NTY1NTIwODMzMzMzMzM0IDAuMjE1NDMzMzMzMzMzMzMzMzQgLTAuMDU4MTIyOTE2NjY2NjY2NjcgMC40MTUxOTc5MTY2NjY2NjY3IC0wLjEzMDA5Mzc1IDAuNjA3NTM1NDE2NjY2NjY2NyAtMC4yMDg1ODEyNTAwMDAwMDAwMiAtMC41MTgwNzUgLTAuNzY4Nzk4OTU4MzMzMzMzMyAtMC44NjA3OTg5NTgzMzMzMzM0IC0xLjY1NDI1MTA0MTY2NjY2NjcgLTEuMDY3NzI3MDgzMzMzMzMzNCAtMi41ODk2ODAyMDgzMzMzMzM3Wm0tMC4yNzcwMzAyMDgzMzMzMzMzNiAtNi42NTQ1NzA4MzMzMzMzMzM1Yy0wLjM2NDU1MDAwMDAwMDAwMDA0IDEuMzYwNjY1NjI1IC0wLjY5MDY5NDc5MTY2NjY2NjcgMy4zMTkxMzk1ODMzMzMzMzM1IC0wLjYwMDkyMjkxNjY2NjY2NjYgNS4yODM1MzEyNSAwLjE2MDY4ODU0MTY2NjY2NjY2IC0wLjA2OTc0MjcwODMzMzMzMzM1IDAuMzMwNDMzMzMzMzMzMzMzMzYgLTAuMTM0NzE3NzA4MzMzMzMzMzUgMC41MTg5ODU0MTY2NjY2NjY2IC0wLjE4ODIxNjY2NjY2NjY2NjY3bDAuMTMxMzM5NTgzMzMzMzMzMzQgLTAuMDI5MzQ4OTU4MzMzMzMzMzM1Yy0wLjE2MDExMzU0MTY2NjY2NjY2IC0yLjA5ODI5NDc5MTY2NjY2NyAwLjE4NTk4ODU0MTY2NjY2NjY3IC00LjIzNjUwNDE2NjY2NjY2NyAwLjU3NTg4NjQ1ODMzMzMzMzQgLTUuNjkxMzAyMDgzMzMzMzMzIDAuMDk4ODI4MTI1IC0wLjM2ODAyMzk1ODMzMzMzMzM1IDAuMTk3OTE5NzkxNjY2NjY2NjUgLTAuNzEwMzQwNjI1MDAwMDAwMSAwLjI5NjcgLTEuMDI5NTg1NDE2NjY2NjY2NyAtMC4xNTkxMDcyOTE2NjY2NjY2OCAwLjEwMTI3MTg3NTAwMDAwMDAxIC0wLjMzMDQzMzMzMzMzMzMzMzM2IDAuMjA1MDU5Mzc1MDAwMDAwMDIgLTAuNTI1NTczOTU4MzMzMzMzNCAwLjMxMTk4NTQxNjY2NjY2NjcgLTAuMTMxNjAzMTI1MDAwMDAwMDIgMC40MDYxODk1ODMzMzMzMzMzIC0wLjI2NDUgMC44NTAxNjE0NTgzMzMzMzM1IC0wLjM5NjQxNDU4MzMzMzMzMzM1IDEuMzQyOTM2NDU4MzMzMzMzNFoiIHN0cm9rZS13aWR0aD0iMSI+PC9wYXRoPjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/plus.js b/frontend/crawlab-ui/src/assets/js/svg/plus.js
new file mode 100644
index 00000000..4b40c702
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/plus.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktcGx1cyI+PHBhdGggZD0iTTggNGEuNS41IDAgMCAxIC41LjV2M2gzYS41LjUgMCAwIDEgMCAxaC0zdjNhLjUuNSAwIDAgMS0xIDB2LTNoLTNhLjUuNSAwIDAgMSAwLTFoM3YtM0EuNS41IDAgMCAxIDggNHoiLz48L3N2Zz4K`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/postgres.js b/frontend/crawlab-ui/src/assets/js/svg/postgres.js
new file mode 100644
index 00000000..cb42ac04
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/postgres.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzIyOTI3Mjc2NTYxIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjczMzEiCiAgICAgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPgogIDxwYXRoCiAgICBkPSJNOTMwLjU2NDE1ODM5IDU3OS4xMjk1NjkxNGMtMTQuMzA1NjkzMzYtMTIuMjYyMDIyNzYtMzIuNjk4NzI3OTMtMTQuMzA1NjkzMzYtNTEuMDkxNzYzMzctOC4xNzQ2ODE1NC0xNC4zMDU2OTMzNiA0LjA4NzM0MTIxLTMwLjY1NTA1NzMyIDYuMTMxMDExODItNDQuOTYwNzUxNTYgNi4xMzEwMTA5NCA0MC44NzM0MTAzNS02Ny40NDExMjczNCA3MS41Mjg0Njc2OC0xNDEuMDEzMjY1NjMgOTEuOTY1MTczNzMtMjE4LjY3Mjc0NDI0IDEwLjIxODM1MjE0LTM0Ljc0MjM5ODU0IDE2LjM0OTM2Mzk2LTcxLjUyODQ2NzY4IDE2LjM0OTM2Mzk2LTExMC4zNTgyMDgzMSA0LjA4NzM0MTIxLTMwLjY1NTA1NzMyLTQuMDg3MzQxMjEtNjEuMzEwMTE1NTItMjIuNDgwMzc1NzgtODUuODM0MTYxMDItNTEuMDkxNzYyNS02NS4zOTc0NTY3NC0xMzAuNzk0OTEyNi0xMDIuMTgzNTI1ODgtMjEyLjU0MTczMzMtMTAwLjEzOTg1NTI4aC02LjEzMTAxMTgxYy0zNC43NDIzOTg1NCAwLTY5LjQ4NDc5NzA3IDYuMTMxMDExODItMTAyLjE4MzUyNTg4IDE0LjMwNTY5MzM2aC0yLjA0MzY3MDYxYy0yMC40MzY3MDUxOC00LjA4NzM0MTIxLTQyLjkxNzA4MDk2LTYuMTMxMDExODItNjMuMzUzNzg1MjUtNi4xMzEwMTE4Mi0zOC44Mjk3Mzk3NC0yLjA0MzY3MDYtNzkuNzAzMTUwMSA4LjE3NDY4MjQyLTExNC40NDU1NDg2NCAyOC42MTEzODc2LTQ5LjA0ODA5Mjc3LTE4LjM5MzAzNDU3LTEwNC4yMjcxOTY0OC0yNi41Njc3MTctMTU3LjM2MjYyOTU5LTI0LjUyNDA0NjM5LTQ5LjA0ODA5Mjc3IDAtOTguMDk2MTg0NjcgMjAuNDM2NzA1MTgtMTMyLjgzODU4NDA4IDU1LjE3OTEwMzcxLTM2Ljc4NjA2OTE0IDM4LjgyOTczOTc0LTU1LjE3OTEwMzcxIDk4LjA5NjE4NDY3LTUzLjEzNTQzMzExIDE4MS44ODY2NzU5OCA0LjA4NzM0MTIxIDM2Ljc4NjA2OTE0IDEwLjIxODM1MjE0IDczLjU3MjEzODI4IDE4LjM5MzAzNDU4IDEwOC4zMTQ1MzY4MSAxMi4yNjIwMjI3NiA1My4xMzU0MzMxIDI2LjU2NzcxNyAxMDQuMjI3MTk2NDggNDQuOTYwNzUxNTYgMTU1LjMxODk1ODk4IDE0LjMwNTY5MzM2IDQ3LjAwNDQyMjE3IDM4LjgyOTczOTc0IDkxLjk2NTE3Mjg2IDcxLjUyODQ2NzY3IDEyOC43NTEyNDI4OCAxNi4zNDkzNjM5NiAxNi4zNDkzNjM5NiAzOC44Mjk3Mzk3NCAyNi41Njc3MTcgNjMuMzUzNzg2MTQgMjguNjExMzg2NzIgMTguMzkzMDM0NTcgMCAzNC43NDIzOTg1NC04LjE3NDY4MjQyIDQ5LjA0ODA5MTg5LTIwLjQzNjcwNDMgMTIuMjYyMDIyNzYgMTQuMzA1NjkzMzYgMjguNjExMzg3NiAyMi40ODAzNzU3OCA0Ny4wMDQ0MjIxNyAyNi41Njc3MTYxMiAyNC41MjQwNDYzOCA2LjEzMTAxMTgyIDQ5LjA0ODA5Mjc3IDguMTc0NjgyNDIgNzMuNTcyMTM4MjggNC4wODczNDEyMSAxMi4yNjIwMjI3Ni0yLjA0MzY3MDYgMjIuNDgwMzc1NzgtNi4xMzEwMTE4MiAzMi42OTg3Mjg4MS0xMC4yMTgzNTMwMyAwIDEyLjI2MjAyMjc2IDAgMjQuNTI0MDQ2MzggMi4wNDM2NzA2IDM2Ljc4NjA3MDAyLTIuMDQzNjcwNiAzNi43ODYwNjkxNCA0LjA4NzM0MTIxIDcxLjUyODQ2NzY4IDE0LjMwNTY5MzM2IDEwNi4yNzA4NjYyMSA0LjA4NzM0MTIxIDIwLjQzNjcwNTE4IDE0LjMwNTY5MzM2IDM4LjgyOTczOTc0IDI4LjYxMTM4NjcyIDU1LjE3OTEwMzcyIDI4LjYxMTM4NzYgMjYuNTY3NzE3IDY3LjQ0MTEyNzM0IDM2Ljc4NjA2OTE0IDEwNC4yMjcxOTY0OCAyOC42MTEzODc1OSAzOC44Mjk3Mzk3NC02LjEzMTAxMTgyIDc1LjYxNTgwODg4LTI2LjU2NzcxNyA5OC4wOTYxODQ2OC01Ny4yMjI3NzQzMSAyNi41Njc3MTctMzYuNzg2MDY5MTQgMzguODI5NzM5NzQtOTEuOTY1MTcyODYgNDAuODczNDEwMzQtMTc5Ljg0MzAwNTM4IDAtNC4wODczNDEyMSAyLjA0MzY3MDYtOC4xNzQ2ODI0MiAyLjA0MzY3MDYxLTEyLjI2MjAyMjc1aDYuMTMxMDExODJjMzQuNzQyMzk4NTQgMi4wNDM2NzA2IDY5LjQ4NDc5NzA3LTQuMDg3MzQxMjEgMTAwLjEzOTg1NDM5LTE2LjM0OTM2NDg0IDI0LjUyNDA0NjM4LTEwLjIxODM1MjE0IDQ3LjAwNDQyMjE3LTI2LjU2NzcxNyA2My4zNTM3ODYxMy00OS4wNDgwOTE5IDQuMDg3MzQxMjEtOC4xNzQ2ODI0MiA4LjE3NDY4MjQyLTE2LjM0OTM2Mzk2IDEwLjIxODM1MzA0LTI0LjUyNDA0NjM5IDIuMDQzNjcwNi0xNi4zNDkzNjM5Ni00LjA4NzM0MTIxLTMwLjY1NTA1NzMyLTE2LjM0OTM2Mzk3LTQwLjg3MzQxMDM1eiBtLTEyLjI2MjAyMzYyIDQ3LjAwNDQyMjE3Yy0xNC4zMDU2OTMzNiAxOC4zOTMwMzQ1Ny0zMC42NTUwNTczMiAzMC42NTUwNTczMi01My4xMzU0MzMxMSAzOC44Mjk3Mzk3NS0yNC41MjQwNDYzOCAxMC4yMTgzNTIxNC01MS4wOTE3NjI1IDE0LjMwNTY5MzM2LTc3LjY1OTQ3OTQ5IDE0LjMwNTY5MzM2LTEyLjI2MjAyMjc2IDAtMjQuNTI0MDQ2MzgtMi4wNDM2NzA2LTM2Ljc4NjA2OTE0LTQuMDg3MzQxMjItNi4xMzEwMTE4MiA1MS4wOTE3NjI1LTEwLjIxODM1MjE0IDEwMi4xODM1MjU4OC0yMC40MzY3MDUxNyAxNTMuMjc1Mjg5MjYtMi4wNDM2NzA2IDI0LjUyNDA0NjM4LTE0LjMwNTY5MzM2IDQ5LjA0ODA5Mjc3LTMwLjY1NTA1ODIxIDY5LjQ4NDc5NzA3LTIwLjQzNjcwNTE4IDE4LjM5MzAzNDU3LTQ0Ljk2MDc1MTU2IDMwLjY1NTA1NzMyLTcxLjUyODQ2NzY3IDMyLjY5ODcyNzkzLTMwLjY1NTA1NzMyIDguMTc0NjgyNDItNjEuMzEwMTE1NTIgMi4wNDM2NzA2LTg1LjgzNDE2MTkyLTE2LjM0OTM2Mzk3LTE0LjMwNTY5MzM2LTEyLjI2MjAyMjc2LTI0LjUyNDA0NjM4LTMwLjY1NTA1NzMyLTMyLjY5ODcyNzkzLTQ5LjA0ODA5MTg5LTQuMDg3MzQxMjEtMTIuMjYyMDIyNzYtNi4xMzEwMTE4Mi0yMi40ODAzNzU3OC04LjE3NDY4MjQyLTM0Ljc0MjM5OTQxLTIuMDQzNjcwNi0xOC4zOTMwMzQ1Ny00LjA4NzM0MTIxLTM2Ljc4NjA2OTE0LTQuMDg3MzQwMzMtNTMuMTM1NDMzMTEtMi4wNDM2NzA2LTMyLjY5ODcyNzkzLTIuMDQzNjcwNi02My4zNTM3ODYxNC0yLjA0MzY3MDYxLTk0LjAwODg0MzQ1LTE2LjM0OTM2Mzk2IDE0LjMwNTY5MzM2LTM2Ljc4NjA2OTE0IDI0LjUyNDA0NjM4LTU3LjIyMjc3NDMyIDI4LjYxMTM4NzYtMjAuNDM2NzA1MTggNC4wODczNDEyMS00Mi45MTcwODA5NiAyLjA0MzY3MDYtNjMuMzUzNzg2MTItNC4wODczNDEyMi02LjEzMTAxMTgyLTIuMDQzNjcwNi0xMi4yNjIwMjI3Ni00LjA4NzM0MTIxLTE4LjM5MzAzNDU4LTguMTc0NjgyNDItOC4xNzQ2ODI0Mi0yLjA0MzY3MDYtMTIuMjYyMDIyNzYtOC4xNzQ2ODI0Mi0xNi4zNDkzNjM5Ni0xNC4zMDU2OTMzNi0yLjA0MzY3MDYtNC4wODczNDEyMS00LjA4NzM0MTIxLTEwLjIxODM1MjE0LTIuMDQzNjcwNjEtMTQuMzA1NjkzMzYgMi4wNDM2NzA2LTQuMDg3MzQxMjEgNC4wODczNDEyMS0xMC4yMTgzNTIxNCA4LjE3NDY4MTU1LTEyLjI2MjAyMzYzIDEwLjIxODM1MjE0LTYuMTMxMDExODIgMjAuNDM2NzA1MTgtMTAuMjE4MzUyMTQgMzIuNjk4NzI4OC0xMi4yNjIwMjI3NiAxNC4zMDU2OTMzNi0yLjA0MzY3MDYgMjguNjExMzg3Ni02LjEzMTAxMTgyIDQyLjkxNzA4MDA4LTEyLjI2MjAyMzYzIDguMTc0NjgyNDItNi4xMzEwMTE4MiAxNC4zMDU2OTMzNi0xNC4zMDU2OTMzNiAyMC40MzY3MDUxOC0yMi40ODAzNzQ5di0yLjA0MzY3MDYxYy0xOC4zOTMwMzQ1NyAwLTM0Ljc0MjM5ODU0LTYuMTMxMDExODItNTEuMDkxNzYyNS0xMi4yNjIwMjM2My02LjEzMTAxMTgyIDYuMTMxMDExODItMzIuNjk4NzI3OTMgMzQuNzQyMzk4NTQtNjkuNDg0Nzk3MDggNzcuNjU5NDc5NDktMTIuMjYyMDIyNzYgMTYuMzQ5MzYzOTYtMjguNjExMzg3NiAyNi41Njc3MTctNDkuMDQ4MDkyNzcgMjguNjExMzg3Ni0xOC4zOTMwMzQ1NyAwLTM0Ljc0MjM5ODU0LTguMTc0NjgyNDItNDcuMDA0NDIyMTYtMjAuNDM2NzA1MTctMzAuNjU1MDU3MzItMzQuNzQyMzk4NTQtNTEuMDkxNzYyNS03NS42MTU4MDg4OC02NS4zOTc0NTU4Ni0xMTguNTMyODg5ODUtMTYuMzQ5MzYzOTYtNTMuMTM1NDMzMS0zMC42NTUwNTczMi0xMDQuMjI3MTk2NDgtNDAuODczNDEwMzYtMTU1LjMxODk1ODk4LTguMTc0NjgyNDItMzQuNzQyMzk4NTQtMTQuMzA1NjkzMzYtNjcuNDQxMTI3MzQtMTguMzkzMDM0NTYtMTAyLjE4MzUyNTg4LTQuMDg3MzQxMjEtNzcuNjU5NDc5NDkgMTQuMzA1NjkzMzYtMTI4Ljc1MTI0MiA0NC45NjA3NTA2OC0xNjEuNDQ5OTcwODEgMzAuNjU1MDU3MzItMzAuNjU1MDU3MzIgNzEuNTI4NDY3NjgtNDcuMDA0NDIyMTcgMTE2LjQ4OTIyMDExLTQ5LjA0ODA5MTg4IDU1LjE3OTEwMzcxLTIuMDQzNjcwNiAxMTAuMzU4MjA3NDIgOC4xNzQ2ODI0MiAxNjEuNDQ5OTY5OTIgMjguNjExMzg2NzEgMzIuNjk4NzI3OTMtMjAuNDM2NzA1MTggNjkuNDg0Nzk3MDctMzAuNjU1MDU3MzIgMTA4LjMxNDUzNzctMzAuNjU1MDU3MzMgMjAuNDM2NzA1MTggMCA0Mi45MTcwODA5NiA0LjA4NzM0MTIxIDYzLjM1Mzc4NjEzIDguMTc0NjgyNDIgOC4xNzQ2ODI0Mi00LjA4NzM0MTIxIDE4LjM5MzAzNDU3LTYuMTMxMDExODIgMjguNjExMzg2NzItOC4xNzQ2ODI0MiAyNi41Njc3MTctNi4xMzEwMTE4MiA1MS4wOTE3NjI1LTguMTc0NjgyNDIgNzcuNjU5NDc5NS0xMC4yMTgzNTMwMiA3Ny42NTk0Nzk0OS0yLjA0MzY3MDYgMTUxLjIzMTYxNzc3IDMwLjY1NTA1NzMyIDIwMC4yNzk3MTA1NCA5MS45NjUxNzM3MyAxMi4yNjIwMjI3NiAyMC40MzY3MDUxOCAxOC4zOTMwMzQ1NyA0Ny4wMDQ0MjIxNyAxNi4zNDkzNjM5NyA3MS41Mjg0Njc2Ny0yLjA0MzY3MDYgMzQuNzQyMzk4NTQtOC4xNzQ2ODI0MiA2OS40ODQ3OTcwNy0xNi4zNDkzNjM5NyAxMDQuMjI3MTk2NDktMjIuNDgwMzc1NzggODUuODM0MTYxOTEtNTkuMjY2NDQ0OTIgMTY3LjU4MDk4MTc0LTEwOC4zMTQ1Mzc3IDI0My4xOTY3OTA2MiAyLjA0MzY3MDYgMi4wNDM2NzA2IDQuMDg3MzQxMjEgMi4wNDM2NzA2IDYuMTMxMDExODIgNC4wODczNDEyMiAyOC42MTEzODc2IDYuMTMxMDExODIgNTcuMjIyNzc0MzIgNi4xMzEwMTE4MiA4NS44MzQxNjE5Mi0yLjA0MzY3MDYxIDEwLjIxODM1MjE0LTQuMDg3MzQxMjEgMjIuNDgwMzc1NzgtMi4wNDM2NzA2IDMyLjY5ODcyNzkzIDIuMDQzNjcwNjEgNC4wODczNDEyMSA0LjA4NzM0MTIxIDguMTc0NjgyNDIgMTAuMjE4MzUyMTQgOC4xNzQ2ODI0MiAxNi4zNDkzNjM5Ni02LjEzMTAxMTgyIDAtNi4xMzEwMTE4MiA2LjEzMTAxMTgyLTguMTc0NjgyNDIgMTAuMjE4MzUzMDN6IgogICAgcC1pZD0iNzMzMiI+PC9wYXRoPgogIDxwYXRoCiAgICBkPSJNNzAzLjcxNjczMDg2IDExMS4xMjkwMjE1OGgtNi4xMzEwMTE4MmMtMjAuNDM2NzA1MTggMC00Mi45MTcwODA5NiAyLjA0MzY3MDYtNjMuMzUzNzg1MjUgOC4xNzQ2ODI0MyA0MC44NzM0MTAzNSAxOC4zOTMwMzQ1NyA3Ny42NTk0Nzk0OSA0OS4wNDgwOTI3NyAxMDYuMjcwODY2MjEgODMuNzkwNDkwNDIgMTYuMzQ5MzYzOTYgMjIuNDgwMzc1NzggMzAuNjU1MDU3MzIgNDQuOTYwNzUxNTYgNDIuOTE3MDgwOTYgNzEuNTI4NDY4NTYgNC4wODczNDEyMSAxMC4yMTgzNTIxNCA4LjE3NDY4MjQyIDE4LjM5MzAzNDU3IDEwLjIxODM1MjE0IDI0LjUyNDA0NjM4IDAgNC4wODczNDEyMSAyLjA0MzY3MDYgNi4xMzEwMTE4MiAyLjA0MzY3MDYxIDEwLjIxODM1MjE2djYuMTMxMDExODFjMi4wNDM2NzA2IDMyLjY5ODcyNzkzLTguMTc0NjgyNDIgNTMuMTM1NDMzMS04LjE3NDY4MTU0IDg1LjgzNDE2MTAzIDAgMjIuNDgwMzc1NzggNi4xMzEwMTE4MiA0OS4wNDgwOTI3NyA2LjEzMTAxMDkzIDc3LjY1OTQ3OTUgNC4wODczNDEyMSAzMC42NTUwNTczMi00LjA4NzM0MTIxIDYxLjMxMDExNTUyLTIwLjQzNjcwNDI5IDg1LjgzNDE2MTkxIDIuMDQzNjcwNiAyLjA0MzY3MDYgNC4wODczNDEyMSA0LjA4NzM0MTIxIDQuMDg3MzQwMzMgNi4xMzEwMTE4MiA0Mi45MTcwODA5Ni02OS40ODQ3OTcwNyA3Ny42NTk0Nzk0OS0xNDUuMTAwNjA2ODMgOTguMDk2MTg0NjctMjI0LjgwMzc1NjkzIDguMTc0NjgyNDItMzIuNjk4NzI3OTMgMTQuMzA1NjkzMzYtNjUuMzk3NDU2NzQgMTQuMzA1Njk0MjQtOTguMDk2MTg0NjggMi4wNDM2NzA2LTE4LjM5MzAzNDU3LTIuMDQzNjcwNi0zNi43ODYwNjkxNC0xMC4yMTgzNTMwMy01NS4xNzkxMDM3LTQwLjg3MzQxMDM1LTU1LjE3OTEwMzcxLTEwNi4yNzA4NjcwOS04My43OTA0OTEzMS0xNzUuNzU1NjY0MTYtODEuNzQ2ODIwNzF6IgogICAgZmlsbD0iIzMwNjA5MiIgcC1pZD0iNzMzMyI+PC9wYXRoPgogIDxwYXRoCiAgICBkPSJNNTI3Ljk2MTA2NjcgMTE5LjMwMzcwNDAxYy0zNi43ODYwNjkxNC0yLjA0MzY3MDYtNzEuNTI4NDY3NjggMTAuMjE4MzUyMTQtOTguMDk2MTg0NjcgMzIuNjk4NzI3OTItMjQuNTI0MDQ2MzggMjAuNDM2NzA1MTgtNDAuODczNDEwMzUgNDkuMDQ4MDkyNzctNTMuMTM1NDMzMSA3Ny42NTk0Nzk1LTEwLjIxODM1MjE0IDI4LjYxMTM4NzYtMTYuMzQ5MzYzOTYgNTkuMjY2NDQ0OTItMTguMzkzMDM0NTggODkuOTIxNTAzMTIgMTYuMzQ5MzYzOTYtOC4xNzQ2ODI0MiAzMi42OTg3Mjc5My0xNC4zMDU2OTMzNiA1MS4wOTE3NjI1LTE4LjM5MzAzNTQ1IDE4LjM5MzAzNDU3LTQuMDg3MzQxMjEgMzguODI5NzM5NzQtNC4wODczNDEyMSA1Ny4yMjI3NzQzMiAyLjA0MzY3MDYxIDIwLjQzNjcwNTE4IDguMTc0NjgyNDIgMzQuNzQyMzk4NTQgMjYuNTY3NzE3IDM4LjgyOTczOTc1IDQ5LjA0ODA5Mjc3IDI2LjU2NzcxNyAxMjAuNTc2NTYwNDUtOC4xNzQ2ODI0MiAxNjUuNTM3MzEyMDEtMjAuNDM2NzA1MTcgMjAyLjMyMzM4MTE1LTYuMTMxMDExODIgMTIuMjYyMDIyNzYtMTAuMjE4MzUyMTQgMjQuNTI0MDQ2MzgtMTIuMjYyMDIyNzYgMzYuNzg2MDY5MTQgMi4wNDM2NzA2IDAgNC4wODczNDEyMSAwIDYuMTMxMDExODItMi4wNDM2NzA2IDYuMTMxMDExODIgMCAxNC4zMDU2OTMzNiAyLjA0MzY3MDYgMjAuNDM2NzA0MyA0LjA4NzM0MTIxIDEyLjI2MjAyMjc2IDYuMTMxMDExODIgMjIuNDgwMzc1NzggMTYuMzQ5MzYzOTYgMjYuNTY3NzE2OTggMzAuNjU1MDU3MzIgMi4wNDM2NzA2IDQuMDg3MzQxMjEgMi4wNDM2NzA2IDguMTc0NjgyNDIgMi4wNDM2NzA2MSAxMC4yMTgzNTIxNXY2LjEzMTAxMTgyYy0yLjA0MzY3MDYgNDcuMDA0NDIyMTctMi4wNDM2NzA2IDk0LjAwODg0MzQ2IDAgMTQxLjAxMzI2NTYyIDAgMTguMzkzMDM0NTcgMi4wNDM2NzA2IDM0Ljc0MjM5ODU0IDQuMDg3MzQxMjEgNTMuMTM1NDMzMSAwIDEwLjIxODM1MjE0IDIuMDQzNjcwNiAxOC4zOTMwMzQ1NyA2LjEzMTAxMDk0IDI4LjYxMTM4NzYgNC4wODczNDEyMSAxNC4zMDU2OTMzNiAxMi4yNjIwMjI3NiAyNi41Njc3MTcgMjQuNTI0MDQ2MzggMzYuNzg2MDY5MTRzMzAuNjU1MDU3MzIgMTYuMzQ5MzYzOTYgNjMuMzUzNzg2MTQgMTAuMjE4MzUyMTZjMjAuNDM2NzA1MTgtMi4wNDM2NzA2IDQwLjg3MzQxMDM1LTEyLjI2MjAyMjc2IDU3LjIyMjc3NDMxLTI2LjU2NzcxNjEyIDEyLjI2MjAyMjc2LTE2LjM0OTM2Mzk2IDIwLjQzNjcwNTE4LTM0Ljc0MjM5ODU0IDIyLjQ4MDM3NTc4LTU1LjE3OTEwMzcxIDguMTc0NjgyNDItMzguODI5NzM5NzQgMjAuNDM2NzA1MTgtMTUzLjI3NTI4ODM4IDIyLjQ4MDM3NTc4LTE3My43MTE5OTQ0NCAwLTEyLjI2MjAyMjc2IDIuMDQzNjcwNi0yNi41Njc3MTcgOC4xNzQ2ODE1NS0zNi43ODYwNjkxNCA0LjA4NzM0MTIxLTguMTc0NjgyNDIgMTIuMjYyMDIyNzYtMTQuMzA1NjkzMzYgMjAuNDM2NzA1MTctMTguMzkzMDM0NTYgNC4wODczNDEyMS0yLjA0MzY3MDYgNi4xMzEwMTE4Mi0yLjA0MzY3MDYgMTAuMjE4MzUzMDQtNC4wODczNDEyMi00LjA4NzM0MTIxLTQuMDg3MzQxMjEtNi4xMzEwMTE4Mi04LjE3NDY4MjQyLTEwLjIxODM1MzA0LTEyLjI2MjAyMjc1LTEwLjIxODM1MjE0LTEyLjI2MjAyMjc2LTE4LjM5MzAzNDU3LTI2LjU2NzcxNy0yNC41MjQwNDU1LTQwLjg3MzQxMDM1LTQuMDg3MzQxMjEtNi4xMzEwMTE4Mi02LjEzMTAxMTgyLTEyLjI2MjAyMjc2LTEwLjIxODM1MzAzLTE4LjM5MzAzNDU4LTYuMTMxMDExODItMTAuMjE4MzUyMTQtMTAuMjE4MzUyMTQtMjAuNDM2NzA1MTgtMTguMzkzMDM0NTctMzIuNjk4NzI3OTItMTQuMzA1NjkzMzYtMjYuNTY3NzE3LTI2LjU2NzcxNy01NS4xNzkxMDM3MS0zNC43NDIzOTg1NC04NS44MzQxNjE5Mi04LjE3NDY4MjQyLTMwLjY1NTA1NzMyLTEwLjIxODM1MjE0LTU5LjI2NjQ0NDkyIDEwLjIxODM1MjE2LTgxLjc0NjgyMDcgMTYuMzQ5MzYzOTYtMTguMzkzMDM0NTcgNDcuMDA0NDIyMTctMjguNjExMzg3NiA5MS45NjUxNzM3Mi0yMi40ODAzNzU3OC0yLjA0MzY3MDYtNC4wODczNDEyMS0yLjA0MzY3MDYtOC4xNzQ2ODI0Mi00LjA4NzM0MTItMTIuMjYyMDIyNzUtOC4xNzQ2ODI0Mi0yNC41MjQwNDYzOC0yMi40ODAzNzU3OC00Ny4wMDQ0MjIxNy0zOC44Mjk3Mzk3Ni02NS4zOTc0NTY3NC00NC45NjA3NTE1Ni02MS4zMTAxMTU1Mi0xMTYuNDg5MjE5MjQtOTYuMDUyNTE0MDYtMTkyLjEwNTAyODEyLTk4LjA5NjE4NDY3bC04LjE3NDY4MjQyLTYuMTMxMDEwOTN6IgogICAgZmlsbD0iIzMwNjA5MiIgcC1pZD0iNzMzNCI+PC9wYXRoPgogIDxwYXRoCiAgICBkPSJNMjc4LjYzMzI2NDI2IDEyMS4zNDczNzQ2MWgtMTQuMzA1Njk0MjRjLTM2Ljc4NjA2OTE0IDAtNzMuNTcyMTM4MjggMTQuMzA1NjkzMzYtOTguMDk2MTg0NjYgNDAuODczNDEwMzUtMjYuNTY3NzE3IDI2LjU2NzcxNy00NC45NjA3NTE1NiA3MS41Mjg0Njc2OC00MC44NzM0MDk0OCAxNDcuMTQ0Mjc2NTYgNC4wODczNDEyMSAzMi42OTg3Mjc5MyAxMC4yMTgzNTIxNCA2NS4zOTc0NTY3NCAxNi4zNDkzNjM5NiA5OC4wOTYxODQ2NyAxMC4yMTgzNTIxNCA1MS4wOTE3NjI1IDI0LjUyNDA0NjM4IDEwMC4xMzk4NTUyOCA0Mi45MTcwODA5NiAxNDkuMTg3OTQ3MTcgMTIuMjYyMDIyNzYgNDAuODczNDEwMzUgMzIuNjk4NzI3OTMgNzcuNjU5NDc5NDkgNTkuMjY2NDQ0MDQgMTEwLjM1ODIwODMgNi4xMzEwMTE4MiA4LjE3NDY4MjQyIDE2LjM0OTM2Mzk2IDEyLjI2MjAyMjc2IDI4LjYxMTM4NzYgMTIuMjYyMDIyNzYgMTIuMjYyMDIyNzYtMi4wNDM2NzA2IDIyLjQ4MDM3NTc4LTguMTc0NjgyNDIgMzAuNjU1MDU3MzMtMTguMzkzMDM0NTggMjIuNDgwMzc1NzgtMjYuNTY3NzE3IDQ0Ljk2MDc1MTU2LTUzLjEzNTQzMzEgNjcuNDQxMTI3MzQtNzUuNjE1ODA4ODgtMzQuNzQyMzk4NTQtMjguNjExMzg3Ni01MS4wOTE3NjI1LTczLjU3MjEzODI4LTQyLjkxNzA4MDk2LTExOC41MzI4ODk4NCAyLjA0MzY3MDYtMjQuNTI0MDQ2MzggNC4wODczNDEyMS00OS4wNDgwOTI3NyA0LjA4NzM0MTIyLTc1LjYxNTgwODg5IDAtMjAuNDM2NzA1MTgtMi4wNDM2NzA2LTMyLjY5ODcyNzkzLTIuMDQzNjcwNjEtNDAuODczNDEwMzZWMzQ4LjE5NDgwMTI3YzAtNDIuOTE3MDgwOTYgOC4xNzQ2ODI0Mi04NS44MzQxNjE5MSAyMi40ODAzNzU3OC0xMjYuNzA3NTcxMzkgMTAuMjE4MzUyMTQtMjguNjExMzg3NiAyNi41Njc3MTctNTUuMTc5MTAzNzEgNDkuMDQ4MDkxODktNzcuNjU5NDgwMzctMzQuNzQyMzk4NTQtMTIuMjYyMDIyNzYtNzEuNTI4NDY3NjgtMTguMzkzMDM0NTctMTA4LjMxNDUzNjgxLTIwLjQzNjcwNTE4LTYuMTMxMDExODItMi4wNDM2NzA2LTEwLjIxODM1MjE0LTIuMDQzNjcwNi0xNC4zMDU2OTMzNi0yLjA0MzY2OTcyek03NjAuOTM5NTA1MTggMzk5LjI4NjU2Mzc3YzIuMDQzNjcwNi0zMi42OTg3Mjc5MyA4LjE3NDY4MjQyLTUzLjEzNTQzMzEgOC4xNzQ2ODI0Mi03NS42MTU4MDg4OC0xMC4yMTgzNTIxNC0yLjA0MzY3MDYtMTguMzkzMDM0NTctMi4wNDM2NzA2LTI4LjYxMTM4NzYtMi4wNDM2NzA2Mi0xNi4zNDkzNjM5Ni0yLjA0MzY3MDYtMzIuNjk4NzI3OTMgNC4wODczNDEyMS00Ny4wMDQ0MjEyOCAxNC4zMDU2OTQyNS0xMC4yMTgzNTIxNCAxMC4yMTgzNTIxNC0xMC4yMTgzNTIxNCAzMi42OTg3Mjc5My02LjEzMTAxMTgyIDU5LjI2NjQ0NDkyIDguMTc0NjgyNDIgMjguNjExMzg3NiAxOC4zOTMwMzQ1NyA1NS4xNzkxMDM3MSAzMi42OTg3Mjc5MiA3OS43MDMxNTAxIDYuMTMxMDExODIgMTIuMjYyMDIyNzYgMTIuMjYyMDIyNzYgMjIuNDgwMzc1NzggMTYuMzQ5MzYzOTcgMzIuNjk4NzI3OTIgNC4wODczNDEyMSA2LjEzMTAxMTgyIDguMTc0NjgyNDIgMTQuMzA1NjkzMzYgMTAuMjE4MzUzMDMgMjAuNDM2NzA1MTggMi4wNDM2NzA2IDQuMDg3MzQxMjEgNC4wODczNDEyMSAxMC4yMTgzNTIxNCA4LjE3NDY4MTU0IDEyLjI2MjAyMjc2IDguMTc0NjgyNDItMjAuNDM2NzA1MTggMTIuMjYyMDIyNzYtNDAuODczNDEwMzUgMTAuMjE4MzUzMDItNjEuMzEwMTE1NTMgMi4wNDM2NzA2LTI0LjUyNDA0NjM4LTQuMDg3MzQxMjEtNTMuMTM1NDMzMS00LjA4NzM0MTItNzkuNzAzMTUwMXogbS0xNC4zMDU2OTMzNi02MS4zMTAxMTQ2NWMwIDIuMDQzNjcwNiAwIDQuMDg3MzQxMjEtMi4wNDM2NzA2IDYuMTMxMDEwOTQtMi4wNDM2NzA2IDIuMDQzNjcwNi0yLjA0MzY3MDYgNC4wODczNDEyMS00LjA4NzM0MTIyIDYuMTMxMDExODEtNC4wODczNDEyMSA0LjA4NzM0MTIxLTEwLjIxODM1MjE0IDYuMTMxMDExODItMTQuMzA1NjkzMzYgOC4xNzQ2ODI0My02LjEzMTAxMTgyIDAtMTAuMjE4MzUyMTQgMC0xNC4zMDU2OTMzNi00LjA4NzM0MTIxLTIuMDQzNjcwNi0yLjA0MzY3MDYtNC4wODczNDEyMS0yLjA0MzY3MDYtNi4xMzEwMTE4Mi00LjA4NzM0MTIyLTIuMDQzNjcwNiAwLTIuMDQzNjcwNi0yLjA0MzY3MDYtMi4wNDM2NzA2LTQuMDg3MzQxMnMwLTQuMDg3MzQxMjEgMi4wNDM2NzA2LTYuMTMxMDExODJsNC4wODczNDEyMi00LjA4NzM0MDMzYzYuMTMxMDExODItNC4wODczNDEyMSAxNC4zMDU2OTMzNi02LjEzMTAxMTgyIDIwLjQzNjcwNTE4LTYuMTMxMDExODJoMTAuMjE4MzUyMTRjMi4wNDM2NzA2IDAgNC4wODczNDEyMSAyLjA0MzY3MDYgNi4xMzEwMTE4MiAyLjA0MzY3MDYxdjYuMTMxMDExODF6IgogICAgZmlsbD0iIzMwNjA5MiIgcC1pZD0iNzMzNSI+PC9wYXRoPgogIDxwYXRoCiAgICBkPSJNNDc4LjkxMjk3NDggMzU2LjM2OTQ4MzY5Yy0yLjA0MzY3MDYtMTQuMzA1NjkzMzYtMTAuMjE4MzUyMTQtMjYuNTY3NzE3LTI0LjUyNDA0NjM4LTMyLjY5ODcyODgxLTYuMTMxMDExODItMi4wNDM2NzA2LTEyLjI2MjAyMjc2LTIuMDQzNjcwNi0xOC4zOTMwMzQ1Ny0yLjA0MzY3MDYxLTguMTc0NjgyNDIgMC0xNC4zMDU2OTMzNiAyLjA0MzY3MDYtMjIuNDgwMzc1NzggMi4wNDM2NzA2MS0xNC4zMDU2OTMzNiA0LjA4NzM0MTIxLTI4LjYxMTM4NzYgMTAuMjE4MzUyMTQtNDIuOTE3MDgwOTYgMTYuMzQ5MzYzOTctNC4wODczNDEyMSAyLjA0MzY3MDYtOC4xNzQ2ODI0MiA2LjEzMTAxMTgyLTEyLjI2MjAyMjc2IDEwLjIxODM1MzAyIDAgNi4xMzEwMTE4MiAyLjA0MzY3MDYgMTguMzkzMDM0NTcgMi4wNDM2NzA2MiAzNi43ODYwNjkxNSAwIDI2LjU2NzcxNyAwIDUzLjEzNTQzMzEtNC4wODczNDEyMiA3Ny42NTk0Nzk0OC0xMC4yMTgzNTIxNCA1NS4xNzkxMDM3MSAyNi41Njc3MTcgMTA2LjI3MDg2NzA5IDgxLjc0NjgyMDcgMTE2LjQ4OTIxOTI0IDQuMDg3MzQxMjEgMCA2LjEzMTAxMTgyIDAgMTAuMjE4MzUyMTUgMi4wNDM2NzA2MiA0LjA4NzM0MTIxLTEyLjI2MjAyMjc2IDEwLjIxODM1MjE0LTI2LjU2NzcxNyAxMi4yNjIwMjI3NS00MC44NzM0MTAzNiAxMi4yNjIwMjI3Ni0zNi43ODYwNjkxNCA0Mi45MTcwODA5Ni02Ny40NDExMjczNCAxOC4zOTMwMzU0NS0xODUuOTc0MDE2MzF6IG0tMTQuMzA1Njk0MjMgMi4wNDM2NzA2MWwtNi4xMzEwMTA5NCA2LjEzMTAxMDkzYy00LjA4NzM0MTIxIDQuMDg3MzQxMjEtMTAuMjE4MzUyMTQgNi4xMzEwMTE4Mi0xNi4zNDkzNjQ4NCA0LjA4NzM0MTIxLTYuMTMxMDExODItMi4wNDM2NzA2LTEwLjIxODM1MjE0LTQuMDg3MzQxMjEtMTQuMzA1NjkzMzYtMTAuMjE4MzUyMTQtMi4wNDM2NzA2LTIuMDQzNjcwNi00LjA4NzM0MTIxLTQuMDg3MzQxMjEtNC4wODczNDEyMi02LjEzMTAxMTgyLTQuMDg3MzQxMjEtNC4wODczNDEyMS00LjA4NzM0MTIxLTYuMTMxMDExODItNC4wODczNDAzMy04LjE3NDY4MjQyIDAtNC4wODczNDEyMSA0LjA4NzM0MTIxLTYuMTMxMDExODIgOC4xNzQ2ODE1NS04LjE3NDY4MTU0IDQuMDg3MzQxMjEtMi4wNDM2NzA2IDguMTc0NjgyNDItMi4wNDM2NzA2IDEwLjIxODM1MzAyLTIuMDQzNjcwNjFoNi4xMzEwMTA5NGM2LjEzMTAxMTgyIDAgMTAuMjE4MzUyMTQgMi4wNDM2NzA2IDE0LjMwNTY5NDI0IDYuMTMxMDEwOTQgMi4wNDM2NzA2IDIuMDQzNjcwNiA0LjA4NzM0MTIxIDIuMDQzNjcwNiA2LjEzMTAxMDk0IDQuMDg3MzQxMjFzMi4wNDM2NzA2IDQuMDg3MzQxMjEgMi4wNDM2NzA2IDguMTc0NjgyNDJjLTIuMDQzNjcwNiAyLjA0MzY3MDYtMi4wNDM2NzA2IDQuMDg3MzQxMjEtMi4wNDM2NzA2IDYuMTMxMDExODJ6TTc3Ny4yODg4NjkxNCA2MTEuODI4Mjk3OTVjLTQuMDg3MzQxMjEgMi4wNDM2NzA2LTguMTc0NjgyNDIgMi4wNDM2NzA2LTEyLjI2MjAyMjc2IDIuMDQzNjcwNjEtNC4wODczNDEyMSAyLjA0MzY3MDYtNi4xMzEwMTE4MiA0LjA4NzM0MTIxLTEwLjIxODM1MzAyIDguMTc0NjgxNTQtMi4wNDM2NzA2IDguMTc0NjgyNDItNC4wODczNDEyMSAxNi4zNDkzNjM5Ni00LjA4NzM0MDMzIDI0LjUyNDA0NjM4IDIuMDQzNjcwNiAyLjA0MzY3MDYgNC4wODczNDEyMSAyLjA0MzY3MDYgNi4xMzEwMTA5MyAyLjA0MzY3MDYxIDguMTc0NjgyNDIgMi4wNDM2NzA2IDE4LjM5MzAzNDU3IDQuMDg3MzQxMjEgMjguNjExMzg3NiA0LjA4NzM0MTIxIDI0LjUyNDA0NjM4IDAgNDcuMDA0NDIyMTctNC4wODczNDEyMSA2OS40ODQ3OTcwNy0xMi4yNjIwMjM2MyAxMi4yNjIwMjI3Ni02LjEzMTAxMTgyIDI0LjUyNDA0NjM4LTEyLjI2MjAyMjc2IDM0Ljc0MjM5OTQyLTIyLjQ4MDM3NTc5LTUzLjEzNTQzMzEgMTAuMjE4MzUyMTQtNzkuNzAzMTUwMSA4LjE3NDY4MjQyLTk4LjA5NjE4NDY3IDAtNi4xMzEwMTE4Mi0yLjA0MzY3MDYtMTAuMjE4MzUyMTQtNC4wODczNDEyMS0xNC4zMDU2OTQyNC02LjEzMTAxMDkzek00NzYuODY5MzA0MiA2MTUuOTE1NjM4MjhjLTIuMDQzNjcwNiAwLTYuMTMxMDExODIgMC0xMC4yMTgzNTMwMyA4LjE3NDY4MjQyLTEyLjI2MjAyMjc2IDE0LjMwNTY5MzM2LTE2LjM0OTM2Mzk2IDI0LjUyNDA0NjM4LTI4LjYxMTM4NjcyIDMyLjY5ODcyNzk0LTE2LjM0OTM2Mzk2IDEwLjIxODM1MjE0LTMyLjY5ODcyNzkzIDE2LjM0OTM2Mzk2LTUzLjEzNTQzMzk4IDE4LjM5MzAzNDU2LTYuMTMxMDExODIgMC0xMi4yNjIwMjI3NiAyLjA0MzY3MDYtMTYuMzQ5MzYzOTYgNi4xMzEwMTE4MmwyLjA0MzY3MDYgMi4wNDM2NzA2MWM0LjA4NzM0MTIxIDIuMDQzNjcwNiAxMC4yMTgzNTIxNCA0LjA4NzM0MTIxIDEyLjI2MjAyMjc2IDYuMTMxMDExODEgMTYuMzQ5MzYzOTYgNC4wODczNDEyMSAzNC43NDIzOTg1NCA2LjEzMTAxMTgyIDUzLjEzNTQzMzk4IDQuMDg3MzQwMzQgMjQuNTI0MDQ2MzgtNC4wODczNDEyMSA0Ny4wMDQ0MjIxNy0xOC4zOTMwMzQ1NyA1OS4yNjY0NDQ5Mi0zOC44Mjk3Mzk3NSA0LjA4NzM0MTIxLTYuMTMxMDExODIgNC4wODczNDEyMS0xMi4yNjIwMjI3NiAwLTE4LjM5MzAzNDU3LTIuMDQzNjcwNi02LjEzMTAxMTgyLTguMTc0NjgyNDItMTIuMjYyMDIyNzYtMTIuMjYyMDIzNjQtMTQuMzA1NjkzMzYgMC02LjEzMTAxMTgyLTQuMDg3MzQxMjEtNi4xMzEwMTE4Mi02LjEzMTAxMDkzLTYuMTMxMDExODJ6IgogICAgZmlsbD0iIzMwNjA5MiIgcC1pZD0iNzMzNiI+PC9wYXRoPgo8L3N2Zz4K`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/proton.js b/frontend/crawlab-ui/src/assets/js/svg/proton.js
new file mode 100644
index 00000000..0570d764
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/proton.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB2aWV3Qm94PSIwIDAgNTAgNTAiIHdpZHRoPSI1MHB4IiBoZWlnaHQ9IjUwcHgiPjxwYXRoIGQ9Ik0gNS4xMzA4NTk0IDguMDI3MzQzOCBDIDQuMDc0MDM5OCA3Ljk2Mjk5NTQgMyA4Ljc3MzcxNCAzIDkuOTQxNDA2MiBMIDMgMTUuNTQ0OTIyIEwgMyAzNS4yNzUzOTEgQyAzIDM3Ljg5OTIzOSA1LjE3MzU5MzggNDAuMDMzMjAzIDcuODEwNTQ2OSA0MC4wMzMyMDMgTCA0MC4xODk0NTMgNDAuMDMzMjAzIEMgNDIuODI2NzIzIDQwLjAzMzIwMyA0NSAzNy44OTg1MDMgNDUgMzUuMjc1MzkxIEwgNDUgOS45OTQxNDA2IEMgNDUgOC40MjEwMTgzIDQzLjA1MTUxNSA3LjUwNDkwMSA0MS44MjYxNzIgOC40ODI0MjE5IEwgMzYgMTMuMTI1IEwgMzYgMTMuMTIxMDk0IEwgMjUuNzMyNDIyIDIxLjMwNjY0MSBMIDI1LjcxODc1IDIxLjMxNDQ1MyBDIDI0LjY4Mjc3MyAyMi4xMzA5ODIgMjMuMjEwNTY5IDIyLjEyNTc5MSAyMi4xODE2NDEgMjEuMjk4ODI4IEwgNi4xNTIzNDM4IDguNDEwMTU2MiBMIDYuMTQwNjI1IDguNDAyMzQzOCBDIDUuODMyMzc1NCA4LjE2NzQ2MzQgNS40ODMxMzI2IDguMDQ4NzkzMSA1LjEzMDg1OTQgOC4wMjczNDM4IHogTSA1IDEwLjA1MDc4MSBMIDIwLjkyNzczNCAyMi44NTc0MjIgQyAyMS40NTYyNjQgMjMuMjgyMjE1IDIyLjA1NzkzNyAyMy41NjQgMjIuNjgzNTk0IDIzLjczNDM3NSBMIDIxLjAxNTYyNSAyNS4wNjQ0NTMgQyAxOS45ODc2MjUgMjUuODg0NDUzIDE4LjQ5Njc1IDI1Ljg4MTU5NCAxNy40Njg3NSAyNS4wNTg1OTQgTCA1IDE1LjA2NDQ1MyBMIDUgMTAuMDUwNzgxIHogTSA0MyAxMC4xMDM1MTYgTCA0MyAzNS4yNzUzOTEgQyA0MyAzNi44MDIyNzggNDEuNzYwMTgzIDM4LjAzMzIwMyA0MC4xODk0NTMgMzguMDMzMjAzIEwgMzYgMzguMDMzMjAzIEwgMzYgMTUuNjgxNjQxIEwgNDMgMTAuMTAzNTE2IHoiLz48L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/puppeteer.js b/frontend/crawlab-ui/src/assets/js/svg/puppeteer.js
new file mode 100644
index 00000000..4bc06544
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/puppeteer.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9Ii02My41IDAgMzgzIDM4MyIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJ4TWlkWU1pZCI+CgkJPGc+CgkJCQk8cGF0aCBkPSJNMjUzLjQyMTYsMjE0LjI0NjExNyBMMi4xOTE2LDIxNC4yNDYxMTcgTDIuMTkxNiwyMDAuMDc4MTE3IEMyLjE5MTYsMTkwLjU1ODExNyA5LjkwODYsMTgyLjg0MTExNyAxOS40Mjc2LDE4Mi44NDExMTcgTDIzNi4xODQ2LDE4Mi44NDExMTcgQzI0NS43MDQ2LDE4Mi44NDExMTcgMjUzLjQyMTYsMTkwLjU1ODExNyAyNTMuNDIxNiwyMDAuMDc4MTE3IEwyNTMuNDIxNiwyMTQuMjQ2MTE3IFoiIGZpbGw9IiNERkRFREYiPgoNPC9wYXRoPgoJCQkJPHBhdGggZD0iTTIzNS45MTkyLDM3OC41NTI4MTcgTDE5LjY5MzIsMzc4LjU1MjgxNyBDMTAuMDI3MiwzNzguNTUyODE3IDIuMTkxMiwzNzAuNzE2ODE3IDIuMTkxMiwzNjEuMDUwODE3IEwyLjE5MTIsMjE0LjI0NTgxNyBMMjUzLjQyMTIsMjE0LjI0NTgxNyBMMjUzLjQyMTIsMzYxLjA1MDgxNyBDMjUzLjQyMTIsMzcwLjcxNjgxNyAyNDUuNTg2MiwzNzguNTUyODE3IDIzNS45MTkyLDM3OC41NTI4MTciIGZpbGw9IiNGRkZGRkYiPgoNPC9wYXRoPgoJCQkJPHBhdGggZD0iTTI1MC4zMDIsMjExLjM5NzUxNyBMMjUwLjMwMiwxOTguNjkxNTE3IEMyNTAuMzAyLDE5MS4xOTA1MTcgMjQ0LjIyMiwxODUuMTA5NTE3IDIzNi43MjEsMTg1LjEwOTUxNyBMMTkuMjc5LDE4NS4xMDk1MTcgQzExLjc3OCwxODUuMTA5NTE3IDUuNjk4LDE5MS4xOTA1MTcgNS42OTgsMTk4LjY5MTUxNyBMNS42OTgsMjExLjM5NzUxNyBMMjUwLjMwMiwyMTEuMzk3NTE3IFogTTI1MC4zMDIsMzYyLjg2NjUxNyBMMjUwLjMwMiwyMTcuMDk1NTE3IEw1LjY5OCwyMTcuMDk1NTE3IEw1LjY5OCwzNjIuODY2NTE3IEM1LjY5OCwzNzAuMzY3NTE3IDExLjc3OCwzNzYuNDQ3NTE3IDE5LjI3OSwzNzYuNDQ3NTE3IEwyMzYuNzIxLDM3Ni40NDc1MTcgQzI0NC4yMjIsMzc2LjQ0NzUxNyAyNTAuMzAyLDM3MC4zNjc1MTcgMjUwLjMwMiwzNjIuODY2NTE3IFogTTIwLjU1NCwxNzkuNDEyNTE3IEwyMS40LDE3MC4xNzY1MTcgTDE3LjgwOCwxNzkuNDg2NTE3IEMxOC4yOTUsMTc5LjQ0OTUxNyAxOC43ODIsMTc5LjQxMjUxNyAxOS4yNzksMTc5LjQxMjUxNyBMMjAuNTU0LDE3OS40MTI1MTcgWiBNMjMzLjk1NSwxNjMuODQyNTE3IEwyMzUuMjA5LDE3OS40MTI1MTcgTDIzNi43MjEsMTc5LjQxMjUxNyBDMjM3LjUzLDE3OS40MTI1MTcgMjM4LjMyMiwxNzkuNDc4NTE3IDIzOS4xMDUsMTc5LjU3NTUxNyBMMjMzLjk1NSwxNjMuODQyNTE3IFogTTI1MS4yMDIsMTg2LjAwNjUxNyBDMjU0LjE3OSwxODkuNDAwNTE3IDI1NiwxOTMuODMyNTE3IDI1NiwxOTguNjkxNTE3IEwyNTYsMzYyLjg2NjUxNyBDMjU2LDM3My40OTc1MTcgMjQ3LjM1MSwzODIuMTQ1NTE3IDIzNi43MjEsMzgyLjE0NTUxNyBMMTkuMjc5LDM4Mi4xNDU1MTcgQzguNjQ5LDM4Mi4xNDU1MTcgLTguNTI2NTEyODNlLTE0LDM3My40OTc1MTcgLTguNTI2NTEyODNlLTE0LDM2Mi44NjY1MTcgTC04LjUyNjUxMjgzZS0xNCwxOTguNjkxNTE3IEMtOC41MjY1MTI4M2UtMTQsMTkzLjUzOTUxNyAyLjA0MywxODguODY0NTE3IDUuMzQ3LDE4NS40MDE1MTcgTDI0LjU2NywxMzUuNTg0NTE3IEwzMi45NDgsNDQuMDMwNTE2NyBMNDIuNDA0LDQ0Ljg5NTUxNjcgTDM0Ljg1MiwxMjcuMzg3NTE3IEwzNy41MTIsMTI4LjQxMzUxNyBMMzMuOTAyLDEzNy43NzE1MTcgTDMwLjA5LDE3OS40MTI1MTcgTDIyNS42ODMsMTc5LjQxMjUxNyBMMjE0LjgyOSw0NC42Njc1MTY3IEwyMjQuMjkzLDQzLjkwNDUxNjcgTDIzMC42OTUsMTIzLjM3MzUxNyBMMjMwLjY5OSwxMjMuMzcyNTE3IEwyNTEuMjAyLDE4Ni4wMDY1MTcgWiBNMjEuMTU2NSwxOTQuMzk2NTE3IEMyMy41MzI1LDE5NC4zOTY1MTcgMjUuNDU4NSwxOTYuMzIyNTE3IDI1LjQ1ODUsMTk4LjY5ODUxNyBDMjUuNDU4NSwyMDEuMDc0NTE3IDIzLjUzMjUsMjAzLjAwMDUxNyAyMS4xNTY1LDIwMy4wMDA1MTcgQzE4Ljc4MDUsMjAzLjAwMDUxNyAxNi44NTQ1LDIwMS4wNzQ1MTcgMTYuODU0NSwxOTguNjk4NTE3IEMxNi44NTQ1LDE5Ni4zMjI1MTcgMTguNzgwNSwxOTQuMzk2NTE3IDIxLjE1NjUsMTk0LjM5NjUxNyBaIE0zNS44NjQ1LDE5NC4zOTY1MTcgQzM4LjI0MDUsMTk0LjM5NjUxNyA0MC4xNjY1LDE5Ni4zMjI1MTcgNDAuMTY2NSwxOTguNjk4NTE3IEM0MC4xNjY1LDIwMS4wNzQ1MTcgMzguMjQwNSwyMDMuMDAwNTE3IDM1Ljg2NDUsMjAzLjAwMDUxNyBDMzMuNDg4NSwyMDMuMDAwNTE3IDMxLjU2MjUsMjAxLjA3NDUxNyAzMS41NjI1LDE5OC42OTg1MTcgQzMxLjU2MjUsMTk2LjMyMjUxNyAzMy40ODg1LDE5NC4zOTY1MTcgMzUuODY0NSwxOTQuMzk2NTE3IFogTTUwLjEwOTEsMTk0LjM5NjUxNyBDNTIuNDg1MSwxOTQuMzk2NTE3IDU0LjQxMTEsMTk2LjMyMjUxNyA1NC40MTExLDE5OC42OTg1MTcgQzU0LjQxMTEsMjAxLjA3NDUxNyA1Mi40ODUxLDIwMy4wMDA1MTcgNTAuMTA5MSwyMDMuMDAwNTE3IEM0Ny43MzMxLDIwMy4wMDA1MTcgNDUuODA3MSwyMDEuMDc0NTE3IDQ1LjgwNzEsMTk4LjY5ODUxNyBDNDUuODA3MSwxOTYuMzIyNTE3IDQ3LjczMzEsMTk0LjM5NjUxNyA1MC4xMDkxLDE5NC4zOTY1MTcgWiIgZmlsbD0iIzAwMDAwMCI+Cg08L3BhdGg+CgkJCQk8cGF0aCBkPSJNMzYuMDIxNywyODcuNzQ4MTE3IEMzNi43NjA3LDI4Ni44OTMxMTcgMzcuMTQ5NywyODUuNzY5MTE3IDM3LjE0OTcsMjg0LjM3NDExNyBDMzcuMTQ5NywyODIuOTc4MTE3IDM2Ljc2MDcsMjgxLjg5OTExNyAzNi4wMjE3LDI4MS4wNDQxMTcgQzM1LjI0MzcsMjgwLjE4OTExNyAzNC4xNTM3LDI3OS43ODQxMTcgMzIuNzUzNywyNzkuNzg0MTE3IEwyNS45MDY3LDI3OS43ODQxMTcgTDI1LjkwNjcsMjg5LjAwNzExNyBMMzIuNzUzNywyODkuMDA3MTE3IEMzNC4xNTM3LDI4OS4wMDcxMTcgMzUuMjQzNywyODguNjAyMTE3IDM2LjAyMTcsMjg3Ljc0ODExNyBaIE0zMi43NTM3LDI3NC4yNTExMTcgQzM1LjcwOTcsMjc0LjI1MTExNyAzOC4wNDQ3LDI3NS4xNTExMTcgMzkuNzk0NywyNzYuOTk1MTE3IEM0MS41NDU3LDI3OC44MzkxMTcgNDIuNDAxNywyODEuMzEzMTE3IDQyLjQwMTcsMjg0LjM3NDExNyBDNDIuNDAxNywyODcuNDc3MTE3IDQxLjU0NTcsMjg5LjkwNzExNyAzOS44MzQ3LDI5MS43NTExMTcgQzM4LjA4MzcsMjkzLjU5NzExNyAzNS43NDk3LDI5NC41NDExMTcgMzIuNzUzNywyOTQuNTQxMTE3IEwyNS45MDY3LDI5NC41NDExMTcgTDI1LjkwNjcsMzA2Ljk1NTExNyBMMjAuNjE1NywzMDYuOTU1MTE3IEwyMC42MTU3LDI3NC4yNTExMTcgTDMyLjc1MzcsMjc0LjI1MTExNyBaIE02MS4yOTgxLDI5NC4zMDc2MTcgTDYxLjI5ODEsMjg0Ljc2NzYxNyBMNjYuOTk2MSwyODQuNzY3NjE3IEw2Ni45OTYxLDMwNi4zMDc2MTcgTDYxLjI5ODEsMzA2LjMwNzYxNyBMNjEuMjk4MSwzMDQuMzY1NjE3IEM1OS41NzExLDMwNi4wOTE2MTcgNTcuMzI3MSwzMDYuOTU0NjE3IDU0LjUyMTEsMzA2Ljk1NDYxNyBDNTEuOTMxMSwzMDYuOTU0NjE3IDQ5Ljg1OTEsMzA2LjA5MTYxNyA0OC4yNjIxLDMwNC40MDg2MTcgQzQ2LjY2NTEsMzAyLjcyNTYxNyA0NS44ODgxLDMwMC41NjY2MTcgNDUuODg4MSwyOTcuODkwNjE3IEw0NS44ODgxLDI4NC43Njc2MTcgTDUxLjU4NjEsMjg0Ljc2NzYxNyBMNTEuNTg2MSwyOTYuNjgxNjE3IEM1MS41ODYxLDI5OC4xOTI2MTcgNTEuOTc0MSwyOTkuNDQ0NjE3IDUyLjc5NDEsMzAwLjM1MTYxNyBDNTMuNTcxMSwzMDEuMjU3NjE3IDU0LjYwNzEsMzAxLjczMjYxNyA1NS45NDUxLDMwMS43MzI2MTcgQzU5LjUyODEsMzAxLjczMjYxNyA2MS4yOTgxLDI5OS4yNzE2MTcgNjEuMjk4MSwyOTQuMzA3NjE3IFogTTg5LjIyNDMsMzAwLjIyMTcxNyBDOTAuMzQ2MywyOTkuMDU1NzE3IDkwLjkwNzMsMjk3LjUwMTcxNyA5MC45MDczLDI5NS41NTk3MTcgQzkwLjkwNzMsMjkzLjYxNzcxNyA5MC4zNDYzLDI5Mi4wMTk3MTcgODkuMjI0MywyOTAuODU0NzE3IEM4OC4wNTgzLDI4OS42ODg3MTcgODYuNjM0MywyODkuMDg0NzE3IDg0Ljg2NDMsMjg5LjA4NDcxNyBDODMuMjY3MywyODkuMDg0NzE3IDgxLjg0MzMsMjg5LjY4ODcxNyA4MC41OTEzLDI5MC44OTc3MTcgQzc5LjMzOTMsMjkyLjEwNjcxNyA3OC43MzUzLDI5My42NTk3MTcgNzguNzM1MywyOTUuNTU5NzE3IEM3OC43MzUzLDI5Ny40NTg3MTcgNzkuMzM5MywyOTkuMDEzNzE3IDgwLjU5MTMsMzAwLjE3NzcxNyBDODEuODQzMywzMDEuMzg3NzE3IDgzLjI2NzMsMzAxLjk5MTcxNyA4NC44NjQzLDMwMS45OTE3MTcgQzg2LjYzNDMsMzAxLjk5MTcxNyA4OC4wNTgzLDMwMS4zODc3MTcgODkuMjI0MywzMDAuMjIxNzE3IFogTTg1Ljg1NzMsMjg0LjEyMDcxNyBDODguNjYzMywyODQuMTIwNzE3IDkxLjE2NjMsMjg1LjIwMDcxNyA5My4yODIzLDI4Ny4zNTg3MTcgQzk1LjM5NzMsMjg5LjU1OTcxNyA5Ni40MzIzLDI5Mi4yNzg3MTcgOTYuNDMyMywyOTUuNTU5NzE3IEM5Ni40MzIzLDI5OC44Mzk3MTcgOTUuMzk3MywzMDEuNTU5NzE3IDkzLjI4MjMsMzAzLjcxNzcxNyBDOTEuMTY2MywzMDUuODc2NzE3IDg4LjcwNjMsMzA2Ljk1NDcxNyA4NS44NTczLDMwNi45NTQ3MTcgQzgzLjA1MTMsMzA2Ljk1NDcxNyA4MC42NzczLDMwNi4xNzc3MTcgNzguNzM1MywzMDQuNjI0NzE3IEw3OC43MzUzLDMxNi41ODM3MTcgTDczLjAzNzMsMzE2LjU4MzcxNyBMNzMuMDM3MywyODQuNzY3NzE3IEw3OC43MzUzLDI4NC43Njc3MTcgTDc4LjczNTMsMjg2LjQ5NDcxNyBDODAuNjM0MywyODQuODk3NzE3IDgzLjAwODMsMjg0LjEyMDcxNyA4NS44NTczLDI4NC4xMjA3MTcgWiBNMTE2LjkzNDgsMzAwLjIyMTcxNyBDMTE4LjA1NjgsMjk5LjA1NTcxNyAxMTguNjE3OCwyOTcuNTAxNzE3IDExOC42MTc4LDI5NS41NTk3MTcgQzExOC42MTc4LDI5My42MTc3MTcgMTE4LjA1NjgsMjkyLjAxOTcxNyAxMTYuOTM0OCwyOTAuODU0NzE3IEMxMTUuNzY4OCwyODkuNjg4NzE3IDExNC4zNDQ4LDI4OS4wODQ3MTcgMTEyLjU3NDgsMjg5LjA4NDcxNyBDMTEwLjk3NzgsMjg5LjA4NDcxNyAxMDkuNTUzOCwyODkuNjg4NzE3IDEwOC4zMDE4LDI5MC44OTc3MTcgQzEwNy4wNDk4LDI5Mi4xMDY3MTcgMTA2LjQ0NDgsMjkzLjY1OTcxNyAxMDYuNDQ0OCwyOTUuNTU5NzE3IEMxMDYuNDQ0OCwyOTcuNDU4NzE3IDEwNy4wNDk4LDI5OS4wMTM3MTcgMTA4LjMwMTgsMzAwLjE3NzcxNyBDMTA5LjU1MzgsMzAxLjM4NzcxNyAxMTAuOTc3OCwzMDEuOTkxNzE3IDExMi41NzQ4LDMwMS45OTE3MTcgQzExNC4zNDQ4LDMwMS45OTE3MTcgMTE1Ljc2ODgsMzAxLjM4NzcxNyAxMTYuOTM0OCwzMDAuMjIxNzE3IFogTTExMy41NjY4LDI4NC4xMjA3MTcgQzExNi4zNzI4LDI4NC4xMjA3MTcgMTE4Ljg3NjgsMjg1LjIwMDcxNyAxMjAuOTkyOCwyODcuMzU4NzE3IEMxMjMuMTA2OCwyODkuNTU5NzE3IDEyNC4xNDI4LDI5Mi4yNzg3MTcgMTI0LjE0MjgsMjk1LjU1OTcxNyBDMTI0LjE0MjgsMjk4LjgzOTcxNyAxMjMuMTA2OCwzMDEuNTU5NzE3IDEyMC45OTI4LDMwMy43MTc3MTcgQzExOC44NzY4LDMwNS44NzY3MTcgMTE2LjQxNjgsMzA2Ljk1NDcxNyAxMTMuNTY2OCwzMDYuOTU0NzE3IEMxMTAuNzYxOCwzMDYuOTU0NzE3IDEwOC4zODc4LDMwNi4xNzc3MTcgMTA2LjQ0NDgsMzA0LjYyNDcxNyBMMTA2LjQ0NDgsMzE2LjU4MzcxNyBMMTAwLjc0NzgsMzE2LjU4MzcxNyBMMTAwLjc0NzgsMjg0Ljc2NzcxNyBMMTA2LjQ0NDgsMjg0Ljc2NzcxNyBMMTA2LjQ0NDgsMjg2LjQ5NDcxNyBDMTA4LjM0NDgsMjg0Ljg5NzcxNyAxMTAuNzE4OCwyODQuMTIwNzE3IDExMy41NjY4LDI4NC4xMjA3MTcgWiBNMTMyLjU1ODMsMjkzLjA1NTcxNyBMMTQyLjk2MTMsMjkzLjA1NTcxNyBDMTQyLjYxNjMsMjkxLjcxNzcxNyAxNDEuOTY4MywyOTAuNzI1NzE3IDE0MS4wNjIzLDI5MC4wNzY3MTcgQzE0MC4xNTUzLDI4OS40Mjk3MTcgMTM5LjA3NzMsMjg5LjA4NDcxNyAxMzcuODY3MywyODkuMDg0NzE3IEMxMzYuNzAzMywyODkuMDg0NzE3IDEzNS41ODAzLDI4OS40Mjk3MTcgMTM0LjU4NzMsMjkwLjEyMDcxNyBDMTMzLjU1MTMsMjkwLjgxMTcxNyAxMzIuODYwMywyOTEuODAzNzE3IDEzMi41NTgzLDI5My4wNTU3MTcgWiBNMTM3Ljg2NzMsMjg0LjEyMDcxNyBDMTQwLjgwMzMsMjg0LjEyMDcxNyAxNDMuMzUwMywyODUuMTU2NzE3IDE0NS40NjUzLDI4Ny4xODU3MTcgQzE0Ny41ODAzLDI4OS4yNTc3MTcgMTQ4LjY1OTMsMjkxLjg4OTcxNyAxNDguNzg5MywyOTUuMTI3NzE3IEwxNDguNzg5MywyOTcuMDcwNzE3IEwxMzIuMzg2MywyOTcuMDcwNzE3IEMxMzIuNjg4MywyOTguNTgwNzE3IDEzMy4zMzUzLDI5OS43ODg3MTcgMTM0LjMyOTMsMzAwLjY5NjcxNyBDMTM1LjMyMTMsMzAxLjYwMjcxNyAxMzYuNDQzMywzMDIuMDc2NzE3IDEzNy43ODEzLDMwMi4wNzY3MTcgQzE0MC4yNDIzLDMwMi4wNzY3MTcgMTQyLjAxMjMsMzAxLjEyNzcxNyAxNDMuMTM0MywyOTkuMjI4NzE3IEwxNDguMjI4MywzMDAuMzA3NzE3IEMxNDcuMzIxMywzMDIuNTA5NzE3IDE0NS45NDAzLDMwNC4xNTA3MTcgMTQ0LjEyNzMsMzA1LjI3MjcxNyBDMTQyLjMxNDMsMzA2LjM5NDcxNyAxNDAuMTk5MywzMDYuOTU0NzE3IDEzNy43ODEzLDMwNi45NTQ3MTcgQzEzNC42NzMzLDMwNi45NTQ3MTcgMTMyLjA0MDMsMzA1Ljg3NjcxNyAxMjkuOTI1MywzMDMuNzYxNzE3IEMxMjcuODEwMywzMDEuNjQ2NzE3IDEyNi43MzAzLDI5OC44ODM3MTcgMTI2LjczMDMsMjk1LjU1OTcxNyBDMTI2LjczMDMsMjkyLjIzNjcxNyAxMjcuODEwMywyODkuNDczNzE3IDEyOS45NjgzLDI4Ny4zMTQ3MTcgQzEzMi4wODMzLDI4NS4yMDA3MTcgMTM0LjcxNzMsMjg0LjEyMDcxNyAxMzcuODY3MywyODQuMTIwNzE3IFogTTE2NC40OTk3LDMwMS42NDY1MTcgQzE2NS41Nzg3LDMwMS42NDY1MTcgMTY2Ljc4NzcsMzAxLjMwMDUxNyAxNjguMDgyNywzMDAuNjEwNTE3IEwxNjkuNzIyNywzMDUuMzU4NTE3IEMxNjcuNzM2NywzMDYuNDM3NTE3IDE2NS44ODA3LDMwNi45NTU1MTcgMTY0LjExMDcsMzA2Ljk1NTUxNyBDMTYxLjQ3NzcsMzA2Ljk1NTUxNyAxNTkuNDA1NywzMDYuMjIxNTE3IDE1Ny44OTQ3LDMwNC43OTY1MTcgQzE1Ni4zODQ3LDMwMy4zNzM1MTcgMTU1LjY1MDcsMzAxLjI1NzUxNyAxNTUuNjUwNywyOTguMzY1NTE3IEwxNTUuNjUwNywyODkuNDI5NTE3IEwxNTEuMDMxNywyODkuNDI5NTE3IEwxNTEuMDMxNywyODQuNzY3NTE3IEwxNTUuNjUwNywyODQuNzY3NTE3IEwxNTUuNjUwNywyNzguMjUwNTE3IEwxNjEuMzQ4NywyNzguMjUwNTE3IEwxNjEuMzQ4NywyODQuNzY3NTE3IEwxNjguMzg0NywyODQuNzY3NTE3IEwxNjguMzg0NywyODkuNDI5NTE3IEwxNjEuMzQ4NywyODkuNDI5NTE3IEwxNjEuMzQ4NywyOTcuOTMzNTE3IEMxNjEuMzQ4NywzMDAuMzk0NTE3IDE2Mi4zODQ3LDMwMS42NDY1MTcgMTY0LjQ5OTcsMzAxLjY0NjUxNyBaIE0xNzYuNDU0MywyOTMuMDU1NzE3IEwxODYuODU3MywyOTMuMDU1NzE3IEMxODYuNTEyMywyOTEuNzE3NzE3IDE4NS44NjQzLDI5MC43MjU3MTcgMTg0Ljk1ODMsMjkwLjA3NjcxNyBDMTg0LjA1MTMsMjg5LjQyOTcxNyAxODIuOTczMywyODkuMDg0NzE3IDE4MS43NjMzLDI4OS4wODQ3MTcgQzE4MC41OTkzLDI4OS4wODQ3MTcgMTc5LjQ3NjMsMjg5LjQyOTcxNyAxNzguNDgzMywyOTAuMTIwNzE3IEMxNzcuNDQ3MywyOTAuODExNzE3IDE3Ni43NTYzLDI5MS44MDM3MTcgMTc2LjQ1NDMsMjkzLjA1NTcxNyBaIE0xODEuNzYzMywyODQuMTIwNzE3IEMxODQuNjk5MywyODQuMTIwNzE3IDE4Ny4yNDYzLDI4NS4xNTY3MTcgMTg5LjM2MTMsMjg3LjE4NTcxNyBDMTkxLjQ3NjMsMjg5LjI1NzcxNyAxOTIuNTU1MywyOTEuODg5NzE3IDE5Mi42ODUzLDI5NS4xMjc3MTcgTDE5Mi42ODUzLDI5Ny4wNzA3MTcgTDE3Ni4yODIzLDI5Ny4wNzA3MTcgQzE3Ni41ODQzLDI5OC41ODA3MTcgMTc3LjIzMTMsMjk5Ljc4ODcxNyAxNzguMjI1MywzMDAuNjk2NzE3IEMxNzkuMjE3MywzMDEuNjAyNzE3IDE4MC4zMzkzLDMwMi4wNzY3MTcgMTgxLjY3NzMsMzAyLjA3NjcxNyBDMTg0LjEzODMsMzAyLjA3NjcxNyAxODUuOTA4MywzMDEuMTI3NzE3IDE4Ny4wMzAzLDI5OS4yMjg3MTcgTDE5Mi4xMjMzLDMwMC4zMDc3MTcgQzE5MS4yMTczLDMwMi41MDk3MTcgMTg5LjgzNjMsMzA0LjE1MDcxNyAxODguMDIzMywzMDUuMjcyNzE3IEMxODYuMjEwMywzMDYuMzk0NzE3IDE4NC4wOTUzLDMwNi45NTQ3MTcgMTgxLjY3NzMsMzA2Ljk1NDcxNyBDMTc4LjU2OTMsMzA2Ljk1NDcxNyAxNzUuOTM2MywzMDUuODc2NzE3IDE3My44MjEzLDMwMy43NjE3MTcgQzE3MS43MDYzLDMwMS42NDY3MTcgMTcwLjYyNjMsMjk4Ljg4MzcxNyAxNzAuNjI2MywyOTUuNTU5NzE3IEMxNzAuNjI2MywyOTIuMjM2NzE3IDE3MS43MDYzLDI4OS40NzM3MTcgMTczLjg2NDMsMjg3LjMxNDcxNyBDMTc1Ljk3OTMsMjg1LjIwMDcxNyAxNzguNjEzMywyODQuMTIwNzE3IDE4MS43NjMzLDI4NC4xMjA3MTcgWiBNMjAyLjA0OTUsMjkzLjA1NTcxNyBMMjEyLjQ1MjUsMjkzLjA1NTcxNyBDMjEyLjEwNzUsMjkxLjcxNzcxNyAyMTEuNDU5NSwyOTAuNzI1NzE3IDIxMC41NTM1LDI5MC4wNzY3MTcgQzIwOS42NDY1LDI4OS40Mjk3MTcgMjA4LjU2ODUsMjg5LjA4NDcxNyAyMDcuMzU4NSwyODkuMDg0NzE3IEMyMDYuMTk0NSwyODkuMDg0NzE3IDIwNS4wNzE1LDI4OS40Mjk3MTcgMjA0LjA3ODUsMjkwLjEyMDcxNyBDMjAzLjA0MjUsMjkwLjgxMTcxNyAyMDIuMzUxNSwyOTEuODAzNzE3IDIwMi4wNDk1LDI5My4wNTU3MTcgWiBNMjA3LjM1ODUsMjg0LjEyMDcxNyBDMjEwLjI5NDUsMjg0LjEyMDcxNyAyMTIuODQxNSwyODUuMTU2NzE3IDIxNC45NTY1LDI4Ny4xODU3MTcgQzIxNy4wNzE1LDI4OS4yNTc3MTcgMjE4LjE1MDUsMjkxLjg4OTcxNyAyMTguMjgwNSwyOTUuMTI3NzE3IEwyMTguMjgwNSwyOTcuMDcwNzE3IEwyMDEuODc3NSwyOTcuMDcwNzE3IEMyMDIuMTc5NSwyOTguNTgwNzE3IDIwMi44MjY1LDI5OS43ODg3MTcgMjAzLjgyMDUsMzAwLjY5NjcxNyBDMjA0LjgxMjUsMzAxLjYwMjcxNyAyMDUuOTM0NSwzMDIuMDc2NzE3IDIwNy4yNzI1LDMwMi4wNzY3MTcgQzIwOS43MzM1LDMwMi4wNzY3MTcgMjExLjUwMzUsMzAxLjEyNzcxNyAyMTIuNjI1NSwyOTkuMjI4NzE3IEwyMTcuNzE5NSwzMDAuMzA3NzE3IEMyMTYuODEyNSwzMDIuNTA5NzE3IDIxNS40MzE1LDMwNC4xNTA3MTcgMjEzLjYxODUsMzA1LjI3MjcxNyBDMjExLjgwNTUsMzA2LjM5NDcxNyAyMDkuNjkwNSwzMDYuOTU0NzE3IDIwNy4yNzI1LDMwNi45NTQ3MTcgQzIwNC4xNjQ1LDMwNi45NTQ3MTcgMjAxLjUzMTUsMzA1Ljg3NjcxNyAxOTkuNDE2NSwzMDMuNzYxNzE3IEMxOTcuMzAxNSwzMDEuNjQ2NzE3IDE5Ni4yMjE1LDI5OC44ODM3MTcgMTk2LjIyMTUsMjk1LjU1OTcxNyBDMTk2LjIyMTUsMjkyLjIzNjcxNyAxOTcuMzAxNSwyODkuNDczNzE3IDE5OS40NTk1LDI4Ny4zMTQ3MTcgQzIwMS41NzQ1LDI4NS4yMDA3MTcgMjA0LjIwODUsMjg0LjEyMDcxNyAyMDcuMzU4NSwyODQuMTIwNzE3IFogTTIzNi4wMTk3LDI4NC40NjU4MTcgQzIzNi42Njc3LDI4NC40NjU4MTcgMjM3LjE4NTcsMjg0LjUwODgxNyAyMzcuNTMxNywyODQuNjM3ODE3IEwyMzcuMjI4NywyOTAuMTIwODE3IEwyMzUuOTc3NywyOTAuMTIwODE3IEMyMzAuODQwNywyOTAuMTIwODE3IDIyOC4yOTI3LDI5My4xODU4MTcgMjI4LjI5MjcsMjk5LjM1ODgxNyBMMjI4LjI5MjcsMzA2LjMwNzgxNyBMMjIyLjU5NTcsMzA2LjMwNzgxNyBMMjIyLjU5NTcsMjg0Ljc2NzgxNyBMMjI4LjI5MjcsMjg0Ljc2NzgxNyBMMjI4LjI5MjcsMjg4LjY5NTgxNyBDMjMwLjIzNTcsMjg1Ljg4OTgxNyAyMzIuODI1NywyODQuNDY1ODE3IDIzNi4wMTk3LDI4NC40NjU4MTcgWiIgZmlsbD0iIzAwMDAwMCI+Cg08L3BhdGg+CgkJCQk8cG9seWdvbiBmaWxsPSIjMDBEOEEyIiBwb2ludHM9IjIxMS4wOTU0IDEzMi42MzgyMTcgMjQxLjgzNjQgMTE3Ljc5NzIxNyAyNDEuODM2NCAxMDIuOTU3MjE3IDE3MC44MTM0IDY5LjAzNTIxNjcgMjQxLjgzNjQgMzIuNDY0MjE2NyAyNDEuODM2NCAxOC4xNTQyMTY3IDIxMi4xNTU0IDQuMzczMjE2NzQgMTI4LjA1OTQgNDYuMjQ0MjE2NyA0Mi41NDk0IDQuMzczMjE2NzQgMTQuOTg4NCAxOS4yMTQyMTY3IDE0Ljk4ODQgMzEuOTM0MjE2NyA4MS43NzA0IDY4LjUwNTIxNjcgMTQuOTg4NCAxMDIuNDI3MjE3IDE0Ljk4ODQgMTE3Ljc5NzIxNyA0My42MDk0IDEzMi42MzgyMTcgMTI4LjQxMjQgODkuMTc2MjE2NyI+Cg08L3BvbHlnb24+CgkJCQk8cGF0aCBkPSJNMjE0Ljk0OTQsMTI2LjU4NDUxNyBMMjE0Ljk0OTQsMTIwLjE5MjUxNyBMMjM4LjA4OTQsMTA4LjIwMTUxNyBMMjM4LjA4OTQsMTE1LjM4MTUxNyBMMjE0Ljk0OTQsMTI2LjU4NDUxNyBaIE0xNi44NTA0LDEwOC4wMzM1MTcgTDM5LjIxMDQsMTIwLjg2OTUxNyBMMzkuMjEwNCwxMjUuOTc5NTE3IEwxNi44NTA0LDExNC42Njc1MTcgTDE2Ljg1MDQsMTA4LjAzMzUxNyBaIE0xMzEuMzgzNCw4MS4wMzk1MTY3IEwyMDguMzAyNCwxMTkuMDQ0NTE3IEwyMDguMzAyNCwxMjYuNjg2NTE3IEwxMzEuMzgzNCw4Ny4yODE1MTY3IEwxMzEuMzgzNCw4MS4wMzk1MTY3IFogTTQ1Ljg1NzQsMTIwLjE1NjUxNyBMMTI0LjczNjQsODEuMDQ3NTE2NyBMMTI0LjczNjQsODcuMjc5NTE2NyBMNDUuODU3NCwxMjcuMTg3NTE3IEw0NS44NTc0LDEyMC4xNTY1MTcgWiBNMTAxLjcwOTQsNTYuMjUxNTE2NyBMMjEuODg3NCwxNy43MTM1MTY3IEw0Mi41NjE0LDcuMjA2NTE2NzQgTDEyOC4wNTk0LDUxLjUyOTUxNjcgTDIxMi4xMjY0LDcuMjI1NTE2NzQgTDIzMy41MzA0LDE4LjE4ODUxNjcgTDE1NS42ODk0LDU3LjM2MjUxNjcgQzE1Mi41Nzc0LDU4LjkyODUxNjcgMTUyLjU4MjQsNjMuMzczNTE2NyAxNTUuNjk3NCw2NC45MzI1MTY3IEwyMzIuNzM1NCwxMDMuNDg5NTE3IEwyMTIuNzY3NCwxMTMuODM2NTE3IEwxMjguMDU5NCw3MC40MzM1MTY3IEw0MS44NTM0LDExNC43MjI1MTcgTDIyLjE2NDQsMTAzLjQxOTUxNyBMMTAxLjc1MzQsNjMuODU3NTE2NyBDMTA0LjkwNjQsNjIuMjkwNTE2NyAxMDQuODgwNCw1Ny43ODI1MTY3IDEwMS43MDk0LDU2LjI1MTUxNjcgTDEwMS43MDk0LDU2LjI1MTUxNjcgWiBNMTcuODQ0NCwzMC43NzA1MTY3IEwxNy44NDQ0LDIzLjc1NjUxNjcgTDkyLjI5MDQsNjAuODk4NTE2NyBMODQuNzY0NCw2My45MDU1MTY3IEwxNy44NDQ0LDMwLjc3MDUxNjcgWiBNMTcwLjQ5ODQsNjQuMzE4NTE2NyBDMTcwLjM5NjQsNjQuMjU2NTE2NyAxNjMuMzY4NCw2MC45NDA1MTY3IDE2My4zNjg0LDYwLjk0MDUxNjcgTDIzOC4wODk0LDIzLjMzNTUxNjcgTDIzOC4wODk0LDMwLjM4MDUxNjcgTDE3MC40OTg0LDY0LjMxODUxNjcgWiBNMTc3Ljk3NzQsNjguMDAxNTE2NyBMMjQzLjY1MjQsMzUuMDI1NTE2NyBDMjQ0LjMxNzQsMzQuNjkxNTE2NyAyNDQuNzM3NCwzNC4wMTA1MTY3IDI0NC43Mzc0LDMzLjI2NjUxNjcgTDI0NC43Mzc0LDE3LjY2MzUxNjcgQzI0NC43Mzc0LDE2LjkyNDUxNjcgMjQ0LjMyMzQsMTYuMjQ4NTE2NyAyNDMuNjY2NCwxNS45MTE1MTY3IEwyMTMuMDMzNCwwLjIyMTUxNjc0MSBDMjEyLjQ3MjQsLTAuMDY1NDgzMjU5MSAyMTEuODA3NCwtMC4wNjY0ODMyNTkxIDIxMS4yNDU0LDAuMjE4NTE2NzQxIEwxMjguOTQyNCw0MS45ODI1MTY3IEMxMjguMzg3NCw0Mi4yNjQ1MTY3IDEyNy43MzE0LDQyLjI2NzUxNjcgMTI3LjE3MzQsNDEuOTg4NTE2NyBMNDMuNDIwNCwwLjIwNjUxNjc0MSBDNDIuODYzNCwtMC4wNzE0ODMyNTkxIDQyLjIwNjQsLTAuMDY4NDgzMjU5MSA0MS42NTA0LDAuMjEzNTE2NzQxIEwxMS4yNzk0LDE1LjY0NzUxNjcgQzEwLjYxODQsMTUuOTgzNTE2NyAxMC4yMDI0LDE2LjY2MTUxNjcgMTAuMjAyNCwxNy40MDI1MTY3IEwxMC4yMDI0LDMzLjI1NzUxNjcgQzEwLjIwMjQsMzQuMDA2NTE2NyAxMC42Mjc0LDM0LjY5MDUxNjcgMTEuMjk5NCwzNS4wMjI1MTY3IEw3Ny4yNTA0LDY3LjYwNjUxNjcgTDExLjYwNDQsMTAwLjIwMTUxNyBDMTAuOTQ3NCwxMDAuNTI4NTE3IDEwLjUyNTQsMTAxLjE5MzUxNyAxMC41MTI0LDEwMS45Mjg1MTcgTDEwLjIyNTQsMTE3LjUxOTUxNyBDMTAuMjExNCwxMTguMjc0NTE3IDEwLjYzMTQsMTE4Ljk3MDUxNyAxMS4zMDQ0LDExOS4zMTE1MTcgTDQyLjg0MTQsMTM1LjI2NzUxNyBDNDMuMzk4NCwxMzUuNTQ4NTE3IDQ0LjA1NTQsMTM1LjU0OTUxNyA0NC42MTM0LDEzNS4yNjk1MTcgTDEyNy4xNzE0LDkzLjgxNDUxNjcgQzEyNy43Mjk0LDkzLjUzMzUxNjcgMTI4LjM4ODQsOTMuNTM1NTE2NyAxMjguOTQ2NCw5My44MTg1MTY3IEwyMTAuNDc1NCwxMzUuMjcyNTE3IEMyMTEuMDIzNCwxMzUuNTUwNTE3IDIxMS42NzA0LDEzNS41NTc1MTcgMjEyLjIyNDQsMTM1LjI4OTUxNyBMMjQzLjYyNjQsMTIwLjA4NjUxNyBDMjQ0LjMwNTQsMTE5Ljc1NzUxNyAyNDQuNzM3NCwxMTkuMDY5NTE3IDI0NC43Mzc0LDExOC4zMTU1MTcgTDI0NC43Mzc0LDEwMi4wMjU1MTcgQzI0NC43Mzc0LDEwMS4yNzQ1MTcgMjQ0LjMxMDQsMTAwLjU4OTUxNyAyNDMuNjM2NCwxMDAuMjU4NTE3IEwxNzcuOTc3NCw2OC4wMDE1MTY3IFoiIGZpbGw9IiMwMDAwMDAiPgoNPC9wYXRoPgoJCTwvZz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/redis.js b/frontend/crawlab-ui/src/assets/js/svg/redis.js
new file mode 100644
index 00000000..ff2dc637
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/redis.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB0PSIxNzIyOTI3NDMyNDQwIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgICAgcC1pZD0iMTA2NzciIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIj4KICA8cGF0aAogICAgZD0iTTk1OS43NDQgNjAyLjE2bDAuMjU2IDAuMDY0djEwMS45NTJjMCAxMC4yNC0xMC43NTIgMjEuNDQtMzUuMDcyIDM1Ljg0LTIyLjk3NiAxMy42OTYtOTEuOTY4IDQ3LjYxNi0xNjMuMzI4IDgyLjYyNGwtMzUuNzEyIDE3LjUzNmMtNjUuMDg4IDMyLTEyNi4wMTYgNjIuMjA4LTE0OS4xODQgNzYuMDMyLTUyLjggMzEuMzYtODIuMDQ4IDMxLjEwNC0xMjMuNzEyIDguMzItNDEuNi0yMi43Mi0zMDUuMjgtMTQ0LjI1Ni0zNTIuNzA0LTE3MC4xNzYtMjMuNzQ0LTEyLjk5Mi0zNi4yMjQtMjMuOTM2LTM2LjIyNC0zNC4yNHYtMTAzLjQyNGMwLjM4NCAxMC4zNjggMTIuNDggMjEuMjQ4IDM2LjIyNCAzNC4yNEMxNDcuNzc2IDY3Ni44IDQxMS4zMjggNzk4LjQgNDUyLjk5MiA4MjEuMTJjNDEuNjY0IDIyLjc4NCA3MC45MTIgMjMuMDQgMTIzLjcxMi04LjMyIDUyLjY3Mi0zMS4zNiAzMDAuNDE2LTE0Ny43MTIgMzQ4LjIyNC0xNzYuMTI4IDIzLjIzMi0xMy44MjQgMzQuNTYtMjQuNzY4IDM0Ljg4LTM0LjU2bC0wLjA2NCAwLjA2NHogbTAtMTY4LjU3NmgwLjE5MnYxMDEuOTUyYzAgMTAuMjQtMTAuNzUyIDIxLjQ0LTM1LjA3MiAzNS45NjgtNDcuODA4IDI4LjQxNi0yOTUuNTUyIDE0NC43NjgtMzQ4LjIyNCAxNzYuMTI4LTUyLjggMzEuMzYtODIuMDQ4IDMxLjA0LTEyMy43MTIgOC4zMi00MS42LTIyLjcyLTMwNS4yOC0xNDQuMzItMzUyLjcwNC0xNzAuMjRDNzYuNDggNTcyLjggNjQgNTYxLjkyIDY0IDU1MS41MzZ2LTEwMy40MjRjMC4zODQgMTAuMjQgMTIuNDggMjEuMjQ4IDM2LjIyNCAzNC4xNzYgNDcuNDg4IDI1LjkyIDMxMS4wNCAxNDcuNTIgMzUyLjcwNCAxNzAuMjQgNDEuNjY0IDIyLjcyIDcwLjkxMiAyMy4wNCAxMjMuNzEyLTguMzIgNTIuNjcyLTMxLjM2IDMwMC40MTYtMTQ3LjcxMiAzNDguMjI0LTE3Ni4xOTIgMjMuMTY4LTEzLjgyNCAzNC41Ni0yNC43MDQgMzQuODgtMzQuNDMyek00NjIuNjU2IDgxLjg0YzU1LjM2LTIyLjcyIDc0LjU2LTIzLjQ4OCAxMjEuNjY0LTMuNzc2IDQ3LjE2OCAxOS43NzYgMjkzLjM3NiAxMzEuNjQ4IDMzOS45NjggMTUxLjEwNCAyNCAxMC4wNDggMzUuODQgMTkuMiAzNS40NTYgMjkuNjMySDk2MHYxMDEuOTUyYzAgMTAuMTc2LTEwLjgxNiAyMS40NC0zNS4wNzIgMzUuOTA0Qzg3Ny4wNTYgNDI1LjA3MiA2MjkuMzc2IDU0MS40NCA1NzYuNjQgNTcyLjhjLTUyLjczNiAzMS4zNi04MS45ODQgMzEuMTA0LTEyMy42NDggOC4zMi00MS42NjQtMjIuNjU2LTMwNS4yOC0xNDQuMzItMzUyLjc2OC0xNzAuMjRDNzYuNTQ0IDM5Ny45MzYgNjQgMzg3LjA1NiA2NCAzNzYuNjg4VjI3My4yOGMtMC4zMi0xMC4zMDQgMTEuMDcyLTE5Ljk2OCAzNC4zNjgtMzAuNDY0IDQ2LjY1Ni0yMC44IDMwOC44LTEzOC4yNCAzNjQuMjg4LTE2MC44OTZ2LTAuMDY0eiBtMTI5Ljc5MiAyMzguNGwtMjA3LjU1MiAzNi4zNTIgMTQ0LjgzMiA2OC42MDggNjIuNzItMTA0Ljk2eiBtMTI4LjcwNC0xMTMuNmwtMTM1LjkzNiA2MS40NCAxMjIuNjg4IDU1LjM2IDEzLjM3Ni01Ljk1MiAxMjIuNzUyLTU1LjQyNC0xMjIuODgtNTUuNDI0eiBtLTM5Mi4zMiAxMy40NGMtNjEuMjQ4IDAtMTEwLjkxMiAyMi4wMTYtMTEwLjkxMiA0OS4xNTIgMCAyNy4wNzIgNDkuNjY0IDQ5LjA4OCAxMTAuOTc2IDQ5LjA4OHMxMTAuOTEyLTIxLjk1MiAxMTAuOTEyLTQ5LjA4OC00OS42LTQ5LjA4OC0xMTAuOTEyLTQ5LjA4OGwtMC4wNjQtMC4wNjR6IG0xMzQuNjU2LTEwMS44ODhsMjAuMDk2IDQyLjMwNC02Ni44OCAyNy41MiA4OS42IDkuMjE2IDI4LjAzMiA1My4yNDggMTcuNDA4LTQ3Ljc0NCA3Ny42MzItOS4yMTYtNjAuMTYtMjUuNzI4IDE2LTQzLjcxMi01OS4xMzYgMjIuMDgtNjIuNTkyLTI3Ljk2OHoiCiAgICBmaWxsPSIjRDgyQTFGIiBwLWlkPSIxMDY3OCI+PC9wYXRoPgo8L3N2Zz4K`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/scrapy.js b/frontend/crawlab-ui/src/assets/js/svg/scrapy.js
new file mode 100644
index 00000000..58ccb03d
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/scrapy.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgd2lkdGg9IjI1NnB4IiBoZWlnaHQ9IjI1NnB4IiBzdHlsZT0ic2hhcGUtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbjsgdGV4dC1yZW5kZXJpbmc6Z2VvbWV0cmljUHJlY2lzaW9uOyBpbWFnZS1yZW5kZXJpbmc6b3B0aW1pemVRdWFsaXR5OyBmaWxsLXJ1bGU6ZXZlbm9kZDsgY2xpcC1ydWxlOmV2ZW5vZGQiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4KPGc+PHBhdGggc3R5bGU9Im9wYWNpdHk6MC45ODEiIGZpbGw9IiM2MWE4M2EiIGQ9Ik0gMTEyLjUsLTAuNSBDIDEyMi44MzMsLTAuNSAxMzMuMTY3LC0wLjUgMTQzLjUsLTAuNUMgMTk3LjU0OCw4LjcwNDEgMjMzLjM4MiwzOS4zNzA4IDI1MSw5MS41QyAyNTIuNzE1LDk4LjI1ODUgMjU0LjIxNSwxMDQuOTI1IDI1NS41LDExMS41QyAyNTUuNSwxMjIuMTY3IDI1NS41LDEzMi44MzMgMjU1LjUsMTQzLjVDIDI0Ni4yOTYsMTk3LjU0OCAyMTUuNjI5LDIzMy4zODIgMTYzLjUsMjUxQyAxNTYuNzQxLDI1Mi43MTUgMTUwLjA3NSwyNTQuMjE1IDE0My41LDI1NS41QyAxMzIuODMzLDI1NS41IDEyMi4xNjcsMjU1LjUgMTExLjUsMjU1LjVDIDU3LjQ1MTcsMjQ2LjI5NiAyMS42MTg0LDIxNS42MjkgNCwxNjMuNUMgMi4yODQ5NCwxNTYuNzQxIDAuNzg0OTQ0LDE1MC4wNzUgLTAuNSwxNDMuNUMgLTAuNSwxMzIuODMzIC0wLjUsMTIyLjE2NyAtMC41LDExMS41QyA4LjcwNDEsNTcuNDUxNyAzOS4zNzA4LDIxLjYxODQgOTEuNSw0QyA5OC41ODk3LDIuMjY5MTQgMTA1LjU5LDAuNzY5MTQzIDExMi41LC0wLjUgWiIvPjwvZz4KPGc+PHBhdGggc3R5bGU9Im9wYWNpdHk6MSIgZmlsbD0iI2ZlZmVmZSIgZD0iTSAxMTIuNSwxMS41IEMgMTcwLjYwMyw4LjQ0ODY1IDIxMS43NywzMy4xMTUzIDIzNiw4NS41QyAyNTEuMDQ3LDEzMC4yODcgMjQzLjM4LDE3MC42MiAyMTMsMjA2LjVDIDE3NS44LDI0Mi41ODkgMTMyLjYzMywyNTIuMDg5IDgzLjUsMjM1QyAzMi41OTMyLDIxMC43MTcgOC43NTk4OSwxNzAuMjE3IDEyLDExMy41QyAyMi41NTYxLDU2Ljc3NzkgNTYuMDU2MSwyMi43Nzc5IDExMi41LDExLjUgWiIvPjwvZz4KPGc+PHBhdGggc3R5bGU9Im9wYWNpdHk6MSIgZmlsbD0iI2E5N2M1MSIgZD0iTSAyMTEuNSw3Mi41IEMgMjExLjY2NSw3NS44NDk5IDIxMS40OTgsNzkuMTgzMiAyMTEsODIuNUMgMjA4Ljg3Miw4NS42MzAxIDIwNi4zNzIsODguNDYzNCAyMDMuNSw5MUMgMTg1LjI5MiwxMDIuMjcyIDE2Ni42MjUsMTEyLjc3MiAxNDcuNSwxMjIuNUMgMTQ3LjE2NywxMjIuNSAxNDYuODMzLDEyMi41IDE0Ni41LDEyMi41QyAxNDMuNDE1LDExOC43NTQgMTQwLjc0OSwxMTQuNzU0IDEzOC41LDExMC41QyAxNTUuMzM4LDk3LjE2NjEgMTcyLjMzOCw4My45OTk0IDE4OS41LDcxQyAxOTUuODkyLDY3LjMyODIgMjAyLjU1OSw2Ni42NjE1IDIwOS41LDY5QyAyMTAuODIzLDY5LjgxNTkgMjExLjQ4OSw3MC45ODI2IDIxMS41LDcyLjUgWiIvPjwvZz4KPGc+PHBhdGggc3R5bGU9Im9wYWNpdHk6MSIgZmlsbD0iI2Y1ZjBlYiIgZD0iTSAxOTUuNSw3NC41IEMgMTk4LjkxLDc0LjM4MzggMjAwLjI0NCw3Ni4wNTA0IDE5OS41LDc5LjVDIDE5Ny44MzMsNzkuNSAxOTYuMTY3LDc5LjUgMTk0LjUsNzkuNUMgMTk0LjM2Niw3Ny43MDg1IDE5NC42OTksNzYuMDQxOCAxOTUuNSw3NC41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjEiIGZpbGw9IiM3ZDU0MzEiIGQ9Ik0gMjExLjUsNzIuNSBDIDIxMi4yMjIsNzIuOTE3NSAyMTIuNzIyLDczLjU4NDIgMjEzLDc0LjVDIDIxMy42NjcsNzcuODMzMyAyMTMuNjY3LDgxLjE2NjcgMjEzLDg0LjVDIDIxMC44NzIsODcuNjMwMSAyMDguMzcyLDkwLjQ2MzQgMjA1LjUsOTNDIDE4Ny4wODksMTA0LjUzOSAxNjguMDg5LDExNS4wMzkgMTQ4LjUsMTI0LjVDIDE0Ny42NjIsMTI0LjE1OCAxNDcuMzI4LDEyMy40OTIgMTQ3LjUsMTIyLjVDIDE2Ni42MjUsMTEyLjc3MiAxODUuMjkyLDEwMi4yNzIgMjAzLjUsOTFDIDIwNi4zNzIsODguNDYzNCAyMDguODcyLDg1LjYzMDEgMjExLDgyLjVDIDIxMS40OTgsNzkuMTgzMiAyMTEuNjY1LDc1Ljg0OTkgMjExLjUsNzIuNSBaIi8+PC9nPgo8Zz48cGF0aCBzdHlsZT0ib3BhY2l0eToxIiBmaWxsPSIjZjVlZmVhIiBkPSJNIDE3NS41LDg4LjUgQyAxNzYuODczLDg4LjM0MzMgMTc4LjIwNyw4OC41MSAxNzkuNSw4OUMgMTgwLjQ1MSw4OS43MTc1IDE4MC42MTcsOTAuNTUwOSAxODAsOTEuNUMgMTc1LjY1Miw5NC41NTc3IDE3NC4xNTIsOTMuNTU3NyAxNzUuNSw4OC41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjEiIGZpbGw9IiNmYmY5ZjciIGQ9Ik0gMTU2LjUsMTAxLjUgQyAxNjAuMTYxLDEwMS4zNSAxNjEuMTYxLDEwMi44NSAxNTkuNSwxMDZDIDE1NS43MDcsMTA2LjQ4NyAxNTQuNzA3LDEwNC45ODcgMTU2LjUsMTAxLjUgWiIvPjwvZz4KPGc+PHBhdGggc3R5bGU9Im9wYWNpdHk6MSIgZmlsbD0iI2QwZDJkMyIgZD0iTSAxMzguNSwxMTAuNSBDIDE0MC43NDksMTE0Ljc1NCAxNDMuNDE1LDExOC43NTQgMTQ2LjUsMTIyLjVDIDE0My42MjUsMTMyLjkxNyAxMzkuMTI1LDE0Mi41ODQgMTMzLDE1MS41QyAxMjMuNjk3LDE2Mi42MzcgMTEzLjg2MywxNzMuMzA0IDEwMy41LDE4My41QyA5OC4yNjcxLDE4OC40NjIgOTIuNjAwNSwxOTIuOTYyIDg2LjUsMTk3QyA4NS4zNjUsMTk3Ljc0OSA4NC4zNjUsMTk3LjU4MyA4My41LDE5Ni41QyA3MC42Njk4LDE3OC4xNzYgNTguMzM2NSwxNTkuNTEgNDYuNSwxNDAuNUMgNTguNDAzOSwxMzIuMjE3IDcxLjQwMzksMTI2LjA1IDg1LjUsMTIyQyAxMDIuOTQ4LDExNi45NDUgMTIwLjYxNCwxMTMuMTExIDEzOC41LDExMC41IFoiLz48L2c+CjxnPjxwYXRoIHN0eWxlPSJvcGFjaXR5OjEiIGZpbGw9IiM2NDY1NjciIGQ9Ik0gMTQ2LjUsMTIyLjUgQyAxNDYuODMzLDEyMi41IDE0Ny4xNjcsMTIyLjUgMTQ3LjUsMTIyLjVDIDE0Ny4zMjgsMTIzLjQ5MiAxNDcuNjYyLDEyNC4xNTggMTQ4LjUsMTI0LjVDIDE0NS43NjcsMTM2LjA2MiAxNDAuNiwxNDYuMzk1IDEzMywxNTUuNUMgMTI0LjIzNywxNjYuMSAxMTQuNDA0LDE3NS40MzMgMTAzLjUsMTgzLjVDIDExMy44NjMsMTczLjMwNCAxMjMuNjk3LDE2Mi42MzcgMTMzLDE1MS41QyAxMzkuMTI1LDE0Mi41ODQgMTQzLjYyNSwxMzIuOTE3IDE0Ni41LDEyMi41IFoiLz48L2c+Cjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/selenium.js b/frontend/crawlab-ui/src/assets/js/svg/selenium.js
new file mode 100644
index 00000000..41e807c8
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/selenium.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCEtLSBVcGxvYWRlZCB0bzogU1ZHIFJlcG8sIHd3dy5zdmdyZXBvLmNvbSwgR2VuZXJhdG9yOiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9Ii02LjUgMCAyNjkgMjY5IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHByZXNlcnZlQXNwZWN0UmF0aW89InhNaWRZTWlkIj4KICAgIDxnPgogICAgICAgIDxwYXRoIGQ9Ik0yMzQuMTUyODc0LDAuMDAzNDM4NTczODEgQzIzNC44MDgwMjUsLTAuMDM3OTU3NTI2MyAyMzUuNDI5NjQ3LDAuMjk3MDI4ODIxIDIzNS43NTUzNDEsMC44NjY5OTQwOTggQzIzNi4wODEwMzYsMS40MzY5NTkzOCAyMzYuMDU0MDQ3LDIuMTQyNTgwMzIgMjM1LjY4NTc2NywyLjY4NjAwMjE2IEwyMzUuNjg1NzY3LDIuNjg2MDAyMTYgTDE2OS4zODgxMjQsOTIuNzQzNDk0MSBDMTY4LjkwMjI1OCw5My4yOTE5MTA0IDE2OC4yMDQ2OTEsOTMuNjA1ODU0OSAxNjcuNDcyMDA3LDkzLjYwNTg1NDkgQzE2Ni43MzkzMjMsOTMuNjA1ODU0OSAxNjYuMDQxNzU2LDkzLjI5MTkxMDQgMTY1LjU1NTg5LDkyLjc0MzQ5NDEgTDE2NS41NTU4OSw5Mi43NDM0OTQxIEwxNDIuMTc5MjY1LDY2LjY4NDMwNSBDMTQxLjYwODMwMiw2Ni4wOTE2MTM1IDE0MC43OTkxOSw2NS43OTAxMDE3IDEzOS45Nzk1OTksNjUuODY0NjEgQzEzOS4xNjAwMDgsNjUuOTM5MTE4MiAxMzguNDE4NTMxLDY2LjM4MTU5MjcgMTM3Ljk2MzgwOCw2Ny4wNjc1MjgzIEwxMzcuOTYzODA4LDY3LjA2NzUyODMgTDEyMy43ODQ1NDMsODUuMDc5MDI2NyBDMTIzLjMzNTkyMiw4Ni4wOTMyNTIyIDEyMy40ODMyOTQsODcuMjcyMjI0OSAxMjQuMTY3NzY2LDg4LjE0NDgxMzcgTDEyNC4xNjc3NjYsODguMTQ0ODEzNyBMMTY2LjMyMjMzNywxMzEuODMyMjc4IEMxNjYuODA4MjAzLDEzMi4zODA2OTQgMTY3LjUwNTc3LDEzMi42OTQ2MzkgMTY4LjIzODQ1NCwxMzIuNjk0NjM5IEMxNjguOTcxMTM4LDEzMi42OTQ2MzkgMTY5LjY2ODcwNSwxMzIuMzgwNjk0IDE3MC4xNTQ1NzEsMTMxLjgzMjI3OCBMMTcwLjE1NDU3MSwxMzEuODMyMjc4IEwyNTIuOTMwODE5LDM4LjcwODk5OSBDMjUzLjQxOTUzMSwzOC4xODM3NTM3IDI1NC4xNzY5MTEsMzguMDA1MzYyNyAyNTQuODQ4NjcyLDM4LjI1NzI3MzMgQzI1NS41MjA0MzQsMzguNTA5MTgzOSAyNTUuOTczNzY0LDM5LjE0MTU5MTEgMjU1Ljk5NjYwNiwzOS44NTg2NjkxIEwyNTUuOTk2NjA2LDM5Ljg1ODY2OTEgTDI1NS45OTY2MDYsMjY2LjM0MzY4MSBDMjU2LjAyODMxOSwyNjYuODYwOTM5IDI1NS44MzY2NSwyNjcuMzY2OTU1IDI1NS40NzAyMDYsMjY3LjczMzM5OCBDMjU1LjEwMzc2MywyNjguMDk5ODQyIDI1NC41OTc3NDcsMjY4LjI5MTUxMSAyNTQuMDgwNDg5LDI2OC4yNTk3OTggTDI1NC4wODA0ODksMjY4LjI1OTc5OCBMMS45MTk1MTEyMywyNjguMjU5Nzk4IEMxLjQwMjI1MzQ1LDI2OC4yOTE1MTEgMC44OTYyMzY5NTIsMjY4LjA5OTg0MiAwLjUyOTc5MzY1OCwyNjcuNzMzMzk4IEMwLjE2MzM1MDM2NCwyNjcuMzY2OTU1IC0wLjAyODMxOTMxMTUsMjY2Ljg2MDkzOSAwLjAwMzM5NDM4MTU3LDI2Ni4zNDM2ODEgTDAuMDAzMzk0MzgxNTcsMjY2LjM0MzY4MSBMMC4wMDMzOTQzODE1NywxLjkxOTU1NTQyIEMtMC4wMjgzMTkzMTE1LDEuNDAyMjk3NjQgMC4xNjMzNTAzNjQsMC44OTYyODExNDQgMC41Mjk3OTM2NTgsMC41Mjk4Mzc4NSBDMC44OTYyMzY5NTIsMC4xNjMzOTQ1NTYgMS40MDIyNTM0NSwtMC4wMjgyNzUxMTkzIDEuOTE5NTExMjMsMC4wMDM0Mzg1NzM4MSBMMS45MTk1MTEyMywwLjAwMzQzODU3MzgxIFogTTE2OS4wMDQ5MDEsMTUyLjkwOTU2MyBDMTU3LjU5NTkwOCwxNTIuNzA4OTQ5IDE0Ni42MDA0NDMsMTU3LjE3OTI4MyAxMzguNTY4MDgyLDE2NS4yODQwMDcgQzEzMC41MzU3MjIsMTczLjM4ODczMSAxMjYuMTY0MTc5LDE4NC40MjM4NDYgMTI2LjQ2NzEwNywxOTUuODMwNTgxIEMxMjYuNDY3MTA3LDIyMi4yNzI5OTMgMTQ1LjYyODI3NSwyMzkuMTM0ODIyIDE3MC41Mzc3OTQsMjM5LjEzNDgyMiBDMTgyLjA2ODczNiwyMzkuNDIxNzY5IDE5My4zNzU5OTMsMjM1LjkyMTkwMyAyMDIuNzI4NTU3LDIyOS4xNzEwMTQgQzIwMy41OTU1MzQsMjI4LjM4Mzc3MyAyMDMuNzU4Mjk5LDIyNy4wODE2NTUgMjAzLjExMTc4MSwyMjYuMTA1MjI3IEwyMDMuMTExNzgxLDIyNi4xMDUyMjcgTDE5Ni4yMTM3NiwyMTUuNzU4MTk2IEMxOTUuNDM4MjI0LDIxNC44Njg5NTMgMTk0LjExODUzNywyMTQuNzAzOTkyIDE5My4xNDc5NzMsMjE1LjM3NDk3MyBDMTg3LjE1MjM2NywyMTkuNDY5NTQzIDE4MC4wOTU3OTksMjIxLjczMjk3IDE3Mi44MzcxMzQsMjIxLjg4OTc3IEMxNTkuNDI0MzE2LDIyMS44ODk3NyAxNTAuOTkzNDAyLDIxMy40NTg4NTYgMTQ5LjQ2MDUwOSwyMDMuNDk1MDQ4IEMxNDkuNDg4MzI1LDIwMy4wODM4MjMgMTQ5LjgxNTczMSwyMDIuNzU2NDE3IDE1MC4yMjY5NTUsMjAyLjcyODYwMSBMMTUwLjIyNjk1NSwyMDIuNzI4NjAxIEwyMDguMDkzNjg0LDIwMi43Mjg2MDEgQzIwOS4zMjczNTgsMjAyLjY0NTE1MyAyMTAuMzA5NTc3LDIwMS42NjI5MzUgMjEwLjM5MzAyNSwyMDAuNDI5MjYxIEwyMTAuMzkzMDI1LDIwMC40MjkyNjEgTDIxMC4zOTMwMjUsMTk4LjEyOTkyMSBDMjEwLjM5MzAyNSwxNzEuMzA0Mjg1IDE5My41MzExOTYsMTUyLjkwOTU2MyAxNjkuMDA0OTAxLDE1Mi45MDk1NjMgWiBNMTExLjkwNDYxOCwxMzguMzQ3MDc1IEMxMDAuMjY0ODcsMTI4LjgzMjc3MSA4NS41MzY4NzU3LDEyMy45MjM0MzkgNzAuNTE2NDk0NSwxMjQuNTUxMDM0IEM0NC40NTczMDUzLDEyNC41NTEwMzQgMjguMzYxOTIzOCwxMzkuODc5OTY5IDI4LjM2MTkyMzgsMTU4LjI3NDY5IEMyOC4zNjE5MjM4LDIwMC4wNDYwMzggOTEuOTc3MDAzMiwxODYuNjMzMjIgOTEuOTc3MDAzMiwyMDYuMTc3NjEyIEM5MS45NzcwMDMyLDIxMi4zMDkxODYgODUuODQ1NDI5MywyMTguNDQwNzYgNzIuODE1ODM0NywyMTguNDQwNzYgQzYwLjQ0MTAwMzEsMjE4LjU0Mjg3IDQ4LjUwMTg0MzgsMjEzLjg3Njk5MiAzOS40NzU0MDE1LDIwNS40MTExNjUgQzM4LjkzNjUxMTMsMjA0LjkwNzM0MSAzOC4yMDY0NjI4LDIwNC42NjA0OTQgMzcuNDcyMzk3MSwyMDQuNzMzOTAxIEMzNi43MzgzMzEzLDIwNC44MDczMDcgMzYuMDcxNjE5OSwyMDUuMTkzODMgMzUuNjQzMTY3OCwyMDUuNzk0Mzg4IEwzNS42NDMxNjc4LDIwNS43OTQzODggTDI1LjY3OTM2MDIsMjE5LjU5MDQzIEMyNS4wMzI4NDIxLDIyMC41NjY4NTggMjUuMTk1NjA2OCwyMjEuODY4OTc2IDI2LjA2MjU4MzUsMjIyLjY1NjIxNyBDMzYuNDA5NjE0NSwyMzIuMjM2ODAxIDUwLjk3MjEwMjYsMjM4Ljc1MTU5OCA3MS4yODI5NDEyLDIzOC43NTE1OTggQzEwMS4xNzQzNjQsMjM4Ljc1MTU5OCAxMTUuNzM2ODUyLDIyMy40MjI2NjMgMTE2LjUwMzI5OSwyMDMuNDk1MDQ4IEMxMTYuNTAzMjk5LDE2Mi4xMDY5MjQgNTIuODg4MjE5NSwxNzMuOTg2ODQ5IDUyLjg4ODIxOTUsMTU2LjM1ODU3NCBDNTIuODg4MjE5NSwxNDkuODQzNzc2IDU4LjYzNjU3LDE0NS4yNDUwOTYgNjguNjAwMzc3NiwxNDUuMjQ1MDk2IEM3OS41ODM5ODk5LDE0NS4wOTQ1ODMgOTAuMjgxMTA5MiwxNDguNzUwNTYxIDk4Ljg3NTAyMzksMTU1LjU5MjEyNyBDOTkuMzgwMjYyNCwxNTYuMDA2MDM2IDEwMC4wMzEwNjMsMTU2LjE5ODcyMiAxMDAuNjgwMjA1LDE1Ni4xMjY1OTUgQzEwMS4zMjkzNDYsMTU2LjA1NDQ2OCAxMDEuOTIxOTc3LDE1NS43MjM2MjQgMTAyLjMyNDAzNCwxNTUuMjA4OTAzIEwxMDIuMzI0MDM0LDE1NS4yMDg5MDMgTDExMi4yODc4NDIsMTQxLjc5NjA4NiBDMTEyLjcwMTc1MSwxNDEuMjkwODQ3IDExMi44OTQ0MzcsMTQwLjY0MDA0NiAxMTIuODIyMzEsMTM5Ljk5MDkwNSBDMTEyLjc1MDE4MywxMzkuMzQxNzYzIDExMi40MTkzMzksMTM4Ljc0OTEzMyAxMTEuOTA0NjE4LDEzOC4zNDcwNzUgWiBNMTY5LjM4ODEyNCwxNjkuNzcxMzkyIEMxNzkuODExNDM5LDE2OS4xODgzNDQgMTg4LjgwOTUzNywxNzYuOTk4MDE0IDE4OS42OTg5NjMsMTg3LjM5OTY2NyBDMTg5LjY3MTE0NywxODcuODEwODkxIDE4OS4zNDM3NCwxODguMTM4Mjk3IDE4OC45MzI1MTYsMTg4LjE2NjExMyBMMTg4LjkzMjUxNiwxODguMTY2MTEzIEwxNDkuODQzNzMyLDE4OC4xNjYxMTMgQzE0OS40MzI1MDcsMTg4LjEzODI5NyAxNDkuMTA1MTAxLDE4Ny44MTA4OTEgMTQ5LjA3NzI4NSwxODcuMzk5NjY3IEMxNTAuMzU4Njc2LDE3Ny4yMDIzNCAxNTkuMTExOTQ4LDE2OS42MDUxNiAxNjkuMzg4MTI0LDE2OS43NzEzOTIgWiIgZmlsbD0iIzJDQjEzNCI+Cg08L3BhdGg+CiAgICA8L2c+Cjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/snowflake.js b/frontend/crawlab-ui/src/assets/js/svg/snowflake.js
new file mode 100644
index 00000000..19789b14
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/snowflake.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIgZmlsbD0iIzI5YjVlOCI+PHBhdGggZD0iTTkuODYgMTUuMjk4bDEzLjAwOCA3LjhhMy43MiAzLjcyIDAgMCAwIDQuNTg5LS42MDEgNC4wMSA0LjAxIDAgMCAwIDEuMjI3LTIuOTA4VjMuOTU2YTMuODEgMy44MSAwIDAgMC0xLjg2MS0zLjQyIDMuODEgMy44MSAwIDAgMC0zLjg5MyAwIDMuODEgMy44MSAwIDAgMC0xLjg2MSAzLjQydjguODk2bC03LjM4Ny00LjQzYTMuNzkgMy43OSAwIDAgMC0yLjkyMi0uNGMtLjk4Ni4yNjUtMS44MTguOTQtMi4zIDEuODQ0LTEuMDU3IDEuOS0uNDQgNC4yOCAxLjQgNS40MjJtMzEuMjcgNy44bDEzLjAwOC03LjhjMS44NC0xLjE0MyAyLjQ1OC0zLjUzMyAxLjQtNS40MjRhMy43NSAzLjc1IDAgMCAwLTUuMjItMS40NTJsLTcuMyA0LjM3di04Ljg0YTMuODEgMy44MSAwIDEgMC03LjYxNSAwdjE1LjMyM2E0LjA4IDQuMDggMCAwIDAgLjQ5NCAyLjM2N2MuNDgyLjkwMyAxLjMxNCAxLjU3IDIuMyAxLjg0NGEzLjcxIDMuNzEgMCAwIDAgMi45MjItLjRNMjkuNTUyIDMxLjk3Yy4wMTMtLjI1LjEwOC0uNS4yNzItLjY4bDEuNTItMS41OGExLjA2IDEuMDYgMCAwIDEgLjY1OC0uMjgyaC4wNTdhMS4wNSAxLjA1IDAgMCAxIC42NTYuMjgybDEuNTIgMS41OGExLjEyIDEuMTIgMCAwIDEgLjI3Mi42ODF2LjA2YTEuMTMgMS4xMyAwIDAgMS0uMjcyLjY4M2wtMS41MiAxLjU4YTEuMDQgMS4wNCAwIDAgMS0uNjU2LjI4NGgtLjA1N2MtLjI0Ni0uMDE0LS40OC0uMTE1LS42NTgtLjI4NGwtMS41Mi0xLjU4YTEuMTMgMS4xMyAwIDAgMS0uMjcyLS42ODN6bS00LjYwNC0uNjV2MS4zNjRhMS41NCAxLjU0IDAgMCAwIC4zNzIuOTNsNS4xNiA1LjM1N2ExLjQyIDEuNDIgMCAwIDAgLjg5NS4zODZoMS4zMTJhMS40MiAxLjQyIDAgMCAwIC44OTUtLjM4Nmw1LjE2LTUuMzU3YTEuNTQgMS41NCAwIDAgMCAuMzcyLS45M1YzMS4zMmExLjU0IDEuNTQgMCAwIDAtLjM3Mi0uOTNsLTUuMTYtNS4zNTdhMS40MiAxLjQyIDAgMCAwLS44OTUtLjM4NmgtMS4zMTJhMS40MiAxLjQyIDAgMCAwLS44OTUuMzg2TDI1LjMyIDMwLjRhMS41NSAxLjU1IDAgMCAwLS4zNzIuOTNNMy4xMyAyNy42Mmw3LjM2NSA0LjQxN0wzLjEzIDM2LjQ1YTQuMDYgNC4wNiAwIDAgMC0xLjM5OSA1LjQyNCAzLjc1IDMuNzUgMCAwIDAgMi4zIDEuODQ0Yy45ODYuMjc0IDIuMDQyLjEzMyAyLjkyMi0uMzkybDEzLjAwOC03LjhjMS4yLS43NjIgMS45LTIuMDc4IDEuOS0zLjQ5MmE0LjE2IDQuMTYgMCAwIDAtMS45LTMuNDkybC0xMy4wMDgtNy44YTMuNzkgMy43OSAwIDAgMC0yLjkyMi0uNGMtLjk4Ni4yNjUtMS44MTguOTQtMi4zIDEuODQ0LTEuMDU3IDEuOS0uNDQgNC4yNzggMS40IDUuNDIybTM4Ljk5NSA0LjQ0MmE0IDQgMCAwIDAgMS45MSAzLjQ3N2wxMyA3LjhjLjg4LjUyNCAxLjkzNC42NjYgMi45Mi4zOTJzMS44MTctLjk0IDIuMy0xLjg0M2E0LjA1IDQuMDUgMCAwIDAtMS40LTUuNDI0TDUzLjUgMzIuMDM4bDcuMzY1LTQuNDE3YzEuODQtMS4xNDMgMi40NTctMy41MyAxLjQtNS40MjJhMy43NCAzLjc0IDAgMCAwLTIuMy0xLjg0NGMtLjk4Ny0uMjc0LTIuMDQyLS4xMzQtMi45Mi40bC0xMyA3LjhhNCA0IDAgMCAwLTEuOTEgMy41MDdNMjUuNDggNDAuNTA4YTMuNyAzLjcgMCAwIDAtMi42MTEuNDY0bC0xMy4wMDggNy44Yy0xLjg0IDEuMTQzLTIuNDU2IDMuNTMtMS40IDUuNDIyLjQ4My45MDMgMS4zMTQgMS41NyAyLjMgMS44NDNhMy43NSAzLjc1IDAgMCAwIDIuOTIyLS4zOTJsNy4zODctNC40M3Y4LjgzYTMuODEgMy44MSAwIDEgMCA3LjYxNCAwVjQ0LjRhMy45MSAzLjkxIDAgMCAwLTMuMjA1LTMuOTAzbTI4LjY2IDguMjc2bC0xMy4wMDgtNy44YTMuNzUgMy43NSAwIDAgMC0yLjkyMi0uMzkyIDMuNzQgMy43NCAwIDAgMC0yLjMgMS44NDMgNC4wOSA0LjA5IDAgMCAwLS40OTQgMi4zN3YxNS4yNWEzLjgxIDMuODEgMCAxIDAgNy42MTQgMFY1MS4yOGw3LjI4NyA0LjM3YTMuNzkgMy43OSAwIDAgMCAyLjkyMi40Yy45ODYtLjI2NSAxLjgxOC0uOTQgMi4zLTEuODQ0IDEuMDU3LTEuOS40NC00LjI4LTEuNC01LjQyMiIvPjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/table.js b/frontend/crawlab-ui/src/assets/js/svg/table.js
new file mode 100644
index 00000000..6f369d6f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/table.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdGFibGUiPjxwYXRoIGQ9Ik0wIDJhMiAyIDAgMCAxIDItMmgxMmEyIDIgMCAwIDEgMiAydjEyYTIgMiAwIDAgMS0yIDJIMmEyIDIgMCAwIDEtMi0yVjJ6bTE1IDJoLTR2M2g0VjR6bTAgNGgtNHYzaDRWOHptMCA0aC00djNoM2ExIDEgMCAwIDAgMS0xdi0yem0tNSAzdi0zSDZ2M2g0em0tNSAwdi0zSDF2MmExIDEgMCAwIDAgMSAxaDN6bS00LTRoNFY4SDF2M3ptMC00aDRWNEgxdjN6bTUtM3YzaDRWNEg2em00IDRINnYzaDRWOHoiLz48L3N2Zz4K`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/text-center.js b/frontend/crawlab-ui/src/assets/js/svg/text-center.js
new file mode 100644
index 00000000..7ea8b4dc
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/text-center.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdGV4dC1jZW50ZXIiIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNNCAxMi41YS41LjUgMCAwIDEgLjUtLjVoN2EuNS41IDAgMCAxIDAgMWgtN2EuNS41IDAgMCAxLS41LS41em0tMi0zYS41LjUgMCAwIDEgLjUtLjVoMTFhLjUuNSAwIDAgMSAwIDFoLTExYS41LjUgMCAwIDEtLjUtLjV6bTItM2EuNS41IDAgMCAxIC41LS41aDdhLjUuNSAwIDAgMSAwIDFoLTdhLjUuNSAwIDAgMS0uNS0uNXptLTItM2EuNS41IDAgMCAxIC41LS41aDExYS41LjUgMCAwIDEgMCAxaC0xMWEuNS41IDAgMCAxLS41LS41eiIvPgo8L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/text-left.js b/frontend/crawlab-ui/src/assets/js/svg/text-left.js
new file mode 100644
index 00000000..dafd817c
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/text-left.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdGV4dC1sZWZ0IiB2aWV3Qm94PSIwIDAgMTYgMTYiPgogIDxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgZD0iTTIgMTIuNWEuNS41IDAgMCAxIC41LS41aDdhLjUuNSAwIDAgMSAwIDFoLTdhLjUuNSAwIDAgMS0uNS0uNXptMC0zYS41LjUgMCAwIDEgLjUtLjVoMTFhLjUuNSAwIDAgMSAwIDFoLTExYS41LjUgMCAwIDEtLjUtLjV6bTAtM2EuNS41IDAgMCAxIC41LS41aDdhLjUuNSAwIDAgMSAwIDFoLTdhLjUuNSAwIDAgMS0uNS0uNXptMC0zYS41LjUgMCAwIDEgLjUtLjVoMTFhLjUuNSAwIDAgMSAwIDFoLTExYS41LjUgMCAwIDEtLjUtLjV6Ii8+Cjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/text-paragraph.js b/frontend/crawlab-ui/src/assets/js/svg/text-paragraph.js
new file mode 100644
index 00000000..948e3149
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/text-paragraph.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdGV4dC1wYXJhZ3JhcGgiIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMiAxMi41YS41LjUgMCAwIDEgLjUtLjVoN2EuNS41IDAgMCAxIDAgMWgtN2EuNS41IDAgMCAxLS41LS41em0wLTNhLjUuNSAwIDAgMSAuNS0uNWgxMWEuNS41IDAgMCAxIDAgMWgtMTFhLjUuNSAwIDAgMS0uNS0uNXptMC0zYS41LjUgMCAwIDEgLjUtLjVoMTFhLjUuNSAwIDAgMSAwIDFoLTExYS41LjUgMCAwIDEtLjUtLjV6bTQtM2EuNS41IDAgMCAxIC41LS41aDdhLjUuNSAwIDAgMSAwIDFoLTdhLjUuNSAwIDAgMS0uNS0uNXoiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/text-right.js b/frontend/crawlab-ui/src/assets/js/svg/text-right.js
new file mode 100644
index 00000000..5fccaf33
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/text-right.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdGV4dC1yaWdodCIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik02IDEyLjVhLjUuNSAwIDAgMSAuNS0uNWg3YS41LjUgMCAwIDEgMCAxaC03YS41LjUgMCAwIDEtLjUtLjV6bS00LTNhLjUuNSAwIDAgMSAuNS0uNWgxMWEuNS41IDAgMCAxIDAgMWgtMTFhLjUuNSAwIDAgMS0uNS0uNXptNC0zYS41LjUgMCAwIDEgLjUtLjVoN2EuNS41IDAgMCAxIDAgMWgtN2EuNS41IDAgMCAxLS41LS41em0tNC0zYS41LjUgMCAwIDEgLjUtLjVoMTFhLjUuNSAwIDAgMSAwIDFoLTExYS41LjUgMCAwIDEtLjUtLjV6Ii8+Cjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/type-bold.js b/frontend/crawlab-ui/src/assets/js/svg/type-bold.js
new file mode 100644
index 00000000..59635c7d
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/type-bold.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdHlwZS1ib2xkIiB2aWV3Qm94PSIwIDAgMTYgMTYiPgogIDxwYXRoIGQ9Ik04LjIxIDEzYzIuMTA2IDAgMy40MTItMS4wODcgMy40MTItMi44MjMgMC0xLjMwNi0uOTg0LTIuMjgzLTIuMzI0LTIuMzg2di0uMDU1YTIuMTc2IDIuMTc2IDAgMCAwIDEuODUyLTIuMTRjMC0xLjUxLTEuMTYyLTIuNDYtMy4wMTQtMi40NkgzLjg0M1YxM0g4LjIxek01LjkwOCA0LjY3NGgxLjY5NmMuOTYzIDAgMS41MTcuNDUxIDEuNTE3IDEuMjQ0IDAgLjgzNC0uNjI5IDEuMzItMS43MyAxLjMySDUuOTA4VjQuNjczem0wIDYuNzg4VjguNTk4aDEuNzNjMS4yMTcgMCAxLjg4LjQ5MiAxLjg4IDEuNDE1IDAgLjk0My0uNjQzIDEuNDQ5LTEuODMyIDEuNDQ5SDUuOTA3eiIvPgo8L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/type-h1.js b/frontend/crawlab-ui/src/assets/js/svg/type-h1.js
new file mode 100644
index 00000000..e147764f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/type-h1.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdHlwZS1oMSIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNOC42MzcgMTNWMy42NjlINy4zNzlWNy42MkgyLjc1OFYzLjY3SDEuNVYxM2gxLjI1OFY4LjcyOGg0LjYyVjEzaDEuMjU5em01LjMyOSAwVjMuNjY5aC0xLjI0NEwxMC41IDUuMzE2djEuMjY1bDIuMTYtMS41NjVoLjA2MlYxM2gxLjI0NHoiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/type-h2.js b/frontend/crawlab-ui/src/assets/js/svg/type-h2.js
new file mode 100644
index 00000000..e712c2a0
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/type-h2.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdHlwZS1oMiIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNNy42MzggMTNWMy42NjlINi4zOFY3LjYySDEuNzU5VjMuNjdILjVWMTNoMS4yNThWOC43MjhoNC42MlYxM2gxLjI1OXptMy4wMjItNi43MzN2LS4wNDhjMC0uODg5LjYzLTEuNjY4IDEuNzE2LTEuNjY4Ljk1NyAwIDEuNjc1LjYwOCAxLjY3NSAxLjU3MiAwIC44NTUtLjU1NCAxLjUwNC0xLjA2NyAyLjA4NWwtMy41MTMgMy45OTlWMTNIMTUuNXYtMS4wOTRoLTQuMjQ1di0uMDc1bDIuNDgxLTIuODQ0Yy44NzUtLjk5OCAxLjU4Ni0xLjc4NCAxLjU4Ni0yLjk1MyAwLTEuNDYzLTEuMTU1LTIuNTU2LTIuOTE5LTIuNTU2LTEuOTQxIDAtMi45NjYgMS4zMjYtMi45NjYgMi43NHYuMDQ5aDEuMjIzeiIvPgo8L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/type-h3.js b/frontend/crawlab-ui/src/assets/js/svg/type-h3.js
new file mode 100644
index 00000000..18b72e5d
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/type-h3.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdHlwZS1oMyIgdmlld0JveD0iMCAwIDE2IDE2Ij4KICA8cGF0aCBkPSJNNy42MzcgMTNWMy42NjlINi4zNzlWNy42MkgxLjc1OFYzLjY3SC41VjEzaDEuMjU4VjguNzI4aDQuNjJWMTNoMS4yNTl6bTMuNjI1LTQuMjcyaDEuMDE4YzEuMTQyIDAgMS45MzUuNjcgMS45NDkgMS42NzQuMDEzIDEuMDA1LS43OCAxLjczNy0yLjAxIDEuNzMtMS4wOC0uMDA3LTEuODUzLS41ODgtMS45MzUtMS4zMkg5LjEwOGMuMDY5IDEuMzI3IDEuMjI0IDIuMzg2IDMuMDgzIDIuMzg2IDEuOTM1IDAgMy4zNDMtMS4xNTUgMy4zMDktMi43ODktLjAyNy0xLjUxLTEuMjUxLTIuMTYtMi4wMzctMi4yNDl2LS4wNjhjLjcwNC0uMTIzIDEuNzY0LS45MSAxLjcyMy0yLjIyOS0uMDM1LTEuMzUzLTEuMTc2LTIuNC0yLjk1NC0yLjM4NS0xLjg3My4wMDYtMi44NTcgMS4xNjItMi44OTggMi4zNThoMS4xOTZjLjA2Mi0uNjkuNzExLTEuMjk5IDEuNjk2LTEuMjk5Ljk5OCAwIDEuNjk1LjYyMiAxLjY5NSAxLjUyNS4wMDcuOTIyLS43MTggMS41OTItMS42OTUgMS41OTJoLS45NjR2MS4wNzR6Ii8+Cjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/type-italic.js b/frontend/crawlab-ui/src/assets/js/svg/type-italic.js
new file mode 100644
index 00000000..84655a30
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/type-italic.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdHlwZS1pdGFsaWMiIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZD0iTTcuOTkxIDExLjY3NCA5LjUzIDQuNDU1Yy4xMjMtLjU5NS4yNDYtLjcxIDEuMzQ3LS44MDdsLjExLS41Mkg3LjIxMWwtLjExLjUyYzEuMDYuMDk2IDEuMTI4LjIxMiAxLjAwNS44MDdMNi41NyAxMS42NzRjLS4xMjMuNTk1LS4yNDYuNzEtMS4zNDYuODA2bC0uMTEuNTJoMy43NzRsLjExLS41MmMtMS4wNi0uMDk1LTEuMTI5LS4yMTEtMS4wMDYtLjgwNnoiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/type-strikethrough.js b/frontend/crawlab-ui/src/assets/js/svg/type-strikethrough.js
new file mode 100644
index 00000000..1ea1244e
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/type-strikethrough.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdHlwZS1zdHJpa2V0aHJvdWdoIiB2aWV3Qm94PSIwIDAgMTYgMTYiPgogIDxwYXRoIGQ9Ik02LjMzMyA1LjY4NmMwIC4zMS4wODMuNTgxLjI3LjgxNEg1LjE2NmEyLjc3NiAyLjc3NiAwIDAgMS0uMDk5LS43NmMwLTEuNjI3IDEuNDM2LTIuNzY4IDMuNDgtMi43NjggMS45NjkgMCAzLjM5IDEuMTc1IDMuNDQ1IDIuODVoLTEuMjNjLS4xMS0xLjA4LS45NjQtMS43NDMtMi4yNS0xLjc0My0xLjIzIDAtMi4xOC42MDItMi4xOCAxLjYwN3ptMi4xOTQgNy40NzhjLTIuMTUzIDAtMy41ODktMS4xMDctMy43MDUtMi44MWgxLjIzYy4xNDQgMS4wNiAxLjEyOSAxLjcwMyAyLjU0NCAxLjcwMyAxLjM0IDAgMi4zMS0uNzA1IDIuMzEtMS42NzUgMC0uODI3LS41NDctMS4zNzQtMS45MTQtMS42NzVMOC4wNDYgOC41SDF2LTFoMTR2MWgtMy41MDRjLjQ2OC40MzcuNjc1Ljk5NC42NzUgMS42OTcgMCAxLjgyNi0xLjQzNiAyLjk2Ny0zLjY0NCAyLjk2N3oiLz4KPC9zdmc+`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/type-underline.js b/frontend/crawlab-ui/src/assets/js/svg/type-underline.js
new file mode 100644
index 00000000..17463475
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/type-underline.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgZmlsbD0iY3VycmVudENvbG9yIiBjbGFzcz0iYmkgYmktdHlwZS11bmRlcmxpbmUiIHZpZXdCb3g9IjAgMCAxNiAxNiI+CiAgPHBhdGggZD0iTTUuMzEzIDMuMTM2aC0xLjIzVjkuNTRjMCAyLjEwNSAxLjQ3IDMuNjIzIDMuOTE3IDMuNjIzczMuOTE3LTEuNTE4IDMuOTE3LTMuNjIzVjMuMTM2aC0xLjIzdjYuMzIzYzAgMS40OS0uOTc4IDIuNTctMi42ODcgMi41Ny0xLjcwOSAwLTIuNjg3LTEuMDgtMi42ODctMi41N1YzLjEzNnpNMTIuNSAxNWgtOXYtMWg5djF6Ii8+Cjwvc3ZnPg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/webmagic.js b/frontend/crawlab-ui/src/assets/js/svg/webmagic.js
new file mode 100644
index 00000000..88c04a08
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/webmagic.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEzNCIgaGVpZ2h0PSIxNTQiPgo8cGF0aCBkPSJNMCAwIEM0NC4yMiAwIDg4LjQ0IDAgMTM0IDAgQzEzNCA1MC44MiAxMzQgMTAxLjY0IDEzNCAxNTQgQzg5Ljc4IDE1NCA0NS41NiAxNTQgMCAxNTQgQzAgMTAzLjE4IDAgNTIuMzYgMCAwIFogIiBmaWxsPSIjRkRGRUZDIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDApIi8+CjxwYXRoIGQ9Ik0wIDAgQzIuNDc2NzYzMTEgMS45MzMwODM0MSAzLjYxNzIwOTUzIDMuMjM0NDE5MDYgNSA2IEM1LjE1NDExMTA2IDguMDMwNzQ3NyA1LjI0OTgyNDUzIDEwLjA2NjA2NDIxIDUuMzE2NDA2MjUgMTIuMTAxNTYyNSBDNS4zNTgzMDA3OCAxMy4zMTMyODEyNSA1LjQwMDE5NTMxIDE0LjUyNSA1LjQ0MzM1OTM4IDE1Ljc3MzQzNzUgQzUuNDgyNjc1NzggMTcuMDQ0NDUzMTMgNS41MjE5OTIxOSAxOC4zMTU0Njg3NSA1LjU2MjUgMTkuNjI1IEM1LjYwNTY4MzU5IDIwLjkwMzc1IDUuNjQ4ODY3MTkgMjIuMTgyNSA1LjY5MzM1OTM4IDIzLjUgQzUuNzk5Njk4ODYgMjYuNjY2NTUzNDggNS45MDE3MjgzMyAyOS44MzMxODc1MyA2IDMzIEM2LjI2ODEyNSAzMi4zNjA2MjUgNi41MzYyNSAzMS43MjEyNSA2LjgxMjUgMzEuMDYyNSBDOCAyOSA4IDI5IDExIDI4IEMxMC40MTMyMTY2OSAzMS41MjA2OTk4NSA5LjY0MzUwMDc5IDMzLjcxMjk5ODQzIDggMzcgQzEyLjQ1NSAzNi41MDUgMTIuNDU1IDM2LjUwNSAxNyAzNiBDMTYuMzQgMzcuMzIgMTUuNjggMzguNjQgMTUgNDAgQzEyLjgzNTkzNzUgNDAuNjA5Mzc1IDEyLjgzNTkzNzUgNDAuNjA5Mzc1IDEwLjM3NSA0MC43NSBDOS4xNDkxMDE1NiA0MC44MzUwNzgxMiA5LjE0OTEwMTU2IDQwLjgzNTA3ODEyIDcuODk4NDM3NSA0MC45MjE4NzUgQzYuOTU4NzEwOTQgNDAuOTYwNTQ2ODggNi45NTg3MTA5NCA0MC45NjA1NDY4OCA2IDQxIEM2LjMzIDQxLjY2IDYuNjYgNDIuMzIgNyA0MyBDNy4wNDAyMjM5MSA0NS4zMzI5ODY2IDcuMDQzMjAyNDcgNDcuNjY3MDY2NjYgNyA1MCBDOS4yMTAzOTkxNyA0OS41NzMwNTk4OSAxMS40MTgyNTg2MSA0OS4xMzI5NjAyMiAxMy42MjUgNDguNjg3NSBDMTQuODU0NzY1NjIgNDguNDQzODY3MTkgMTYuMDg0NTMxMjUgNDguMjAwMjM0MzggMTcuMzUxNTYyNSA0Ny45NDkyMTg3NSBDMjUuNjMzMDYxNDMgNDUuNzk0NjEwMzUgMzAuNzU1Njg0NTIgNDEuNDI2MjMxODggMzUuMTA5Mzc1IDM0LjE0NDUzMTI1IEMzNS43OTIzMTU3OSAzMi43ODgzMTM5MiAzNi40MDYwNjY5MyAzMS4zOTc0ODk1NyAzNyAzMCBDMzguNDg1IDMwLjQ5NSAzOC40ODUgMzAuNDk1IDQwIDMxIEMzOS42NTI0NDYzOCAzNy45NTEwNzI0MyAzNS42NTg0ODU3OCA0Mi41Nzk5MjMyNyAzMC42OTE0MDYyNSA0Ny4xMDE1NjI1IEMyMy45MDc3OTI0NCA1MS44ODY1MjA4NyAxNS4xODgwNTg2NCA1My42NDIyMDA2OCA3IDUzIEM2LjY3IDU0LjMyIDYuMzQgNTUuNjQgNiA1NyBDNy4yOTkzNzUgNTcuMDYxODc1IDguNTk4NzUgNTcuMTIzNzUgOS45Mzc1IDU3LjE4NzUgQzIwLjE3MzQzNjA0IDU4LjQwMTEyMTEyIDI4LjI3MDkyMDA4IDY1LjU3OTE0NTUxIDM1IDczIEMzNSA3My45OSAzNSA3NC45OCAzNSA3NiBDMzEuOTI1NTkzNSA3NC41OTY5MTUxNSAyOS41MzMwODkxNSA3Mi45MDQwOTY0MiAyNi45Mzc1IDcwLjc1IEMxNy44NTY3MDE0MiA2My41MTI3ODI3NSAxNy44NTY3MDE0MiA2My41MTI3ODI3NSA2LjkzNzUgNjAuNSBDMy44ODY0NDYyNyA2MC43Mjc5MzQ1NCAzLjg4NjQ0NjI3IDYwLjcyNzkzNDU0IDIgNjMgQzMuOTc4NjcwODIgNjUuMzEyOTI1OTIgNS45NTc5MTIwNiA2Ny42MjUzNTkyNiA3LjkzNzUgNjkuOTM3NSBDOC41MDE0NjQ4NCA3MC41OTY4NTU0NyA5LjA2NTQyOTY5IDcxLjI1NjIxMDk0IDkuNjQ2NDg0MzggNzEuOTM1NTQ2ODggQzEwLjE4NDY2Nzk3IDcyLjU2Mzk2NDg0IDEwLjcyMjg1MTU2IDczLjE5MjM4MjgxIDExLjI3NzM0Mzc1IDczLjgzOTg0Mzc1IEMxMi4wMjM1OTAwOSA3NC43MTE3NzM2OCAxMi4wMjM1OTAwOSA3NC43MTE3NzM2OCAxMi43ODQ5MTIxMSA3NS42MDEzMTgzNiBDMTMuNDk4NjY4MyA3Ni40MjI5MTk1NyAxNC4yMzA0Mjk4NSA3Ny4yMzA0Mjk4NSAxNSA3OCBDMTUuMDc1ODU4NDYgODcuNTEyNjUxMzggMTIuODk1MjQ5MzQgOTYuMDE3OTkzOTIgMTAgMTA1IEM4LjY4IDEwNSA3LjM2IDEwNSA2IDEwNSBDNi40NjI1MzcyOCAxMDAuNzYwMDc0OTcgNy4wMzQ3NTA1IDk2LjcwMjY1NCA4LjA2MjUgOTIuNTYyNSBDOC4zODU4NzI3OSA5MS4wNDM5NDEzNyA4LjY5ODUzMzk1IDg5LjUyMzA1ODkzIDkgODggQzkuMTU4NTU0NjkgODcuMzAyNjE3MTkgOS4zMTcxMDkzOCA4Ni42MDUyMzQzNyA5LjQ4MDQ2ODc1IDg1Ljg4NjcxODc1IEMxMC4xMzMyMDQ1OSA4MC43MTIxMzE3NSA4LjQ3Mzg5MTc5IDc3LjgyNzMyNDg4IDUuNDY3NzczNDQgNzMuNzg2MTMyODEgQzAuNDgzMTQ4MTMgNjYuOTA5MDc1ODkgMC40ODMxNDgxMyA2Ni45MDkwNzU4OSAtNy4wNjg2MDM1MiA2My45NDYwNDQ5MiBDLTEwLjk5NzM5ODIxIDYzLjU4MjU0NDI3IC0xNC4yODgzNDI2NSA2My41MDYzNjM4MiAtMTggNjUgQy0yMy43MTM0NzY5IDY5Ljc2NzMwNDMgLTI5LjI0NDk2MTIgNzcuMDU5OTk5MzYgLTMyIDg0IEMtMzEuNTQwMjUwMTEgODkuNDIwODc4MzYgLTI4LjQ4NjQ1NjY0IDkyLjU5NDExODgxIC0yNS4xNTAzOTA2MiA5Ni43MTQ4NDM3NSBDLTIyIDEwMC43MDk3MjExMSAtMjIgMTAwLjcwOTcyMTExIC0yMiAxMDQgQy0yNy40MDk4Mjg0MSAxMDMuMTgxNDA3NTQgLTI5Ljc3MTA1MDYxIDk5LjA2NjA4NDQxIC0zMyA5NSBDLTMzLjQ1Mzc1IDk0LjQ0MzEyNSAtMzMuOTA3NSA5My44ODYyNSAtMzQuMzc1IDkzLjMxMjUgQy0zNi4wMjEwOTMwMyA5MS4xMTc3MDkzIC0zNy4wMjg4NTc0OSA4OS42MTcxMDY0MSAtMzYuODk4NDM3NSA4Ni44MjgxMjUgQy0zNS42MTE5MDc4NyA4Mi43NzgzNTM0NiAtMzMuNDIzMzUxOSA3OS40ODkyMDI0NSAtMzEuMTI1IDc1LjkzNzUgQy0zMC42Nzc2OTUzMSA3NS4yMzA0NDkyMiAtMzAuMjMwMzkwNjIgNzQuNTIzMzk4NDQgLTI5Ljc2OTUzMTI1IDczLjc5NDkyMTg4IEMtMjcuNjI5NDc3ODUgNzAuNDMzMzMzNTcgLTI1LjQ2MTg5NjE4IDY3LjEzODUxMDAzIC0yMyA2NCBDLTI3Ljg2NDQzMzQ5IDU5LjQ2ODg3MDI5IC0zMS4yNTcyMTQ5MSA1OS4zNDU5ODc4OSAtMzcuNjk5MjE4NzUgNTkuNTQyOTY4NzUgQy00My43MTk0NzMzNiA2MC4zNzY1NDI0NiAtNDcuNzI3MjExMjkgNjMuMjUxNzAwOTkgLTUyLjQzNzUgNjYuODc1IEMtNTQuNTM0Nzg2MzMgNjkuNzI3MzA5NDEgLTU1LjIxMDUyNDM4IDcyLjU3ODkzODk4IC01NiA3NiBDLTU2Ljk5IDc2IC01Ny45OCA3NiAtNTkgNzYgQy01OC42MjczMzUxNCA3MC4wMzczNjIxOCAtNTcuMTI0MzA5NDcgNjUuNDUwODk1MDcgLTUzIDYxIEMtNDcuMTEzMTk5MDYgNTcuMTQyMTQ1MDIgLTQxLjMwMDE3ODMzIDU0LjgzMzgyMDkxIC0zNC4yNSA1NC45Mzc1IEMtMzMuNDUwNzgxMjUgNTQuOTQ2NTIzNDQgLTMyLjY1MTU2MjUgNTQuOTU1NTQ2ODggLTMxLjgyODEyNSA1NC45NjQ4NDM3NSBDLTMxLjIyNDg0Mzc1IDU0Ljk3NjQ0NTMxIC0zMC42MjE1NjI1IDU0Ljk4ODA0Njg4IC0zMCA1NSBDLTMwIDU0LjAxIC0zMCA1My4wMiAtMzAgNTIgQy0zMS42MDg3NSA1Mi4wOTI4MTI1IC0zMS42MDg3NSA1Mi4wOTI4MTI1IC0zMy4yNSA1Mi4xODc1IEMtNDEuMDMzNDc2MjUgNTIuMTU5ODk5MDIgLTQ5Ljc5MjQ4ODg0IDUwLjI2NjkyODIyIC01Ni4zMDg1OTM3NSA0NS44NTkzNzUgQy02MC40NDMzNTkyIDQxLjMxMzk5NzczIC02MyAzNC4xMTQwMjIgLTYzIDI4IEMtNjEuMTg3NSAyOC4xMjUgLTYxLjE4NzUgMjguMTI1IC01OSAyOSBDLTU3LjQxNTE3NTI1IDMxLjU5OTExMjU5IC01Ni4yNDA5NTk1NyAzNC4wODM2NTAwOSAtNTUuNDY0ODQzNzUgMzcuMDM1MTU2MjUgQy01NC41OTM3NzAwOSA0MC4xMjE0MTA2MSAtNTMuODc4MjEyMjUgNDIuMzEzNTgxMjMgLTUxLjMwMDc4MTI1IDQ0LjMyODEyNSBDLTQ0LjY2NzYzNjYzIDQ3LjYxNDA0NTA3IC0zNy4yNzI4NzM3NyA0OC4yMTM3NDMzOCAtMzAgNDkgQy0zMC4wMjA2MjUgNDguMDY5Mjk2ODcgLTMwLjA0MTI1IDQ3LjEzODU5Mzc1IC0zMC4wNjI1IDQ2LjE3OTY4NzUgQy0yOS45OTg3MjQ5MiA0Mi45MzUxMzA1IC0yOS4zNjg3MjExNyA0MC45MDg1MzI0OCAtMjggMzggQy0yOC45OSAzNy41MDUgLTI4Ljk5IDM3LjUwNSAtMzAgMzcgQy0yNy41MjUgMzYuNTA1IC0yNy41MjUgMzYuNTA1IC0yNSAzNiBDLTI1LjAxNTcxMDQ1IDM1LjM4NTkyMjg1IC0yNS4wMzE0MjA5IDM0Ljc3MTg0NTcgLTI1LjA0NzYwNzQyIDM0LjEzOTE2MDE2IEMtMjUuMTEwNzYxOTQgMzEuMzAxMjA0MTEgLTI1LjE0OTI0MTc3IDI4LjQ2MzM5NjY3IC0yNS4xODc1IDI1LjYyNSBDLTI1LjIxMjYzNjcyIDI0LjY1OTQ5MjE5IC0yNS4yMzc3NzM0NCAyMy42OTM5ODQzNyAtMjUuMjYzNjcxODggMjIuNjk5MjE4NzUgQy0yNS4zMjk3MzA5NCAxNi4xMzczNTE2NSAtMjQuNzU4Nzg5MzUgMTAuMDM3ODU3NTEgLTIyIDQgQy0xOS4xODc1IDEuNSAtMTkuMTg3NSAxLjUgLTE2IDAgQy0xNS4wOTI1IC0wLjQ1Mzc1IC0xNC4xODUgLTAuOTA3NSAtMTMuMjUgLTEuMzc1IEMtOC41NDc1MDk5NyAtMi4yNzkzMjUwMSAtNC41MTI3NjAzOSAtMS41MzI2MzU2IDAgMCBaICIgZmlsbD0iIzdEOUM2NyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzksMzIpIi8+CjxwYXRoIGQ9Ik0wIDAgQzIuNDc2NzYzMTEgMS45MzMwODM0MSAzLjYxNzIwOTUzIDMuMjM0NDE5MDYgNSA2IEM1LjE1NDExMTA2IDguMDMwNzQ3NyA1LjI0OTgyNDUzIDEwLjA2NjA2NDIxIDUuMzE2NDA2MjUgMTIuMTAxNTYyNSBDNS4zNTgzMDA3OCAxMy4zMTMyODEyNSA1LjQwMDE5NTMxIDE0LjUyNSA1LjQ0MzM1OTM4IDE1Ljc3MzQzNzUgQzUuNDgyNjc1NzggMTcuMDQ0NDUzMTMgNS41MjE5OTIxOSAxOC4zMTU0Njg3NSA1LjU2MjUgMTkuNjI1IEM1LjYwNTY4MzU5IDIwLjkwMzc1IDUuNjQ4ODY3MTkgMjIuMTgyNSA1LjY5MzM1OTM4IDIzLjUgQzUuNzk5Njk4ODYgMjYuNjY2NTUzNDggNS45MDE3MjgzMyAyOS44MzMxODc1MyA2IDMzIEM2LjI2ODEyNSAzMi4zNjA2MjUgNi41MzYyNSAzMS43MjEyNSA2LjgxMjUgMzEuMDYyNSBDOCAyOSA4IDI5IDExIDI4IEMxMC40MTMyMTY2OSAzMS41MjA2OTk4NSA5LjY0MzUwMDc5IDMzLjcxMjk5ODQzIDggMzcgQzEyLjQ1NSAzNi41MDUgMTIuNDU1IDM2LjUwNSAxNyAzNiBDMTYuMzQgMzcuMzIgMTUuNjggMzguNjQgMTUgNDAgQzEyLjIxNDg0Mzc1IDQwLjY0NDUzMTI1IDEyLjIxNDg0Mzc1IDQwLjY0NDUzMTI1IDguOTM3NSA0MC44MTI1IEM3LjMxMTM0NzY2IDQwLjkxNDk4MDQ3IDcuMzExMzQ3NjYgNDAuOTE0OTgwNDcgNS42NTIzNDM3NSA0MS4wMTk1MzEyNSBDMyA0MSAzIDQxIDIgNDAgQy0wLjIxODU1MjY0IDM5LjkxMjE2NDU4IC0yLjQzOTg4NDk2IDM5Ljg5MzA1MzkxIC00LjY2MDE1NjI1IDM5LjkwMjM0Mzc1IEMtNS4zMjQ2MjI2NSAzOS45MDM3NTg3IC01Ljk4OTA4OTA1IDM5LjkwNTE3MzY1IC02LjY3MzY5MDggMzkuOTA2NjMxNDcgQy04LjgwMzMzODk5IDM5LjkxMjI0NDgyIC0xMC45MzI4ODMzOSAzOS45MjQ3OTk0NiAtMTMuMDYyNSAzOS45Mzc1IEMtMTQuNTAzMjUzNjkgMzkuOTQyNTEzMjkgLTE1Ljk0NDAwOTAzIDM5Ljk0NzA3NjQ5IC0xNy4zODQ3NjU2MiAzOS45NTExNzE4OCBDLTIwLjkyMzIxMTAyIDM5Ljk2MjIxODU1IC0yNC40NjE1OTUzOSAzOS45Nzk0OTIwOCAtMjggNDAgQy0yOCAzOS4zNCAtMjggMzguNjggLTI4IDM4IEMtMjguNjYgMzcuNjcgLTI5LjMyIDM3LjM0IC0zMCAzNyBDLTI3LjUyNSAzNi41MDUgLTI3LjUyNSAzNi41MDUgLTI1IDM2IEMtMjUuMDE1NzEwNDUgMzUuMzg1OTIyODUgLTI1LjAzMTQyMDkgMzQuNzcxODQ1NyAtMjUuMDQ3NjA3NDIgMzQuMTM5MTYwMTYgQy0yNS4xMTA3NjE5NCAzMS4zMDEyMDQxMSAtMjUuMTQ5MjQxNzcgMjguNDYzMzk2NjcgLTI1LjE4NzUgMjUuNjI1IEMtMjUuMjEyNjM2NzIgMjQuNjU5NDkyMTkgLTI1LjIzNzc3MzQ0IDIzLjY5Mzk4NDM3IC0yNS4yNjM2NzE4OCAyMi42OTkyMTg3NSBDLTI1LjMyOTczMDk0IDE2LjEzNzM1MTY1IC0yNC43NTg3ODkzNSAxMC4wMzc4NTc1MSAtMjIgNCBDLTE5LjE4NzUgMS41IC0xOS4xODc1IDEuNSAtMTYgMCBDLTE0LjYzODc1IC0wLjY4MDYyNSAtMTQuNjM4NzUgLTAuNjgwNjI1IC0xMy4yNSAtMS4zNzUgQy04LjU0NzUwOTk3IC0yLjI3OTMyNTAxIC00LjUxMjc2MDM5IC0xLjUzMjYzNTYgMCAwIFogIiBmaWxsPSIjNzM5QjU3IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg3OSwzMikiLz4KPHBhdGggZD0iTTAgMCBDMC45OSAwIDEuOTggMCAzIDAgQzMgNC42MiAzIDkuMjQgMyAxNCBDNC4zMiAxNC4zMyA1LjY0IDE0LjY2IDcgMTUgQzE2LjA2NjkxMjc5IDIxLjY2NzI5NDMzIDI0LjE4Mjc4MjA4IDMxLjI3MTEzMzA3IDI4IDQyIEMyOC45NDAxMjQ1NCA0OC4zNDU4NDA2NCAyNi41NjAyOTI0MyA1My4zMTkzNTExNiAyNCA1OSBDMjIuNjggNTkgMjEuMzYgNTkgMjAgNTkgQzE5LjM0IDYwLjY1IDE4LjY4IDYyLjMgMTggNjQgQzE3LjY3IDY0IDE3LjM0IDY0IDE3IDY0IEMxNi45NzQ5NDM4NSA2My4xNzI3NDQxNCAxNi45NDk4ODc3IDYyLjM0NTQ4ODI4IDE2LjkyNDA3MjI3IDYxLjQ5MzE2NDA2IEMxNi44MDgwMzY1MSA1Ny43NDUyODgwOCAxNi42ODUzNDQgNTMuOTk3NjU4NzUgMTYuNTYyNSA1MC4yNSBDMTYuNTIzMTgzNTkgNDguOTQ4MDQ2ODggMTYuNDgzODY3MTkgNDcuNjQ2MDkzNzUgMTYuNDQzMzU5MzggNDYuMzA0Njg3NSBDMTYuNDAxNDY0ODQgNDUuMDU0Mjk2ODcgMTYuMzU5NTcwMzEgNDMuODAzOTA2MjUgMTYuMzE2NDA2MjUgNDIuNTE1NjI1IEMxNi4yNjE0MTk2OCA0MC43ODc0NzU1OSAxNi4yNjE0MTk2OCA0MC43ODc0NzU1OSAxNi4yMDUzMjIyNyAzOS4wMjQ0MTQwNiBDMTYuMDM1NDc4MjYgMzYuNTIyNTk3NzYgMTUuNzIwMzE1MjUgMzQuMzg5MDg4MzIgMTUgMzIgQzcuMzQ5NjU3MjMgMjkuMjc2MzE3NzYgMS44MTEyNjkzNSAyOC4zMDU2NjQyIC01Ljc3NzM0Mzc1IDMxLjYyNSBDLTguOTQ4MzM2MDYgMzMuNTg2NjY4MzUgLTkuOTg0ODM0ODggMzUuNDQ2OTIyMDggLTExIDM5IEMtMTEuNzg0MTA3MzIgNDIuNDU5NTkxNzIgLTEyLjE3MTM4ODIxIDQ1Ljc1ODE5NzcyIC0xMi4zMTY0MDYyNSA0OS4zMDA3ODEyNSBDLTEyLjM1ODMwMDc4IDUwLjI3NzI0NjA5IC0xMi40MDAxOTUzMSA1MS4yNTM3MTA5NCAtMTIuNDQzMzU5MzggNTIuMjU5NzY1NjIgQy0xMi40ODI2NzU3OCA1My4yNjcxNjc5NyAtMTIuNTIxOTkyMTkgNTQuMjc0NTcwMzEgLTEyLjU2MjUgNTUuMzEyNSBDLTEyLjYwNTY4MzU5IDU2LjMzOTIzODI4IC0xMi42NDg4NjcxOSA1Ny4zNjU5NzY1NiAtMTIuNjkzMzU5MzggNTguNDIzODI4MTIgQy0xMi43OTkwNjgyNSA2MC45NDkwOTU2OSAtMTIuOTAxMTY0NjggNjMuNDc0NDU1NjIgLTEzIDY2IEMtMTQuOTggNjYuNDk1IC0xNC45OCA2Ni40OTUgLTE3IDY3IEMtMTYuNjcgNjcuOTkgLTE2LjM0IDY4Ljk4IC0xNiA3MCBDLTE3LjQ4MTE3ODAxIDY5LjcxODcyNTc5IC0xOC45NTk5MzI0OSA2OS40MjQ2NjI2OCAtMjAuNDM3NSA2OS4xMjUgQy0yMS4yNjEyMTA5NCA2OC45NjI1NzgxMiAtMjIuMDg0OTIxODcgNjguODAwMTU2MjUgLTIyLjkzMzU5Mzc1IDY4LjYzMjgxMjUgQy0yMy42MTU1MDc4MSA2OC40MjM5ODQzOCAtMjQuMjk3NDIxODcgNjguMjE1MTU2MjUgLTI1IDY4IEMtMjUuMzMgNjcuMzQgLTI1LjY2IDY2LjY4IC0yNiA2NiBDLTIyLjUzNSA2NS41MDUgLTIyLjUzNSA2NS41MDUgLTE5IDY1IEMtMTkuNDIxNTIzNDQgNjQuMDk2MzY3MTkgLTE5Ljg0MzA0Njg3IDYzLjE5MjczNDM4IC0yMC4yNzczNDM3NSA2Mi4yNjE3MTg3NSBDLTIwLjgyNTE5NTMxIDYxLjA4MjIyNjU2IC0yMS4zNzMwNDY4OCA1OS45MDI3MzQzOCAtMjEuOTM3NSA1OC42ODc1IEMtMjIuNDgyNzczNDQgNTcuNTE1NzQyMTkgLTIzLjAyODA0Njg4IDU2LjM0Mzk4NDM3IC0yMy41ODk4NDM3NSA1NS4xMzY3MTg3NSBDLTI1LjY5NjYwMTA0IDUwLjQ1MDQ5Njg1IC0yNi4zODczNjgxMiA0Ny4xNDg3Njc4NyAtMjYgNDIgQy0yMS4yNjQ0NTU3MiAzMS4wNTUzNTExMyAtMTMuNzk3NDc0NzMgMjEuNDUzNzc0MjggLTUuMDYyNSAxMy40Mzc1IEMtMS44NTY5MTQyOCAxMC4zNTMwODc2MyAtMC40MjQ5NjU2MSA4LjM5NDMyODQzIDAuMDYyNSAzLjkzNzUgQzAuMDQxODc1IDIuNjM4MTI1IDAuMDIxMjUgMS4zMzg3NSAwIDAgWiAiIGZpbGw9IiM3RjlENjgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDY3LDIpIi8+CjxwYXRoIGQ9Ik0wIDAgQzguOTEgMCAxNy44MiAwIDI3IDAgQzI4Ljk3NTY3MTQgMy45NTEzNDI4IDI5LjMyMTQ2Njg0IDUuNzE3MzQ3MDkgMjkuMzc1IDEwIEMyOS40MDMzNTkzOCAxMS4wMzEyNSAyOS40MzE3MTg3NSAxMi4wNjI1IDI5LjQ2MDkzNzUgMTMuMTI1IEMyOC44ODg3OTUwNCAxNi42OTM2MTczNyAyNy44MTQ0NTExMSAxNy44MDUzMzc3NiAyNSAyMCBDMjAuOTc4MTAwNTUgMjEuMzQwNjMzMTUgMTcuMjczMzc0NzggMjEuMTg0MTcxMjUgMTMuMDYyNSAyMS4xODc1IEMxMi4yNTEwMzUxNiAyMS4xOTk3NDYwOSAxMS40Mzk1NzAzMSAyMS4yMTE5OTIxOSAxMC42MDM1MTU2MiAyMS4yMjQ2MDkzOCBDOS44MTc4MzIwMyAyMS4yMjY1NDI5NyA5LjAzMjE0ODQ0IDIxLjIyODQ3NjU2IDguMjIyNjU2MjUgMjEuMjMwNDY4NzUgQzcuNTA4NTk2MTkgMjEuMjM0NTc3NjQgNi43OTQ1MzYxMyAyMS4yMzg2ODY1MiA2LjA1ODgzNzg5IDIxLjI0MjkxOTkyIEMzLjY1Mjg2NzY4IDIwLjk1OTA0MjI2IDIuMDY1NzcwMzIgMjAuMjQ5Njk3ODcgMCAxOSBDLTIuMTQ2MTk5NDUgMTUuOTAyOTUyNyAtMi4yNzAzNzcwNyAxMy4yMDg2MjY1IC0yLjI1IDkuNSBDLTIuMjU1MTU2MjUgOC41NjE1NjI1IC0yLjI2MDMxMjUgNy42MjMxMjUgLTIuMjY1NjI1IDYuNjU2MjUgQy0yIDQgLTIgNCAwIDAgWiAiIGZpbGw9IiNGMkY3RUUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU0LDcyKSIvPgo8cGF0aCBkPSJNMCAwIEMwLjMzIDAgMC42NiAwIDEgMCBDMSA1LjYxIDEgMTEuMjIgMSAxNyBDLTAuMjU4MTI1IDE3LjM3MTI1IC0xLjUxNjI1IDE3Ljc0MjUgLTIuODEyNSAxOC4xMjUgQy02LjEyODk2OTUyIDE5LjM2NzIxMTIgLTYuODY5NTk4OCAxOS44MDY1MDE0NCAtOC45Mzc1IDIyLjg3NSBDLTEwLjQ2NTU0Njg3IDI3LjM2OTI1NTUgLTExLjEyMjU5MTAzIDMxLjU2NjE1MjI2IC0xMS4zMTY0MDYyNSAzNi4zMDA3ODEyNSBDLTExLjM1ODMwMDc4IDM3LjI3NzI0NjA5IC0xMS40MDAxOTUzMSAzOC4yNTM3MTA5NCAtMTEuNDQzMzU5MzggMzkuMjU5NzY1NjIgQy0xMS40ODI2NzU3OCA0MC4yNjcxNjc5NyAtMTEuNTIxOTkyMTkgNDEuMjc0NTcwMzEgLTExLjU2MjUgNDIuMzEyNSBDLTExLjYwNTY4MzU5IDQzLjMzOTIzODI4IC0xMS42NDg4NjcxOSA0NC4zNjU5NzY1NiAtMTEuNjkzMzU5MzggNDUuNDIzODI4MTIgQy0xMS43OTkwNjgyNSA0Ny45NDkwOTU2OSAtMTEuOTAxMTY0NjggNTAuNDc0NDU1NjIgLTEyIDUzIEMtMTYuMTI5MDQzMzMgNDkuMzI5NzM5MjcgLTE4LjA2ODY3NDI5IDQ2LjIwNTgwODI3IC0xOS44NzUgNDEgQy0yMC4zMDU1NDY4NyAzOS44MDM3NSAtMjAuNzM2MDkzNzUgMzguNjA3NSAtMjEuMTc5Njg3NSAzNy4zNzUgQy0yMi42MzE1MjM4IDMxLjQwMTczMDY1IC0yMC40MzYzMzMzMSAyNy40NTY3Mzk2MyAtMTcuNDkzMTY0MDYgMjIuMzUwMDk3NjYgQy0xNS4wNzc4Mjk4MSAxOC41NDg1OTIxOCAtMTIuMjc0NTI3MjQgMTUuMTI2NTUxMTQgLTkuMzc1IDExLjY4NzUgQy04LjIzNjQzMjc3IDEwLjMxNjg1OTk1IC03LjA5ODM5NDUxIDguOTQ1NzgwMjggLTUuOTYwOTM3NSA3LjU3NDIxODc1IEMtNS4xNzkyODIyMyA2LjYzOTA4NDQ3IC01LjE3OTI4MjIzIDYuNjM5MDg0NDcgLTQuMzgxODM1OTQgNS42ODUwNTg1OSBDLTMuMjY5Mjc1ODYgNC4zMjgzNjQzIC0yLjIwNzg4MTYzIDIuOTI5Nzk3NTIgLTEuMTY0MDYyNSAxLjUxOTUzMTI1IEMtMC41ODc4NTE1NiAwLjc2NzM2MzI4IC0wLjU4Nzg1MTU2IDAuNzY3MzYzMjggMCAwIFogIiBmaWxsPSIjRjhGQ0Y0IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NiwxNSkiLz4KPHBhdGggZD0iTTAgMCBDMS42Mjc2MDQxNyAxLjM5OTczOTU4IDMuMjU1MjA4MzMgMi43OTk0NzkxNyA0Ljg4MjgxMjUgNC4xOTkyMTg3NSBDNi41Mzg1MzI3OSA1LjYwNzQ5NzQ0IDguMjQzMjM5NjkgNi45NTgyMjgyMiA5Ljk2NDg0Mzc1IDguMjg1MTU2MjUgQzE1LjQ0MzczNCAxMi44NTk3ODg3IDIwLjE2MjE2NDQxIDIwLjQ3MDAwNzU2IDIxLjQ2MDkzNzUgMjcuNDkyMTg3NSBDMjEuMjk0NDY1NDEgMjkuODg0MTI4NjIgMjAuODExODg1OTkgMzEuNzQwNTA1OTggMjAgMzQgQzE5LjYyMzU5Mzc1IDM1LjA0OTI5Njg4IDE5LjI0NzE4NzUgMzYuMDk4NTkzNzUgMTguODU5Mzc1IDM3LjE3OTY4NzUgQzE1LjUxNDgwMjYzIDQ1Ljc0MjU5ODY4IDE1LjUxNDgwMjYzIDQ1Ljc0MjU5ODY4IDEzIDQ3IEMxMi45NzQ5NDM4NSA0Ni4xNzI3NDQxNCAxMi45NDk4ODc3IDQ1LjM0NTQ4ODI4IDEyLjkyNDA3MjI3IDQ0LjQ5MzE2NDA2IEMxMi44MDgwMzY1MSA0MC43NDUyODgwOCAxMi42ODUzNDQgMzYuOTk3NjU4NzUgMTIuNTYyNSAzMy4yNSBDMTIuNTIzMTgzNTkgMzEuOTQ4MDQ2ODggMTIuNDgzODY3MTkgMzAuNjQ2MDkzNzUgMTIuNDQzMzU5MzggMjkuMzA0Njg3NSBDMTIuNDAxNDY0ODQgMjguMDU0Mjk2ODcgMTIuMzU5NTcwMzEgMjYuODAzOTA2MjUgMTIuMzE2NDA2MjUgMjUuNTE1NjI1IEMxMi4yNzk3NDg1NCAyNC4zNjM1MjUzOSAxMi4yNDMwOTA4MiAyMy4yMTE0MjU3OCAxMi4yMDUzMjIyNyAyMi4wMjQ0MTQwNiBDMTIuMDc1OTkxNDcgMTguODA0NTM4MiAxMi4wNzU5OTE0NyAxOC44MDQ1MzgyIDExIDE1IEM3LjM3IDE0LjAxIDMuNzQgMTMuMDIgMCAxMiBDMCA4LjA0IDAgNC4wOCAwIDAgWiAiIGZpbGw9IiNGMUY3RUIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDcxLDE5KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjMzIDAgMC42NiAwIDEgMCBDMS4zMyAxLjY1IDEuNjYgMy4zIDIgNSBDMi4zMyAzLjY4IDIuNjYgMi4zNiAzIDEgQzkuNzUwNTg5MjggMi4wMDg3MDg3NCAxNC45NjMyMDU4OSA4LjQ5NzMyMjkyIDE5LjA2NjQwNjI1IDEzLjUzOTA2MjUgQzI5LjE1NjI4MzE4IDI3LjYxNjk0Njk0IDI5LjE1NjI4MzE4IDI3LjYxNjk0Njk0IDI4IDM2IEMyNi44MzY3MTU3IDM5LjQxNDY0MDU2IDI1LjQ4MTgzNTI1IDQyLjcxMjE3ODA1IDI0IDQ2IEMyMy4wMSA0NiAyMi4wMiA0NiAyMSA0NiBDMjEuNDg5MzM4NDQgNDMuMjI3MDgyMTkgMjIuMTAyMzM1MjggNDAuNzE3MDczMDUgMjMuMDYyNSAzOC4wNjI1IEMyNC4zOTc1MDU0OSAzMy43MDE0ODIwOCAyNC42MDAwMTMyNCAzMC40NDIwMzE4MSAyMi44MjgxMjUgMjYuMTgzNTkzNzUgQzE4LjA5NTM0NDQgMTcuOTQ4MDg2OTEgMTMuMjEyMzIyOTkgMTIuMTM5NTUxODMgNiA2IEM1LjY3IDUuNjcgNS4zNCA1LjM0IDUgNSBDNC42NyA4LjYzIDQuMzQgMTIuMjYgNCAxNiBDMi42OCAxNi4zMyAxLjM2IDE2LjY2IDAgMTcgQzAgMTEuMzkgMCA1Ljc4IDAgMCBaICIgZmlsbD0iIzgxOUU2RSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjcsMTUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzEuODEyNSAwLjEyNSAxLjgxMjUgMC4xMjUgNCAxIEM1LjU4NDgyNDc1IDMuNTk5MTEyNTkgNi43NTkwNDA0MyA2LjA4MzY1MDA5IDcuNTM1MTU2MjUgOS4wMzUxNTYyNSBDOC40NDA1ODk4OCAxMi4yNDMxNDk1NiA5LjExNzU1MDgzIDE0LjM1ODM4NTkyIDExLjg3NSAxNi4zNjMyODEyNSBDMTcuMTQ1ODUzNDUgMTguODc5NzQ3NzQgMjIuMjA5NDk1NjEgMTkuNjMyMzQ4OTMgMjggMjAgQzI4IDIwLjY2IDI4IDIxLjMyIDI4IDIyIEMyOS42NSAyMi4zMyAzMS4zIDIyLjY2IDMzIDIzIEMzMyAyMy4zMyAzMyAyMy42NiAzMyAyNCBDMjQuMDk1OTQ3OTYgMjQuNTEzNjk1MzEgMTQuMjExMjI3NTQgMjIuOTQ1ODUxMiA2LjY5MTQwNjI1IDE3Ljg1OTM3NSBDMi41NTY2NDA4IDEzLjMxMzk5NzczIDAgNi4xMTQwMjIgMCAwIFogIiBmaWxsPSIjODhBNDczIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNiw2MCkiLz4KPHBhdGggZD0iTTAgMCBDMS40MTM2NjE1OCAwLjIwODk1MzYzIDIuODMwNjA2MjMgMC4zOTY5MDQwNiA0LjI1IDAuNTYyNSBDNS40ODc1IDAuNzA2ODc1IDYuNzI1IDAuODUxMjUgOCAxIEM4IDEuMzMgOCAxLjY2IDggMiBDNy4zODI1MzkwNiAyLjAxNDE3OTY5IDYuNzY1MDc4MTMgMi4wMjgzNTkzNyA2LjEyODkwNjI1IDIuMDQyOTY4NzUgQy0xLjgyODEyMjUxIDIuNDkxMDgxNDggLTcuMTc3MTQ5NTggNS4wNTkzNDU4MyAtMTMuNDM3NSA5Ljg3NSBDLTE1LjUzNDc4NjMzIDEyLjcyNzMwOTQxIC0xNi4yMTA1MjQzOCAxNS41Nzg5Mzg5OCAtMTcgMTkgQy0xNy45OSAxOSAtMTguOTggMTkgLTIwIDE5IEMtMTkuNjI2ODY5NzcgMTMuMDI5OTE2NCAtMTguMTE3OTM5NzUgOC40NjI2NTQxNiAtMTQgNCBDLTkuNDgyMjI4MjEgMC45MDc3Mzg3MiAtNS40NDg0MzEwNiAtMS44MjcwMTg4IDAgMCBaICIgZmlsbD0iIzg2QTA3MCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNDAsODkpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuMzMgMCAwLjY2IDAgMSAwIEMxLjMzIDEuNjUgMS42NiAzLjMgMiA1IEMyLjMzIDMuNjggMi42NiAyLjM2IDMgMSBDNy4xMzgxMzcwNSAxLjUzMTc2OTk1IDkuMjcwODIzMTggMy43NzkzMjE2NiAxMi4yNSA2LjU2MjUgQzEzLjE0MjAzMTI1IDcuMzg4Nzg5MDYgMTQuMDM0MDYyNSA4LjIxNTA3ODEyIDE0Ljk1MzEyNSA5LjA2NjQwNjI1IEMxNS42Mjg1OTM3NSA5LjcwNDQ5MjE5IDE2LjMwNDA2MjUgMTAuMzQyNTc4MTIgMTcgMTEgQzE2LjAxIDExLjY2IDE1LjAyIDEyLjMyIDE0IDEzIEMxMi42ODcxMjM3OSAxMS44NTQ1OTc3NSAxMS4zNzQ3MDk3OSAxMC43MDg2NjU2NyAxMC4wNjI1IDkuNTYyNSBDOS4zMzE2MDE1NiA4LjkyNDQxNDA2IDguNjAwNzAzMTIgOC4yODYzMjgxMyA3Ljg0NzY1NjI1IDcuNjI4OTA2MjUgQzYuODc4NjA2NDcgNi43NzQ1ODU0MSA1LjkxMzQ4ODI1IDUuOTEzNDg4MjUgNSA1IEM0LjY3IDguNjMgNC4zNCAxMi4yNiA0IDE2IEMyLjY4IDE2LjMzIDEuMzYgMTYuNjYgMCAxNyBDMCAxMS4zOSAwIDUuNzggMCAwIFogIiBmaWxsPSIjOEZBOTdBIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NywxNSkiLz4KPHBhdGggZD0iTTAgMCBDMC45OSAwIDEuOTggMCAzIDAgQzMgNS45NCAzIDExLjg4IDMgMTggQzIuMzQgMTguMzMgMS42OCAxOC42NiAxIDE5IEMxIDE3LjAyIDEgMTUuMDQgMSAxMyBDLTAuMTEzNzUgMTQuMTc1NjI1IC0wLjExMzc1IDE0LjE3NTYyNSAtMS4yNSAxNS4zNzUgQy0zLjU0NDMzMzE4IDE3LjU2NTA0NTMxIC01LjEyMTU4ODU4IDE4LjgyNzMxMzg2IC04IDIwIEMtNy4wNDIwMDE0NCAxNi4yNTUwOTY1NSAtNi4zNDk3ODQ2MyAxNC4zMTU2NTkzIC0zLjQzNzUgMTEuNjg3NSBDLTAuMTA3MTI5MjkgOC4wMTU1NTI4MSAtMC4xMTUzODgyNyA0LjgxNDgzNzg2IDAgMCBaICIgZmlsbD0iIzdDOTk2NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjcsMikiLz4KPHBhdGggZD0iTTAgMCBDMi44NjUwMzA2OCAxLjI4NDMyNDEgNS4xNTQyNzcwNSAyLjQxNTk4Nzg3IDcgNSBDNy40MjQ3ODU4NiA5LjA3Nzk0NDI1IDcuMzEyODI1ODcgMTAuNTMwNzYxMTkgNSAxNCBDNC4zNCAxNCAzLjY4IDE0IDMgMTQgQzIuODk2ODc1IDEyLjkyNzUgMi43OTM3NSAxMS44NTUgMi42ODc1IDEwLjc1IEMyLjAwNTg1ODQ5IDcuMDMxOTU1NCAxLjIzODE5MDgyIDUuODQ4NjA2NDkgLTEgMyBDLTAuNjcgMi4wMSAtMC4zNCAxLjAyIDAgMCBaICIgZmlsbD0iIzc4OUM2MSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoODcsMTA1KSIvPgo8cGF0aCBkPSJNMCAwIEMxLjgxMjUgMC4xMjUgMS44MTI1IDAuMTI1IDQgMSBDNi42NjI4MDY5MyA1LjA4OTMxMDY0IDcuMzQ1Njg1NzYgOC4xNjAzOTkzNCA3IDEzIEM2LjAxIDEzIDUuMDIgMTMgNCAxMyBDMy4zMjY5NDk4MiAxMS4zOTg1MDExNCAyLjY2MTc3NzczIDkuNzkzNjg5NzEgMiA4LjE4NzUgQzEuNjI4NzUgNy4yOTQxNzk2OSAxLjI1NzUgNi40MDA4NTkzNyAwLjg3NSA1LjQ4MDQ2ODc1IEMwIDMgMCAzIDAgMCBaICIgZmlsbD0iIzc5OUI2NCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTYsNjApIi8+CjxwYXRoIGQ9Ik0wIDAgQy0wLjY2IDEuMzIgLTEuMzIgMi42NCAtMiA0IEMxLjYzIDQuMzMgNS4yNiA0LjY2IDkgNSBDOSA1LjMzIDkgNS42NiA5IDYgQzMuMDYgNiAtMi44OCA2IC05IDYgQy04LjAxIDUuNjcgLTcuMDIgNS4zNCAtNiA1IEMtNiA0LjAxIC02IDMuMDIgLTYgMiBDLTMgMCAtMyAwIDAgMCBaICIgZmlsbD0iI0MyRDRCNSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjcsODcpIi8+CjxwYXRoIGQ9Ik0wIDAgQy0wLjYxNzE4NzUgMS45NTMxMjUgLTAuNjE3MTg3NSAxLjk1MzEyNSAtMiA0IEMtNC44MjAzMTI1IDQuNjA5Mzc1IC00LjgyMDMxMjUgNC42MDkzNzUgLTguMTI1IDQuNzUgQy05LjIyMDcwMzEyIDQuODA2NzE4NzUgLTEwLjMxNjQwNjI1IDQuODYzNDM3NSAtMTEuNDQ1MzEyNSA0LjkyMTg3NSBDLTEyLjI4ODM1OTM3IDQuOTQ3NjU2MjUgLTEzLjEzMTQwNjI1IDQuOTczNDM3NSAtMTQgNSBDLTE0IDQuMDEgLTE0IDMuMDIgLTE0IDIgQy0xMy4wOTI1IDIuMDQxMjUgLTEyLjE4NSAyLjA4MjUgLTExLjI1IDIuMTI1IEMtNi45NDA2NTQ0NCAxLjk1OTI1NTk0IC00LjM4OTkwNzQ0IDAgMCAwIFogIiBmaWxsPSIjN0M5QjYzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5Niw2OCkiLz4KPHBhdGggZD0iTTAgMCBDMC42NiAwLjMzIDEuMzIgMC42NiAyIDEgQzIgNS45NSAyIDEwLjkgMiAxNiBDMS4zNCAxNi4zMyAwLjY4IDE2LjY2IDAgMTcgQy0xLjE0ODE5Mjc3IDExLjEzMTQ1OTIgLTAuNzczMTMwNDUgNS45MTE0NzQ0IDAgMCBaICIgZmlsbD0iIzczOTk1RCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNjgsNCkiLz4KPHBhdGggZD0iTTAgMCBDMCAwLjk5IDAgMS45OCAwIDMgQy0yLjMxIDQuMzIgLTQuNjIgNS42NCAtNyA3IEMtNy4zMyA1LjM1IC03LjY2IDMuNyAtOCAyIEMtNC45MTE5MDMwNSAwLjIzNTM3MzE3IC0zLjc2Njg3ODY0IDAgMCAwIFogIiBmaWxsPSIjN0U5QjY2IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzNCw5MSkiLz4KPHBhdGggZD0iTTAgMCBDMC4zMyAwIDAuNjYgMCAxIDAgQzEuNTk4MjMwMzggNC4xODc2MTI2NCAyLjAwNjIwNDA1IDcuNjA3OTU4MzEgLTAuNTU4NTkzNzUgMTEuMTU2MjUgQy0yLjMwODI3ODA4IDEyLjg3NDI0OTkxIC00LjA3OTU2MDAzIDE0LjQ3NjM4NTY0IC02IDE2IEMtNi4zMyAxNS4wMSAtNi42NiAxNC4wMiAtNyAxMyBDLTUuNzEwMzQ4MjQgMTEuNjI0MzcxNDYgLTQuMzczMTA3MDcgMTAuMjkyMzM2MDcgLTMgOSBDLTIuMzQgOSAtMS42OCA5IC0xIDkgQy0wLjY3IDYuMDMgLTAuMzQgMy4wNiAwIDAgWiAiIGZpbGw9IiNFQkY1REUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDY2LDIpIi8+CjxwYXRoIGQ9Ik0wIDAgQy0xLjk4IDAuOTkgLTEuOTggMC45OSAtNCAyIEMtMy42NyAyLjY2IC0zLjM0IDMuMzIgLTMgNCBDLTUuNjQgNC4zMyAtOC4yOCA0LjY2IC0xMSA1IEMtMTEgNC4zNCAtMTEgMy42OCAtMTEgMyBDLTExLjY2IDIuNjcgLTEyLjMyIDIuMzQgLTEzIDIgQy0xMS4yMTA4OTA5NyAxLjQ3MDQ0NzY2IC05LjQxODYwODUyIDAuOTUxNjA2MyAtNy42MjUgMC40Mzc1IEMtNi42MjcyNjU2MyAwLjE0NzQ2MDk0IC01LjYyOTUzMTI1IC0wLjE0MjU3ODEyIC00LjYwMTU2MjUgLTAuNDQxNDA2MjUgQy0yIC0xIC0yIC0xIDAgMCBaICIgZmlsbD0iIzg1OUY3MCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTA1LDc5KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjI0NzUgMC42MTg3NSAwLjQ5NSAxLjIzNzUgMC43NSAxLjg3NSBDMi4yMDYyODYzOSA0LjM1MDY4Njg3IDMuMzc4MTI1MDYgNC45NTEyNTAwMiA2IDYgQzUuNjcgNi42NiA1LjM0IDcuMzIgNSA4IEMxLjM0NjgzMzI5IDYuNzUwMjMyNDQgMC4yMTkzNDE2NCA2LjMyOTAxMjQ3IC0yIDMgQy0xLjM0IDIuMDEgLTAuNjggMS4wMiAwIDAgWiAiIGZpbGw9IiM4MkExNkYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUyLDg3KSIvPgo8cGF0aCBkPSJNMCAwIEMxLjg3NSAwLjE4NzUgMS44NzUgMC4xODc1IDQgMSBDNS4yNSAzLjU2MjUgNS4yNSAzLjU2MjUgNiA2IEM0LjY4IDYuMzMgMy4zNiA2LjY2IDIgNyBDMC41MzkzNzE1IDQuMzUyNjEwODQgMCAzLjEwNTUxNjY2IDAgMCBaICIgZmlsbD0iIzgwOUU2QyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTYsNjApIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuNjYgMC42NiAxLjMyIDEuMzIgMiAyIEMxLjA1MTI1IDEuOTc5Mzc1IDAuMTAyNSAxLjk1ODc1IC0wLjg3NSAxLjkzNzUgQy0zLjkwNjY2MjgzIDEuNzc0NDgxNzQgLTMuOTA2NjYyODMgMS43NzQ0ODE3NCAtNiAzIEMtNi42NTIxMzI5MiA1LjAyNDYzMjU1IC02LjY1MjEzMjkyIDUuMDI0NjMyNTUgLTcgNyBDLTcuOTkgNS4zNSAtOC45OCAzLjcgLTEwIDIgQy02LjMyNjUzMjI5IC0wLjQ0ODk3ODQ3IC00LjMzNjQzNjAxIC0wLjMxNTM3NzE2IDAgMCBaICIgZmlsbD0iI0U3RjJERCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTAsOTIpIi8+CjxwYXRoIGQ9Ik0wIDAgQy0wLjUwNjAzOTkyIDIuMTY4NzQyNTIgLTAuOTk5ODM4MTMgMy45OTk2NzYyNyAtMiA2IEMtNC4wNjI1IDYuNjI1IC00LjA2MjUgNi42MjUgLTYgNyBDLTUuNjg3NSA0LjYyNSAtNS42ODc1IDQuNjI1IC01IDIgQy0yIDAgLTIgMCAwIDAgWiAiIGZpbGw9IiM3ODlBNjIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUyLDMxKSIvPgo8cGF0aCBkPSJNMCAwIEMwLjk4MTIwNDY0IDMuMDUyNjM2NjQgMC45ODEyMDQ2NCA0Ljk0NzM2MzM2IDAgOCBDMC42NiA4IDEuMzIgOCAyIDggQzIgOC45OSAyIDkuOTggMiAxMSBDMC42OCAxMSAtMC42NCAxMSAtMiAxMSBDLTEuMTI1IDMuMzc1IC0xLjEyNSAzLjM3NSAwIDAgWiAiIGZpbGw9IiM4Q0E0NzgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDg3LDEyNikiLz4KPHBhdGggZD0iTTAgMCBDMC4zMyAwIDAuNjYgMCAxIDAgQzEgMy4zIDEgNi42IDEgMTAgQzAuMDEgMTAgLTAuOTggMTAgLTIgMTAgQy0xLjg1OTUwNDgyIDguNTIwMzE2NzYgLTEuNzEyNDUxNzQgNy4wNDEyNTQ4NCAtMS41NjI1IDUuNTYyNSBDLTEuNDgxMjg5MDYgNC43Mzg3ODkwNiAtMS40MDAwNzgxMiAzLjkxNTA3ODEyIC0xLjMxNjQwNjI1IDMuMDY2NDA2MjUgQy0xIDEgLTEgMSAwIDAgWiAiIGZpbGw9IiM3NTlBNTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDg5LDEyNCkiLz4KPHBhdGggZD0iTTAgMCBDMC45OSAwLjMzIDEuOTggMC42NiAzIDEgQzAuNjkgMy42NCAtMS42MiA2LjI4IC00IDkgQy00LjY2IDguNjcgLTUuMzIgOC4zNCAtNiA4IEMtNC4wMiA1LjM2IC0yLjA0IDIuNzIgMCAwIFogIiBmaWxsPSIjRjBGOUVBIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MSw5NikiLz4KPHBhdGggZD0iTTAgMCBDMS4zMiAxLjMyIDIuNjQgMi42NCA0IDQgQzMuMzQgNS4zMiAyLjY4IDYuNjQgMiA4IEMwLjM1IDUuNjkgLTEuMyAzLjM4IC0zIDEgQy0yLjAxIDAuNjcgLTEuMDIgMC4zNCAwIDAgWiAiIGZpbGw9IiM4NkEwNzEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDgwLDk1KSIvPgo8cGF0aCBkPSJNMCAwIEMyLjIyMTg5ODI0IDIuMjIxODk4MjQgMi45MjU5ODg5MiA0LjA4NDgyNzA4IDQgNyBDMy4wMSA3LjY2IDIuMDIgOC4zMiAxIDkgQzAuMDEgNi42OSAtMC45OCA0LjM4IC0yIDIgQy0xLjM0IDIgLTAuNjggMiAwIDIgQzAgMS4zNCAwIDAuNjggMCAwIFogIiBmaWxsPSIjODZBMTcxIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0Nyw1OCkiLz4KPHBhdGggZD0iTTAgMCBDMS45Mzc1IDAuMjUgMS45Mzc1IDAuMjUgNCAxIEM0LjY2IDEuOTkgNS4zMiAyLjk4IDYgNCBDNS4wMSA0LjMzIDQuMDIgNC42NiAzIDUgQzEuNjggMy42OCAwLjM2IDIuMzYgLTEgMSBDLTAuNjcgMC42NyAtMC4zNCAwLjM0IDAgMCBaICIgZmlsbD0iIzhBQTY3OCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzgsMjMpIi8+CjxwYXRoIGQ9Ik0wIDAgQzIuNDc1IDAuOTkgMi40NzUgMC45OSA1IDIgQzUgMi45OSA1IDMuOTggNSA1IEMzLjY4IDQuNjcgMi4zNiA0LjM0IDEgNCBDMC42NyAyLjY4IDAuMzQgMS4zNiAwIDAgWiAiIGZpbGw9IiM3QTk3NUUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUyLDEzMSkiLz4KPHBhdGggZD0iTTAgMCBDMiAxLjMxMjUgMiAxLjMxMjUgNCAzIEM0IDMuOTkgNCA0Ljk4IDQgNiBDMi42OCA1LjM0IDEuMzYgNC42OCAwIDQgQzAgMi42OCAwIDEuMzYgMCAwIFogIiBmaWxsPSIjOEFBMjc5IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMTAsMTAyKSIvPgo8cGF0aCBkPSJNMCAwIEMyLjk2MTc1MTIzIDAuNjEyNzc2MTIgNC4zODA1ODc4MyAxLjI1MzcyNTIyIDcgMyBDNS4zNSAzLjMzIDMuNyAzLjY2IDIgNCBDMS4zNCAyLjY4IDAuNjggMS4zNiAwIDAgWiAiIGZpbGw9IiM5OEE4ODciIHRyYW5zZm9ybT0idHJhbnNsYXRlKDY5LDg3KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjMzIDAuOTkgMC42NiAxLjk4IDEgMyBDLTAuNDM3NSA1LjE4NzUgLTAuNDM3NSA1LjE4NzUgLTIgNyBDLTIuNjYgNi4zNCAtMy4zMiA1LjY4IC00IDUgQy0yLjI1IDEuMTI1IC0yLjI1IDEuMTI1IDAgMCBaICIgZmlsbD0iIzdCOUE2NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoODksNjApIi8+Cjwvc3ZnPgo=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/wechat_work.js b/frontend/crawlab-ui/src/assets/js/svg/wechat_work.js
new file mode 100644
index 00000000..ff1d7702
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/wechat_work.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzIxNTU4MjkzMzc0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEyMjggMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQyODgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjM5Ljg0Mzc1IiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTEwNDUuODQgNzQ3LjAyN2ExNTMuNTYzIDE1My41NjMgMCAwIDAtNTMuMTU2IDIxLjUxNSAxMjkuMDk0IDEyOS4wOTQgMCAwIDEtNTguMDkyIDM1LjFjMi45NTMtMTkuODI4IDEyLjc4My0zNy45MjYgMjcuNjMzLTUxLjNhMTkxLjE4NiAxOTEuMTg2IDAgMCAwIDI2LjQ1Mi02Mi4xNDIgNTYuOTUzIDU2Ljk1MyAwIDEgMSA1Ny4xNjQgNTYuODI3ek05NDEuNjM5IDYxMC42MzRhMTkwLjgxNCAxOTAuODE0IDAgMCAwLTYxLjkzMi0yNi43NDcgNTYuOTUzIDU2Ljk1MyAwIDEgMSA1Ni45NTMtNTYuOTUzIDE1NS4yNjYgMTU1LjI2NiAwIDAgMCAyMS4yNjMgNTMuMzI1IDEyOS42NjYgMTI5LjY2NiAwIDAgMSAzNC43NjIgNTguMzQ2IDg1Ljk3OCA4NS45NzggMCAwIDEtNTAuODc4LTI3Ljk3aC0wLjIxeiBtLTkzLjgyNi0yMDAuNzI4Yy0xNy4xNy0xNDMuODE3LTE2Ni4wOTItMjU2LjUtMzQ2LjI3NC0yNTYuNS0xOTEuOTU0IDAtMzQ4LjEzMiAxMjcuNzQ0LTM0OC4xMzIgMjg0Ljg1YTI2Ni4zMyAyNjYuMzMgMCAwIDAgMTI0LjM2OSAyMTYuMTY5IDM1MS43NjIgMzUxLjc2MiAwIDAgMCAzNy45NjkgMjQuMzg0bC0xNS40NCA2MS42MzZjNS41NjggMi42MTYgMTAuOTY4IDUuNCAxNi42NjMgNy44MDVsNzcuOTYzLTM4Ljk4MWMxMS4zOSAyLjk1MyAyMy4zNzIgNC44NTEgMzUuMjY4IDYuODc2IDcuNTk0IDEuMzUgMTUuMTg4IDIuNzQyIDIyLjk5MyAzLjY3YTQwMS4xMTkgNDAxLjExOSAwIDAgMCAxNDUuNTQ3LTguMzUzIDI4MS4wMTEgMjgxLjAxMSAwIDAgMCAxMS40NzQgNjIuMTg1IDQ4MS4xNTMgNDgxLjE1MyAwIDAgMS0xMDguNjc1IDEyLjY5OCA0NzIuNSA0NzIuNSAwIDAgMS05Ny42MjEtMTAuNzU4TDI2Mi40NiA4NDYuMjFhMzEuMjE5IDMxLjIxOSAwIDAgMS0zMy44NzctMy41NDMgMzEuNjQgMzEuNjQgMCAwIDEtMTAuOTI2LTMyLjMxNmwyNS4zMTItMTAxLjkyNUEzMzAuMDc1IDMzMC4wNzUgMCAwIDEgOTAuMTI1IDQzOC4yNTZjMC0xOTIuMjkgMTg0LjE5LTM0OC4xMzEgNDExLjQxMy0zNDguMTMxIDIxNS43NDYgMCAzOTIuNDI4IDE0MC42NTMgNDA5LjY0IDMxOS40NDRhMjc2LjkxOSAyNzYuOTE5IDAgMCAwLTI5LjkxLTIuOTUzYy0xMS4xOCAwLjQyMi0yMi4zNiAxLjQ3Ni0zMy40NTYgMy4yNDh6TTcxNi4zOTkgNjM0LjQ3YzE4Ljk0My0zLjc5NyAzNi45NTctMTEuMDUzIDUzLjE1Ny0yMS41MTVhMTI5LjA5NCAxMjkuMDk0IDAgMCAxIDU4LjEzNC0zNS4wMTYgODYuMzU4IDg2LjM1OCAwIDAgMS0yNy42NzUgNTEuMjE2Yy0xMi40NDUgMTguOTg0LTIxLjM4OSA0MC4wNzgtMjYuNDUxIDYyLjE4NGE1Ni45NTMgNTYuOTUzIDAgMSAxLTU3LjE2NS01Ni44Njl6IG0xMDIuNiAxMzcuMDI1YzE4LjgxNiAxMi42MTQgMzkuNzQxIDIxLjcyNyA2MS43NjMgMjdhNTYuOTUzIDU2Ljk1MyAwIDEgMS01Ni45NTMgNTYuOTUzIDE1NC40MDYgMTU0LjQwNiAwIDAgMC0yMS4wOTQtNTMuNDA5IDEyOS41NTggMTI5LjU1OCAwIDAgMS0zNC41MS01OC41MTQgODUuODg4IDg1Ljg4OCAwIDAgMSA1MC43OTQgMjguMzA4di0wLjMzOHoiIHAtaWQ9IjQyODkiPjwvcGF0aD48L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/xxl.js b/frontend/crawlab-ui/src/assets/js/svg/xxl.js
new file mode 100644
index 00000000..2325f6a4
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/xxl.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjMwMCIgaGVpZ2h0PSIzMDAiPgo8cGF0aCBkPSJNMCAwIEM5OSAwIDE5OCAwIDMwMCAwIEMzMDAgOTkgMzAwIDE5OCAzMDAgMzAwIEMyMDEgMzAwIDEwMiAzMDAgMCAzMDAgQzAgMjAxIDAgMTAyIDAgMCBaICIgZmlsbD0iI0ZERkVGRSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCwwKSIvPgo8cGF0aCBkPSJNMCAwIEM5OSAwIDE5OCAwIDMwMCAwIEMzMDAgOTkgMzAwIDE5OCAzMDAgMzAwIEMyMDEgMzAwIDEwMiAzMDAgMCAzMDAgQzAgMjAxIDAgMTAyIDAgMCBaIE0xMTYgMTQgQzExNC43OTczMDQ2OSAxNC4yOTAwMzkwNiAxMTMuNTk0NjA5MzggMTQuNTgwMDc4MTIgMTEyLjM1NTQ2ODc1IDE0Ljg3ODkwNjI1IEM5My42MTE1Mjc2MyAxOS44MTgwNDg0IDc1LjU2NTUxMDA1IDI4LjA1NTQ5MDI2IDYxIDQxIEM2MC4wMzU3ODEyNSA0MS44NDQzMzU5NCA1OS4wNzE1NjI1IDQyLjY4ODY3MTg3IDU4LjA3ODEyNSA0My41NTg1OTM3NSBDMzkuODQ1NDUyMzkgNTkuNzQ0MDg5MDggMjYuMTkyODM5NTQgNzguOTcwMDk1MDMgMTcuNDM3NSAxMDEuNzUgQzE3LjE3MzU2NDQ1IDEwMi40Mjk4OTk5IDE2LjkwOTYyODkxIDEwMy4xMDk3OTk4IDE2LjYzNzY5NTMxIDEwMy44MTAzMDI3MyBDMTUuMTM2MjU2NTMgMTA3Ljg0MjIwMzkyIDE1LjEzNjI1NjUzIDEwNy44NDIyMDM5MiAxNiAxMTIgQzE1LjY2MDE1NjI1IDExNC4yMDcwMzEyNSAxNS42NjAxNTYyNSAxMTQuMjA3MDMxMjUgMTUgMTE3IEMxMi41MDY4Nzc1MiAxMjguODE5NzI1NzcgMTEuNjE4NjEyMjggMTM5LjkyMjcyMjE0IDEyIDE1MiBDMTIuMzMgMTUyIDEyLjY2IDE1MiAxMyAxNTIgQzEzLjEyMTE3MTg3IDE1MC43MjM4MjgxMyAxMy4yNDIzNDM3NSAxNDkuNDQ3NjU2MjUgMTMuMzY3MTg3NSAxNDguMTMyODEyNSBDMTMuNTM1OTk0OTEgMTQ2LjQ2MzQ5NDc0IDEzLjcwNTI5MTEgMTQ0Ljc5NDIyNjM1IDEzLjg3NSAxNDMuMTI1IEMxMy45NTM2MzI4MSAxNDIuMjgzMjQyMTkgMTQuMDMyMjY1NjMgMTQxLjQ0MTQ4NDM3IDE0LjExMzI4MTI1IDE0MC41NzQyMTg3NSBDMTQuMTk3MDcwMzEgMTM5Ljc2ODU1NDY5IDE0LjI4MDg1OTM4IDEzOC45NjI4OTA2MiAxNC4zNjcxODc1IDEzOC4xMzI4MTI1IEMxNC40NDA1MDI5MyAxMzcuMzg5MTg0NTcgMTQuNTEzODE4MzYgMTM2LjY0NTU1NjY0IDE0LjU4OTM1NTQ3IDEzNS44NzkzOTQ1MyBDMTUgMTM0IDE1IDEzNCAxNyAxMzIgQzE3LjczODI2MzggMTI5LjYzODQ2NDE2IDE3LjczODI2MzggMTI5LjYzODQ2NDE2IDE4LjI5Njg3NSAxMjYuODYzMjgxMjUgQzE4LjUzMDAzNDE4IDEyNS44MTgyNTQzOSAxOC43NjMxOTMzNiAxMjQuNzczMjI3NTQgMTkuMDAzNDE3OTcgMTIzLjY5NjUzMzIgQzE5LjI0OTc5MDA0IDEyMi41Nzk4MDIyNSAxOS40OTYxNjIxMSAxMjEuNDYzMDcxMjkgMTkuNzUgMTIwLjMxMjUgQzI3LjMwODY4MTc5IDg3LjY1OTI3MjQxIDQ1LjY2MzQ0OTI2IDU5LjUxNjc2NjYyIDczLjY4NzUgNDAuNjI1IEM3NC4zMDUyODMyIDQwLjIwNzgyNzE1IDc0LjkyMzA2NjQxIDM5Ljc5MDY1NDMgNzUuNTU5NTcwMzEgMzkuMzYwODM5ODQgQzc5LjU2MzQ2OTIzIDM2LjczODI5Njc1IDgzLjU1MjMxNjQ3IDM0Ljc2OTkzODI1IDg4IDMzIEM4Ny4zMDUxOTUzMSAzMy40MDYwNTQ2OSA4Ni42MTAzOTA2MiAzMy44MTIxMDkzNyA4NS44OTQ1MzEyNSAzNC4yMzA0Njg3NSBDNjkuMjcxNTUwMyA0NC43ODU3NDU2MyA1NS4wMjQwNDAwNyA2MC4wNDYyNjY0MSA0NC4xMTEzMjgxMiA3Ni4zNTgzOTg0NCBDNDIuMjkzNTUwNTQgNzkuMDQzNTMzODYgNDAuMzU5OTk3NTkgODEuNjM4OTQ4MzEgMzguNDM3NSA4NC4yNSBDMzUuMzkyMzgwNDQgODguNDI4MzM0NTMgMzIuOTU3NDM2MzggOTEuODU3MzkxMzYgMzEuNjIxMDkzNzUgOTYuOTAyMzQzNzUgQzMwLjk0MzY5OSA5OS4xOTAxNDg2NiAzMC4wNzMwMTEwMSAxMDEuMzQ2OTQ0MzQgMjkuMTg3NSAxMDMuNTYyNSBDMjIuNjg5NDY3MzggMTIxLjYwOTg2NjA4IDIxLjA0ODQzNzgzIDEzOC45MjAyNjkzMyAyMiAxNTggQzIyLjA0NTQzOTQ1IDE1OC45MzcxNDg0NCAyMi4wOTA4Nzg5MSAxNTkuODc0Mjk2ODcgMjIuMTM3Njk1MzEgMTYwLjgzOTg0Mzc1IEMyNC4yNzcwNzE1IDE5OC44NzkzMzI1MyA0NS41MTM2Mjk1MyAyMzEuNzIwMDE4NDUgNzMgMjU3IEM3OC45Njk3MTMyMiAyNjIuMDI1Nzg3ODMgODUuNTA2MTA0MTYgMjY1LjU5MzgyNjM1IDkyLjQzNzUgMjY5LjEyNSBDOTMuNDIyOTg4MjggMjY5LjYzNjc1NzgxIDk0LjQwODQ3NjU2IDI3MC4xNDg1MTU2MyA5NS40MjM4MjgxMiAyNzAuNjc1NzgxMjUgQzEwMy43NTc5MDcwOSAyNzQuOTcyNjA5MjQgMTEyLjAxMTYzNjc0IDI3OS4wMjcxOTk3MSAxMjEuMDYyNSAyODEuNTYyNSBDMTIxLjgwNTQ4MzQgMjgxLjc3MTU2OTgyIDEyMi41NDg0NjY4IDI4MS45ODA2Mzk2NSAxMjMuMzEzOTY0ODQgMjgyLjE5NjA0NDkyIEMxMzMuNjczNjEwOTMgMjg0LjkyMzQzMTkyIDE0NC4zNjY0NDgxOSAyODUuOTEyODE2MTMgMTU1IDI4NyBDMTU1IDI4Ny4zMyAxNTUgMjg3LjY2IDE1NSAyODggQzE2My45NTc1NDAxNiAyODguNDYwMzc0NTUgMTcyLjE4ODE0ODkgMjg3LjA3NjI1NjkzIDE4MC45Mzc1IDI4NS4yNSBDMTgyLjIxODE4MzU5IDI4NC45OTczNDM3NSAxODMuNDk4ODY3MTkgMjg0Ljc0NDY4NzUgMTg0LjgxODM1OTM4IDI4NC40ODQzNzUgQzE5NS4wMjQ3NDc0IDI4Mi40MDQ2MjE5NiAyMDQuNzk0NDYyNjggMjc5Ljg0NTEwMDQ3IDIxNC4wMTU2MjUgMjc0LjkyOTY4NzUgQzIxNi4yNjI5NDk5IDI3My44NzY4MDY5MyAyMTguNDgzNzkzMjIgMjczLjIyMzQyMzYxIDIyMC44NzUgMjcyLjU2MjUgQzIzMC44NzA4OTUwNCAyNjkuNjkzNDQ5MjEgMjM4Ljg0MjU0MDIzIDI2NC4zMjkyODA5OSAyNDcgMjU4IEMyNDcuOTU3MTI4OTEgMjU3LjI3NjgzNTk0IDI0Ny45NTcxMjg5MSAyNTcuMjc2ODM1OTQgMjQ4LjkzMzU5Mzc1IDI1Ni41MzkwNjI1IEMyNTQuNDYwOTcwMTUgMjUyLjEyMjI2NzUgMjU4LjcxNjUzMjczIDI0Ni41OTA5Njc1OCAyNjMgMjQxIEMyNjMuNTQzOTg0MzggMjQwLjMxODA4NTk0IDI2NC4wODc5Njg3NSAyMzkuNjM2MTcxODggMjY0LjY0ODQzNzUgMjM4LjkzMzU5Mzc1IEMyNjguNjYzNjE1MDIgMjMzLjM3NTAwOTg1IDI2OC44NzY3NjEwNiAyMjcuNzIyMTIzOSAyNjkgMjIxIEMyNjQuMzM4NjcxMzcgMjI0LjU3NzI5ODcyIDI2MS4yNjA4ODQ2NiAyMjguMTA4NjczMDEgMjU4IDIzMyBDMjU4LjMzIDIzMi4wMSAyNTguNjYgMjMxLjAyIDI1OSAyMzAgQzI1OC42NyAyMjkuMzQgMjU4LjM0IDIyOC42OCAyNTggMjI4IEMyNTcuNjQ4MDg1OTQgMjI4LjU0MjY5NTMxIDI1Ny4yOTYxNzE4OCAyMjkuMDg1MzkwNjMgMjU2LjkzMzU5Mzc1IDIyOS42NDQ1MzEyNSBDMjUzLjM2ODI0NjE0IDIzNC44MTEwMjcxNCAyNTAuODg3ODU1MjggMjM2LjkyODM0NzIyIDI0NSAyMzkgQzI0My42NDIzMjA0IDIzOS45NjY2ODcwNiAyNDIuMzAzMzg5MjQgMjQwLjk2MTI3Mjc3IDI0MSAyNDIgQzI0MS41MzM2NzE4OCAyNDEuNDUyMTQ4NDQgMjQyLjA2NzM0Mzc1IDI0MC45MDQyOTY4OCAyNDIuNjE3MTg3NSAyNDAuMzM5ODQzNzUgQzI1MC45OTk1NjkzOSAyMzEuNDIwODczNCAyNTguODIzNjEwNDggMjIxLjIzNTA4NDk3IDI2My4yODEyNSAyMDkuNzM0Mzc1IEMyNjQgMjA4IDI2NCAyMDggMjY2IDIwNSBDMjY2LjY5MTg0NDg5IDIwMi45NTI0NzUyNSAyNjcuMzMxODgyNjggMjAwLjg4NzE2NDk0IDI2Ny45Mzc1IDE5OC44MTI1IEMyNjkuMDExNjU3NDggMTk1LjE5MjE5MTQ2IDI3MC4wODI2OTU4NyAxOTEuNjYxMTQ1NDggMjcxLjU2MjUgMTg4LjE4NzUgQzI3NS4yODQ5OTgxNyAxNzguNTg2MzAyNTMgMjc2LjU0MDcyNDc5IDE2Ny4yMTUzOTQxIDI3NyAxNTcgQzI3Ny4wNzkyNzczNCAxNTUuMjcxMzY3MTkgMjc3LjA3OTI3NzM0IDE1NS4yNzEzNjcxOSAyNzcuMTYwMTU2MjUgMTUzLjUwNzgxMjUgQzI3Ny40OTk5ODQxOSAxMzguODIxMzM1MzUgMjc1LjQyNDExOTQzIDEyNC4zNTEyMzU1MyAyNzEuNTYyNSAxMTAuMTg3NSBDMjcxLjM2OTU0MzQ2IDEwOS40NzczODc3IDI3MS4xNzY1ODY5MSAxMDguNzY3Mjc1MzkgMjcwLjk3Nzc4MzIgMTA4LjAzNTY0NDUzIEMyNjkuNjk0MTk1NDYgMTAzLjYzMDczNTYgMjY3Ljk2MzA3MDU0IDk5LjY5NTcwODg5IDI2NS43ODQ5MTIxMSA5NS42NTc0NzA3IEMyNjUgOTQgMjY1IDk0IDI2NSA5MiBDMjY1LjIzMDc0MjE5IDkyLjYzODA4NTk0IDI2NS40NjE0ODQzNyA5My4yNzYxNzE4OCAyNjUuNjk5MjE4NzUgOTMuOTMzNTkzNzUgQzI2Ni42NzQ1MDExMiA5Ni40MDQ1NjYwNyAyNjcuNjQ4MzgzMDggOTguNzExMzE5NTkgMjY5IDEwMSBDMjcxLjYwODc3MDUgMTAyLjIxMTM0NTMgMjcxLjYwODc3MDUgMTAyLjIxMTM0NTMgMjc0IDEwMiBDMjY1LjUyOTI0MTg0IDczLjc2NDEzOTQ4IDI0NC4zMzc1MDE3NyA0OS45Mzc1MDExNiAyMjAgMzQgQzI0OC45NTU5NjMxNiA0OC44NzEyMTA3MSAyNjkuNDU0NjU5MTcgNzQuOTE5NjAzNjEgMjc5LjkwNjI1IDEwNS41MDM5MDYyNSBDMjg4LjQ5ODQwNzA2IDEzMi45NjgxMjI1NyAyODYuNDU2Mzk1MDEgMTU4LjM1NjYwMDAyIDI4MCAxODYgQzI4My4wNjg3OCAxODUuMzY2MjI2NjIgMjgzLjA2ODc4IDE4NS4zNjYyMjY2MiAyODMuODg5ODkyNTggMTgzLjI2MDk4NjMzIEMyOTMuOTcxOTQ5MzkgMTUwLjYxMDc2OTkgMjg5Ljg1MjMzNjkgMTEyLjY4NDMzNDgxIDI3NC4xMzU0OTgwNSA4Mi40MDYyNSBDMjcxLjEyNTEzMTA4IDc2LjkxMzU0ODU0IDI2Ny41OTUwMjczMiA3MS44MDc1MzcwNyAyNjMuODgyODEyNSA2Ni43Njk1MzEyNSBDMjYyIDY0IDI2MiA2NCAyNjAuOTg4MjgxMjUgNjEuNDI5Njg3NSBDMjUzLjEwNzkxMjg4IDQyLjA1NTgxNzQ0IDIyNS4zMDM3NjEzNSAyNy45ODUwNTg0MiAyMDYuOTk0MTQwNjIgMjAuMTQwNjI1IEMxNzkuNTE0NjE1OSA4LjgxNzY3MjE1IDE0NC44NTEwMTk5NiA2LjczMjY2MiAxMTYgMTQgWiAiIGZpbGw9IiNGREZFRkUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsMCkiLz4KPHBhdGggZD0iTTAgMCBDMC42NTc1ODMwMSAwLjI5MjQ1NjA1IDEuMzE1MTY2MDIgMC41ODQ5MTIxMSAxLjk5MjY3NTc4IDAuODg2MjMwNDcgQzIxLjIzMzg4NjYzIDkuNTc1MzY0NDEgNDQuODQ3MjM5ODUgMjQuNzk1MzMzNTQgNTMgNDUgQzU0LjAzNDYxOTY0IDQ2LjQ5ODgwMDM1IDU1LjEwMTAxNDY3IDQ3Ljk3NTg2MDU0IDU2LjE4NzUgNDkuNDM3NSBDNzYuNDU3NDYwODYgNzcuOTg2MTM5NjEgODEuNTAzODc0OTkgMTE1LjUzNjAyMDYgNzUuOTQ5MjE4NzUgMTQ5LjQ0OTIxODc1IEM3NS4wNzQ5Njc0NiAxNTQuMDIwMDIxODQgNzQuMTMwMzUyMjUgMTU4LjgzNjkwMDExIDcyIDE2MyBDNzEuMDEgMTYzLjMzIDcwLjAyIDE2My42NiA2OSAxNjQgQzY5LjI2NTU0Njg3IDE2Mi44NjMwNDY4OCA2OS41MzEwOTM3NSAxNjEuNzI2MDkzNzUgNjkuODA0Njg3NSAxNjAuNTU0Njg3NSBDNzguMDM2NDg5NTYgMTI0LjgxODcwNzQ5IDc2LjI2MDM0NDYxIDg4LjYwNDgyNzM2IDU2LjY2OTE4OTQ1IDU2LjU2Nzg3MTA5IEM0NC41ODE1NDg5MiAzNy41MjA1Mjg1IDI5LjIyMTI4NzIgMjMuNzY3MzYyODQgMTAgMTIgQzExLjU3NzgxMjUgMTMuMTEzNzUgMTEuNTc3ODEyNSAxMy4xMTM3NSAxMy4xODc1IDE0LjI1IEMxOC45NzM0NjY3NyAxOC41OTIwMzUyNCAyNC4xODgyNTA1NSAyMy41NDA4ODU5NSAyOS40Mjc3MzQzOCAyOC41MTU2MjUgQzMwLjgyODM4MjIyIDI5LjgzNzk3NTc2IDMyLjI0ODgyOTQ3IDMxLjEzOTI4MzAyIDMzLjY3MTg3NSAzMi40Mzc1IEM0Ni4yNjU4NDQzIDQ0LjMwOTUyMjAxIDU3LjcwOTY5MTQ3IDYxLjUwNDc1MjI5IDYzIDc4IEM2MyA3OC42NiA2MyA3OS4zMiA2MyA4MCBDNjAuNzUgODAuMDYyNSA2MC43NSA4MC4wNjI1IDU4IDc5IEM1Ni4zMDY5OTU3MyA3Ni4xMzMyNTE1NiA1NS4xMzAzNjM3NyA3My4xMjU4NjYyOSA1NCA3MCBDNTkuMzYxNzkzNjMgODMuMDMyNDkzNiA2My4wMjA3OTAxMSA5Ni4wNTY5MDk0NiA2NSAxMTAgQzY1LjEzMTQ4NDM4IDExMC44MjUgNjUuMjYyOTY4NzUgMTExLjY1IDY1LjM5ODQzNzUgMTEyLjUgQzY4LjM3NTQ0Mzg1IDEzMy43NDEzNDI2NCA2NS4xNTk5MjIwNyAxNTYuMTY0MjY2ODEgNTYuODc1IDE3NS44MDg1OTM3NSBDNTYuMDU2NTk5MDggMTc3Ljg1ODI0OTY0IDU1LjQzNTQ0Nzg2IDE3OS44Njc2NzEwMyA1NC44NzUgMTgyIEM1NCAxODUgNTQgMTg1IDUyIDE4NyBDNTEuNjI4NzUgMTg4LjExMzc1IDUxLjI1NzUgMTg5LjIyNzUgNTAuODc1IDE5MC4zNzUgQzQ3LjQxMDYzNTg0IDE5OS45NTM5NjY5MSA0MC4zNjY3OTAyOCAyMDguMDI5OTkyNzEgMzMuODgyODEyNSAyMTUuNzIyNjU2MjUgQzMxLjg5MDI3NTMgMjE4LjA1MzM3MDQ2IDMxLjg5MDI3NTMgMjE4LjA1MzM3MDQ2IDMwIDIyMSBDMzAuNDU4OTA2MjUgMjIwLjU3ODQ3NjU2IDMwLjkxNzgxMjUgMjIwLjE1Njk1MzEzIDMxLjM5MDYyNSAyMTkuNzIyNjU2MjUgQzMyLjMxMTAxNTYzIDIxOC45MDA4Nzg5MSAzMi4zMTEwMTU2MyAyMTguOTAwODc4OTEgMzMuMjUgMjE4LjA2MjUgQzMzLjg1MzI4MTI1IDIxNy41MTcyMjY1NiAzNC40NTY1NjI1IDIxNi45NzE5NTMxMiAzNS4wNzgxMjUgMjE2LjQxMDE1NjI1IEMzNyAyMTUgMzcgMjE1IDM5LjAzMTI1IDIxNC41NjI1IEM0MS44MDcyMjI0OSAyMTMuNzY5MzY1IDQyLjYyMTEyOTU1IDIxMi4yNjk3NDYzMiA0NC4yNSAyMDkuOTM3NSBDNDQuNzcwNzgxMjUgMjA5LjIwNDAyMzQ0IDQ1LjI5MTU2MjUgMjA4LjQ3MDU0Njg3IDQ1LjgyODEyNSAyMDcuNzE0ODQzNzUgQzQ2LjIxNDg0Mzc1IDIwNy4xNDg5NDUzMSA0Ni42MDE1NjI1IDIwNi41ODMwNDY4OCA0NyAyMDYgQzQ3LjMzIDIwNi42NiA0Ny42NiAyMDcuMzIgNDggMjA4IEM0Ny42NyAyMDguOTkgNDcuMzQgMjA5Ljk4IDQ3IDIxMSBDNDcuNTQyNjk1MzEgMjEwLjMwMTMyODEyIDQ4LjA4NTM5MDYzIDIwOS42MDI2NTYyNSA0OC42NDQ1MzEyNSAyMDguODgyODEyNSBDNDkuMzU5OTYwOTQgMjA3Ljk3MjczNDM3IDUwLjA3NTM5MDYzIDIwNy4wNjI2NTYyNSA1MC44MTI1IDIwNi4xMjUgQzUxLjUyMDE5NTMxIDIwNS4yMjAwNzgxMiA1Mi4yMjc4OTA2MiAyMDQuMzE1MTU2MjUgNTIuOTU3MDMxMjUgMjAzLjM4MjgxMjUgQzU1IDIwMSA1NSAyMDEgNTggMTk5IEM1Ny45NzAwOTA3OSAyMDcuODM4MTcyOSA1Ni41NTQwNjM2NyAyMTMuMTU2ODE1MjYgNTEgMjIwIEM1MC4zNzQ4MDQ2OSAyMjAuODM1MzEyNSA0OS43NDk2MDkzNyAyMjEuNjcwNjI1IDQ5LjEwNTQ2ODc1IDIyMi41MzEyNSBDNDIuODM0NDg2MzIgMjMwLjc0NDEzNDI5IDM1LjgyNDExNDQ5IDIzNi41NzI5OTMyNiAyNy4zMTI1IDI0Mi4zMTI1IEMyNi42NzA0NjYzMSAyNDIuNzQ1NjI1IDI2LjAyODQzMjYyIDI0My4xNzg3NSAyNS4zNjY5NDMzNiAyNDMuNjI1IEMyMC41OTMyMjk1MyAyNDYuNzcxNDk0NjggMTYuMDMwOTU3NzggMjQ4Ljc3MjU1MjM3IDEwLjUgMjUwLjI1IEM2LjQwMDEwNDEyIDI1MS40Mzk1MTMyNCAyLjc1ODMzNjIzIDI1My4wMzkwMzY5NCAtMS4wNjI1IDI1NC45Mzc1IEMtOC4wODk0OTU4NyAyNTguMjYyMTg4NDcgLTE1LjA2OTQ0MjIyIDI1OS45NzAyODA4MSAtMjIuNjQyMDg5ODQgMjYxLjUxNDY0ODQ0IEMtMjUuMTQ4NDM2MDYgMjYyLjAzMDU1NDA0IC0yNy42NDU5NzQyNSAyNjIuNTgwNDg1MTQgLTMwLjE0NDUzMTI1IDI2My4xMzI4MTI1IEMtMzguNzk3MzE3NTEgMjY1LjAwMzcyODc2IC00Ny4wODk1NzEwNCAyNjYuNzM1NzIzNDkgLTU2IDI2NiBDLTU2IDI2NS42NyAtNTYgMjY1LjM0IC01NiAyNjUgQy01Ni43Njg1MjI5NSAyNjQuOTE1NDg1ODQgLTU3LjUzNzA0NTkgMjY0LjgzMDk3MTY4IC01OC4zMjg4NTc0MiAyNjQuNzQzODk2NDggQy05Mi4yMTQyNDM1NyAyNjAuODg4NTIxNzIgLTEyMC43OTA4MjQxIDI1MC44OTMwNjY4MSAtMTQ2IDIyNyBDLTE0Ny4zMjgzNzg5MSAyMjUuNzUyODMyMDMgLTE0Ny4zMjgzNzg5MSAyMjUuNzUyODMyMDMgLTE0OC42ODM1OTM3NSAyMjQuNDgwNDY4NzUgQy0xNzIuNTU0NDA3MDggMjAxLjQyODkzNDUxIC0xODcuMzM4NDQzMzcgMTY5LjAwMjczOTg2IC0xODkgMTM2IEMtMTg5LjA1NTkxMzA5IDEzNS4wMDY3NzczNCAtMTg5LjExMTgyNjE3IDEzNC4wMTM1NTQ2OSAtMTg5LjE2OTQzMzU5IDEzMi45OTAyMzQzOCBDLTE5MC4xMzQ5NzI1IDExMy40NjAxNTQxMiAtMTg4LjMzMjI5NzUyIDkzLjUwMjk3OTk1IC0xNzkgNzYgQy0xNzguNzExMjUgNzQuODY1NjI1IC0xNzguNDIyNSA3My43MzEyNSAtMTc4LjEyNSA3Mi41NjI1IEMtMTc1LjI4NjgyNTggNjMuNTc0OTQ4MzggLTE2OC4xMzUzMzkwMSA1Ni4xMDczODEzMSAtMTYyLjM4MzA1NjY0IDQ4Ljc3OTI5Njg4IEMtMTYxLjM5MTIwMzMgNDcuNTAzMjgxNDkgLTE2MC40MTI3MTY3NCA0Ni4yMTY4MzI4NiAtMTU5LjQ0MzExNTIzIDQ0LjkyMzgyODEyIEMtMTU0Ljk5MTQ4MjQxIDM5LjA0Mzk0OTczIC0xNTAuMTQxODcyMzcgMzMuNzIxMzE2MSAtMTQ0LjkzNzUgMjguNSBDLTE0My43MDAzNjI1NSAyNy4yNTcwNjE3NyAtMTQzLjcwMDM2MjU1IDI3LjI1NzA2MTc3IC0xNDIuNDM4MjMyNDIgMjUuOTg5MDEzNjcgQy0xMzUuODQ2NzY5NSAxOS41MTk5NTcyNiAtMTI5LjExNjYzMDAzIDE0LjQwOTE5NzA2IC0xMjEgMTAgQy0xNDUuNTc2NTQ0NjkgMjEuMzA2NjY0NzkgLTE2NC43MzYwMzkxMyAzOS41Nzk1MDY4MyAtMTc4IDYzIEMtMTc4LjM2NzU0Mzk1IDYzLjYzOTIxMzg3IC0xNzguNzM1MDg3ODkgNjQuMjc4NDI3NzMgLTE3OS4xMTM3Njk1MyA2NC45MzcwMTE3MiBDLTE4Ni42ODY0NDc0OCA3OC4zMDg0MzI2MyAtMTkwLjg4Mzk1MTQ1IDkyLjg3MDAwMDk5IC0xOTMuNDU5NDcyNjYgMTA3Ljk3MzE0NDUzIEMtMTk0IDExMCAtMTk0IDExMCAtMTk2IDExMiBDLTE5Ni40NTk3NDUwMyAxMTMuOTI5MDExNDEgLTE5Ni40NTk3NDUwMyAxMTMuOTI5MDExNDEgLTE5Ni42MzI4MTI1IDExNi4xMzI4MTI1IEMtMTk2Ljc1ODQ5NjA5IDExNy4zNDEzMDg1OSAtMTk2Ljc1ODQ5NjA5IDExNy4zNDEzMDg1OSAtMTk2Ljg4NjcxODc1IDExOC41NzQyMTg3NSBDLTE5Ni45NjUzNTE1NiAxMTkuNDE1OTc2NTYgLTE5Ny4wNDM5ODQzOCAxMjAuMjU3NzM0MzggLTE5Ny4xMjUgMTIxLjEyNSBDLTE5Ny4yMTEzNjcxOSAxMjEuOTc0NDkyMTkgLTE5Ny4yOTc3MzQzOCAxMjIuODIzOTg0MzggLTE5Ny4zODY3MTg3NSAxMjMuNjk5MjE4NzUgQy0xOTcuNTk5MDI1NTcgMTI1Ljc5ODY5NzI3IC0xOTcuODAwNTM2MDMgMTI3Ljg5OTI2MjQyIC0xOTggMTMwIEMtMTk4LjMzIDEzMCAtMTk4LjY2IDEzMCAtMTk5IDEzMCBDLTE5OS41NDcxODE1NCAxMTcuNDE0ODI0NSAtMTk4LjI0OTYwNjUgMTA2LjAzODcxMDc0IC0xOTUuMzY3MTg3NSA5My43NzczNDM3NSBDLTE5NC43OTEzNzk4NyA5MC45OTAxODk4MiAtMTk1LjA2ODkwMDU2IDg5Ljc5MzI5ODMxIC0xOTYgODcgQy0xOTUuMzA3NjEzMTggODQuNTM3NTExMjIgLTE5NC41NDQ4Mzc3IDgyLjI1MjEwMjI3IC0xOTMuNjI1IDc5Ljg3NSBDLTE5My4yMTk0Mjg3MSA3OC44MTg1MzI3MSAtMTkzLjIxOTQyODcxIDc4LjgxODUzMjcxIC0xOTIuODA1NjY0MDYgNzcuNzQwNzIyNjYgQy0xODUuMzEwOTgzMSA1OC44MzY5MTQ3NyAtMTc0LjMwODUzNDk0IDQyLjM4OTE4NjUgLTE2MCAyOCBDLTE1OS40OTU5NzY1NiAyNy40OTE5NDgyNCAtMTU4Ljk5MTk1MzEzIDI2Ljk4Mzg5NjQ4IC0xNTguNDcyNjU2MjUgMjYuNDYwNDQ5MjIgQy0xMTguNTYzMzA2NTQgLTEzLjIzNzQxMDc5IC01MC45MDQzMjkxMiAtMjMuMDMxNjQxNyAwIDAgWiBNLTE1MyA0NyBDLTE1OC43ODY0NTYxNyA1NC41Mjk2NDY1NiAtMTYzLjcwMTU4MzU1IDYyLjUzNjM1NzQ2IC0xNjggNzEgQy0xNjguMzA2NDc0NjEgNzEuNTcyOTA3NzEgLTE2OC42MTI5NDkyMiA3Mi4xNDU4MTU0MyAtMTY4LjkyODcxMDk0IDcyLjczNjA4Mzk4IEMtMTcxLjc1MDI0MjQyIDc4LjEyOTUwODA4IC0xNzMuNzQxNDQ1MjcgODMuNjU3NjUzOTcgLTE3NS42MjUgODkuNDM3NSBDLTE3NS45MjQwNjI1IDkwLjMyNzU5NzY2IC0xNzYuMjIzMTI1IDkxLjIxNzY5NTMxIC0xNzYuNTMxMjUgOTIuMTM0NzY1NjIgQy0xODYuNjc5OTczMDUgMTIyLjg3MjgyMjgxIC0xODQuMzE5NjM3MzQgMTU3Ljc4ODA0NjAxIC0xNzAuMDk1NzAzMTIgMTg2Ljg1NTQ2ODc1IEMtMTY1LjA4MDE0NDg0IDE5Ni42NzIwMTU5OCAtMTU5LjIzNDU2ODIxIDIwNS42NTU1ODMyNSAtMTUyIDIxNCBDLTE1MS4yOTg3NSAyMTQuODU3MjI2NTYgLTE1MC41OTc1IDIxNS43MTQ0NTMxMyAtMTQ5Ljg3NSAyMTYuNTk3NjU2MjUgQy0xMzYuNDQyMDI0NzEgMjMyLjM2Nzc4NzcxIC0xMTguNTMzOTkyNDMgMjQzLjQwNzc3ODggLTk5LjE4NzUgMjUwLjQzNzUgQy05OC4yMjA3MDMxMiAyNTAuNzkwMjYwMDEgLTk4LjIyMDcwMzEyIDI1MC43OTAyNjAwMSAtOTcuMjM0Mzc1IDI1MS4xNTAxNDY0OCBDLTg0LjkzNjM1Mzg2IDI1NS4zMjExNDI4NCAtNzIuNDY1NTYwNzggMjU1LjM0NDE1MTA3IC01OS42MjU0ODgyOCAyNTUuMzE1NjczODMgQy01Ni42MTA2NDQxOCAyNTUuMzEyNDg0ODEgLTUzLjU5Njc1NjY3IDI1NS4zMzYxNDQyOSAtNTAuNTgyMDMxMjUgMjU1LjM2MTMyODEyIEMtNDguNjUxMDQyODkgMjU1LjM2MzU5ODg5IC00Ni43MjAwNTE5MiAyNTUuMzY0MzA1NjUgLTQ0Ljc4OTA2MjUgMjU1LjM2MzI4MTI1IEMtNDMuODk1MjI4NTggMjU1LjM3MjQ5MSAtNDMuMDAxMzk0NjUgMjU1LjM4MTcwMDc0IC00Mi4wODA0NzQ4NSAyNTUuMzkxMTg5NTggQy0zNi45MTE3MzU3MSAyNTUuMzUyOTQwODcgLTMzLjU0Njg0NzcgMjU0LjcwOTI5NzY4IC0yOSAyNTIgQy0yNy4yNjMxNDA3NiAyNTEuNTQ5OTk1NTYgLTI1LjUxMTIyMjY0IDI1MS4xNTQ5NTk5NiAtMjMuNzUgMjUwLjgxMjUgQy0wLjUwMDIyODk3IDI0NS4yODIzNTUyMSAxNy45Mjc1Njk3OSAyMzEuMDMyMjczIDMzIDIxMyBDMzIuNjcgMjEyLjM0IDMyLjM0IDIxMS42OCAzMiAyMTEgQzI5LjY5IDIxMy4zMSAyNy4zOCAyMTUuNjIgMjUgMjE4IEMyNi42NDE5MzQ3NSAyMTUuNzY4NjUyNzcgMjguMzI3NDQ3NTIgMjEzLjYwNDEzMDYxIDMwLjA5Mzc1IDIxMS40Njg3NSBDNDEuODQzMDc4MTMgMTk2Ljk3MjgyNTY4IDQ5Ljk5ODE1MjQ5IDE4MC45NTAxNzg1OCA1NSAxNjMgQzU1LjMxNDUzMTI1IDE2MS45MDA0Mjk2OSA1NS42MjkwNjI1IDE2MC44MDA4NTkzNyA1NS45NTMxMjUgMTU5LjY2Nzk2ODc1IEM1Ni43MjI0OTg1IDE1Ni43OTAzNjEwMSA1Ny4zODc4NTQwMSAxNTMuOTE0Nzg3ODQgNTggMTUxIEM1OC4yMDQ5NjA5NCAxNTAuMDgzNDc2NTYgNTguNDA5OTIxODggMTQ5LjE2Njk1MzEyIDU4LjYyMTA5Mzc1IDE0OC4yMjI2NTYyNSBDNjEuODI2NTk3NDkgMTMxLjM5OTI4ODM3IDYwLjkwMDYyNzU2IDExMy42MDI2NzExNSA1NyA5NyBDNTYuODUwMTQ2NDggOTYuMzQ3MDg5ODQgNTYuNzAwMjkyOTcgOTUuNjk0MTc5NjkgNTYuNTQ1ODk4NDQgOTUuMDIxNDg0MzggQzUwLjg5Nzk4OTY4IDcwLjc3NTA0ODA1IDM3Ljc2Nzg0MjY2IDQ1LjcxMjkwMDU3IDE4IDMwIEMxNy4xMzExNzE4OCAyOS4yMzQyOTY4NyAxNi4yNjIzNDM3NSAyOC40Njg1OTM3NSAxNS4zNjcxODc1IDI3LjY3OTY4NzUgQy00Ljc0OTAyMTkgMTAuMTM4MzUyOSAtMzAuMzEyNDk3MTcgLTEuMTIzMDc2NzIgLTU3IC0zIEMtNTcuOTI1NTQ2ODggLTMuMDgyNSAtNTguODUxMDkzNzUgLTMuMTY1IC01OS44MDQ2ODc1IC0zLjI1IEMtOTUuNTk5MDQ2NTEgLTQuOTkwNzU5MSAtMTMxLjQwODQyOTI0IDIwLjgzNDg0NTI5IC0xNTMgNDcgWiAiIGZpbGw9IiM2REJDOTciIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIxMSwyMikiLz4KPHBhdGggZD0iTTAgMCBDMC42NTc1ODMwMSAwLjI5MjQ1NjA1IDEuMzE1MTY2MDIgMC41ODQ5MTIxMSAxLjk5MjY3NTc4IDAuODg2MjMwNDcgQzIxLjIzMzg4NjYzIDkuNTc1MzY0NDEgNDQuODQ3MjM5ODUgMjQuNzk1MzMzNTQgNTMgNDUgQzU0LjAzNDYxOTY0IDQ2LjQ5ODgwMDM1IDU1LjEwMTAxNDY3IDQ3Ljk3NTg2MDU0IDU2LjE4NzUgNDkuNDM3NSBDNzYuNDU3NDYwODYgNzcuOTg2MTM5NjEgODEuNTAzODc0OTkgMTE1LjUzNjAyMDYgNzUuOTQ5MjE4NzUgMTQ5LjQ0OTIxODc1IEM3NS4wNzQ5Njc0NiAxNTQuMDIwMDIxODQgNzQuMTMwMzUyMjUgMTU4LjgzNjkwMDExIDcyIDE2MyBDNzEuMDEgMTYzLjMzIDcwLjAyIDE2My42NiA2OSAxNjQgQzY5LjI2NTU0Njg3IDE2Mi44NjMwNDY4OCA2OS41MzEwOTM3NSAxNjEuNzI2MDkzNzUgNjkuODA0Njg3NSAxNjAuNTU0Njg3NSBDNzguMDM2NDg5NTYgMTI0LjgxODcwNzQ5IDc2LjI2MDM0NDYxIDg4LjYwNDgyNzM2IDU2LjY2OTE4OTQ1IDU2LjU2Nzg3MTA5IEM0NC41ODE1NDg5MiAzNy41MjA1Mjg1IDI5LjIyMTI4NzIgMjMuNzY3MzYyODQgMTAgMTIgQzExLjU3NzgxMjUgMTMuMTEzNzUgMTEuNTc3ODEyNSAxMy4xMTM3NSAxMy4xODc1IDE0LjI1IEMyMS45MjYxNDU0NCAyMC44MDc4NzgzIDMwLjE5NTc2NjMgMjguNDQ2MTA2MiAzNyAzNyBDMzUuNjggMzcuMzMgMzQuMzYgMzcuNjYgMzMgMzggQzMzLjg0NTYyNSAzOC44ODY4NzUgMzQuNjkxMjUgMzkuNzczNzUgMzUuNTYyNSA0MC42ODc1IEMzOC4wMjYwMzI1MSA0My42MDA4MzQ4IDM4LjAyNjAzMjUxIDQzLjYwMDgzNDggMzcuODEyNSA0Ni44NzUgQzM3LjU0NDM3NSA0Ny41NzYyNSAzNy4yNzYyNSA0OC4yNzc1IDM3IDQ5IEMzNi4wMSA0OSAzNS4wMiA0OSAzNCA0OSBDMzIuMjM4MjgxMjUgNDYuODM5ODQzNzUgMzIuMjM4MjgxMjUgNDYuODM5ODQzNzUgMzAuMzEyNSA0My45Mzc1IEMyNi42NjAwNTQ5NiAzOC43NDA1NTQ2IDIyLjkxNzI5NzM5IDM0Ljk1NjE1MDI2IDE4IDMxIEMxNy4xMjczMDQ2OSAzMC4yMzA0Mjk2OSAxNi4yNTQ2MDkzOCAyOS40NjA4NTkzNyAxNS4zNTU0Njg3NSAyOC42Njc5Njg3NSBDLTQuNzYyNDMzMTcgMTEuMTM1NTAwMDEgLTMwLjMxNjI5MTUzIC0wLjEyMzM0MzU4IC01NyAtMiBDLTU3LjkyNTU0Njg4IC0yLjA4MjUgLTU4Ljg1MTA5Mzc1IC0yLjE2NSAtNTkuODA0Njg3NSAtMi4yNSBDLTgzLjM4NzEwMTkzIC0zLjM5Njg2NTE0IC0xMDkuNDMxMjY3NTEgOS41MDk4OTUyIC0xMjggMjMgQy0xMjggMjEuNjggLTEyOCAyMC4zNiAtMTI4IDE5IEMtMTMwLjMxIDE5LjMzIC0xMzIuNjIgMTkuNjYgLTEzNSAyMCBDLTEzMi4wMzM1MjI1NSAxNi44MDc1MDUyMyAtMTI4Ljk1MDM2MzY0IDE0LjU0MDcwMTMgLTEyNS4yNSAxMi4yNSBDLTEyNC4yNjUxNTYyNSAxMS42MzY0MDYyNSAtMTIzLjI4MDMxMjUgMTEuMDIyODEyNSAtMTIyLjI2NTYyNSAxMC4zOTA2MjUgQy0xMjEuNTE3OTY4NzUgOS45MzE3MTg3NSAtMTIwLjc3MDMxMjUgOS40NzI4MTI1IC0xMjAgOSBDLTE0NC43MDM3Nzg0NyAyMS4zODU2Mzc1NyAtMTY0LjM3NTQ0ODU3IDM4Ljk0MjgwMzQ2IC0xNzggNjMgQy0xNzguMzY3NTQzOTUgNjMuNjM5MjEzODcgLTE3OC43MzUwODc4OSA2NC4yNzg0Mjc3MyAtMTc5LjExMzc2OTUzIDY0LjkzNzAxMTcyIEMtMTg2LjY4NjQ0NzQ4IDc4LjMwODQzMjYzIC0xOTAuODgzOTUxNDUgOTIuODcwMDAwOTkgLTE5My40NTk0NzI2NiAxMDcuOTczMTQ0NTMgQy0xOTQgMTEwIC0xOTQgMTEwIC0xOTYgMTEyIEMtMTk2LjQ1OTc0NTAzIDExMy45MjkwMTE0MSAtMTk2LjQ1OTc0NTAzIDExMy45MjkwMTE0MSAtMTk2LjYzMjgxMjUgMTE2LjEzMjgxMjUgQy0xOTYuNzU4NDk2MDkgMTE3LjM0MTMwODU5IC0xOTYuNzU4NDk2MDkgMTE3LjM0MTMwODU5IC0xOTYuODg2NzE4NzUgMTE4LjU3NDIxODc1IEMtMTk2Ljk2NTM1MTU2IDExOS40MTU5NzY1NiAtMTk3LjA0Mzk4NDM4IDEyMC4yNTc3MzQzOCAtMTk3LjEyNSAxMjEuMTI1IEMtMTk3LjIxMTM2NzE5IDEyMS45NzQ0OTIxOSAtMTk3LjI5NzczNDM4IDEyMi44MjM5ODQzOCAtMTk3LjM4NjcxODc1IDEyMy42OTkyMTg3NSBDLTE5Ny41OTkwMjU1NyAxMjUuNzk4Njk3MjcgLTE5Ny44MDA1MzYwMyAxMjcuODk5MjYyNDIgLTE5OCAxMzAgQy0xOTguMzMgMTMwIC0xOTguNjYgMTMwIC0xOTkgMTMwIEMtMTk5LjU0NzE4MTU0IDExNy40MTQ4MjQ1IC0xOTguMjQ5NjA2NSAxMDYuMDM4NzEwNzQgLTE5NS4zNjcxODc1IDkzLjc3NzM0Mzc1IEMtMTk0Ljc5MTM3OTg3IDkwLjk5MDE4OTgyIC0xOTUuMDY4OTAwNTYgODkuNzkzMjk4MzEgLTE5NiA4NyBDLTE5NS4zMDc2MTMxOCA4NC41Mzc1MTEyMiAtMTk0LjU0NDgzNzcgODIuMjUyMTAyMjcgLTE5My42MjUgNzkuODc1IEMtMTkzLjIxOTQyODcxIDc4LjgxODUzMjcxIC0xOTMuMjE5NDI4NzEgNzguODE4NTMyNzEgLTE5Mi44MDU2NjQwNiA3Ny43NDA3MjI2NiBDLTE4NS4zMTA5ODMxIDU4LjgzNjkxNDc3IC0xNzQuMzA4NTM0OTQgNDIuMzg5MTg2NSAtMTYwIDI4IEMtMTU5LjQ5NTk3NjU2IDI3LjQ5MTk0ODI0IC0xNTguOTkxOTUzMTMgMjYuOTgzODk2NDggLTE1OC40NzI2NTYyNSAyNi40NjA0NDkyMiBDLTExOC41NjMzMDY1NCAtMTMuMjM3NDEwNzkgLTUwLjkwNDMyOTEyIC0yMy4wMzE2NDE3IDAgMCBaICIgZmlsbD0iIzZCQkM5NiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjExLDIyKSIvPgo8cGF0aCBkPSJNMCAwIEMxMS4yMDYzMjE1OSA0LjE4MjM1OTMxIDIwLjIwNjc0NDU5IDEyLjEyNjAyNTgyIDI5IDIwIEMyOS43MzA4OTg0NCAyMC42NDU4MjAzMSAzMC40NjE3OTY4OCAyMS4yOTE2NDA2MiAzMS4yMTQ4NDM3NSAyMS45NTcwMzEyNSBDNTQuNzM5NDAzMDUgNDQuMDM4MzY2MDUgNjYuOTgxMzk2MDIgNzcuMzQwMTkwNDMgNjguMTkxNDA2MjUgMTA5LjA4MjAzMTI1IEM2OC40NzYwNTgzNiAxMjEuODQ5ODMyNTMgNjYuNDg2NTM5MjkgMTM0LjAwNzE2NTg1IDYzLjIzMzM5ODQ0IDE0Ni4zMTYxNjIxMSBDNjIuMTY5NDk5NjQgMTUwLjE2NTU0MjcyIDYyLjE2OTQ5OTY0IDE1MC4xNjU1NDI3MiA2Mi41NTA3ODEyNSAxNTQuMDU4NTkzNzUgQzYzLjMwNDE1NzEgMTU3LjMxNDQ4NzYzIDYxLjY5NTIwNzMgMTYwLjAwNzE1MDM4IDYwLjM3NSAxNjIuOTM3NSBDNTkuOTIyMDU1NjYgMTYzLjk1OTc2Njg1IDU5LjkyMjA1NTY2IDE2My45NTk3NjY4NSA1OS40NTk5NjA5NCAxNjUuMDAyNjg1NTUgQzU0LjYyNjk0MzkyIDE3NS40NDY3MDA0MyA0OC40NzIzMjU1MyAxODguMDYzMTM2NjIgNDAgMTk2IEMzNy4zMzk3NjkyNiAxOTguNTk1MzQ3MDcgMzUuMDY1OTM1NTIgMjAwLjkwMTA5NjcxIDMzIDIwNCBDMzEuODg2MjUgMjA0LjQ3NDM3NSAzMC43NzI1IDIwNC45NDg3NSAyOS42MjUgMjA1LjQzNzUgQzI2LjE2OTMxNTk4IDIwNi45MjcwMTg5NyAyNC40MTkwMzAxNiAyMDguMTkzOTI1MDIgMjIgMjExIEMyMS4zNCAyMTAuNjcgMjAuNjggMjEwLjM0IDIwIDIxMCBDMjAuNjA1ODU5MzcgMjA5LjM3OTk2MDk0IDIxLjIxMTcxODc1IDIwOC43NTk5MjE4OCAyMS44MzU5Mzc1IDIwOC4xMjEwOTM3NSBDMzAuNTM1NjQwODIgMTk4LjkyNjE3NjU0IDM4LjY2NDk1NzQzIDE4OC42NDQ0MDk4MyA0My4yODEyNSAxNzYuNzM0Mzc1IEM0NCAxNzUgNDQgMTc1IDQ2IDE3MiBDNDYuNjkxODQ0ODkgMTY5Ljk1MjQ3NTI1IDQ3LjMzMTg4MjY4IDE2Ny44ODcxNjQ5NCA0Ny45Mzc1IDE2NS44MTI1IEM0OS4wMTE2NTc0OCAxNjIuMTkyMTkxNDYgNTAuMDgyNjk1ODcgMTU4LjY2MTE0NTQ4IDUxLjU2MjUgMTU1LjE4NzUgQzU1LjI4NDk5ODE3IDE0NS41ODYzMDI1MyA1Ni41NDA3MjQ3OSAxMzQuMjE1Mzk0MSA1NyAxMjQgQzU3LjA1Mjg1MTU2IDEyMi44NDc1NzgxMiA1Ny4xMDU3MDMxMiAxMjEuNjk1MTU2MjUgNTcuMTYwMTU2MjUgMTIwLjUwNzgxMjUgQzU3LjQ5OTk4NDE5IDEwNS44MjEzMzUzNSA1NS40MjQxMTk0MyA5MS4zNTEyMzU1MyA1MS41NjI1IDc3LjE4NzUgQzUxLjM2OTU0MzQ2IDc2LjQ3NzM4NzcgNTEuMTc2NTg2OTEgNzUuNzY3Mjc1MzkgNTAuOTc3NzgzMiA3NS4wMzU2NDQ1MyBDNDkuNjk3NTQ3IDcwLjY0MjIzNzE0IDQ3Ljk3OTIyNjE4IDY2LjgxNzE0NzU1IDQ1LjY4NzI1NTg2IDYyLjg1Mjc4MzIgQzQ0LjgyNzM2MTM0IDYwLjUzNDU4MDkgNDUuMjA1MTAxNzQgNTkuMjk3Njc4MzQgNDYgNTcgQzQ2LjY3MTE5NTQ5IDU4LjQ1NjI1NDUgNDcuMzM2ODE4MDcgNTkuOTE1MDc4NjcgNDggNjEuMzc1IEM0OC4zNzEyNSA2Mi4xODcxMDkzOCA0OC43NDI1IDYyLjk5OTIxODc1IDQ5LjEyNSA2My44MzU5Mzc1IEM1MCA2NiA1MCA2NiA1MCA2OCBDNTEuNjUgNjguMzMgNTMuMyA2OC42NiA1NSA2OSBDNDQuNjc2NTI1MDkgNDEuOTA2MzIzMjMgMjYuNTE1OTE3ODMgMTkuNTE2MjU4MzMgMi43ODkwNjI1IDMuMDExNzE4NzUgQzEuODY4NjcxODcgMi4zNDc4NTE1NiAwLjk0ODI4MTI1IDEuNjgzOTg0MzggMCAxIEMwIDAuNjcgMCAwLjM0IDAgMCBaICIgZmlsbD0iIzc3QkM5QyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjE5LDMzKSIvPgo8cGF0aCBkPSJNMCAwIEM4LjkxIDAgMTcuODIgMCAyNyAwIEMyNyAxLjMyIDI3IDIuNjQgMjcgNCBDMjQuNjkgNCAyMi4zOCA0IDIwIDQgQzIyLjgyMTQzMTc1IDEwLjA4MTE0NDE2IDI2LjE1MzMzNzYyIDE1LjIwNzY2MTA0IDMwLjI1IDIwLjUgQzMxLjA4MTQ0NTMxIDIxLjU5MDU0Njg4IDMxLjA4MTQ0NTMxIDIxLjU5MDU0Njg4IDMxLjkyOTY4NzUgMjIuNzAzMTI1IEMzMy4yODA2NDIxOCAyNC40NzMzNDE0NyAzNC42MzczOTc5MSAyNi4yMzg3NDM4NiAzNiAyOCBDMzguOTQ3OTMzNjYgMjYuNjkyNDg3MDQgNDAuODA2NjUwNDggMjUuMzg0NzIzODcgNDIuODc1IDIyLjkxNzk2ODc1IEM0My4zODU0Njg3NSAyMi4zMTUzMzIwMyA0My44OTU5Mzc1IDIxLjcxMjY5NTMxIDQ0LjQyMTg3NSAyMS4wOTE3OTY4OCBDNDQuOTQyNjU2MjUgMjAuNDYzMzc4OTEgNDUuNDYzNDM3NSAxOS44MzQ5NjA5NCA0NiAxOS4xODc1IEM0Ni43ODExNzE4NyAxOC4yNjIyNzUzOSA0Ni43ODExNzE4NyAxOC4yNjIyNzUzOSA0Ny41NzgxMjUgMTcuMzE4MzU5MzggQzQ5LjA2Mjg5MzEzIDE1LjU1NDI1OTg2IDUwLjUzMjM3MzU2IDEzLjc3ODM2MzM2IDUyIDEyIEM1Mi43ODM3NSAxMS4wNjgwMDc4MSA1My41Njc1IDEwLjEzNjAxNTYyIDU0LjM3NSA5LjE3NTc4MTI1IEM1Ni4xOTk0NjI1OCA3LjE4NTUzNjIgNTYuMTk5NDYyNTggNy4xODU1MzYyIDU2IDUgQzUzLjY5IDQuNjcgNTEuMzggNC4zNCA0OSA0IEM0OSAyLjY4IDQ5IDEuMzYgNDkgMCBDNTYuNTkgMCA2NC4xOCAwIDcyIDAgQzcyIDEuNjUgNzIgMy4zIDcyIDUgQzcwLjcwMDYyNSA1LjEyMzc1IDY5LjQwMTI1IDUuMjQ3NSA2OC4wNjI1IDUuMzc1IEM2MS4zMjc1MDA3OCA2Ljg3MTY2NjQ5IDU4LjE2NDU5MTAzIDExLjg2MDU2NDk1IDU0IDE3IEM1Mi40MDU0MTQxIDE4Ljc3OTQ2NTQyIDUwLjgwMTQ3Mzg3IDIwLjU1MDYwMDUzIDQ5LjE4NzUgMjIuMzEyNSBDNDYuMzM1NTg4NzQgMjUuNDU1MzE2NTkgNDMuNjI2MDkzOTUgMjguNjY1NzQ1ODggNDEgMzIgQzQyLjY2MzIxNzU5IDM1Ljc4MzM2MzA5IDQ1LjA5MzEzNjEzIDM4Ljg5NDU0MzM0IDQ3LjYyNSA0Mi4xMjUgQzQ4LjA2OTQwNDMgNDIuNjk2MTM1MjUgNDguNTEzODA4NTkgNDMuMjY3MjcwNTEgNDguOTcxNjc5NjkgNDMuODU1NzEyODkgQzQ5Ljg3ODY5MDU3IDQ1LjAxOTAxNzY1IDUwLjc4NzUzNjg4IDQ2LjE4MDg5Mzc3IDUxLjY5ODI0MjE5IDQ3LjM0MTMwODU5IEM1Mi44Nzg1OTY3NiA0OC44NDUzMDg3NyA1NC4wNTI5MjkzNiA1MC4zNTQwMzAyMSA1NS4yMjY1NjI1IDUxLjg2MzI4MTI1IEM2MS4zNDE3NzI2NyA2MC40NDMwMzI1OCA2MS4zNDE3NzI2NyA2MC40NDMwMzI1OCA2OS44NzUgNjUuODc1IEM3MC41NzYyNSA2NS45MTYyNSA3MS4yNzc1IDY1Ljk1NzUgNzIgNjYgQzcyIDY3LjMyIDcyIDY4LjY0IDcyIDcwIEM2My4wOSA3MCA1NC4xOCA3MCA0NSA3MCBDNDUgNjguMzUgNDUgNjYuNyA0NSA2NSBDNDcuNjQgNjUgNTAuMjggNjUgNTMgNjUgQzQ4LjYxNjE0MDcyIDU4LjkwODE0MzYgNDQuMTg0NTgyNjUgNTIuODcxMTQ0MjMgMzkuNTQyOTY4NzUgNDYuOTcyNjU2MjUgQzM4LjAwMTE0MzA2IDQ1LjAwMTQ2MTM4IDM2LjQ5NTAyODYyIDQzLjAwNjg0MDIzIDM1IDQxIEMzMC41MjcwMDg1MiA0Mi42MDUxNDA0OCAyOC4wNjc5MjI4OSA0Ni4wODU5MzcxMiAyNS4xMjUgNDkuNjI1IEMyNC41OTQ4NzMwNSA1MC4yNTI5MzQ1NyAyNC4wNjQ3NDYwOSA1MC44ODA4NjkxNCAyMy41MTg1NTQ2OSA1MS41Mjc4MzIwMyBDMTkuODQ3OTAyNzYgNTUuOTAzNTEwODQgMTYuMzM3OTkxNjIgNjAuMzYzOTAwNTIgMTMgNjUgQzE1LjY0IDY1IDE4LjI4IDY1IDIxIDY1IEMyMSA2Ni42NSAyMSA2OC4zIDIxIDcwIEMxMy40MSA3MCA1LjgyIDcwIC0yIDcwIEMtMiA2OC42OCAtMiA2Ny4zNiAtMiA2NiBDLTAuODEyNzczNDQgNjUuODAyNzczNDQgMC4zNzQ0NTMxMyA2NS42MDU1NDY4OCAxLjU5NzY1NjI1IDY1LjQwMjM0Mzc1IEM2LjY0NzM1OTEzIDY0LjEzMDE5NjM4IDkuMTM3NDY3MyA2MS40Mzc2MzU3NCAxMi40Mzc1IDU3LjU2MjUgQzEyLjk5MDI2NjExIDU2Ljk0MDg0OTYxIDEzLjU0MzAzMjIzIDU2LjMxOTE5OTIyIDE0LjExMjU0ODgzIDU1LjY3ODcxMDk0IEMxOC4zMDA3MDU0NyA1MC45MzE0NDU0MSAyMi4zNDY1Mjg1NiA0Ni4wNTg4OTkyNSAyNi4yOTI5Njg3NSA0MS4xMDkzNzUgQzI4IDM5IDI4IDM5IDMwIDM3IEMyOS42NTY4ODA4MiAzMy42NzUzMDgzMSAyOC4yMzk4NDk0OCAzMS43NDk0MTkzMyAyNi4xMjEwOTM3NSAyOS4yNDIxODc1IEMyNS41MzMzNjE4MiAyOC41MzgzNTkzNyAyNC45NDU2Mjk4OCAyNy44MzQ1MzEyNSAyNC4zNDAwODc4OSAyNy4xMDkzNzUgQzIzLjcxMjIzMzg5IDI2LjM3MjAzMTI1IDIzLjA4NDM3OTg4IDI1LjYzNDY4NzUgMjIuNDM3NSAyNC44NzUgQzIxLjE1MjU1MjY5IDIzLjM1MjQ4NDQgMTkuODY4NjkyNzkgMjEuODI5MDUwMzkgMTguNTg1OTM3NSAyMC4zMDQ2ODc1IEMxNy45NjU1NzYxNyAxOS41NjgzMTA1NSAxNy4zNDUyMTQ4NCAxOC44MzE5MzM1OSAxNi43MDYwNTQ2OSAxOC4wNzMyNDIxOSBDMTUuMjU4MzY3MTIgMTYuMzEzOTc0NDYgMTMuODc5NjI2OTggMTQuNTM2NDE2MTggMTIuNTM5MDYyNSAxMi42OTUzMTI1IEM4LjgyMDY4ODggNy4yNTUwNTIxOCA4LjgyMDY4ODggNy4yNTUwNTIxOCAzIDQuODc1IEMyLjAxIDQuOTE2MjUgMS4wMiA0Ljk1NzUgMCA1IEMwIDMuMzUgMCAxLjcgMCAwIFogIiBmaWxsPSIjNzJCRDlBIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjUsMTExKSIvPgo8cGF0aCBkPSJNMCAwIEM4LjU4IDAgMTcuMTYgMCAyNiAwIEMyNiAxLjMyIDI2IDIuNjQgMjYgNCBDMjQuMDIgNCAyMi4wNCA0IDIwIDQgQzIyLjAyNzM3MjMgOS4yNjE4MzEzOSAyNC40NzA0NjEzMyAxMy4zMTE5MTAxNSAyOCAxNy42ODc1IEMyOC44NzUwODE5NiAxOC43OTU1MDgxOSAyOS43NTAwODM5MyAxOS45MDM1Nzk1NyAzMC42MjUgMjEuMDExNzE4NzUgQzMxLjQwODc1IDIxLjk5Nzg1MTU2IDMyLjE5MjUgMjIuOTgzOTg0MzggMzMgMjQgQzM0LjAwNTg4NjggMjUuMzI4ODk3ODYgMzUuMDA4ODg5OTQgMjYuNjYwMDQ1MjkgMzYgMjggQzM5Ljc4MDk0NDMyIDI2LjczOTY4NTIzIDQwLjc0MTQzNzI1IDI1LjExNzEwNjUyIDQzLjE4NzUgMjIgQzQ2LjgyMzgwMTg0IDE3LjQyNzc5NzUyIDUwLjU5NDA1ODgyIDEzLjA1OTQyNTg3IDU0LjU2NjQwNjI1IDguNzc3MzQzNzUgQzU2LjIwOTI3MzExIDcuMTIxMTIxODkgNTYuMjA5MjczMTEgNy4xMjExMjE4OSA1NiA1IEM1My42OSA0LjY3IDUxLjM4IDQuMzQgNDkgNCBDNDkgMi42OCA0OSAxLjM2IDQ5IDAgQzU2LjI2IDAgNjMuNTIgMCA3MSAwIEM3MSAxLjMyIDcxIDIuNjQgNzEgNCBDNjkuOTk5Njg3NSA0LjE3MjczNDM4IDY4Ljk5OTM3NSA0LjM0NTQ2ODc1IDY3Ljk2ODc1IDQuNTIzNDM3NSBDNjIuODExODE3OTMgNi4wMDQ2Mjg3NSA1OS45NDc5MDEzNyA5LjcwNzg5OTY4IDU2LjUgMTMuNjI1IEM1NS44NzQ4MDQ2OSAxNC4zMTIwNzAzMSA1NS4yNDk2MDkzOCAxNC45OTkxNDA2MiA1NC42MDU0Njg3NSAxNS43MDcwMzEyNSBDNTMuMzUwMTU5MTQgMTcuMDkyMjkwMSA1Mi4xMDM5NzM4MyAxOC40ODU4NzM2OCA1MC44NjcxODc1IDE5Ljg4NzY5NTMxIEM0OS41OTUwMTAzOSAyMS4zMjY4Nzg5NCA0OC4zMDc4MjkwNyAyMi43NTI5MDU1NCA0Ny4wMDc4MTI1IDI0LjE2Njk5MjE5IEM0Ni4zNzYxNzE4NyAyNC44NjIxMTkxNCA0NS43NDQ1MzEyNSAyNS41NTcyNDYwOSA0NS4wOTM3NSAyNi4yNzM0Mzc1IEM0NC41MjMzMzk4NCAyNi44OTM5NTk5NiA0My45NTI5Mjk2OSAyNy41MTQ0ODI0MiA0My4zNjUyMzQzOCAyOC4xNTM4MDg1OSBDNDEuODQ3ODY1MTkgMzAuMjA1NzMwMjIgNDEuMTgxNjg1MzEgMzEuNDUwMjE1MzkgNDEgMzQgQzQxLjk4NTUyMTMxIDM2LjAxMDgwODc5IDQxLjk4NTUyMTMxIDM2LjAxMDgwODc5IDQzLjYxMzI4MTI1IDM3LjkwNjI1IEM0NC4xOTcxNDYgMzguNjQ0ODgyODEgNDQuNzgxMDEwNzQgMzkuMzgzNTE1NjMgNDUuMzgyNTY4MzYgNDAuMTQ0NTMxMjUgQzQ2LjMzNzg4NDUyIDQxLjMxMDQ4ODI4IDQ2LjMzNzg4NDUyIDQxLjMxMDQ4ODI4IDQ3LjMxMjUgNDIuNSBDNDcuOTUzODg5MTYgNDMuMjk3OTI5NjkgNDguNTk1Mjc4MzIgNDQuMDk1ODU5MzggNDkuMjU2MTAzNTIgNDQuOTE3OTY4NzUgQzUwLjU1NDQwNDE4IDQ2LjUyOTA0ODQ4IDUxLjg1ODAwNzcxIDQ4LjEzNTg2OTg5IDUzLjE2Njk5MjE5IDQ5LjczODI4MTI1IEM1NC43ODE1MjU1MiA1MS43MzA0Mjc4NyA1Ni4zNTU5MjYxNSA1My43NDQ5NjEyNCA1Ny45MTQwNjI1IDU1Ljc4MTI1IEM1OC43NTQ0ODYyMiA1Ni44NTQ3MzI0IDU5LjU5NTU3Njg3IDU3LjkyNzY5MzE3IDYwLjQzNzUgNTkgQzYxLjE1ODA4NTk0IDU5LjkyODEyNSA2MS44Nzg2NzE4OCA2MC44NTYyNSA2Mi42MjEwOTM3NSA2MS44MTI1IEM2NS42ODcwNTY2NSA2NC42MzE3NzYyMyA2Ny45NjQ1MTY5MSA2NS4xMzcwMDQ4MyA3MiA2NiBDNzIgNjcuMzIgNzIgNjguNjQgNzIgNzAgQzYzLjA5IDcwIDU0LjE4IDcwIDQ1IDcwIEM0NSA2OC42OCA0NSA2Ny4zNiA0NSA2NiBDNDcuNjQgNjYgNTAuMjggNjYgNTMgNjYgQzUwLjA3MDgzNTg4IDYwLjI2OTAyNjczIDQ2LjMxNjg5NjIxIDU1LjU5ODkwNjM2IDQyLjE5NTMxMjUgNTAuNjY3OTY4NzUgQzM5LjYyNTQxMDUxIDQ3LjU0NDc2MDQ2IDM3LjI5MzQ0MTkxIDQ0LjMyOTE4OTg2IDM1IDQxIEMyNi4zNzAwNDczIDQ0LjQ3MTgyMDA1IDE3LjEzOTU3MDI5IDU2LjcyMDg1OTQzIDEzIDY1IEMxNS42NCA2NSAxOC4yOCA2NSAyMSA2NSBDMjEgNjYuNjUgMjEgNjguMyAyMSA3MCBDMTMuNDEgNzAgNS44MiA3MCAtMiA3MCBDLTIgNjguMzUgLTIgNjYuNyAtMiA2NSBDLTAuODg2MjUgNjUuMDYxODc1IDAuMjI3NSA2NS4xMjM3NSAxLjM3NSA2NS4xODc1IEM4LjIxODg5ODY4IDY0LjM2MzgwOTU0IDExLjkxOTAyNjU5IDU3Ljk5MDgyOTc4IDE2IDUzIEMxNy4yMjM3MDM4OSA1MS41OTkzNzUwNyAxOC40NTIxOTgzMiA1MC4yMDI5MDY4MyAxOS42ODc1IDQ4LjgxMjUgQzIwLjc5MjE4MjU1IDQ3LjU0MjExNTA3IDIxLjg5NjMzOTI4IDQ2LjI3MTI3Mjc1IDIzIDQ1IEMyNC4zMTE5NTE0IDQzLjQ5OTUyMDE1IDI1LjYyNDQ0MjMzIDQxLjk5OTUxMTg2IDI2LjkzNzUgNDAuNSBDMjcuNTI0MDIzNDQgMzkuODI5Njg3NSAyOC4xMTA1NDY4NyAzOS4xNTkzNzUgMjguNzE0ODQzNzUgMzguNDY4NzUgQzI5LjEzODk0NTMxIDM3Ljk4NDA2MjUgMjkuNTYzMDQ2ODcgMzcuNDk5Mzc1IDMwIDM3IEMyOC43MTk3ODQ4MyAzNC4zMDA2MDg2NSAyNy4zMTk4MTUyMiAzMS45MjIyNzkzIDI1LjUzMTI1IDI5LjUzMTI1IEMyNC44MDU0MjcyNSAyOC41NTg3NzMxOSAyNC44MDU0MjcyNSAyOC41NTg3NzMxOSAyNC4wNjQ5NDE0MSAyNy41NjY2NTAzOSBDMjEuMzg5OTIyNTkgMjQuMDM0MDM5NzQgMTguNzAyMzU4MzYgMjAuNTExNzQwMzcgMTYgMTcgQzE1LjUyMzM2OTE0IDE2LjM3MjM4NzcgMTUuMDQ2NzM4MjggMTUuNzQ0Nzc1MzkgMTQuNTU1NjY0MDYgMTUuMDk4MTQ0NTMgQzEzLjI2MjkzNDM5IDEzLjQwNDk3NDkxIDExLjk0ODkxNzYxIDExLjczMTMzODQyIDEwLjYyNSAxMC4wNjI1IEM5Ljg3NDc2NTYzIDkuMTA0NzI2NTYgOS4xMjQ1MzEyNSA4LjE0Njk1MzEyIDguMzUxNTYyNSA3LjE2MDE1NjI1IEM1LjU1NzY5MTEzIDQuNTkzNjkzMDIgMy43MzExMTkxIDQuMjUyNjgzODYgMCA0IEMwIDIuNjggMCAxLjM2IDAgMCBaICIgZmlsbD0iIzZGQkM5OCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNDYsMTExKSIvPgo8cGF0aCBkPSJNMCAwIEM4LjkxIDAgMTcuODIgMCAyNyAwIEMyNyAxLjMyIDI3IDIuNjQgMjcgNCBDMjQuOTQ5MjE4NzUgNC4yMjc4NjQ1OCAyMi44OTg0Mzc1IDQuNDU1NzI5MTcgMjAuODQ3NjU2MjUgNC42ODM1OTM3NSBDMTguOTg4NTk3NzEgNC44MTg4NjYxNCAxOC45ODg1OTc3MSA0LjgxODg2NjE0IDE4IDYgQzE3LjkwNjIzOTQ3IDcuODEwNTUwMjUgMTcuODgyNTU4OSA5LjYyNDc3MjAxIDE3Ljg4NjQ3NDYxIDExLjQzNzc0NDE0IEMxNy44ODY1ODc5MSAxMy4xNzk0MzYyNiAxNy44ODY1ODc5MSAxMy4xNzk0MzYyNiAxNy44ODY3MDM0OSAxNC45NTYzMTQwOSBDMTcuODkxODY0NzggMTYuMjE5ODMyIDE3Ljg5NzAyNjA2IDE3LjQ4MzM0OTkxIDE3LjkwMjM0Mzc1IDE4Ljc4NTE1NjI1IEMxNy45MDM3NTg3IDIwLjA3MTExMTkxIDE3LjkwNTE3MzY1IDIxLjM1NzA2NzU3IDE3LjkwNjYzMTQ3IDIyLjY4MTk5MTU4IEMxNy45MTA0NTU4MiAyNi4xMDY0MzQ0MyAxNy45MjAyODUzOCAyOS41MzA4MzUwOSAxNy45MzEzMzU0NSAzMi45NTUyNjEyMyBDMTcuOTQxNTUxMzYgMzYuNDQ3MzgxNzggMTcuOTQ2MTg0MTggMzkuOTM5NTA5MzMgMTcuOTUxMTcxODggNDMuNDMxNjQwNjIgQzE3Ljk2MTM3NzUzIDUwLjI4Nzc5NzcyIDE3Ljk4MDk0OTIzIDU3LjE0MzgzODg1IDE4IDY0IEMyOC4yMyA2NCAzOC40NiA2NCA0OSA2NCBDNDkuNjYgNjAuMDQgNTAuMzIgNTYuMDggNTEgNTIgQzUyLjMyIDUyIDUzLjY0IDUyIDU1IDUyIEM1NC40MDI0Nzk3NyA1OC4yNzM5NjI0MSA1My4zOTMxNjY3NiA2My43MzA3NDk2IDUyIDcwIEMzNS4xNyA3MCAxOC4zNCA3MCAxIDcwIEMxIDY4LjM1IDEgNjYuNyAxIDY1IEMzLjY0IDY1IDYuMjggNjUgOSA2NSBDOC42NyA0NS4yIDguMzQgMjUuNCA4IDUgQzUuMzYgNC42NyAyLjcyIDQuMzQgMCA0IEMwIDIuNjggMCAxLjM2IDAgMCBaICIgZmlsbD0iIzZDQkM5NiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjAzLDExMSkiLz4KPHBhdGggZD0iTTAgMCBDNS45MDMyNTU2IC0wLjI1MDM2MTI5IDEwLjQ0MzIyOTU2IDAuNDQzMTU0MDMgMTYgMi4zNzUgQzE3LjMzMjU0OTI3IDIuODIyNjUzMjcgMTguNjY1ODg1OCAzLjI2Nzk2OTg5IDIwIDMuNzEwOTM3NSBDMjAuNjU4Mzg4NjcgMy45MzE3NzAwMiAyMS4zMTY3NzczNCA0LjE1MjYwMjU0IDIxLjk5NTExNzE5IDQuMzgwMTI2OTUgQzI0LjAwOTUzNTA5IDUuMDAyOTQ4MDggMjYuMDIxMzQyNDEgNS40ODAwMzcyMyAyOC4wNzgxMjUgNS45NDE0MDYyNSBDNDYuNjA4ODcyOTcgMTAuNDQzMDIyMSA2NS42Mzk4MjAyNyAyOC40NTMxMzE3NCA3NyA0MyBDNzguMDY5MDI5MzkgNDUuMzAyNTI0ODMgNzkuMDcxNzU4MTcgNDcuNjM3MjAyNiA4MCA1MCBDODEuMDM0MDMxNzggNTEuNDk5MjA1OTcgODIuMTAwNjEyNzMgNTIuOTc2MTU5NCA4My4xODc1IDU0LjQzNzUgQzEwMy40NTc0NjA4NiA4Mi45ODYxMzk2MSAxMDguNTAzODc0OTkgMTIwLjUzNjAyMDYgMTAyLjk0OTIxODc1IDE1NC40NDkyMTg3NSBDMTAyLjA3NDk2NzQ2IDE1OS4wMjAwMjE4NCAxMDEuMTMwMzUyMjUgMTYzLjgzNjkwMDExIDk5IDE2OCBDOTguMDEgMTY4LjMzIDk3LjAyIDE2OC42NiA5NiAxNjkgQzk2LjM5ODMyMDMxIDE2Ny4yOTQ1NzAzMSA5Ni4zOTgzMjAzMSAxNjcuMjk0NTcwMzEgOTYuODA0Njg3NSAxNjUuNTU0Njg3NSBDMTA1LjAzNjQ4OTU2IDEyOS44MTg3MDc0OSAxMDMuMjYwMzQ0NjEgOTMuNjA0ODI3MzYgODMuNjY5MTg5NDUgNjEuNTY3ODcxMDkgQzcxLjU4MTU0ODkyIDQyLjUyMDUyODUgNTYuMjIxMjg3MiAyOC43NjczNjI4NCAzNyAxNyBDMzcuNTE5NDkyMTkgMTcuMzc1MTE3MTkgMzguMDM4OTg0MzcgMTcuNzUwMjM0MzggMzguNTc0MjE4NzUgMTguMTM2NzE4NzUgQzQyLjcxMTc0NjgxIDIxLjE3Mjk0NTQ0IDQ2LjU4NDA3OTQzIDI0LjE1NzA4OTM2IDUwIDI4IEM1MCAyOS42NjY2NjY2NyA1MCAzMS4zMzMzMzMzMyA1MCAzMyBDNDguMjkyNTAwMiAzMS43MTkzNzUxNSA0Ni42MjYyNjQwNCAzMC4zODIzMjQ0MyA0NSAyOSBDNDUgMjguMzQgNDUgMjcuNjggNDUgMjcgQzQyLjUyNSAyNi4wMSA0Mi41MjUgMjYuMDEgNDAgMjUgQzQwIDI0LjM0IDQwIDIzLjY4IDQwIDIzIEMzOS4yNjkxMDE1NiAyMi43MzMxNjQwNiAzOC41MzgyMDMxMiAyMi40NjYzMjgxMyAzNy43ODUxNTYyNSAyMi4xOTE0MDYyNSBDMzQuMjMwMDcxMDkgMjAuNjcwNjQ3NTIgMzEuMzI2MjU3NDEgMTguNTY3NDg1NzYgMjguMTgzNTkzNzUgMTYuMzM5ODQzNzUgQzI2LjA2NDI4NTc4IDE1LjAzOTQ0NTQ4IDI0LjM4MDE4MzM2IDE0LjU4ODUzNTQ5IDIyIDE0IEMyMS42NyAxMy4zNCAyMS4zNCAxMi42OCAyMSAxMiBDMjAuMDEgMTIgMTkuMDIgMTIgMTggMTIgQzE3IDExIDE2IDEwIDE1IDkgQzE0LjAxIDkgMTMuMDIgOSAxMiA5IEMxMiA4LjM0IDEyIDcuNjggMTIgNyBDMTAuNjggNi4zNCA5LjM2IDUuNjggOCA1IEMxMC45NyA1LjQ5NSAxMC45NyA1LjQ5NSAxNCA2IEMxNCA1LjM0IDE0IDQuNjggMTQgNCBDMTMuMjQwNzQyMTkgMy44Nzg4MjgxMiAxMi40ODE0ODQzOCAzLjc1NzY1NjI1IDExLjY5OTIxODc1IDMuNjMyODEyNSBDMTAuNzA1MzUxNTYgMy40NjUyMzQzOCA5LjcxMTQ4NDM3IDMuMjk3NjU2MjUgOC42ODc1IDMuMTI1IEM3LjIwODMwMDc4IDIuODgxMzY3MTkgNy4yMDgzMDA3OCAyLjg4MTM2NzE5IDUuNjk5MjE4NzUgMi42MzI4MTI1IEMzIDIgMyAyIDAgMCBaICIgZmlsbD0iIzg0QkNBMyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTg0LDE3KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjQ5NSAzLjQ2NSAwLjQ5NSAzLjQ2NSAxIDcgQzAuMjEyMzgyODEgNy40NjE0ODQzNyAtMC41NzUyMzQzNyA3LjkyMjk2ODc1IC0xLjM4NjcxODc1IDguMzk4NDM3NSBDLTI5LjQ2MTIxMjkzIDI1LjIwOTkwOTc2IC01My4zNTAwMzAyOCA1MC42NTE2Nzg2NyAtNjEuNzk2ODc1IDgzLjA1NzEyODkxIEMtNjIuOTEyMTAyMDggODcuNTkzNjU3IC02My45OTAxOTM4NCA5Mi4xMzUzOTEyOSAtNjQuOTk3MDcwMzEgOTYuNjk3MjY1NjIgQy02NS4yMzAwNjgzNiA5Ny43NDIwNTA3OCAtNjUuNDYzMDY2NDEgOTguNzg2ODM1OTQgLTY1LjcwMzEyNSA5OS44NjMyODEyNSBDLTY2LjAwNzE4MjYyIDEwMS4yNzU0MDg5NCAtNjYuMDA3MTgyNjIgMTAxLjI3NTQwODk0IC02Ni4zMTczODI4MSAxMDIuNzE2MDY0NDUgQy02NyAxMDUgLTY3IDEwNSAtNjkgMTA3IEMtNjkuNDU5NzQ1MDMgMTA4LjkyOTAxMTQxIC02OS40NTk3NDUwMyAxMDguOTI5MDExNDEgLTY5LjYzMjgxMjUgMTExLjEzMjgxMjUgQy02OS43NTg0OTYwOSAxMTIuMzQxMzA4NTkgLTY5Ljc1ODQ5NjA5IDExMi4zNDEzMDg1OSAtNjkuODg2NzE4NzUgMTEzLjU3NDIxODc1IEMtNjkuOTY1MzUxNTYgMTE0LjQxNTk3NjU2IC03MC4wNDM5ODQzOCAxMTUuMjU3NzM0MzcgLTcwLjEyNSAxMTYuMTI1IEMtNzAuMjExMzY3MTkgMTE2Ljk3NDQ5MjE5IC03MC4yOTc3MzQzOCAxMTcuODIzOTg0MzggLTcwLjM4NjcxODc1IDExOC42OTkyMTg3NSBDLTcwLjU5OTAyNTU3IDEyMC43OTg2OTcyNyAtNzAuODAwNTM2MDMgMTIyLjg5OTI2MjQyIC03MSAxMjUgQy03MS4zMyAxMjUgLTcxLjY2IDEyNSAtNzIgMTI1IEMtNzIuNTQ3MTgxNTQgMTEyLjQxNDgyNDUgLTcxLjI0OTYwNjUgMTAxLjAzODcxMDc0IC02OC4zNjcxODc1IDg4Ljc3NzM0Mzc1IEMtNjcuNzkxMzc5ODcgODUuOTkwMTg5ODIgLTY4LjA2ODkwMDU2IDg0Ljc5MzI5ODMxIC02OSA4MiBDLTY4LjMwNzYxMzE4IDc5LjUzNzUxMTIyIC02Ny41NDQ4Mzc3IDc3LjI1MjEwMjI3IC02Ni42MjUgNzQuODc1IEMtNjYuMjE5NDI4NzEgNzMuODE4NTMyNzEgLTY2LjIxOTQyODcxIDczLjgxODUzMjcxIC02NS44MDU2NjQwNiA3Mi43NDA3MjI2NiBDLTU4LjMxMDk4MzEgNTMuODM2OTE0NzcgLTQ3LjMwODUzNDk0IDM3LjM4OTE4NjUgLTMzIDIzIEMtMzIuNDk1OTc2NTYgMjIuNDkxOTQ4MjQgLTMxLjk5MTk1MzEzIDIxLjk4Mzg5NjQ4IC0zMS40NzI2NTYyNSAyMS40NjA0NDkyMiBDLTIzLjY3Mjg1OTU4IDEzLjcwMTk4NTYzIC0xNS40MTI0MDczNSA3LjY1NzU5MjU2IC02IDIgQy0zIDAgLTMgMCAwIDAgWiAiIGZpbGw9IiM3MEJDOTkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDg0LDI3KSIvPgo8cGF0aCBkPSJNMCAwIEMxMS4yMDYzMjE1OSA0LjE4MjM1OTMxIDIwLjIwNjc0NDU5IDEyLjEyNjAyNTgyIDI5IDIwIEMyOS43MzA4OTg0NCAyMC42NDU4MjAzMSAzMC40NjE3OTY4OCAyMS4yOTE2NDA2MiAzMS4yMTQ4NDM3NSAyMS45NTcwMzEyNSBDNTQuNzM5NDAzMDUgNDQuMDM4MzY2MDUgNjYuOTgxMzk2MDIgNzcuMzQwMTkwNDMgNjguMTkxNDA2MjUgMTA5LjA4MjAzMTI1IEM2OC40NzQxMDU3MiAxMjEuNjg5NTE1NjEgNjYuNDYwNjY3ODUgMTMzLjU5Nzk4ODYgNjMuNSAxNDUuODEyNSBDNjMuMjg2NDE4NDYgMTQ2LjcwMzU2NDQ1IDYzLjA3MjgzNjkxIDE0Ny41OTQ2Mjg5MSA2Mi44NTI3ODMyIDE0OC41MTI2OTUzMSBDNjIuNjQzNTUyMjUgMTQ5LjMzOTk1MTE3IDYyLjQzNDMyMTI5IDE1MC4xNjcyMDcwMyA2Mi4yMTg3NSAxNTEuMDE5NTMxMjUgQzYxLjkzOTM0NTcgMTUyLjEzNDg1MjI5IDYxLjkzOTM0NTcgMTUyLjEzNDg1MjI5IDYxLjY1NDI5Njg4IDE1My4yNzI3MDUwOCBDNjEgMTU1IDYxIDE1NSA1OSAxNTYgQzU5LjQyMjY2NTIyIDE1Mi40MDczNDU2NiA1OS44NTE0MzkwMSAxNDguODM3ODQxOTQgNjAuNDgzMTU0MyAxNDUuMjc1MTQ2NDggQzYyLjA1NzU4NjU3IDEzNi4yNTI1MjIzOSA2Mi40NDI0NzU4MSAxMjcuMzc5NzM4NDQgNjIuNDM3NSAxMTguMjUgQzYyLjQzNzkwMjgzIDExNy40ODc0Mjg4OSA2Mi40MzgzMDU2NiAxMTYuNzI0ODU3NzkgNjIuNDM4NzIwNyAxMTUuOTM5MTc4NDcgQzYyLjMyMTI1MTU2IDc3LjA4NTc1MjUgNDcuODcxMTg1NCA0NC42NjUwNTc2MSAyMC42MDU3MTI4OSAxNy4yMTg5OTQxNCBDMTUuMTAzNjE5MzggMTEuODU5NDg0MjQgOS4wODM3ODc1NCA3LjM5MDM2NjM3IDIuNzg5MDYyNSAzLjAxMTcxODc1IEMxLjg2ODY3MTg3IDIuMzQ3ODUxNTYgMC45NDgyODEyNSAxLjY4Mzk4NDM4IDAgMSBDMCAwLjY3IDAgMC4zNCAwIDAgWiAiIGZpbGw9IiNFQ0ZCRjUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIxOSwzMykiLz4KPHBhdGggZD0iTTAgMCBDMC4zMyAwIDAuNjYgMCAxIDAgQzEgMS42NSAxIDMuMyAxIDUgQzIuMzIgNC42NyAzLjY0IDQuMzQgNSA0IEM0LjcxNzU4MzU5IDUuNDYwMTUyOTEgNC40MjM3MzM4NiA2LjkxODA5NzkzIDQuMTI1IDguMzc1IEMzLjk2MjU3ODEyIDkuMTg3MTA5MzggMy44MDAxNTYyNSA5Ljk5OTIxODc1IDMuNjMyODEyNSAxMC44MzU5Mzc1IEMzIDEzIDMgMTMgMSAxNSBDMC42Mjg3NSAxNi4xMTM3NSAwLjI1NzUgMTcuMjI3NSAtMC4xMjUgMTguMzc1IEMtMy45MDI4NzYyOCAyOC44MjA4Mjc5MiAtMTEuODkzNDQ0OTEgMzcuNjQzMjE3NjMgLTE5IDQ2IEMtMTkuOTkgNDUuMzQgLTIwLjk4IDQ0LjY4IC0yMiA0NCBDLTIxLjM0IDQzLjAxIC0yMC42OCA0Mi4wMiAtMjAgNDEgQy0yMS45OCA0Mi45OCAtMjMuOTYgNDQuOTYgLTI2IDQ3IEMtMjYuNjYgNDYuNjcgLTI3LjMyIDQ2LjM0IC0yOCA0NiBDLTI3LjQ1NDcyNjU2IDQ1LjQ0MDU0Njg3IC0yNi45MDk0NTMxMiA0NC44ODEwOTM3NSAtMjYuMzQ3NjU2MjUgNDQuMzA0Njg3NSBDLTE0LjczNjQ2MDYzIDMxLjk1OTEwODU3IC00Ljk5NzgwNDM3IDE2LjI3NDI1NzQ0IDAgMCBaICIgZmlsbD0iIzc0QkU5QyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYyLDE5NCkiLz4KPHBhdGggZD0iTTAgMCBDMi4zNjQyNjIzMiAxLjgwNzM0MTk3IDIuOTU3MjM5OTcgMi43MjM2NzU3NCAzLjQxOTkyMTg4IDUuNzEzNjIzMDUgQzMuNDc0MDk4OSA5LjM3MzAxMDAyIDMuNTE2ODYzODUgMTMuMDI3NTk0MDcgMy41IDE2LjY4NzUgQzMuNDk5NTY2OTYgMTcuMzU1Mjg0NzMgMy40OTkxMzM5MSAxOC4wMjMwNjk0NiAzLjQ5ODY4Nzc0IDE4LjcxMTA5MDA5IEMzLjQ1MTk4NzUgMzEuODk2NTIxMTggMS4zNjc0NDk0OCA0NC4yNjgwNDcxNSAtMiA1NyBDLTIuMjIwNTEwMjUgNTcuOTE3OTczNjMgLTIuNDQxMDIwNTEgNTguODM1OTQ3MjcgLTIuNjY4MjEyODkgNTkuNzgxNzM4MjggQy00LjI3NDQwMzA0IDY2LjIxNjU5NzE2IC02LjU5NjM5Njk5IDcxLjcxMzc5Njk5IC05LjgxMjUgNzcuNSBDLTEwLjI1ODI3MzkzIDc4LjMyMDk3MTY4IC0xMC43MDQwNDc4NSA3OS4xNDE5NDMzNiAtMTEuMTYzMzMwMDggNzkuOTg3NzkyOTcgQy0xMi4wNDIyOTI0OCA4MS42MDQ2MjQxMSAtMTIuOTI1NTAzNTkgODMuMjE5MTU0NjIgLTEzLjgxMzQ3NjU2IDg0LjgzMTA1NDY5IEMtMTQuODEzMjE1OTkgODYuNjU4NTYxOTEgLTE1Ljc4MTQxOTgxIDg4LjUwMzI2OTUgLTE2Ljc0MjE4NzUgOTAuMzUxNTYyNSBDLTIxLjQ2MjQwNjY0IDk5LjQwOTI4MDMxIC0yNi45OTYxMDc4MiAxMDYuNjI5NTU1MiAtMzUgMTEzIEMtMzUuNjYgMTEyLjY3IC0zNi4zMiAxMTIuMzQgLTM3IDExMiBDLTM2LjA5MTIxMDk0IDExMS4wNjk5NDE0MSAtMzYuMDkxMjEwOTQgMTExLjA2OTk0MTQxIC0zNS4xNjQwNjI1IDExMC4xMjEwOTM3NSBDLTI2LjQ2NDM1OTE4IDEwMC45MjYxNzY1NCAtMTguMzM1MDQyNTcgOTAuNjQ0NDA5ODMgLTEzLjcxODc1IDc4LjczNDM3NSBDLTEzIDc3IC0xMyA3NyAtMTEgNzQgQy0xMC4zMDgxNTUxMSA3MS45NTI0NzUyNSAtOS42NjgxMTczMiA2OS44ODcxNjQ5NCAtOS4wNjI1IDY3LjgxMjUgQy03Ljk4ODM0MjUyIDY0LjE5MjE5MTQ2IC02LjkxNzMwNDEzIDYwLjY2MTE0NTQ4IC01LjQzNzUgNTcuMTg3NSBDLTAuMDE5MjcwNjEgNDMuMjEyNjEzMjcgMC4xNzAxODg4OSAyNy4wNjU4Nzc3NiAwLjA2MjUgMTIuMzEyNSBDMC4wNTc5ODgyOCAxMS4xMjIwNTA3OCAwLjA1MzQ3NjU2IDkuOTMxNjAxNTYgMC4wNDg4MjgxMiA4LjcwNTA3ODEyIEMwLjAzNzE3NDUyIDUuODAzMzMwNjQgMC4wMjA4MTU1OSAyLjkwMTY5Mzc0IDAgMCBaICIgZmlsbD0iI0Q2RjdFNyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjc2LDEzMSkiLz4KPHBhdGggZD0iTTAgMCBDMi45NjA1MjQ4NCAzLjM2NDIzMjc3IDMuOTIyODk4MjIgNi42OTE1OTI4NyA1IDExIEM3Ljk3IDExLjQ5NSA3Ljk3IDExLjQ5NSAxMSAxMiBDMTMuMzU5ODgxNTcgMTkuNTU4ODA4NDkgMTUuMzQ4MzIxMTIgMjcuMDc5NzI4NSAxNi43NSAzNC44NzUgQzE2Ljg3NzM3NTQ5IDM1LjU2MzI3ODgxIDE3LjAwNDc1MDk4IDM2LjI1MTU1NzYyIDE3LjEzNTk4NjMzIDM2Ljk2MDY5MzM2IEMxNy45NDgwMjA1MyA0MS42OTU3NTc1NCAxOC4xMDc0NTczMyA0Ni4xOTY2NTc0MyAxOCA1MSBDMTcuNjcgNTEgMTcuMzQgNTEgMTcgNTEgQzE2LjY3IDU0Ljk2IDE2LjM0IDU4LjkyIDE2IDYzIEMxNS42NyA2MyAxNS4zNCA2MyAxNSA2MyBDMTQuOTg1NDE3NDggNjIuNDM0OTA3MjMgMTQuOTcwODM0OTYgNjEuODY5ODE0NDUgMTQuOTU1ODEwNTUgNjEuMjg3NTk3NjYgQzE0Ljg4MTQzOTQyIDU4LjczMjY5OTgxIDE0Ljc4NDc0NzU4IDU2LjE3OTEyNTAyIDE0LjY4NzUgNTMuNjI1IEMxNC42NTM2NjIxMSA1Mi4yOTA4MjAzMSAxNC42NTM2NjIxMSA1Mi4yOTA4MjAzMSAxNC42MTkxNDA2MiA1MC45Mjk2ODc1IEMxNC41ODM2OTE0MSA1MC4wNzg5MDYyNSAxNC41NDgyNDIxOSA0OS4yMjgxMjUgMTQuNTExNzE4NzUgNDguMzUxNTYyNSBDMTQuNDg1NTM0NjcgNDcuNTY2MDQwMDQgMTQuNDU5MzUwNTkgNDYuNzgwNTE3NTggMTQuNDMyMzczMDUgNDUuOTcxMTkxNDEgQzE0LjE1MTEwNDAzIDQzLjU5OTU5ODQ0IDE0LjE1MTEwNDAzIDQzLjU5OTU5ODQ0IDExIDQyIEMxMC4zNzE1ODIwMyA0MC4xNzUwNDg4MyAxMC4zNzE1ODIwMyA0MC4xNzUwNDg4MyAxMC4wMDc4MTI1IDM3Ljk1NzAzMTI1IEM5Ljg2MjYzMTg0IDM3LjEzNzEwNjkzIDkuNzE3NDUxMTcgMzYuMzE3MTgyNjIgOS41Njc4NzEwOSAzNS40NzI0MTIxMSBDOS40MjE3MjM2MyAzNC41OTQ2NDExMSA5LjI3NTU3NjE3IDMzLjcxNjg3MDEyIDkuMTI1IDMyLjgxMjUgQzYuNDUzMjU3MzUgMTcuNzE4OTk2MTIgNi40NTMyNTczNSAxNy43MTg5OTYxMiAwIDQgQzAgMi42OCAwIDEuMzYgMCAwIFogIiBmaWxsPSIjODhDREFDIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNjQsOTApIi8+CjxwYXRoIGQ9Ik0wIDAgQzUuOTAzMjU1NiAtMC4yNTAzNjEyOSAxMC40NDMyMjk1NiAwLjQ0MzE1NDAzIDE2IDIuMzc1IEMxNy4zMzI1NDkyNyAyLjgyMjY1MzI3IDE4LjY2NTg4NTggMy4yNjc5Njk4OSAyMCAzLjcxMDkzNzUgQzIwLjY1ODM4ODY3IDMuOTMxNzcwMDIgMjEuMzE2Nzc3MzQgNC4xNTI2MDI1NCAyMS45OTUxMTcxOSA0LjM4MDEyNjk1IEMyNC4wMDk1MzUwOSA1LjAwMjk0ODA4IDI2LjAyMTM0MjQxIDUuNDgwMDM3MjMgMjguMDc4MTI1IDUuOTQxNDA2MjUgQzQ3LjIyMDU2MTMxIDEwLjU5MTYxNzYxIDY0LjYzMjUwNTU2IDI4LjY5MDc0OTE5IDc3IDQzIEM3Ni42NyA0My45OSA3Ni4zNCA0NC45OCA3NiA0NiBDNzUuMzAxMzI4MTIgNDUuMjE3NTM5MDYgNzQuNjAyNjU2MjUgNDQuNDM1MDc4MTIgNzMuODgyODEyNSA0My42Mjg5MDYyNSBDNzIuOTYzNzAwMiA0Mi42MDY2MjgyOCA3Mi4wNDQ0MjA2NiA0MS41ODQ1MDA2NSA3MS4xMjUgNDAuNTYyNSBDNzAuNjY0ODA0NjkgNDAuMDQ2MjMwNDcgNzAuMjA0NjA5MzggMzkuNTI5OTYwOTQgNjkuNzMwNDY4NzUgMzguOTk4MDQ2ODggQzY3LjE4MjA2OTE1IDM2LjEwNTEyMDYzIDY3LjE4MjA2OTE1IDM2LjEwNTEyMDYzIDY0IDM0IEM2NCAzMy4zNCA2NCAzMi42OCA2NCAzMiBDNjMuNDI2MzY3MTkgMzEuNzQyMTg3NSA2Mi44NTI3MzQzOCAzMS40ODQzNzUgNjIuMjYxNzE4NzUgMzEuMjE4NzUgQzU5Ljc5MzEyOTA3IDI5Ljg4ODUyNTUxIDU3LjkzNDExNzU0IDI4LjMzMjQ3Njc0IDU1LjgxMjUgMjYuNSBDMzkuMjcyNzQ5NzYgMTMuMTIyMjYwODMgMjAuMzQyNDU0NTMgNi4yNjYyMDI0MyAwIDEgQzAgMC42NyAwIDAuMzQgMCAwIFogIiBmaWxsPSIjQUZERkM5IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxODQsMTcpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuMzMgMC45OSAwLjY2IDEuOTggMSAzIEMxLjY2IDMuMzMgMi4zMiAzLjY2IDMgNCBDMi4zNzM1MTU2MiA0LjY3NTQ2ODc1IDEuNzQ3MDMxMjUgNS4zNTA5Mzc1IDEuMTAxNTYyNSA2LjA0Njg3NSBDMC4yODQyOTY4NyA2LjkzODkwNjI1IC0wLjUzMjk2ODc1IDcuODMwOTM3NSAtMS4zNzUgOC43NSBDLTIuMTg3MTA5MzggOS42MzE3MTg3NSAtMi45OTkyMTg3NSAxMC41MTM0Mzc1IC0zLjgzNTkzNzUgMTEuNDIxODc1IEMtNS42NjI1ODQ4IDEzLjU5ODAyNTIxIC02Ljg5Nzg3OTk0IDE1LjQwODIwMTE5IC04IDE4IEMtNS4zNiAxOCAtMi43MiAxOCAwIDE4IEMwIDE5LjY1IDAgMjEuMyAwIDIzIEMtNy41OSAyMyAtMTUuMTggMjMgLTIzIDIzIEMtMjMgMjEuMzUgLTIzIDE5LjcgLTIzIDE4IEMtMjEuODg2MjUgMTguMDYxODc1IC0yMC43NzI1IDE4LjEyMzc1IC0xOS42MjUgMTguMTg3NSBDLTEyLjc5NjU2NjY3IDE3LjM4NzAwODcgLTkuMDQ0MTc5ODkgMTAuOTk0NTI3NDggLTUgNiBDLTMuOTU4OTUzNzcgNC43NzAzMDc4MSAtMi45MTc0NTMgMy41NDA5OTk4NSAtMS44NzUgMi4zMTI1IEMtMS4yNTYyNSAxLjU0OTM3NSAtMC42Mzc1IDAuNzg2MjUgMCAwIFogIiBmaWxsPSIjODlDNkFBIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2NywxNTgpIi8+CjxwYXRoIGQ9Ik0wIDAgQzcuOTQ2NjI0NDUgMi40ODI0NTMzNiAxNS40OTQ1NzM2NyA1LjM5NTAzMTQ1IDIzIDkgQzIyLjY3IDEwLjMyIDIyLjM0IDExLjY0IDIyIDEzIEMyMi43NTkyNTc4MSAxMy41NDI2OTUzMSAyMy41MTg1MTU2MiAxNC4wODUzOTA2MyAyNC4zMDA3ODEyNSAxNC42NDQ1MzEyNSBDMjUuMjk0NjQ4NDQgMTUuMzU5OTYwOTQgMjYuMjg4NTE1NjMgMTYuMDc1MzkwNjIgMjcuMzEyNSAxNi44MTI1IEMyOC4yOTg2MzI4MSAxNy41MjAxOTUzMSAyOS4yODQ3NjU2MyAxOC4yMjc4OTA2MyAzMC4zMDA3ODEyNSAxOC45NTcwMzEyNSBDMzQuMzIwMzgyMTcgMjEuOTk5MzYzMDYgMzQuMzIwMzgyMTcgMjEuOTk5MzYzMDYgMzYgMjQgQzM2IDI1LjY2NjY2NjY3IDM2IDI3LjMzMzMzMzMzIDM2IDI5IEMzNC4yOTI1MDAyIDI3LjcxOTM3NTE1IDMyLjYyNjI2NDA0IDI2LjM4MjMyNDQzIDMxIDI1IEMzMSAyNC4zNCAzMSAyMy42OCAzMSAyMyBDMjguNTI1IDIyLjAxIDI4LjUyNSAyMi4wMSAyNiAyMSBDMjYgMjAuMzQgMjYgMTkuNjggMjYgMTkgQzI1LjI2OTEwMTU2IDE4LjczMzE2NDA2IDI0LjUzODIwMzEyIDE4LjQ2NjMyODEzIDIzLjc4NTE1NjI1IDE4LjE5MTQwNjI1IEMyMC4yMzAwNzEwOSAxNi42NzA2NDc1MiAxNy4zMjYyNTc0MSAxNC41Njc0ODU3NiAxNC4xODM1OTM3NSAxMi4zMzk4NDM3NSBDMTIuMDY0Mjg1NzggMTEuMDM5NDQ1NDggMTAuMzgwMTgzMzYgMTAuNTg4NTM1NDkgOCAxMCBDNy42NyA5LjM0IDcuMzQgOC42OCA3IDggQzYuMDEgOCA1LjAyIDggNCA4IEMzIDcgMiA2IDEgNSBDMC4wMSA1IC0wLjk4IDUgLTIgNSBDLTIgNC4zNCAtMiAzLjY4IC0yIDMgQy0zLjMyIDIuMzQgLTQuNjQgMS42OCAtNiAxIEMtMy4wMyAxLjQ5NSAtMy4wMyAxLjQ5NSAwIDIgQzAgMS4zNCAwIDAuNjggMCAwIFogIiBmaWxsPSIjN0NDN0EzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxOTgsMjEpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuNjYgMC4zMyAxLjMyIDAuNjYgMiAxIEMwLjY1ODA4NTk0IDEuODE3OTEwMTYgMC42NTgwODU5NCAxLjgxNzkxMDE2IC0wLjcxMDkzNzUgMi42NTIzNDM3NSBDLTEzLjY3OTc1Mjk4IDEwLjY4NDAwMzIzIC0yMy45NzA3MDczOSAyMC44MjIyMzAzMyAtMzQuNjQ2NDg0MzggMzEuNjMwNjE1MjMgQy0zNS43NjA0MjE3NSAzMi43NTc2MTI4NCAtMzYuODc5NTEzNDggMzMuODc5NTEzNDggLTM4IDM1IEMtMzguNDk1IDMzLjUxNSAtMzguNDk1IDMzLjUxNSAtMzkgMzIgQy00MC42NSAzMy4zMiAtNDIuMyAzNC42NCAtNDQgMzYgQy00My41NzE4MjI3NSAzMS41NDY5NTY2MiAtNDAuNTE5MzA3OTUgMjguOTg0NTUyMDQgLTM3LjUgMjYgQy0zNi45MjgzMDA3OCAyNS40MzMyOTU5IC0zNi4zNTY2MDE1NiAyNC44NjY1OTE4IC0zNS43Njc1NzgxMiAyNC4yODI3MTQ4NCBDLTI1LjY0NzE2OTU0IDE0LjU1MzgzNzU0IC0xMy4wNDAxMjQxMyA1LjQwMzgyNzQ0IDAgMCBaICIgZmlsbD0iI0VCRkNGNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTEsMzApIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuNjYgMC4zMyAxLjMyIDAuNjYgMiAxIEMxLjQ1NjA2NzE1IDUuMjc0ODUyNTYgMC4wMzUwNTcxOSA3LjcxOTIwMDk1IC0yLjU2MjUgMTEuMTI1IEMtMy4yNzc5Mjk2OSAxMi4wNzUwMzkwNiAtMy45OTMzNTkzNyAxMy4wMjUwNzgxMiAtNC43MzA0Njg3NSAxNC4wMDM5MDYyNSBDLTUuNDc5NDE0MDYgMTQuOTkyNjE3MTkgLTYuMjI4MzU5MzcgMTUuOTgxMzI4MTMgLTcgMTcgQy04LjQ2MTk4MDQ0IDE5LjAxODE5NjM5IC05LjkyMDQ3MjcxIDIxLjAzODkyNTQ4IC0xMS4zNzUgMjMuMDYyNSBDLTEzLjE2OTEwNzEzIDI1LjU1MzUyMTI2IC0xNC44MzAwNzgxNCAyNy44MzAwNzgxNCAtMTcgMzAgQy0xOS4xMjUgMjkuNjI1IC0xOS4xMjUgMjkuNjI1IC0yMSAyOSBDLTE3Ljk1MjQ0MjU5IDIwLjYzNDc2NTg5IC0xMS4yMTAwMTE0NCA3LjA5MzUwNTcgLTIuODkwNjI1IDIuOTQ5MjE4NzUgQy0wLjgyNDYyNDg4IDIuMTQ4MTQ4MDcgLTAuODI0NjI0ODggMi4xNDgxNDgwNyAwIDAgWiAiIGZpbGw9IiNGMkZERjgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDUxLDYxKSIvPgo8cGF0aCBkPSJNMCAwIEMyLjAxODIyOTE3IDAuMDY1MTA0MTcgNC4wMzY0NTgzMyAwLjEzMDIwODMzIDYuMDU0Njg3NSAwLjE5NTMxMjUgQzUuNzI0Njg3NSAwLjg1NTMxMjUgNS4zOTQ2ODc1IDEuNTE1MzEyNSA1LjA1NDY4NzUgMi4xOTUzMTI1IEMyLjc0NDY4NzUgMi4xOTUzMTI1IDAuNDM0Njg3NSAyLjE5NTMxMjUgLTEuOTQ1MzEyNSAyLjE5NTMxMjUgQy0xLjc3NzgxNDk0IDMuNDQxMTEwODQgLTEuNjEwMzE3MzggNC42ODY5MDkxOCAtMS40Mzc3NDQxNCA1Ljk3MDQ1ODk4IEMtMC44MTYwMDY0IDExLjU5NzkyOTkxIC0wLjkxNzM4MDQxIDE3LjE0MDgzOTU5IC0xLjA2NjQwNjI1IDIyLjc5Mjk2ODc1IEMtMS4wOTAwMTcyNCAyMy44Njc4NDA0MiAtMS4xMTM2MjgyMyAyNC45NDI3MTIxIC0xLjEzNzk1NDcxIDI2LjA1MDE1NTY0IEMtMS4yMTM5NzU2NiAyOS40NTI4NjMzNCAtMS4yOTgyNjE3NSAzMi44NTUzMDcxNSAtMS4zODI4MTI1IDM2LjI1NzgxMjUgQy0xLjQzNjA5NDk1IDM4LjU3NzQ2MTQzIC0xLjQ4ODgzNDgyIDQwLjg5NzEyMjkgLTEuNTQxMDE1NjIgNDMuMjE2Nzk2ODggQy0xLjY2ODQ5NTg5IDQ4Ljg3NjUxNjE2IC0xLjgwNzE3MjAyIDU0LjUzNTgwMzQgLTEuOTQ1MzEyNSA2MC4xOTUzMTI1IEM2LjYzNDY4NzUgNjAuMTk1MzEyNSAxNS4yMTQ2ODc1IDYwLjE5NTMxMjUgMjQuMDU0Njg3NSA2MC4xOTUzMTI1IEMyNC4wNTQ2ODc1IDYwLjUyNTMxMjUgMjQuMDU0Njg3NSA2MC44NTUzMTI1IDI0LjA1NDY4NzUgNjEuMTk1MzEyNSBDMTkuNTc1NTM0MjIgNjEuMjI0MTc1ODggMTUuMDk2NDA0NTkgNjEuMjQyMDY0MzcgMTAuNjE3MTg3NSA2MS4yNTc4MTI1IEM5LjMzNzc5Mjk3IDYxLjI2NjE5MTQxIDguMDU4Mzk4NDQgNjEuMjc0NTcwMzEgNi43NDAyMzQzOCA2MS4yODMyMDMxMiBDNS41MjUyOTI5NyA2MS4yODY0MjU3OCA0LjMxMDM1MTU2IDYxLjI4OTY0ODQ0IDMuMDU4NTkzNzUgNjEuMjkyOTY4NzUgQzEuOTMyNjc4MjIgNjEuMjk4MjA1NTcgMC44MDY3NjI3IDYxLjMwMzQ0MjM4IC0wLjM1MzI3MTQ4IDYxLjMwODgzNzg5IEMtMi45NDUzMTI1IDYxLjE5NTMxMjUgLTIuOTQ1MzEyNSA2MS4xOTUzMTI1IC0zLjk0NTMxMjUgNjAuMTk1MzEyNSBDLTQuMDQ5NTQxODYgNTguMzk1Njc1OSAtNC4wODM4NTM1MiA1Ni41OTE5NDY1IC00LjA5MDU3NjE3IDU0Ljc4OTMwNjY0IEMtNC4wOTY5NTYwMiA1My42MzQ2MzM5NCAtNC4xMDMzMzU4OCA1Mi40Nzk5NjEyNCAtNC4xMDk5MDkwNiA1MS4yOTAyOTg0NiBDLTQuMTExMDIxODggNTAuMDMzMDU0NjYgLTQuMTEyMTM0NyA0OC43NzU4MTA4NSAtNC4xMTMyODEyNSA0Ny40ODA0Njg3NSBDLTQuMTE2NjA5NjUgNDYuMTk5MjU2NDQgLTQuMTE5OTM4MDUgNDQuOTE4MDQ0MTMgLTQuMTIzMzY3MzEgNDMuNTk4MDA3MiBDLTQuMTI5MzY0NDIgNDAuODgxODE3MiAtNC4xMzEyMDY2MyAzOC4xNjU2NTkwNSAtNC4xMzA2MTUyMyAzNS40NDk0NjI4OSBDLTQuMTMwNTEzNiAzMS45NjQ0MDY1OCAtNC4xNDQxNjQ2NiAyOC40Nzk1NTM5NSAtNC4xNjEzODc0NCAyNC45OTQ1NDU5NCBDLTQuMTc1MTYzMTYgMjEuNjc3MzUwMTcgLTQuMTc0NTk1NzQgMTguMzYwMTkyOCAtNC4xNzU3ODEyNSAxNS4wNDI5Njg3NSBDLTQuMTgyOTY2NzcgMTMuNzg3NzQ5MTggLTQuMTkwMTUyMjggMTIuNTMyNTI5NiAtNC4xOTc1NTU1NCAxMS4yMzkyNzMwNyBDLTQuMTk0NDc4OTEgMTAuMDgxNDQ4MjEgLTQuMTkxNDAyMjggOC45MjM2MjMzNSAtNC4xODgyMzI0MiA3LjczMDcxMjg5IEMtNC4xODk2NTc0NCA2LjcwNzgwNjU1IC00LjE5MTA4MjQ2IDUuNjg0OTAwMjEgLTQuMTkyNTUwNjYgNC42MzA5OTY3IEMtMy44ODMyNjMyOSAxLjU4NDAzMDI5IC0zLjE2NjU1NDMgMC4zMTc5MjcxNCAwIDAgWiAiIGZpbGw9IiNEMUVDRTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyMy45NDUzMTI1LDExNC44MDQ2ODc1KSIvPgo8cGF0aCBkPSJNMCAwIEM4LjkxIDAgMTcuODIgMCAyNyAwIEMyNyAxLjMyIDI3IDIuNjQgMjcgNCBDMjQuMzYgNCAyMS43MiA0IDE5IDQgQzE5IDMuMzQgMTkgMi42OCAxOSAyIEMxNi4wMyAyIDEzLjA2IDIgMTAgMiBDMTAuMzMgMi45OSAxMC42NiAzLjk4IDExIDUgQzExLjA2MDY4NzgzIDYuOTE3OTE3NjggMTEuMDU4NTQ0NTUgOC44MzgwNjc0MSAxMS4wMjE3Mjg1MiAxMC43NTY1OTE4IEMxMC45OTE1ODQwOSAxMi41MjgwODg0NiAxMC45OTE1ODQwOSAxMi41MjgwODg0NiAxMC45NjA4MzA2OSAxNC4zMzUzNzI5MiBDMTAuOTIwMjc4MDkgMTYuMjQ3OTc2NjEgMTAuOTIwMjc4MDkgMTYuMjQ3OTc2NjEgMTAuODc4OTA2MjUgMTguMTk5MjE4NzUgQzEwLjg1NTI5NTI2IDE5LjUwNDU0MDU2IDEwLjgzMTY4NDI3IDIwLjgwOTg2MjM3IDEwLjgwNzM1Nzc5IDIyLjE1NDczOTM4IEMxMC43NDQ1ODIyNSAyNS42MjE5NzcyOSAxMC42NzU0OTcyOCAyOS4wODkwNjE4OCAxMC42MDUxMDI1NCAzMi41NTYxNTIzNCBDMTAuNTM0MzU5OTkgMzYuMDk1MzA2NjkgMTAuNDY5NTc4NDkgMzkuNjM0NTY5NTggMTAuNDA0Mjk2ODggNDMuMTczODI4MTIgQzEwLjI3NTM2ODggNTAuMTE2MDAzMzkgMTAuMTM5Nzc1NTkgNTcuMDU4MDM0OTEgMTAgNjQgQzkuNjcgNjQgOS4zNCA2NCA5IDY0IEM4LjY3IDQ0LjUzIDguMzQgMjUuMDYgOCA1IEM1LjM2IDQuNjcgMi43MiA0LjM0IDAgNCBDMCAyLjY4IDAgMS4zNiAwIDAgWiAiIGZpbGw9IiM3QUJBOUQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIwMywxMTEpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuNjYgMCAxLjMyIDAgMiAwIEMxLjY3IDIuMzEgMS4zNCA0LjYyIDEgNyBDMy42NCA3IDYuMjggNyA5IDcgQzkgOC42NSA5IDEwLjMgOSAxMiBDMS40MSAxMiAtNi4xOCAxMiAtMTQgMTIgQy0xNCAxMC42OCAtMTQgOS4zNiAtMTQgOCBDLTEyLjgwMzc1IDcuNzkzNzUgLTExLjYwNzUgNy41ODc1IC0xMC4zNzUgNy4zNzUgQy01LjQ2NzAyMTA1IDYuMTkxNTE1NzEgLTMuMzQ3MDIwMDMgMy42NjU3ODM4NCAwIDAgWiAiIGZpbGw9IiM3QkJGQTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEzNywxNjkpIi8+CjxwYXRoIGQ9Ik0wIDAgQzE1LjI3OTg1NzgyIDUuNzAyNjYxMjIgMzIuNTcyNjcyNTggMTkuOTU0NDU0MzEgNDEgMzQgQzQwLjAxIDMzLjY3IDM5LjAyIDMzLjM0IDM4IDMzIEMzNy4zNCAzMy42NiAzNi42OCAzNC4zMiAzNiAzNSBDMzUuNjU3MTA5MzggMzQuNTEyNzM0MzggMzUuMzE0MjE4NzUgMzQuMDI1NDY4NzUgMzQuOTYwOTM3NSAzMy41MjM0Mzc1IEMyNi4wMDMyODU2NSAyMS4zODg2ODc0NSAxNS4xMjA4NjkyOSAxMS41OTA3OTUyIDIuNzg5MDYyNSAzLjAxMTcxODc1IEMxLjg2ODY3MTg3IDIuMzQ3ODUxNTYgMC45NDgyODEyNSAxLjY4Mzk4NDM4IDAgMSBDMCAwLjY3IDAgMC4zNCAwIDAgWiAiIGZpbGw9IiNFNUZBRjIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIxOSwzMykiLz4KPHBhdGggZD0iTTAgMCBDMTAuMzU0MjExOCAzLjAzMjc0MjUxIDIwLjE5NjA1NDAyIDEwLjY0NDk1MzU2IDI4IDE4IEMyOCAxOC42NiAyOCAxOS4zMiAyOCAyMCBDMjguNTcyMzQzNzUgMjAuMjM4NDc2NTYgMjkuMTQ0Njg3NSAyMC40NzY5NTMxMyAyOS43MzQzNzUgMjAuNzIyNjU2MjUgQzMyLjQ3MzM5MzM3IDIyLjI2Njg5NTkxIDM0LjE2NjcwMjEzIDI0LjA4ODAwMjk2IDM2LjI1IDI2LjQzNzUgQzM2Ljk1NjQwNjI1IDI3LjIxOTk2MDk0IDM3LjY2MjgxMjUgMjguMDAyNDIxODcgMzguMzkwNjI1IDI4LjgwODU5Mzc1IEM0MCAzMSA0MCAzMSA0MCAzNCBDMzYuODMxOTkzNCAzMi42MzE1MzA2MiAzNS4wODU0NzczMSAzMS4xODA4NTE0OCAzMi45Mzc1IDI4LjUgQzI0LjI2MjM0OTcyIDE4LjMzOTI1MzE2IDE0LjE2NTM1NzQxIDExLjExNTE3ODc0IDMgNCBDMi4wMSAzLjM0IDEuMDIgMi42OCAwIDIgQzAgMS4zNCAwIDAuNjggMCAwIFogIiBmaWxsPSIjNzlCQjlDIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMjAsMzEpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuNjYgMC45OSAxLjMyIDEuOTggMiAzIEMxLjI0MjE4NzUgNS45NDE0MDYyNSAxLjI0MjE4NzUgNS45NDE0MDYyNSAtMC4xMjUgOS41NjI1IEMtMi41ODk1MzQ5OCAxNi4zMzc2NDc3NiAtNC4yNjE2NzQ4NyAyMy4xMTQyMDM1MiAtNS42NTQ3ODUxNiAzMC4xODM4Mzc4OSBDLTYuNzk5NjU1MjcgMzUuNzk5NjU1MjcgLTYuNzk5NjU1MjcgMzUuNzk5NjU1MjcgLTkgMzggQy05LjQ1OTc0NTAzIDM5LjkyOTAxMTQxIC05LjQ1OTc0NTAzIDM5LjkyOTAxMTQxIC05LjYzMjgxMjUgNDIuMTMyODEyNSBDLTkuNzU4NDk2MDkgNDMuMzQxMzA4NTkgLTkuNzU4NDk2MDkgNDMuMzQxMzA4NTkgLTkuODg2NzE4NzUgNDQuNTc0MjE4NzUgQy05Ljk2NTM1MTU2IDQ1LjQxNTk3NjU2IC0xMC4wNDM5ODQzOCA0Ni4yNTc3MzQzNyAtMTAuMTI1IDQ3LjEyNSBDLTEwLjIxMTM2NzE5IDQ3Ljk3NDQ5MjE5IC0xMC4yOTc3MzQzOCA0OC44MjM5ODQzOCAtMTAuMzg2NzE4NzUgNDkuNjk5MjE4NzUgQy0xMC41OTkwMjU1NyA1MS43OTg2OTcyNyAtMTAuODAwNTM2MDMgNTMuODk5MjYyNDIgLTExIDU2IEMtMTEuMzMgNTYgLTExLjY2IDU2IC0xMiA1NiBDLTEyLjM4MTAzMjE2IDQzLjkzMzk4MTc2IC0xMS41MTE3MTYyNyAzMi44MTEwNDY3NSAtOSAyMSBDLTguMzQgMjEgLTcuNjggMjEgLTcgMjEgQy02LjY2MjI2NTYyIDE5LjkwMzAwNzgxIC02LjMyNDUzMTI1IDE4LjgwNjAxNTYzIC01Ljk3NjU2MjUgMTcuNjc1NzgxMjUgQy01LjUyNjE0ODk4IDE2LjIyMTMyMDkzIC01LjA3NTYyMjE2IDE0Ljc2Njg5NTY5IC00LjYyNSAxMy4zMTI1IEMtNC40MDMyODEyNSAxMi41OTEyNjk1MyAtNC4xODE1NjI1IDExLjg3MDAzOTA2IC0zLjk1MzEyNSAxMS4xMjY5NTMxMiBDLTIuNzc5NDcyODEgNy4zNDcxMDI3IC0xLjU0MDk2NzY4IDMuNjUwNzk3MjYgMCAwIFogIiBmaWxsPSIjOUJDQkI1IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNCw5NikiLz4KPHBhdGggZD0iTTAgMCBDMy4yMTQ2NTcxNCA0LjgyMTk4NTcyIDIuMzgxMDgwNTMgMTAuMDc4MDAzNzcgMi4zNzUgMTUuNzUgQzIuMzc2Mjg5MDYgMTYuOTY1OTg4NzcgMi4zNzc1NzgxMyAxOC4xODE5Nzc1NCAyLjM3ODkwNjI1IDE5LjQzNDgxNDQ1IEMyLjI3MjA4Mjk0IDI4LjU0MDg5NDk4IDEuMjAyNjM0NzggMzYuNDY0NDA2NDIgLTIgNDUgQy0yLjY2IDQ1IC0zLjMyIDQ1IC00IDQ1IEMtNC4xNzAwOTA2MyA0MC40OTI1OTgyMSAtMy45NzQ5MzQyNyAzNi40ODI3MzcxNSAtMy4wNjI1IDMyLjA2MjUgQy0xLjgyNjI0MDA0IDI1LjUwODc4NjQ2IC0xLjMzMDMyNjkgMTguOTU5NTI2MyAtMC44NzUgMTIuMzEyNSBDLTAuNzg4NjMyODEgMTEuMTIyMDUwNzggLTAuNzAyMjY1NjIgOS45MzE2MDE1NiAtMC42MTMyODEyNSA4LjcwNTA3ODEyIEMtMC40MDM1NDQ2MSA1LjgwMzcyMTI1IC0wLjE5OTIxNjc2IDIuOTAyMDk0ODkgMCAwIFogIiBmaWxsPSIjNzVCQjk5IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyODAsMTM4KSIvPgo8cGF0aCBkPSJNMCAwIEMwIDQuMTY3NDM3MjcgLTIuMjc3OTc5OSA1Ljg4MjQ2NzMgLTUgOC43NSBDLTUuNDk2MDQ3MzYgOS4yODIwNjA1NSAtNS45OTIwOTQ3MyA5LjgxNDEyMTA5IC02LjUwMzE3MzgzIDEwLjM2MjMwNDY5IEMtMTAuNDMxMDc4NDkgMTQuNTIzNTY5IC0xNC41NTkzNTE1IDE4LjM4NjQwNjM4IC0xOSAyMiBDLTIwLjEyNSAyMC4yNSAtMjAuMTI1IDIwLjI1IC0yMSAxOCBDLTE5Ljg4NDI0MjY4IDE0LjE2MzYyODk2IC0xNy4wNDI5NjI2OCAxMS45NjM5MzQyNSAtMTQuMTI1IDkuMzc1IEMtMTMuMjYxMzI4MTMgOC41OTg5ODQzNyAtMTIuMzk3NjU2MjUgNy44MjI5Njg3NSAtMTEuNTA3ODEyNSA3LjAyMzQzNzUgQy05LjM2ODQ1MzIxIDUuMjk3Mjg3NzkgLTcuNTA2NjAwNDcgNC4wODQyNTA0MyAtNSAzIEMtNS42NiA0LjMyIC02LjMyIDUuNjQgLTcgNyBDLTQuNjkgNC42OSAtMi4zOCAyLjM4IDAgMCBaICIgZmlsbD0iI0VFRjlGMyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjQzLDIzMykiLz4KPHBhdGggZD0iTTAgMCBDMC42NiAwIDEuMzIgMCAyIDAgQzIgMC45OSAyIDEuOTggMiAzIEMyLjk5IDMuMzMgMy45OCAzLjY2IDUgNCBDNC4zNCA1LjY1IDMuNjggNy4zIDMgOSBDMi4zNCA5IDEuNjggOSAxIDkgQzAuMjA2NDAyNjQgMTEuMzUzNTMxMSAtMC41ODQ5MTc4MyAxMy43MDc3ODg4MyAtMS4zNzUgMTYuMDYyNSBDLTEuNTk2NzE4NzUgMTYuNzE5Mjc3MzQgLTEuODE4NDM3NSAxNy4zNzYwNTQ2OSAtMi4wNDY4NzUgMTguMDUyNzM0MzggQy0zLjQ4MzA2MDE5IDIyLjM0MDE2OTU3IC00Ljc4Nzc1MDQ3IDI2LjY0MzgzMjMgLTYgMzEgQy02LjMzIDMxIC02LjY2IDMxIC03IDMxIEMtNi42NyAyNy43IC02LjM0IDI0LjQgLTYgMjEgQy02LjY2IDIxLjY2IC03LjMyIDIyLjMyIC04IDIzIEMtNi41ODI1Nzg0NyAxNC43Nzg5NTUxNSAtMy40MTM4NjcxOCA3LjU0NzAwODI1IDAgMCBaICIgZmlsbD0iIzdBQkM5RCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjMsODkpIi8+CjxwYXRoIGQ9Ik0wIDAgQzMuNjU2NzAzMjIgMC41NTk5OTY4OSAzLjY1NjcwMzIyIDAuNTU5OTk2ODkgNS4xNzI2MDc0MiAyLjUwMjkyOTY5IEM1LjQ4MDQ1MTY2IDMuMTM2MTgxNjQgNS43ODgyOTU5IDMuNzY5NDMzNTkgNi4xMDU0Njg3NSA0LjQyMTg3NSBDNi40NDkwMDM5MSA1LjExNzk2ODc1IDYuNzkyNTM5MDYgNS44MTQwNjI1IDcuMTQ2NDg0MzggNi41MzEyNSBDNy40OTAwMTk1MyA3LjI2MzQzNzUgNy44MzM1NTQ2OSA3Ljk5NTYyNSA4LjE4NzUgOC43NSBDOC41NDI2MzY3MiA5LjQ2NjcxODc1IDguODk3NzczNDQgMTAuMTgzNDM3NSA5LjI2MzY3MTg4IDEwLjkyMTg3NSBDMTEuMDM4MjcyNzcgMTQuNTg1NTY3MTggMTIuNDYyMDkwOTggMTcuOTIxNTI1NjcgMTMgMjIgQzEwLjczNDM3NSAyMS45NDkyMTg3NSAxMC43MzQzNzUgMjEuOTQ5MjE4NzUgOCAyMSBDNi40NzM0MzI5NCAxOC41NjI5MDE4MSA1LjM1NjA2NTggMTYuNDI4ODY2NDIgNC4yNSAxMy44MTI1IEMzLjc5NTYwNTQ3IDEyLjgyMTUzMzIgMy43OTU2MDU0NyAxMi44MjE1MzMyIDMuMzMyMDMxMjUgMTEuODEwNTQ2ODggQzEuNTkyNDM2ODcgNy44OTQ1MjY2NCAwLjUwNzE4OTg5IDQuMjkxMDkzNDEgMCAwIFogIiBmaWxsPSIjNzFCQzk5IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNjEsODApIi8+CjxwYXRoIGQ9Ik0wIDAgQzEuNDc5NDkyMTkgMS4wODQ3MTY4IDEuNDc5NDkyMTkgMS4wODQ3MTY4IDMgMyBDMy4zNTY0NDUzMSA1Ljk5OTI2NzU4IDMuMzU2NDQ1MzEgNS45OTkyNjc1OCAzLjMyODEyNSA5LjY0NDUzMTI1IEMzLjMyMjk2ODc1IDEwLjk2MTMwODU5IDMuMzE3ODEyNSAxMi4yNzgwODU5NCAzLjMxMjUgMTMuNjM0NzY1NjIgQzMuMzAyMTg3NSAxNC4zMjA5NDk3MSAzLjI5MTg3NSAxNS4wMDcxMzM3OSAzLjI4MTI1IDE1LjcxNDExMTMzIEMzLjI1MDI2NjMgMTcuNzk0NjE4MTggMy4yNDAyOTI0MiAxOS44NzQzNjM5OCAzLjIzNDM3NSAyMS45NTUwNzgxMiBDMy4xOTQ2MDg0MiAyNy43MzQ3MzMzOSAzLjA4NDcyNTkyIDMzLjMwMTkyODIgMiAzOSBDMC41MTUgMzkuNDk1IDAuNTE1IDM5LjQ5NSAtMSA0MCBDLTAuOTI3ODEyNSAzOS4yOTEwMTU2MiAtMC44NTU2MjUgMzguNTgyMDMxMjUgLTAuNzgxMjUgMzcuODUxNTYyNSBDMC4zMTcwODc3NSAyNS4yNTExODc3NiAwLjA5MjY0NzMyIDEyLjYzNzA5Mzk2IDAgMCBaICIgZmlsbD0iI0M1RjJERSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjc2LDEzMSkiLz4KPHBhdGggZD0iTTAgMCBDMC45OSAwLjY2IDEuOTggMS4zMiAzIDIgQzIuNjcgMi45OSAyLjM0IDMuOTggMiA1IEMzLjEzODg4NjcyIDUuODE0MDQyOTcgMy4xMzg4ODY3MiA1LjgxNDA0Mjk3IDQuMzAwNzgxMjUgNi42NDQ1MzEyNSBDNS4yOTQ2NDg0NCA3LjM1OTk2MDk0IDYuMjg4NTE1NjMgOC4wNzUzOTA2MiA3LjMxMjUgOC44MTI1IEM4LjI5ODYzMjgxIDkuNTIwMTk1MzEgOS4yODQ3NjU2MyAxMC4yMjc4OTA2MyAxMC4zMDA3ODEyNSAxMC45NTcwMzEyNSBDMTQuMzIwMzgyMTcgMTMuOTk5MzYzMDYgMTQuMzIwMzgyMTcgMTMuOTk5MzYzMDYgMTYgMTYgQzE2IDE3LjY2NjY2NjY3IDE2IDE5LjMzMzMzMzMzIDE2IDIxIEMxNC4yOTI1MDAyIDE5LjcxOTM3NTE1IDEyLjYyNjI2NDA0IDE4LjM4MjMyNDQzIDExIDE3IEMxMSAxNi4zNCAxMSAxNS42OCAxMSAxNSBDOC41MjUgMTQuMDEgOC41MjUgMTQuMDEgNiAxMyBDNiAxMi4zNCA2IDExLjY4IDYgMTEgQzQuOTc5MDYyNSAxMC42NTk2ODc1IDQuOTc5MDYyNSAxMC42NTk2ODc1IDMuOTM3NSAxMC4zMTI1IEMwLjIwMzg1Njc2IDguNjQ0Mjc2NDMgLTIuNzIxNjM0MjEgNi40NDI3MDM5MiAtNiA0IEMtNS4wMSAzLjY3IC00LjAyIDMuMzQgLTMgMyBDLTMuNjYgMi4zNCAtNC4zMiAxLjY4IC01IDEgQy0zLjM1IDEuMzMgLTEuNyAxLjY2IDAgMiBDMCAxLjM0IDAgMC42OCAwIDAgWiAiIGZpbGw9IiM4NUNBQTkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIxOCwyOSkiLz4KPHBhdGggZD0iTTAgMCBDMTQuMzc2ODc3MjQgMC41NTQwMjIyNCAyNy45ODMwOTk1NyAzLjg4MDMwMTE0IDQxIDEwIEMzNi45MzU5NjcxNiAxMS4zNTQ2Nzc2MSAzNC4yNDU3MzA4NiA5LjkyMTM4MzA0IDMwLjM3NSA4LjU2MjUgQzI1LjA4NjM0NjQ5IDYuNzkzMzIwODggMjAuNzEwOTAyOTYgNS42MzM2NDExNSAxNS4wOTM3NSA2LjAxMTcxODc1IEMxMC4zMjI3ODA1NCA1Ljk4NTAxNTU2IDQuNzk2NDk5NDkgNC42MTIxNDI1NiAxLjIxMDkzNzUgMS4zOTQ1MzEyNSBDMC44MTEzMjgxMiAwLjkzNDMzNTk0IDAuNDExNzE4NzUgMC40NzQxNDA2MyAwIDAgWiAiIGZpbGw9IiM4MUJGQTMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE3MCwxMikiLz4KPHBhdGggZD0iTTAgMCBDMC42NiAwLjMzIDEuMzIgMC42NiAyIDEgQzIgMS42NiAyIDIuMzIgMiAzIEM0LjY0IDMuMzMgNy4yOCAzLjY2IDEwIDQgQzEwIDQuOTkgMTAgNS45OCAxMCA3IEMzLjA3IDcgLTMuODYgNyAtMTEgNyBDLTExIDYuMDEgLTExIDUuMDIgLTExIDQgQy05LjM5MTI1IDMuNTY2ODc1IC05LjM5MTI1IDMuNTY2ODc1IC03Ljc1IDMuMTI1IEMtNC4xNjU0Njg2MiAyLjE3MzU3NjU0IC00LjE2NTQ2ODYyIDIuMTczNTc2NTQgLTEuNjg3NSAwLjgxMjUgQy0xLjEzMDYyNSAwLjU0NDM3NSAtMC41NzM3NSAwLjI3NjI1IDAgMCBaICIgZmlsbD0iIzZDQjk5NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNTYsMTczKSIvPgo8cGF0aCBkPSJNMCAwIEMyLjEyNSAtMC4zMTI1IDIuMTI1IC0wLjMxMjUgNSAwIEM1LjgwNDM3NSAwLjcwMTI1IDYuNjA4NzUgMS40MDI1IDcuNDM3NSAyLjEyNSBDMTIuMDIxMjQ2NDggNi4xMDk3OTA0MSAxNy40OTAwOTc2NSA4LjU4MzU3NDczIDIyLjg3NSAxMS4zMTI1IEMyMy45NDQ5MjE4OCAxMS44NjIyODUxNiAyNS4wMTQ4NDM3NSAxMi40MTIwNzAzMSAyNi4xMTcxODc1IDEyLjk3ODUxNTYyIEMyOC43NDE0MjIxNSAxNC4zMjU3Nzg5NSAzMS4zNjg5OTU3MSAxNS42NjYwMTkwMiAzNCAxNyBDMzMuMDEgMTcuNjYgMzIuMDIgMTguMzIgMzEgMTkgQzcuMzM5NjIyNjQgNy4xNjk4MTEzMiA3LjMzOTYyMjY0IDcuMTY5ODExMzIgMCAxIEMwIDAuNjcgMCAwLjM0IDAgMCBaICIgZmlsbD0iI0U0RjVFRCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzAsMjU3KSIvPgo8cGF0aCBkPSJNMCAwIEMxLjAyODA0NjQyIDMuMTc3NTk4MDMgMC45MjMzMzY0NCA1LjE2Nzk2MjI5IDAuMDYyNSA4LjM3NSBDLTAuNjMwODc0MyAxMS4xNjY3NDM4OSAtMC45OTU2MzQ4NSAxMy4yMTgyNTc1NyAtMC45Mzc1IDE2LjEyNSBDLTEuMDIxODQxNTUgMjAuMDA0NzExMTQgLTIuMzI3MDQ4MTQgMjIuNTE0NjgzNjMgLTQgMjYgQy00LjMzIDI1LjAxIC00LjY2IDI0LjAyIC01IDIzIEMtNS45OSAyMy4zMyAtNi45OCAyMy42NiAtOCAyNCBDLTcuNDA5ODQ4MTUgMTguNDkxOTE2MDMgLTYuNDMzMzYyMzEgMTMuMzUxMjE5MzEgLTUgOCBDLTQuNjcgOC45OSAtNC4zNCA5Ljk4IC00IDExIEMtMy4zNCAxMSAtMi42OCAxMSAtMiAxMSBDLTEuOTM5NDE0MDYgMTAuMzczNTE1NjMgLTEuODc4ODI4MTIgOS43NDcwMzEyNSAtMS44MTY0MDYyNSA5LjEwMTU2MjUgQy0xLjczMjYxNzE5IDguMjg0Mjk2ODcgLTEuNjQ4ODI4MTMgNy40NjcwMzEyNSAtMS41NjI1IDYuNjI1IEMtMS40ODEyODkwNiA1LjgxMjg5MDYyIC0xLjQwMDA3ODEyIDUuMDAwNzgxMjUgLTEuMzE2NDA2MjUgNC4xNjQwNjI1IEMtMSAyIC0xIDIgMCAwIFogIiBmaWxsPSIjODBDMUEzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyODAsMTcyKSIvPgo8cGF0aCBkPSJNMCAwIEMyLjAyMDc1NTY2IC0wLjAyNzM0ODU3IDQuMDQxNjIyMjEgLTAuMDQ2NTUxNyA2LjA2MjUgLTAuMDYyNSBDNy4xODc4NTE1NiAtMC4wNzQxMDE1NiA4LjMxMzIwMzEzIC0wLjA4NTcwMzEzIDkuNDcyNjU2MjUgLTAuMDk3NjU2MjUgQzEzIDAgMTMgMCAxNi4yNjc1NzgxMiAwLjUgQzE5Ljk4MTc5Mzk0IDAuOTk3NTYxMDkgMjMuNTAxMDk5MjIgMS4xMTM3MjkxOSAyNy4yNDYwOTM3NSAxLjA5NzY1NjI1IEMyNy45MDIwNzA0NyAxLjA5NjI0MTMgMjguNTU4MDQ3MTggMS4wOTQ4MjYzNSAyOS4yMzM5MDE5OCAxLjA5MzM2ODUzIEMzMS4zMDE4MTcwMiAxLjA4NzgzMDMxIDMzLjM2OTYxODQyIDEuMDc1MjkwMDIgMzUuNDM3NSAxLjA2MjUgQzM2Ljg1MDkwOTg4IDEuMDU3NDc2OTkgMzguMjY0MzIxNDggMS4wNTI5MTU1IDM5LjY3NzczNDM4IDEuMDQ4ODI4MTIgQzQzLjExODUyNTY4IDEuMDM3ODgxMjYgNDYuNTU5MjUxNzIgMS4wMjA2NjAzOCA1MCAxIEM1MCAxLjMzIDUwIDEuNjYgNTAgMiBDNDEuMjM1MDczNSAzLjk4NzE4OTgxIDMzLjA1MDU0MTY2IDUuNTA4MDM0NTYgMjQgNSBDMjQgNC42NyAyNCA0LjM0IDI0IDQgQzEyLjEyIDIuNTE1IDEyLjEyIDIuNTE1IDAgMSBDMCAwLjY3IDAgMC4zNCAwIDAgWiAiIGZpbGw9IiM3RUJGQTEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEzMSwyODMpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuMzMgMCAwLjY2IDAgMSAwIEMxLjMzMzM3ODAzIDQuNzc4NDE4NDUgMC41NTE5NDA2NiA3LjAxODk3MjU3IC0yIDExIEMtMi43MDY3MDcyOCAxMy42OTI1NDk5MSAtMi43MDY3MDcyOCAxMy42OTI1NDk5MSAtMyAxNiBDLTMuNjYgMTYgLTQuMzIgMTYgLTUgMTYgQy02LjU4NDU1ODY2IDE3Ljc4NzIzOTQxIC02LjU4NDU1ODY2IDE3Ljc4NzIzOTQxIC04LjEyNSAyMCBDLTguOTMzMjQyMTkgMjEuMTEzNzUgLTguOTMzMjQyMTkgMjEuMTEzNzUgLTkuNzU3ODEyNSAyMi4yNSBDLTEwLjE2NzczNDM4IDIyLjgyNzUgLTEwLjU3NzY1NjI1IDIzLjQwNSAtMTEgMjQgQy0xMi4zMiAyMy42NyAtMTMuNjQgMjMuMzQgLTE1IDIzIEMtMTQuNDgwNTA3ODEgMjIuMzk0MTQwNjMgLTEzLjk2MTAxNTYyIDIxLjc4ODI4MTI1IC0xMy40MjU3ODEyNSAyMS4xNjQwNjI1IEMtOS4xODM1MzUwOSAxNi4wNjcwOTM5MiAtNS43NzY4NDE3MyAxMS4xNjA3NDI2NSAtMy4wMDc4MTI1IDUuMTI4OTA2MjUgQy0yIDMgLTIgMyAwIDAgWiAiIGZpbGw9IiM4NUM2QTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2MywyMTMpIi8+CjxwYXRoIGQ9Ik0wIDAgQzIuNDAwMjQ5NjEgMy42MDAzNzQ0MiAyLjEyODE2MDI5IDQuNzYyNjE4OTQgMS43NTc4MTI1IDguOTgwNDY4NzUgQzEuNjYxMTMyODEgMTAuMTQzODQ3NjYgMS41NjQ0NTMxMiAxMS4zMDcyMjY1NiAxLjQ2NDg0Mzc1IDEyLjUwNTg1OTM4IEMxLjM1MjY5NTMxIDEzLjcyMDgwMDc4IDEuMjQwNTQ2ODcgMTQuOTM1NzQyMTkgMS4xMjUgMTYuMTg3NSBDMS4wMjA1ODU5NCAxNy40MTQwNDI5NyAwLjkxNjE3MTg3IDE4LjY0MDU4NTk0IDAuODA4NTkzNzUgMTkuOTA0Mjk2ODggQzAuNTQ4OTY3NzYgMjIuOTM3MjAwNDggMC4yNzkwNDA5MSAyNS45Njg4MzExIDAgMjkgQy0wLjMzIDI5IC0wLjY2IDI5IC0xIDI5IEMtMS4zMyAzMi4zIC0xLjY2IDM1LjYgLTIgMzkgQy0yLjMzIDM5IC0yLjY2IDM5IC0zIDM5IEMtMy4zNDMyMTA3OCAyNS42MDk0OTkyNiAtMy4xODUxODE5NCAxMy4wNjMwMzc4MiAwIDAgWiAiIGZpbGw9IiNFQkY1RjIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDEyMCkiLz4KPHBhdGggZD0iTTAgMCBDMC43ODc3MzgwNCAwLjAwMTQxNDk1IDEuNTc1NDc2MDcgMC4wMDI4Mjk5IDIuMzg3MDg0OTYgMC4wMDQyODc3MiBDNC44NzI2ODA2NCAwLjAwOTgzMDEzIDcuMzU4MTgyMTEgMC4wMjIzNzE0IDkuODQzNzUgMC4wMzUxNTYyNSBDMTEuNTQxNjY1MzUgMC4wNDAxNzg3MSAxMy4yMzk1ODIxNCAwLjA0NDc0MDMgMTQuOTM3NSAwLjA0ODgyODEyIEMxOS4wNzI5NTIyOSAwLjA1OTc4MDYxIDIzLjIwODMzNTI0IDAuMDc3MDA0NTkgMjcuMzQzNzUgMC4wOTc2NTYyNSBDMjcuMzQzNzUgMC40Mjc2NTYyNSAyNy4zNDM3NSAwLjc1NzY1NjI1IDI3LjM0Mzc1IDEuMDk3NjU2MjUgQzI2LjM2MDkyMDQxIDEuMTIyNzEyNCAyNS4zNzgwOTA4MiAxLjE0Nzc2ODU1IDI0LjM2NTQ3ODUyIDEuMTczNTgzOTggQzIwLjY4MDgwMDMxIDEuMjY4NTM3NDQgMTYuOTk2MjcyNTUgMS4zNjg1MzQ2MyAxMy4zMTE3Njc1OCAxLjQ2OTk3MDcgQzExLjcyNDIzODU1IDEuNTEyOTkwMTEgMTAuMTM2NjczIDEuNTU0Njg0NjIgOC41NDkwNzIyNyAxLjU5NDk3MDcgQy00Ljk3MDI5NDU1IDAuNzM0OTMxNzcgLTQuOTcwMjk0NTUgMC43MzQ5MzE3NyAtMTYuNjU2MjUgNC4wOTc2NTYyNSBDLTE4LjI3MzA5MTk0IDQuNDY4NTQ0NzIgLTE5LjkwMDI0MDc3IDQuNzk1MDkyMjIgLTIxLjUzMTI1IDUuMDk3NjU2MjUgQy0yNC4zNTM2MjIxMyA1LjYyNDI3OTYyIC0yNi45MjY0MzE3OSA2LjE4NzcxNjg1IC0yOS42NTYyNSA3LjA5NzY1NjI1IEMtMzIuMzQzNzUgNi42NjAxNTYyNSAtMzIuMzQzNzUgNi42NjAxNTYyNSAtMzQuNjU2MjUgNi4wOTc2NTYyNSBDLTM0LjY1NjI1IDUuNzY3NjU2MjUgLTM0LjY1NjI1IDUuNDM3NjU2MjUgLTM0LjY1NjI1IDUuMDk3NjU2MjUgQy0zMS44MjM5MzQ1MyA0LjQ0NzA3MDk2IC0yOC45OTA1Mjc5OSAzLjgwMjA4OTEyIC0yNi4xNTYyNSAzLjE2MDE1NjI1IEMtMjUuMzY0NzY1NjMgMi45Nzc3NTM5MSAtMjQuNTczMjgxMjUgMi43OTUzNTE1NiAtMjMuNzU3ODEyNSAyLjYwNzQyMTg4IEMtMjIuNTc4MzIwMzEgMi4zNDE1NTI3MyAtMjIuNTc4MzIwMzEgMi4zNDE1NTI3MyAtMjEuMzc1IDIuMDcwMzEyNSBDLTIwLjY2Mjc5Mjk3IDEuOTA3OTcxMTkgLTE5Ljk1MDU4NTk0IDEuNzQ1NjI5ODggLTE5LjIxNjc5Njg4IDEuNTc4MzY5MTQgQy0xMi43NjY1MzQ5MSAwLjM2NzQwNzQ5IC02LjU0Mjc2NDM2IC0wLjA0NTI1NjggMCAwIFogIiBmaWxsPSIjOTZDQ0IzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNDIuNjU2MjUsMTAuOTAyMzQzNzUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzIuMjE3MzY2MzQgMS4xMDg2ODMxNyA0LjAzNjE3OTkzIDIuNDk0NDA0NjEgNiA0IEM0Ljg5MjA1MDc4IDQuNDU0Mzk0NTMgNC44OTIwNTA3OCA0LjQ1NDM5NDUzIDMuNzYxNzE4NzUgNC45MTc5Njg3NSBDLTMuMjI3MzQyMzcgNy44MDQxNzU2OSAtMTAuMTM3Mjg2ODUgMTAuODI1MzE5NyAtMTcgMTQgQy0xNyAxMy4zNCAtMTcgMTIuNjggLTE3IDEyIEMtMTUuNzEwODY1MTMgMTEuMzI4NDUwNjcgLTE0LjQxODU5Nzk2IDEwLjY2MjkxMTQxIC0xMy4xMjUgMTAgQy0xMi40MDU3MDMxMyA5LjYyODc1IC0xMS42ODY0MDYyNSA5LjI1NzUgLTEwLjk0NTMxMjUgOC44NzUgQy05IDggLTkgOCAtNyA4IEMtNy42NiA2LjY4IC04LjMyIDUuMzYgLTkgNCBDLTcuNTQ2MTgxMDMgMy4zMDMwMDQ0OCAtNi4wODY3NzA0NyAyLjYxNzY2MTM0IC00LjYyNSAxLjkzNzUgQy0zLjgxMjg5MDYyIDEuNTU0NjQ4NDQgLTMuMDAwNzgxMjUgMS4xNzE3OTY4NyAtMi4xNjQwNjI1IDAuNzc3MzQzNzUgQy0xLjQ0OTkyMTg4IDAuNTIwODIwMzEgLTAuNzM1NzgxMjUgMC4yNjQyOTY4NyAwIDAgWiAiIGZpbGw9IiM3NUJGOUMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDExMywyMikiLz4KPHBhdGggZD0iTTAgMCBDMS42NSAwIDMuMyAwIDUgMCBDNSAwLjMzIDUgMC42NiA1IDEgQzEwLjYxIDEgMTYuMjIgMSAyMiAxIEMyMiAwLjY3IDIyIDAuMzQgMjIgMCBDMjMuNjUgMCAyNS4zIDAgMjcgMCBDMjcgMS4zMiAyNyAyLjY0IDI3IDQgQzE4LjA5IDQgOS4xOCA0IDAgNCBDMCAyLjY4IDAgMS4zNiAwIDAgWiAiIGZpbGw9IiM4MUJEQTEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDkxLDE3NykiLz4KPHBhdGggZD0iTTAgMCBDMS4yMDg0OTYwOSAwLjA5OTkwMjM0IDIuNDE2OTkyMTkgMC4xOTk4MDQ2OSAzLjY2MjEwOTM4IDAuMzAyNzM0MzggQzYuNjI5NTYxNzcgMC41NDkwNDQ2NSA5LjU5NjIyMDkxIDAuODAyNTAxMDcgMTIuNTYyNSAxLjA2MjUgQzEyLjIzMjUgMi4wNTI1IDExLjkwMjUgMy4wNDI1IDExLjU2MjUgNC4wNjI1IEM2Ljk0MjUgNC4wNjI1IDIuMzIyNSA0LjA2MjUgLTIuNDM3NSA0LjA2MjUgQy0yLjQzNzUgMy43MzI1IC0yLjQzNzUgMy40MDI1IC0yLjQzNzUgMy4wNjI1IEMtOC4zNzc1IDIuNDAyNSAtMTQuMzE3NSAxLjc0MjUgLTIwLjQzNzUgMS4wNjI1IEMtMjAuNDM3NSAwLjczMjUgLTIwLjQzNzUgMC40MDI1IC0yMC40Mzc1IDAuMDYyNSBDLTEzLjU2NTEyMzk2IC0xLjM4MDU5ODA1IC02Ljk0OTA2ODM1IC0wLjYxMDM2NTUyIDAgMCBaICIgZmlsbD0iI0M5RTlEQiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTQ4LjQzNzUsMjg0LjkzNzUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuOTkgMS40ODUgMC45OSAxLjQ4NSAyIDMgQzIuNjYgMi42NyAzLjMyIDIuMzQgNCAyIEM3LjQ0NDE5MTM0IDE1LjYzMzI1NzQgNy40NDQxOTEzNCAxNS42MzMyNTc0IDcgMjIgQzYuMDEgMjIgNS4wMiAyMiA0IDIyIEMzLjMyODEwMzQ0IDE5LjA4NDQ0ODg1IDIuNjYyMzA2OTIgMTYuMTY3NzMwNDkgMiAxMy4yNSBDMS44MDkyMTg3NSAxMi40MjUgMS42MTg0Mzc1IDExLjYgMS40MjE4NzUgMTAuNzUgQzEuMjQxNDA2MjUgOS45NTA3ODEyNSAxLjA2MDkzNzUgOS4xNTE1NjI1IDAuODc1IDguMzI4MTI1IEMwLjcwNzQyMTg4IDcuNTk0OTcwNyAwLjUzOTg0Mzc1IDYuODYxODE2NDEgMC4zNjcxODc1IDYuMTA2NDQ1MzEgQzAgNCAwIDQgMCAwIFogIiBmaWxsPSIjOTJEN0I4IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNzEsMTA4KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjMzIDAgMC42NiAwIDEgMCBDMS4zMyAxLjY1IDEuNjYgMy4zIDIgNSBDMi45OSA0LjAxIDMuOTggMy4wMiA1IDIgQzUuODQ2ODExMzMgNy4wODA4Njc5NiA2LjA5MDM4MDk5IDExLjg0ODI4Mzc3IDYgMTcgQzUuNjcgMTcgNS4zNCAxNyA1IDE3IEM0LjY3IDIwLjk2IDQuMzQgMjQuOTIgNCAyOSBDMy42NyAyOSAzLjM0IDI5IDMgMjkgQzIuOTg1NDE3NDggMjguNDM0OTA3MjMgMi45NzA4MzQ5NiAyNy44Njk4MTQ0NSAyLjk1NTgxMDU1IDI3LjI4NzU5NzY2IEMyLjg4MTQzOTQyIDI0LjczMjY5OTgxIDIuNzg0NzQ3NTggMjIuMTc5MTI1MDIgMi42ODc1IDE5LjYyNSBDMi42NjQ5NDE0MSAxOC43MzU1NDY4OCAyLjY0MjM4MjgxIDE3Ljg0NjA5Mzc1IDIuNjE5MTQwNjIgMTYuOTI5Njg3NSBDMi41ODM2OTE0MSAxNi4wNzg5MDYyNSAyLjU0ODI0MjE5IDE1LjIyODEyNSAyLjUxMTcxODc1IDE0LjM1MTU2MjUgQzIuNDg1NTM0NjcgMTMuNTY2MDQwMDQgMi40NTkzNTA1OSAxMi43ODA1MTc1OCAyLjQzMjM3MzA1IDExLjk3MTE5MTQxIEMyLjE1MTEwNDAzIDkuNTk5NTk4NDQgMi4xNTExMDQwMyA5LjU5OTU5ODQ0IC0xIDggQy0wLjY3IDUuMzYgLTAuMzQgMi43MiAwIDAgWiAiIGZpbGw9IiM4RkM4QUMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI3NiwxMjQpIi8+CjxwYXRoIGQ9Ik0wIDAgQzUuMTI1ODM2MTEgLTAuMTAyNTE2NzIgOS45NDk5Nzc3OCAtMC4wMzU5MDE5OSAxNSAxIEMxNC42NyAxLjk5IDE0LjM0IDIuOTggMTQgNCBDMTMuMzQgNCAxMi42OCA0IDEyIDQgQzEyIDQuOTkgMTIgNS45OCAxMiA3IEMxMC42OCA3IDkuMzYgNyA4IDcgQzcuNjcgNi4wMSA3LjM0IDUuMDIgNyA0IEM0LjY5IDMuNjcgMi4zOCAzLjM0IDAgMyBDMCAyLjAxIDAgMS4wMiAwIDAgWiAiIGZpbGw9IiM2NkJCOTMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDk1LDExMikiLz4KPHBhdGggZD0iTTAgMCBDLTAuOTUzMzYwMjEgMS4zMTYxMTY3NyAtMS45MTI4NTc3NCAyLjYyNzc4OTYxIC0yLjg3NSAzLjkzNzUgQy0zLjQwODY3MTg4IDQuNjY4Mzk4NDQgLTMuOTQyMzQzNzUgNS4zOTkyOTY4OCAtNC40OTIxODc1IDYuMTUyMzQzNzUgQy02IDggLTYgOCAtOCA5IEMtOC42NiA4LjM0IC05LjMyIDcuNjggLTEwIDcgQy05LjM0IDYuMDEgLTguNjggNS4wMiAtOCA0IEMtOS45OCA1Ljk4IC0xMS45NiA3Ljk2IC0xNCAxMCBDLTE0LjY2IDkuNjcgLTE1LjMyIDkuMzQgLTE2IDkgQy0xNC43MzY1NzUxMiA3LjUxNDUwMDQ0IC0xMy40NjM5ODk3IDYuMDM2Nzg4MTEgLTEyLjE4NzUgNC41NjI1IEMtMTEuNDc5ODA0NjkgMy43Mzg3ODkwNiAtMTAuNzcyMTA5MzggMi45MTUwNzgxMyAtMTAuMDQyOTY4NzUgMi4wNjY0MDYyNSBDLTYuNjcyNDE0IC0xLjM0MjgxNjQzIC00LjQwMjk1MjA1IC0xLjA3MDk4ODM0IDAgMCBaICIgZmlsbD0iIzdDQkRBMCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjUwLDIzMSkiLz4KPHBhdGggZD0iTTAgMCBDMC42NiAwLjMzIDEuMzIgMC42NiAyIDEgQzEuMDgyMTg3NSAxLjU4MDA3ODEyIDAuMTY0Mzc1IDIuMTYwMTU2MjUgLTAuNzgxMjUgMi43NTc4MTI1IEMtNy4zNzk1MDk2NiA2Ljk1MDI1MDcxIC0xMy44Mjk1MjgxNSAxMS4xODcwMzE5NiAtMjAgMTYgQy0yMCAxNS4zNCAtMjAgMTQuNjggLTIwIDE0IEMtMjAuNjYgMTMuNjcgLTIxLjMyIDEzLjM0IC0yMiAxMyBDLTYuMjM2MTYyMzYgMi42MzgzNzYzOCAtNi4yMzYxNjIzNiAyLjYzODM3NjM4IDAgMCBaICIgZmlsbD0iI0Q3RjhFQSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTEsMzApIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuMzMgMCAwLjY2IDAgMSAwIEMwLjY0NzA3OTg5IDguNjQ2NTQyNjkgLTAuOTY3MjU2NTEgMTYuODc0NTM2MzIgLTQgMjUgQy00LjY2IDI1LjMzIC01LjMyIDI1LjY2IC02IDI2IEMtNS4zMTc4MTc5NSAyMC42MzQ5MjI0NCAtNC4yNTg1NjQ5MyAxNS40NDUyMDA2MSAtMyAxMC4xODc1IEMtMi44MjIxMDkzOCA5LjQyNTAxOTUzIC0yLjY0NDIxODc1IDguNjYyNTM5MDYgLTIuNDYwOTM3NSA3Ljg3Njk1MzEyIEMtMS4xMjk5MTA3MSAyLjI1OTgyMTQzIC0xLjEyOTkxMDcxIDIuMjU5ODIxNDMgMCAwIFogIiBmaWxsPSIjREZGQkYwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNzYsMTY5KSIvPgo8cGF0aCBkPSJNMCAwIEMyIDIgMiAyIDIuMDgyMDMxMjUgNC4wNjY0MDYyNSBDMS40NjE0MzEwNyA5LjgxMzcwMzYxIDAuNzU2MzU4NjMgMTUuODI4ODgyNzUgLTIgMjEgQy0yLjk5IDIxLjMzIC0zLjk4IDIxLjY2IC01IDIyIEMtNC4zNTQ4MjAyNSAxOS4wODMxODczNiAtMy43MDg4ODE4IDE2LjE2NjU0NjI3IC0zLjA2MjUgMTMuMjUgQy0yLjg4MDA5NzY2IDEyLjQyNSAtMi42OTc2OTUzMSAxMS42IC0yLjUwOTc2NTYyIDEwLjc1IEMtMi4zMzI1MTk1MyA5Ljk1MDc4MTI1IC0yLjE1NTI3MzQ0IDkuMTUxNTYyNSAtMS45NzI2NTYyNSA4LjMyODEyNSBDLTEuNzI5MTQ0MjkgNy4yMjgzOTM1NSAtMS43MjkxNDQyOSA3LjIyODM5MzU1IC0xLjQ4MDcxMjg5IDYuMTA2NDQ1MzEgQy0xLjAxNDQ2NDU0IDQuMDYzMzgyNDcgLTAuNTE1NzkzNTEgMi4wMzEwNjIzOCAwIDAgWiAiIGZpbGw9IiM4OUJGQTciIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI4NSwxNjQpIi8+CjxwYXRoIGQ9Ik0wIDAgQzguMjA0MzE4NzggMy4wMTE3MTE5NiAxNS41MDc3NzIzMyA4LjIwOTYzNDc4IDIyIDE0IEMyMS4wMSAxNC42NiAyMC4wMiAxNS4zMiAxOSAxNiBDMTguNDY2MzI4MTIgMTUuNTAzNzEwOTQgMTcuOTMyNjU2MjUgMTUuMDA3NDIxODcgMTcuMzgyODEyNSAxNC40OTYwOTM3NSBDMTIuNDU4MTg2MzggMTAuMDA1OTkzNDYgNy40MTI4MTQwNiA2LjA1MDU3MTM0IDEuODcxMDkzNzUgMi4zNDc2NTYyNSBDMS4yNTM2MzI4MSAxLjkwMjkyOTY5IDAuNjM2MTcxODggMS40NTgyMDMxMiAwIDEgQzAgMC42NyAwIDAuMzQgMCAwIFogIiBmaWxsPSIjRTBGQUVEIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMTksMzMpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuMzMgMCAwLjY2IDAgMSAwIEMyLjIxMDc0MDc0IDcuODAyNTUxNDQgMS43MjUyNjUzMiAxNS4xNTU5NzY2NiAxIDIzIEMwLjM0IDIzIC0wLjMyIDIzIC0xIDIzIEMtMi41OTYwNjU0IDE1LjE3OTI3OTUzIC0xLjIyNTE4MzU4IDcuNzc2MzcxOCAwIDAgWiAiIGZpbGw9IiNDOEU4REEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE1LDEyNykiLz4KPHBhdGggZD0iTTAgMCBDLTEuNDY2NjY2NjcgNS41IC0xLjQ2NjY2NjY3IDUuNSAtNCA3Ljg3NSBDLTYgOSAtNiA5IC04IDkgQy05LjU3MTExMzE3IDEwLjMyNzcxNzY2IC05LjU3MTExMzE3IDEwLjMyNzcxNzY2IC0xMS4xMjUgMTIgQy0xMS42NjM4MjgxMyAxMi41NTY4NzUgLTEyLjIwMjY1NjI1IDEzLjExMzc1IC0xMi43NTc4MTI1IDEzLjY4NzUgQy0xMy4zNzI2OTUzMSAxNC4zMzcxODc1IC0xMy4zNzI2OTUzMSAxNC4zMzcxODc1IC0xNCAxNSBDLTE0Ljk5IDE0LjY3IC0xNS45OCAxNC4zNCAtMTcgMTQgQy0xNi4zMzYxMzI4MSAxMy40MDgzMjAzMSAtMTUuNjcyMjY1NjIgMTIuODE2NjQwNjIgLTE0Ljk4ODI4MTI1IDEyLjIwNzAzMTI1IEMtOS4zNTk3MzQxOSA3LjIxMzIwMzkzIC05LjM1OTczNDE5IDcuMjEzMjAzOTMgLTQgMS45Mzc1IEMtMiAwIC0yIDAgMCAwIFogIiBmaWxsPSIjNzhCRDlEIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNDIsMjM5KSIvPgo8cGF0aCBkPSJNMCAwIEMxLjMyIDAgMi42NCAwIDQgMCBDMy4wMzY0NDY0NyA5LjQxMDAyMjc4IDMuMDM2NDQ2NDcgOS40MTAwMjI3OCAyIDE0IEMxLjY3IDEzLjAxIDEuMzQgMTIuMDIgMSAxMSBDMC4wMSAxMSAtMC45OCAxMSAtMiAxMSBDLTEuMzQgNy4zNyAtMC42OCAzLjc0IDAgMCBaICIgZmlsbD0iIzc5QkM5QyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjU0LDE2MykiLz4KPHBhdGggZD0iTTAgMCBDMC45OSAwIDEuOTggMCAzIDAgQzQuNzIzMzA5MDcgNy4wOTMyNjMyMSA1LjI0OTQ3ODMgMTMuNzA5Njg5NzYgNSAyMSBDMi41NjE5ODY2NyAxNy4wNTY3Nzg0NCAyLjEwNTUwODY5IDEyLjc0NDg2NDcxIDEuMzc1IDguMjUgQzEuMTczOTA2MjUgNy4wNjA4Mzk4NCAxLjE3MzkwNjI1IDcuMDYwODM5ODQgMC45Njg3NSA1Ljg0NzY1NjI1IEMwLjY0MDEyNzAyIDMuODk5MzkxNDIgMC4zMTkyOTI5NSAxLjk0OTgxNTYxIDAgMCBaICIgZmlsbD0iIzc2QkI5QSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjc1LDExMikiLz4KPHBhdGggZD0iTTAgMCBDMS41NjI1IDEuMTg3NSAxLjU2MjUgMS4xODc1IDMgMyBDMi44NDk0NjQxOCA2LjAxMDcxNjM5IDIuNDEzMTk5ODcgNy41MjA2ODgxNSAwLjQzNzUgOS44MTI1IEMtMC4wMzY4NzUgMTAuMjA0Mzc1IC0wLjUxMTI1IDEwLjU5NjI1IC0xIDExIEMtMS42NiAxMC42NyAtMi4zMiAxMC4zNCAtMyAxMCBDLTQuMjYyMjk1MDggNS41MjQ1OTAxNiAtNC4yNjIyOTUwOCA1LjUyNDU5MDE2IC0zIDMgQy0yLjAxIDIuNjcgLTEuMDIgMi4zNCAwIDIgQzAgMS4zNCAwIDAuNjggMCAwIFogIiBmaWxsPSIjNkRCQjk3IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyOCw4MikiLz4KPHBhdGggZD0iTTAgMCBDNC41NjQ1NDI1MSAwLjU0MzM5NzkyIDcuOTcyODE3NiAxLjc5NDYzODIxIDEyIDQgQzEyIDQuNjYgMTIgNS4zMiAxMiA2IEMxMS4wMSA2IDEwLjAyIDYgOSA2IEM5LjMzIDYuNjYgOS42NiA3LjMyIDEwIDggQzUuNTY5ODM5MzMgNi42MDc2NjM3OSAzLjA4NDQ5NzM3IDQuNDM3MDExMzUgMCAxIEMwIDAuNjcgMCAwLjM0IDAgMCBaICIgZmlsbD0iIzZEQkI5NyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjA2LDI1KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjY2IDAuOTkgMS4zMiAxLjk4IDIgMyBDMC4zNzE4MzU2IDYuNzU3MzAyNDcgLTIuMDE0MDE5MTcgOS42MTY4MjMgLTQuNjI1IDEyLjc1IEMtNS44NTA4OTg0NCAxNC4yMjcyNjU2MyAtNS44NTA4OTg0NCAxNC4yMjcyNjU2MyAtNy4xMDE1NjI1IDE1LjczNDM3NSBDLTcuNzI4MDQ2ODggMTYuNDgyMDMxMjUgLTguMzU0NTMxMjUgMTcuMjI5Njg3NSAtOSAxOCBDLTkuNjYgMTcuNjcgLTEwLjMyIDE3LjM0IC0xMSAxNyBDLTEwLjU0MTA5Mzc1IDE2LjM1OTMzNTk0IC0xMC4wODIxODc1IDE1LjcxODY3MTg4IC05LjYwOTM3NSAxNS4wNTg1OTM3NSBDLTYuMTMyMzE2NiAxMC4xNTcxMTAxNyAtMi44Njk4ODYwNCA1LjI4MDU5MDMxIDAgMCBaICIgZmlsbD0iI0Q2RjdFOCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjY3LDIxNikiLz4KPHBhdGggZD0iTTAgMCBDMS4wMjgwNDY0MiAzLjE3NzU5ODAzIDAuOTIzMzM2NDQgNS4xNjc5NjIyOSAwLjA2MjUgOC4zNzUgQy0wLjYzMDg3NDMgMTEuMTY2NzQzODkgLTAuOTk1NjM0ODUgMTMuMjE4MjU3NTcgLTAuOTM3NSAxNi4xMjUgQy0xLjAyMTg0MTU1IDIwLjAwNDcxMTE0IC0yLjMyNzA0ODE0IDIyLjUxNDY4MzYzIC00IDI2IEMtNi42MTY4MTk0MiAxOC4xNDk1NDE3MyAtMi4yNzI4NzA5OCA3LjY5NzQ1NjM4IDAgMCBaICIgZmlsbD0iI0IwRTFDQSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjgwLDE3MikiLz4KPHBhdGggZD0iTTAgMCBDMi40NzU2NjY5NiAzLjcxMzUwMDQ0IDIuNzU2MzMxNzQgNy4xMTQyNzI4NCAzLjI1IDExLjQzNzUgQzMuMzUzMTI1IDEyLjIwMTI2OTUzIDMuNDU2MjUgMTIuOTY1MDM5MDYgMy41NjI1IDEzLjc1MTk1MzEyIEMzLjY1MDE1NjI1IDE0LjQ5MjUxOTUzIDMuNzM3ODEyNSAxNS4yMzMwODU5NCAzLjgyODEyNSAxNS45OTYwOTM3NSBDMy45NTA0MjQ4IDE3LjAwNDM0MjA0IDMuOTUwNDI0OCAxNy4wMDQzNDIwNCA0LjA3NTE5NTMxIDE4LjAzMjk1ODk4IEMzLjk4MzIyNjkzIDIwLjQzODc2ODM3IDMuMTkzMDIyOTcgMjEuOTM0Njg3MDQgMiAyNCBDMS40NjkwNTU2OSAyMC4zNzY0MDUyMSAwLjk1MTk2NTEyIDE2Ljc1MDk2ODE0IDAuNDM3NSAxMy4xMjUgQzAuMjEwMzAyNzMgMTEuNTc2MTkxNDEgMC4yMTAzMDI3MyAxMS41NzYxOTE0MSAtMC4wMjE0ODQzOCA5Ljk5NjA5Mzc1IEMtMC4xNjAwNTg1OSA5LjAwOTk2MDk0IC0wLjI5ODYzMjgxIDguMDIzODI4MTIgLTAuNDQxNDA2MjUgNy4wMDc4MTI1IEMtMC41NzIzMjY2NiA2LjA5NjYwNjQ1IC0wLjcwMzI0NzA3IDUuMTg1NDAwMzkgLTAuODM4MTM0NzcgNC4yNDY1ODIwMyBDLTEgMiAtMSAyIDAgMCBaICIgZmlsbD0iIzkyQzFBQiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjg0LDExNCkiLz4KPHBhdGggZD0iTTAgMCBDMCA0LjA3MDYzODU4IC0xLjU4MTI2MDUzIDUuNTcyNzc0NDkgLTQuMTk5MjE4NzUgOC41NDY4NzUgQy02IDEwIC02IDEwIC05Ljg3NSAxMC40Mzc1IEMtMTAuOTA2MjUgMTAuMjkzMTI1IC0xMS45Mzc1IDEwLjE0ODc1IC0xMyAxMCBDLTExLjAyIDcuNjkgLTkuMDQgNS4zOCAtNyAzIEMtNi4zNCAzLjMzIC01LjY4IDMuNjYgLTUgNCBDLTUuOTkgNS40ODUgLTUuOTkgNS40ODUgLTcgNyBDLTQuNjkgNC42OSAtMi4zOCAyLjM4IDAgMCBaICIgZmlsbD0iI0RERjVFQyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjQzLDIzMykiLz4KPHBhdGggZD0iTTAgMCBDMS41NjI1IDEuMjUgMS41NjI1IDEuMjUgMyAzIEMyLjY3NzUxNzQzIDQuMzM1OTk5MjEgMi4zNDIyNzQxNyA1LjY2ODkzMzc4IDIgNyBDMi42NDI1MjM2NSA5LjAwNzg4NjQgMy4zMDc0MTc3MSAxMS4wMDg4MjU5MiA0IDEzIEMzLjY3IDEzLjY2IDMuMzQgMTQuMzIgMyAxNSBDMi4zNCAxNC42NyAxLjY4IDE0LjM0IDEgMTQgQzAuMjg2MzQwOSAxMS42ODA2MDc5MyAtMC4zNzQ3MjkyIDkuMzQ0NzY1NTEgLTEgNyBDLTEuNjQxOTk2NDkgNS4zMjM2NzU4MyAtMi4yOTg5NDE1NyAzLjY1MjQ5NDg3IC0zIDIgQy0yLjAxIDIuMzMgLTEuMDIgMi42NiAwIDMgQzAgMi4wMSAwIDEuMDIgMCAwIFogIiBmaWxsPSIjOEREMUIxIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNjAsNzUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuMzMgMC45OSAwLjY2IDEuOTggMSAzIEMtMi45NiA2Ljk2IC02LjkyIDEwLjkyIC0xMSAxNSBDLTExLjY2IDE0LjY3IC0xMi4zMiAxNC4zNCAtMTMgMTQgQy04LjcxIDkuMzggLTQuNDIgNC43NiAwIDAgWiAiIGZpbGw9IiNFMkY5RUYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEwOSwxMTcpIi8+CjxwYXRoIGQ9Ik0wIDAgQzIuMzE5MjA2MSAzLjQ3ODgwOTE1IDIuMTgzOTkzODYgNC41MTkxMzkzMSAxLjgxMjUgOC41NjI1IEMxLjczNjQ0NTMxIDkuNTMzMTY0MDYgMS42NjAzOTA2MiAxMC41MDM4MjgxMyAxLjU4MjAzMTI1IDExLjUwMzkwNjI1IEMxIDE0IDEgMTQgLTIgMTYgQy0yLjIzOTY5NDMgMTAuMzY3MTg0MDEgLTEuMzg1Mjk4MjEgNS40NTQ2MTE2OSAwIDAgWiAiIGZpbGw9IiNFNEYyRUIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyLDEyMCkiLz4KPHBhdGggZD0iTTAgMCBDMC45OSAwIDEuOTggMCAzIDAgQzIuMDEgMy4zIDEuMDIgNi42IDAgMTAgQy0wLjY2IDEwIC0xLjMyIDEwIC0yIDEwIEMtMi42NiAxMS4zMiAtMy4zMiAxMi42NCAtNCAxNCBDLTMuMDA5NTYyOTUgOS4xNjM3MTY5NSAtMi4wNDMwMTE1NCA0LjUyMzgxMTI3IDAgMCBaICIgZmlsbD0iIzk5Q0NCNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTksOTgpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuNjYgMC42NiAxLjMyIDEuMzIgMiAyIEMxLjY0NDIxOTQgNS4zNzk5MTU3NCAwLjY0Mjg0MjAxIDguMDQyODg0MzggLTEgMTEgQy0xLjk5IDEwLjY3IC0yLjk4IDEwLjM0IC00IDEwIEMtMi44OTA1OTI4IDYuNTEzMjkxNjYgLTEuNzM2NjIzMzQgMy4yMjUxNTc2MyAwIDAgWiAiIGZpbGw9IiM3Q0MzQTMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2OCwyMDMpIi8+CjxwYXRoIGQ9Ik0wIDAgQzYuNDM1IDIuOTcgNi40MzUgMi45NyAxMyA2IEMxMi4wMSA2LjY2IDExLjAyIDcuMzIgMTAgOCBDNiA2IDIgNCAtMiAyIEMtMS4zNCAxLjM0IC0wLjY4IDAuNjggMCAwIFogIiBmaWxsPSIjQkZFNUQzIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5MSwyNjgpIi8+CjxwYXRoIGQ9Ik0wIDAgQy0wLjk5IDAuNDk1IC0wLjk5IDAuNDk1IC0yIDEgQy0xLjM0IDEuNjYgLTAuNjggMi4zMiAwIDMgQy0zLjMgMy42NiAtNi42IDQuMzIgLTEwIDUgQy05LjY3IDMuNjggLTkuMzQgMi4zNiAtOSAxIEMtNS4zOTMxMjc1IC0wLjY0NjYxNTcxIC0zLjgxNDg3NzkgLTEuMjcxNjI1OTcgMCAwIFogIiBmaWxsPSIjRUVGOUY1IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxOTEsMjY5KSIvPgo8cGF0aCBkPSJNMCAwIEMyLjkyNzQ2Mjk4IDIuNDQ1Njg2MjIgMi45Mjc0NjI5OCAyLjQ0NTY4NjIyIDMuMjQ4Nzc5MyA0LjkwMTEyMzA1IEMzLjE5MTE3NDMyIDUuNTk3NjE5NjMgMy4xMzM1NjkzNCA2LjI5NDExNjIxIDMuMDc0MjE4NzUgNy4wMTE3MTg3NSBDMy4wMTk0MzM1OSA3Ljc2OTA0Mjk3IDIuOTY0NjQ4NDQgOC41MjYzNjcxOSAyLjkwODIwMzEyIDkuMzA2NjQwNjIgQzIuODM1MzcxMDkgMTAuMDkyMzI0MjIgMi43NjI1MzkwNiAxMC44NzgwMDc4MSAyLjY4NzUgMTEuNjg3NSBDMi42MjYyNjk1MyAxMi40ODQ3ODUxNiAyLjU2NTAzOTA2IDEzLjI4MjA3MDMxIDIuNTAxOTUzMTIgMTQuMTAzNTE1NjIgQzIuMzQ4ODYwMzEgMTYuMDcwMTY5NTMgMi4xNzYyOTI5MSAxOC4wMzUyODk4OSAyIDIwIEMxLjY3IDIwIDEuMzQgMjAgMSAyMCBDMC4yMDIxNjMgMTMuMzAzODY4MDQgLTAuMTI5MTA4NTMgNi43NDIzMzQxOSAwIDAgWiAiIGZpbGw9IiNBRkU1Q0IiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI3NiwxMzEpIi8+CjxwYXRoIGQ9Ik0wIDAgQy0wLjMzIDMuOTYgLTAuNjYgNy45MiAtMSAxMiBDLTEuMzMgMTIgLTEuNjYgMTIgLTIgMTIgQy0yLjMzIDEwLjAyIC0yLjY2IDguMDQgLTMgNiBDLTQuOTggNi45OSAtNC45OCA2Ljk5IC03IDggQy0yLjQ1OTQ1OTQ2IDAgLTIuNDU5NDU5NDYgMCAwIDAgWiAiIGZpbGw9IiM4MEJDQTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2OSwyMjEpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuOTkgMCAxLjk4IDAgMyAwIEMyLjY4NzUgMS44NzUgMi42ODc1IDEuODc1IDIgNCBDMS4wMSA0LjY2IDAuMDIgNS4zMiAtMSA2IEMtMS4zMyA2Ljk5IC0xLjY2IDcuOTggLTIgOSBDLTIuNjYgOSAtMy4zMiA5IC00IDkgQy0zLjQ3NTU3NTc4IDUuMjI0MTQ1NiAtMi42NzM0NTMzMyAyLjc3NjI3ODQ1IDAgMCBaICIgZmlsbD0iIzY5QkQ5NSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjYxLDIwNSkiLz4KPHBhdGggZD0iTTAgMCBDMC4zMyAwLjY2IDAuNjYgMS4zMiAxIDIgQy0xLjYzODg2NjU0IDYuMjAyNjM5MyAtNC4zNjQ0MDMzOSA5LjU2NjM4MDk4IC04IDEzIEMtOC45OSAxMi42NyAtOS45OCAxMi4zNCAtMTEgMTIgQy0xMC40NTczMDQ2OSAxMS40Njg5MDYyNSAtOS45MTQ2MDkzNyAxMC45Mzc4MTI1IC05LjM1NTQ2ODc1IDEwLjM5MDYyNSBDLTguNjQwMDM5MDYgOS42ODQyMTg3NSAtNy45MjQ2MDkzNyA4Ljk3NzgxMjUgLTcuMTg3NSA4LjI1IEMtNi40Nzk4MDQ2OSA3LjU1MzkwNjI1IC01Ljc3MjEwOTM4IDYuODU3ODEyNSAtNS4wNDI5Njg3NSA2LjE0MDYyNSBDLTMuMTc3MjQxMjEgNC4xODU3MTM1NSAtMS41NjgzMDM1OCAyLjE5NjYxNDQ3IDAgMCBaICIgZmlsbD0iI0UyRjlFRSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTg4LDExNykiLz4KPHBhdGggZD0iTTAgMCBDMC42NiAwLjY2IDEuMzIgMS4zMiAyIDIgQzAuOTI2NDMzMjMgMy4xNTQ5ODg5NyAtMC4xNTkyOTc4OCA0LjI5ODY3ODY3IC0xLjI1IDUuNDM3NSBDLTEuODUzMjgxMjUgNi4wNzU1ODU5NCAtMi40NTY1NjI1IDYuNzEzNjcxODcgLTMuMDc4MTI1IDcuMzcxMDkzNzUgQy01LjEwMDQ4MDE2IDkuMDg1MTYzMDYgLTYuNDEyNzA2MDUgOS41OTM1ODg3OCAtOSAxMCBDLTguMzQgOS4wMSAtNy42OCA4LjAyIC03IDcgQy02LjM0IDcgLTUuNjggNyAtNSA3IEMtNC43MzE4NzUgNi40MDE4NzUgLTQuNDYzNzUgNS44MDM3NSAtNC4xODc1IDUuMTg3NSBDLTMuMDE1MDY1MzMgMy4wMjc3NTE5MiAtMS44MDYzMDcxOCAxLjY0MjA5NzQ0IDAgMCBaICIgZmlsbD0iI0U1RjhFRiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjU5LDI0NSkiLz4KPHBhdGggZD0iTTAgMCBDMC45OSAwLjY2IDEuOTggMS4zMiAzIDIgQzIuMDEgNC45NyAxLjAyIDcuOTQgMCAxMSBDLTAuOTkgMTAuNjcgLTEuOTggMTAuMzQgLTMgMTAgQy0yLjAxIDYuNyAtMS4wMiAzLjQgMCAwIFogIiBmaWxsPSIjQ0RFRURFIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNjksMTkyKSIvPgo8cGF0aCBkPSJNMCAwIEMxLjMyIDAuNjYgMi42NCAxLjMyIDQgMiBDMi42MDkyMjY5OSAyLjgyMTEzMTc3IDEuMjEyMTExMDcgMy42MzE1MjQ5NSAtMC4xODc1IDQuNDM3NSBDLTAuOTY0ODA0NjkgNC44ODk5NjA5NCAtMS43NDIxMDkzNyA1LjM0MjQyMTg3IC0yLjU0Mjk2ODc1IDUuODA4NTkzNzUgQy01LjA0OTMxOTc3IDcuMDIzOTE0OTkgLTcuMjU3OTAzNjUgNy41NzYyNjU3NSAtMTAgOCBDLTcuMDk0OTI2NzkgNS41NzkxMDU2NiAtNC4zNjg1Mjk1NyAzLjY4NDI2NDc4IC0xIDIgQy0wLjY3IDEuMzQgLTAuMzQgMC42OCAwIDAgWiAiIGZpbGw9IiM3OUMzQTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDkzLDI5KSIvPgo8cGF0aCBkPSJNMCAwIEMwIDMgMCAzIC0xLjY0NDUzMTI1IDQuODI0MjE4NzUgQy0yLjcxNzY3NTc4IDUuNzQ2NTQyOTcgLTIuNzE3Njc1NzggNS43NDY1NDI5NyAtMy44MTI1IDYuNjg3NSBDLTQuNTIwMTk1MzEgNy4zMTAxMTcxOSAtNS4yMjc4OTA2MiA3LjkzMjczNDM3IC01Ljk1NzAzMTI1IDguNTc0MjE4NzUgQy04IDEwIC04IDEwIC0xMSAxMCBDLTkuNzM2NTc1MTIgOC41MTQ1MDA0NCAtOC40NjM5ODk3IDcuMDM2Nzg4MTEgLTcuMTg3NSA1LjU2MjUgQy02LjQ3OTgwNDY5IDQuNzM4Nzg5MDYgLTUuNzcyMTA5MzggMy45MTUwNzgxMyAtNS4wNDI5Njg3NSAzLjA2NjQwNjI1IEMtMyAxIC0zIDEgMCAwIFogIiBmaWxsPSIjQTZDREJCIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNDUsMjMwKSIvPgo8cGF0aCBkPSJNMCAwIEMzLjI1NTk3NzU4IDAuMjQxMTgzNTIgNC43Mjc2MzA0IDEuNTIyMjQyMTIgNi44MTI1IDMuODc1IEM3LjUzNDM3NSA0LjkwNjI1IDguMjU2MjUgNS45Mzc1IDkgNyBDOC42NyA3Ljk5IDguMzQgOC45OCA4IDEwIEM2LjY2MzcwMjU0IDguNTIzNTEwNjIgNS4zMzEwMzMyOSA3LjA0MzczNjcgNCA1LjU2MjUgQzMuMjU3NSA0LjczODc4OTA2IDIuNTE1IDMuOTE1MDc4MTMgMS43NSAzLjA2NjQwNjI1IEMwIDEgMCAxIDAgMCBaICIgZmlsbD0iI0MyRTZENiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjUyLDUzKSIvPgo8cGF0aCBkPSJNMCAwIEMtMS4yOTI5OTY5NCAyLjg4NDM3Nzc5IC0yLjY1OTQxMzYzIDQuODcyMTk0MjEgLTUgNyBDLTUuNjYgNi42NyAtNi4zMiA2LjM0IC03IDYgQy00Ljg0NjE1Mzg1IDAgLTQuODQ2MTUzODUgMCAwIDAgWiAiIGZpbGw9IiM2REI4OTciIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE3MiwxMzQpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuOTkgMC42NiAxLjk4IDEuMzIgMyAyIEMyLjY3IDIuNjYgMi4zNCAzLjMyIDIgNCBDMS4wMSA0IDAuMDIgNCAtMSA0IEMtMSA0LjY2IC0xIDUuMzIgLTEgNiBDLTMuNDc1IDUuMDEgLTMuNDc1IDUuMDEgLTYgNCBDLTUuMDEgMy42NyAtNC4wMiAzLjM0IC0zIDMgQy0zLjY2IDIuMzQgLTQuMzIgMS42OCAtNSAxIEMtMy4zNSAxLjMzIC0xLjcgMS42NiAwIDIgQzAgMS4zNCAwIDAuNjggMCAwIFogIiBmaWxsPSIjODlEMUFFIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMTgsMjkpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuOTkgMC42NiAxLjk4IDEuMzIgMyAyIEMtMS4wMTg4NTE1IDMuODEyNDIzMjMgLTQuNjExMTA2NzUgNS4zMzUwMTYxNyAtOSA2IEMtOC42NyA0LjY4IC04LjM0IDMuMzYgLTggMiBDLTYuODY1NjI1IDEuODU1NjI1IC01LjczMTI1IDEuNzExMjUgLTQuNTYyNSAxLjU2MjUgQy0xLjE5MjIyNzk5IDEuMzg4Mjg1MjIgLTEuMTkyMjI3OTkgMS4zODgyODUyMiAwIDAgWiAiIGZpbGw9IiM3Q0JEOUYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIzMCwyNjYpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAgMy45MDgzODg2NiAtMS41MDU2Nzg3NiA1LjExNTk0MTA3IC00IDggQy01LjMyIDcuNjcgLTYuNjQgNy4zNCAtOCA3IEMtNS40MjIxNTE1NCA0LjM1MjQ3OTk2IC0zLjA4MzUxOTAyIDIuMDU1Njc5MzUgMCAwIFogIiBmaWxsPSIjOTJEM0I0IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNTYsMjI5KSIvPgo8cGF0aCBkPSJNMCAwIEMyIDEuODEyNSAyIDEuODEyNSA0IDQgQzQgNC45OSA0IDUuOTggNCA3IEMxLjM2IDcgLTEuMjggNyAtNCA3IEMtMy4zNCA2LjU4NzUgLTIuNjggNi4xNzUgLTIgNS43NSBDLTEuMzQgNS4xNzI1IC0wLjY4IDQuNTk1IDAgNCBDMCAyLjY4IDAgMS4zNiAwIDAgWiAiIGZpbGw9IiNDRkYwRTEiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDk1LDE3MCkiLz4KPHBhdGggZD0iTTAgMCBDMi4yOTAxMzY1MyAzLjQzNTIwNDggMi4xNzg0Mjk3MyA0Ljk4NTMzMSAyIDkgQzEuMDEgOC42NyAwLjAyIDguMzQgLTEgOCBDLTEuMzMgNy4zNCAtMS42NiA2LjY4IC0yIDYgQy0yLjY2IDUuNjcgLTMuMzIgNS4zNCAtNCA1IEMtMi42OCAzLjM1IC0xLjM2IDEuNyAwIDAgWiAiIGZpbGw9IiM3QkJGQTAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU4LDE2OCkiLz4KPHBhdGggZD0iTTAgMCBDMS42NSAwLjMzIDMuMyAwLjY2IDUgMSBDNSAxLjY2IDUgMi4zMiA1IDMgQzQuMDEgMyAzLjAyIDMgMiAzIEMyIDMuNjYgMiA0LjMyIDIgNSBDMC42OCA1IC0wLjY0IDUgLTIgNSBDLTEuMzQgMy4zNSAtMC42OCAxLjcgMCAwIFogIiBmaWxsPSIjNjNCRDkyIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg2MSw0MikiLz4KPHBhdGggZD0iTTAgMCBDMS4zMiAwIDIuNjQgMCA0IDAgQzMuNjcgMi45NyAzLjM0IDUuOTQgMyA5IEMyLjY3IDkgMi4zNCA5IDIgOSBDMiA3LjM1IDIgNS43IDIgNCBDMS4zNCA0IDAuNjggNCAwIDQgQzAgMi42OCAwIDEuMzYgMCAwIFogIiBmaWxsPSIjOEFDMUFBIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNTQsMTYzKSIvPgo8cGF0aCBkPSJNMCAwIEMxLjk4IDAuOTkgMS45OCAwLjk5IDQgMiBDNC4zMyAxLjM0IDQuNjYgMC42OCA1IDAgQzUuNjYgMS4zMiA2LjMyIDIuNjQgNyA0IEM2LjAxIDQuNDk1IDYuMDEgNC40OTUgNSA1IEM1IDUuOTkgNSA2Ljk4IDUgOCBDMCAyLjI1IDAgMi4yNSAwIDAgWiAiIGZpbGw9IiM4MkM5QTciIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI0OCw2NCkiLz4KPHBhdGggZD0iTTAgMCBDMS44NzUgMC4yNSAxLjg3NSAwLjI1IDQgMSBDNS42NDg3MjUwNyAzLjUxNjQ3NTEgNi4zNzMxMjcwNiA2LjA3NDU5Mjk2IDcgOSBDMy4yMDU1MTg3NSA2LjY0NDgwNDc0IDEuNzk0MjE2MzEgNC4wMzY5ODY3IDAgMCBaICIgZmlsbD0iIzdDQzNBMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjMyLDQ1KSIvPgo8cGF0aCBkPSJNMCAwIEM0LjgxNDMwNzIxIC0wLjU2NjM4OTA4IDcuMjAzMDc4MTQgMS4xNzQzODM3MyAxMSA0IEM5LjQzNzUgNC43NSA5LjQzNzUgNC43NSA3IDUgQzQuNDk3MTM1OTEgMy44NjkxNTU2MyAyLjMyOTkyMjc4IDIuNDc4NjA0ODQgMCAxIEMwIDAuNjcgMCAwLjM0IDAgMCBaICIgZmlsbD0iI0Q2RjBFNSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoODAsMjY0KSIvPgo8cGF0aCBkPSJNMCAwIEMyLjY0IDAgNS4yOCAwIDggMCBDNy4wMSAxLjQ4NSA3LjAxIDEuNDg1IDYgMyBDMi44NzUgMy4xODc1IDIuODc1IDMuMTg3NSAwIDMgQzAgMi4wMSAwIDEuMDIgMCAwIFogIiBmaWxsPSIjNzFCNjk2IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5NSwxMTIpIi8+CjxwYXRoIGQ9Ik0wIDAgQzEuMzIgMC42NiAyLjY0IDEuMzIgNCAyIEMzLjM0IDIgMi42OCAyIDIgMiBDMi4zMyAyLjk5IDIuNjYgMy45OCAzIDUgQzEuMzUgNSAtMC4zIDUgLTIgNSBDLTIgNC4zNCAtMiAzLjY4IC0yIDMgQy0zLjMyIDIuMzQgLTQuNjQgMS42OCAtNiAxIEMtMy4wMyAxLjQ5NSAtMy4wMyAxLjQ5NSAwIDIgQzAgMS4zNCAwIDAuNjggMCAwIFogIiBmaWxsPSIjODVDRkFCIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxOTgsMjEpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAgMC4zMyAwIDAuNjYgMCAxIEMtNS4wMjEyNTkyNiAxLjc3OTE2MDkyIC04Ljk3NzAzOTk2IDIuMTMwMTY2MDEgLTE0IDEgQy0xNCAwLjY3IC0xNCAwLjM0IC0xNCAwIEMtOS4yMDY5OTg2NyAtMS4zOTE1MTY1MiAtNC44NDg2NDIxOCAtMC42ODY2MjY4OSAwIDAgWiAiIGZpbGw9IiNFOUY5RjQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEzNSwyODQpIi8+CjxwYXRoIGQ9Ik0wIDAgQzMuNDY1IDEuNDg1IDMuNDY1IDEuNDg1IDcgMyBDNyA0LjY1IDcgNi4zIDcgOCBDNS44Mjc0ODY5IDcuMDQ4ODI4NjYgNC42NjIxMjk4NSA2LjA4ODgyOTkxIDMuNSA1LjEyNSBDMi44NTAzMTI1IDQuNTkxMzI4MTIgMi4yMDA2MjUgNC4wNTc2NTYyNSAxLjUzMTI1IDMuNTA3ODEyNSBDMS4wMjU5Mzc1IDMuMDEwMjM0MzggMC41MjA2MjUgMi41MTI2NTYyNSAwIDIgQzAgMS4zNCAwIDAuNjggMCAwIFogIiBmaWxsPSIjQ0ZERkQ4IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0MSwyMzcpIi8+CjxwYXRoIGQ9Ik0wIDAgQy0wLjMzIDEuMzIgLTAuNjYgMi42NCAtMSA0IEMtMi4zMiA0LjMzIC0zLjY0IDQuNjYgLTUgNSBDLTQuNjI1IDMuMDYyNSAtNC42MjUgMy4wNjI1IC00IDEgQy0yIDAgLTIgMCAwIDAgWiAiIGZpbGw9IiM2OUI5OTYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDk2LDEyOSkiLz4KPHBhdGggZD0iTTAgMCBDMC4zMyAwIDAuNjYgMCAxIDAgQzEuMDgxMzE0NjMgMS40MzY1NTg1NCAxLjEzOTMzNTU5IDIuODc0NDQ4MyAxLjE4NzUgNC4zMTI1IEMxLjIzOTcwNzAzIDUuNTEzMjYxNzIgMS4yMzk3MDcwMyA1LjUxMzI2MTcyIDEuMjkyOTY4NzUgNi43MzgyODEyNSBDMC45MzgwMTYwOCA5LjQ3ODUxNTg0IDAuMTA2MzM3MTggMTAuMjkxNzEwNzkgLTIgMTIgQy0xIDMuNDI4NTcxNDMgLTEgMy40Mjg1NzE0MyAwIDAgWiAiIGZpbGw9IiM4NkMzQTciIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2LDExNCkiLz4KPHBhdGggZD0iTTAgMCBDMC45OSAwIDEuOTggMCAzIDAgQzIuMzkwMDkxMDkgMy41NTc4MDE5NiAxLjQyMTYzMTkgNi42ODI4NTg5IDAgMTAgQy0xLjIzMTM5NCA2LjMwNTgxNzk5IC0wLjY0NDMyNzY0IDMuNzk0MzczOSAwIDAgWiAiIGZpbGw9IiM3NkJCOUMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDM4LDk2KSIvPgo8cGF0aCBkPSJNMCAwIEMwLjk5IDAgMS45OCAwIDMgMCBDMy42NiAxLjMyIDQuMzIgMi42NCA1IDQgQzQuMDEgNC4zMyAzLjAyIDQuNjYgMiA1IEMxLjAxIDQuMDEgMC4wMiAzLjAyIC0xIDIgQy0wLjY3IDEuMzQgLTAuMzQgMC42OCAwIDAgWiAiIGZpbGw9IiM2NUJCOTMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDc4LDI1NikiLz4KPHBhdGggZD0iTTAgMCBDMC4zMyAwLjk5IDAuNjYgMS45OCAxIDMgQzEuNjYgMyAyLjMyIDMgMyAzIEMzIDQuOTggMyA2Ljk2IDMgOSBDMS42OCA4LjM0IDAuMzYgNy42OCAtMSA3IEMtMC42NyA0LjY5IC0wLjM0IDIuMzggMCAwIFogIiBmaWxsPSIjRTlGOEYyIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMywxNDcpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuNjYgMCAxLjMyIDAgMiAwIEMxLjY3IDEuOTggMS4zNCAzLjk2IDEgNiBDMS42NiA2LjMzIDIuMzIgNi42NiAzIDcgQzIuMDEgNy40OTUgMi4wMSA3LjQ5NSAxIDggQy0xLjEyNSA3LjA2MjUgLTEuMTI1IDcuMDYyNSAtMyA2IEMtMi41MDUgNS41NjY4NzUgLTIuMDEgNS4xMzM3NSAtMS41IDQuNjg3NSBDMC4zMzIxODE5MiAzLjAwMjU5NTE3IDAuMzMyMTgxOTIgMy4wMDI1OTUxNyAwIDAgWiAiIGZpbGw9IiM3QkJFOUUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDc2LDE0NSkiLz4KPHBhdGggZD0iTTAgMCBDMS43NjQ2MjY4MyAzLjA4ODA5Njk1IDIgNC4yMzMxMjEzNiAyIDggQzEuMzQgOCAwLjY4IDggMCA4IEMtMC4zODA2MzIyMSA1LjY3MzkxNDMgLTAuNzEyNjk5NDUgMy4zMzk0NDczNiAtMSAxIEMtMC42NyAwLjY3IC0wLjM0IDAuMzQgMCAwIFogIiBmaWxsPSIjNjRCQzkyIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNTYsNzgpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuMzMgMC45OSAwLjY2IDEuOTggMSAzIEMtMC40MDQ0NTgzNSA1LjI2MzY1NjQgLTIuMTk2NzkxMyA3LjAyMDg2ODUgLTQgOSBDLTQuNjYgOC42NyAtNS4zMiA4LjM0IC02IDggQy00LjAyIDUuMzYgLTIuMDQgMi43MiAwIDAgWiAiIGZpbGw9IiM3QkJCOUUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU2LDYxKSIvPgo8cGF0aCBkPSJNMCAwIEMwLjY2IDAuMzMgMS4zMiAwLjY2IDIgMSBDMS40NDg1NTA4OSA1LjA4MDcyMzQyIDEuMDg2NjkyMDQgNS45MTMzMDc5NiAtMiA5IEMtMS4zNCA2LjAzIC0wLjY4IDMuMDYgMCAwIFogIiBmaWxsPSIjODVDNUE4IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNjAsMjA5KSIvPgo8cGF0aCBkPSJNMCAwIEMyLjMxIDAgNC42MiAwIDcgMCBDNS4zNSAxLjY1IDMuNyAzLjMgMiA1IEMxLjM0IDQuMzQgMC42OCAzLjY4IDAgMyBDMCAyLjAxIDAgMS4wMiAwIDAgWiAiIGZpbGw9IiNEQUY1RTciIHRyYW5zZm9ybT0idHJhbnNsYXRlKDE0NSwxMTUpIi8+CjxwYXRoIGQ9Ik0wIDAgQzAuNjYgMC45OSAxLjMyIDEuOTggMiAzIEMwLjYyNSA2LjY4NzUgMC42MjUgNi42ODc1IC0xIDEwIEMtMS4zMyAxMCAtMS42NiAxMCAtMiAxMCBDLTEuMTI1IDIuMjUgLTEuMTI1IDIuMjUgMCAwIFogIiBmaWxsPSIjOTVDRkI0IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNCw5NikiLz4KPHBhdGggZD0iTTAgMCBDMyAzLjc1IDMgMy43NSAzIDYgQzIuMDEgNi4zMyAxLjAyIDYuNjYgMCA3IEMtMC42NiA1LjM1IC0xLjMyIDMuNyAtMiAyIEMtMS4zNCAyIC0wLjY4IDIgMCAyIEMwIDEuMzQgMCAwLjY4IDAgMCBaICIgZmlsbD0iIzhDQzNBOSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjcyLDgwKSIvPgo8cGF0aCBkPSJNMCAwIEMzLjU5MzkxMTE0IDEuMzM0ODgxMjggNi43NTQ5MDY5NiAyLjk2MDIyNzIzIDEwIDUgQzkuMzQgNS42NiA4LjY4IDYuMzIgOCA3IEM2LjY2MzgyNjQ4IDYuMDI0NzEyNjMgNS4zMzExMjQ5IDUuMDQ0NjY2ODEgNCA0LjA2MjUgQzMuMjU3NSAzLjUxNzIyNjU2IDIuNTE1IDIuOTcxOTUzMTIgMS43NSAyLjQxMDE1NjI1IEMxLjE3MjUgMS45NDQ4MDQ2OSAwLjU5NSAxLjQ3OTQ1MzEyIDAgMSBDMCAwLjY3IDAgMC4zNCAwIDAgWiAiIGZpbGw9IiNDM0Y0REUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIxOSwzMykiLz4KPC9zdmc+Cg==`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/js/svg/zoho.js b/frontend/crawlab-ui/src/assets/js/svg/zoho.js
new file mode 100644
index 00000000..b512965a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/js/svg/zoho.js
@@ -0,0 +1 @@
+module.exports = `data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzIxNTU4NTAwNjg0IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjUyNzUiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTEyOCAwaDc2OGExMjggMTI4IDAgMCAxIDEyOCAxMjh2NzY4YTEyOCAxMjggMCAwIDEtMTI4IDEyOEgxMjhhMTI4IDEyOCAwIDAgMS0xMjgtMTI4VjEyOGExMjggMTI4IDAgMCAxIDEyOC0xMjh6IiBmaWxsPSIjRDc0ODM0IiBwLWlkPSI1Mjc2Ij48L3BhdGg+PHBhdGggZD0iTTU4NC4wNjQgMzExLjA4MjY2N2wtMTkxLjc0NC0wLjcyNTMzNGMtNTMuMzMzMzMzIDAtODMuMi0xNi4yNTYtODkuNDcyLTQ4Ljg1MzMzMy05LjQ3Mi00OC44OTYgMTUuNDg4LTY1LjY2NCA0Mi41ODEzMzMtNzEuMzgxMzMzbDQuMjY2NjY3LTAuNjgyNjY3IDkuODEzMzMzLTAuODUzMzMzIDguNzg5MzM0LTAuNDY5MzM0IDI5Ljc4MTMzMy0wLjg1MzMzMyA2MC41ODY2NjctMC4yMTMzMzMgODIuNjAyNjY2IDAuNzY4IDg4LjMyIDEuNDUwNjY2YzYwLjE2LTAuMTcwNjY3IDkwLjcwOTMzMyAyMi4xODY2NjcgOTEuNjkwNjY3IDY3LjExNDY2NyAwLjk4MTMzMyA0NC45MjgtMTMuMDU2IDk2LjM0MTMzMy00Mi4xMTIgMTU0LjM2OC00OS40OTMzMzMgNjEuNDQtOTUuMTA0IDExMC43NjI2NjctMTM2LjkxNzMzMyAxNDcuOTY4bC0xMS4zMDY2NjcgOS44MTMzMzNjLTY3LjI4NTMzMyA1Ny4zNDQtMTA4LjYyOTMzMyAxMTAuMDgtMTEzLjM2NTMzMyAxMzYuOTZsMTI4LjE3MDY2NiAyLjAwNTMzNCA5NS42NTg2NjcgMi4wMDUzMzNjNTYuNzQ2NjY3IDIuNDMyIDg0LjY1MDY2NyAyMy41OTQ2NjcgODMuNjY5MzMzIDYzLjQ4OC0wLjk4MTMzMyAzOS44NTA2NjctMzAuNzIgNTkuODE4NjY3LTg5LjE3MzMzMyA1OS45MDRsLTI3OC4zNTczMzMtMi45MDEzMzNjLTU1LjUwOTMzMy0zLjE1NzMzMy04My41ODQtMjUuMjU4NjY3LTg0LjE4MTMzNC02Ni4yNjEzMzQtMC45Mzg2NjctNjEuNDgyNjY3IDMyLjE3MDY2Ny0xNDguMDEwNjY3IDg1Ljg4OC0yMDMuMzQ5MzMzbDE0LjMzNi0xNC41MDY2NjcgMjQuMTkyLTIzLjYzNzMzMyAzNS4yLTMzLjE1MiAyNi43MDkzMzQtMjQuNTc2IDI1LjQyOTMzMy0yMy4wNCAyOS40NC0yNi4xOTczMzMgMjQuOTE3MzMzLTIxLjU4OTMzNGMxNy40NTA2NjctMTQuNTkyIDMyLjI5ODY2Ny00Mi4xNTQ2NjcgNDQuNTg2NjY3LTgyLjYwMjY2NnoiIGZpbGw9IiNGRkZGRkYiIHAtaWQ9IjUyNzciPjwvcGF0aD48L3N2Zz4=`
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/LICENSE.md b/frontend/crawlab-ui/src/assets/lexical/images/icons/LICENSE.md
new file mode 100644
index 00000000..ce74f6ab
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/LICENSE.md
@@ -0,0 +1,5 @@
+Bootstrap Icons
+https://icons.getbootstrap.com
+
+Licensed under MIT license
+https://github.com/twbs/icons/blob/main/LICENSE.md
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/arrow-clockwise.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/arrow-clockwise.svg
new file mode 100644
index 00000000..b072eb09
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/arrow-clockwise.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/arrow-counterclockwise.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/arrow-counterclockwise.svg
new file mode 100644
index 00000000..b0b23b9b
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/arrow-counterclockwise.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/chat-square-quote.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/chat-square-quote.svg
new file mode 100644
index 00000000..40893f49
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/chat-square-quote.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/chevron-down.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/chevron-down.svg
new file mode 100644
index 00000000..1f0b8bc7
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/chevron-down.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/code.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/code.svg
new file mode 100644
index 00000000..079f5c67
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/code.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/image.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/image.svg
new file mode 100644
index 00000000..579f7b8b
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/image.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/journal-code.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/journal-code.svg
new file mode 100644
index 00000000..82098b9c
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/journal-code.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/journal-text.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/journal-text.svg
new file mode 100644
index 00000000..9b66f43a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/journal-text.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/justify.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/justify.svg
new file mode 100644
index 00000000..009bd721
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/justify.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/link.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/link.svg
new file mode 100644
index 00000000..df35bc8a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/link.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/list-ol.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/list-ol.svg
new file mode 100644
index 00000000..5782568d
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/list-ol.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/list-ul.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/list-ul.svg
new file mode 100644
index 00000000..217d1539
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/list-ul.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/pencil-fill.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/pencil-fill.svg
new file mode 100644
index 00000000..59d2830c
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/pencil-fill.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/plus.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/plus.svg
new file mode 100644
index 00000000..eb15d6ba
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/plus.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/table.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/table.svg
new file mode 100644
index 00000000..667ede67
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/table.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/text-center.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/text-center.svg
new file mode 100644
index 00000000..2887a99f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/text-center.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/text-left.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/text-left.svg
new file mode 100644
index 00000000..04526116
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/text-left.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/text-paragraph.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/text-paragraph.svg
new file mode 100644
index 00000000..9779beab
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/text-paragraph.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/text-right.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/text-right.svg
new file mode 100644
index 00000000..34686b0f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/text-right.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/type-bold.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-bold.svg
new file mode 100644
index 00000000..276d133c
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-bold.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/type-h1.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-h1.svg
new file mode 100644
index 00000000..4c891817
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-h1.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/type-h2.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-h2.svg
new file mode 100644
index 00000000..b6ab7650
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-h2.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/type-h3.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-h3.svg
new file mode 100644
index 00000000..154c293f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-h3.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/type-italic.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-italic.svg
new file mode 100644
index 00000000..3ac6b09f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-italic.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/type-strikethrough.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-strikethrough.svg
new file mode 100644
index 00000000..1c940e42
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-strikethrough.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/icons/type-underline.svg b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-underline.svg
new file mode 100644
index 00000000..c299b8bf
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/icons/type-underline.svg
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/lexical/images/image-broken.svg b/frontend/crawlab-ui/src/assets/lexical/images/image-broken.svg
new file mode 100644
index 00000000..bb4c600a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/lexical/images/image-broken.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/logo.svg b/frontend/crawlab-ui/src/assets/logo.svg
new file mode 100644
index 00000000..c52872b4
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/logo.svg
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/163.svg b/frontend/crawlab-ui/src/assets/svg/icons/163.svg
new file mode 100644
index 00000000..75a0fcbf
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/163.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/anthropic.svg b/frontend/crawlab-ui/src/assets/svg/icons/anthropic.svg
new file mode 100644
index 00000000..5b81844c
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/anthropic.svg
@@ -0,0 +1 @@
+Anthropic
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/aol.svg b/frontend/crawlab-ui/src/assets/svg/icons/aol.svg
new file mode 100644
index 00000000..eff387e8
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/aol.svg
@@ -0,0 +1,57 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/azure.svg b/frontend/crawlab-ui/src/assets/svg/icons/azure.svg
new file mode 100644
index 00000000..ed50209c
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/azure.svg
@@ -0,0 +1 @@
+Azure
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/azure/devops.svg b/frontend/crawlab-ui/src/assets/svg/icons/azure/devops.svg
new file mode 100644
index 00000000..3d4a462f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/azure/devops.svg
@@ -0,0 +1 @@
+Icon-devops-261
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/cassandra.svg b/frontend/crawlab-ui/src/assets/svg/icons/cassandra.svg
new file mode 100644
index 00000000..743f4709
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/cassandra.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/cheerio.svg b/frontend/crawlab-ui/src/assets/svg/icons/cheerio.svg
new file mode 100644
index 00000000..72212d66
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/cheerio.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/chromium.svg b/frontend/crawlab-ui/src/assets/svg/icons/chromium.svg
new file mode 100644
index 00000000..5eb9f6ad
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/chromium.svg
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/clickhouse.svg b/frontend/crawlab-ui/src/assets/svg/icons/clickhouse.svg
new file mode 100644
index 00000000..f2144b5d
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/clickhouse.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/colly.svg b/frontend/crawlab-ui/src/assets/svg/icons/colly.svg
new file mode 100644
index 00000000..f89aea5f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/colly.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/crawlee.svg b/frontend/crawlab-ui/src/assets/svg/icons/crawlee.svg
new file mode 100644
index 00000000..d34fe6a1
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/crawlee.svg
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/db2.svg b/frontend/crawlab-ui/src/assets/svg/icons/db2.svg
new file mode 100644
index 00000000..41c158bf
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/db2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/deepseek.svg b/frontend/crawlab-ui/src/assets/svg/icons/deepseek.svg
new file mode 100644
index 00000000..3fc23024
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/deepseek.svg
@@ -0,0 +1 @@
+DeepSeek
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/dingtalk.svg b/frontend/crawlab-ui/src/assets/svg/icons/dingtalk.svg
new file mode 100644
index 00000000..d14c9273
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/dingtalk.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/drission-page.svg b/frontend/crawlab-ui/src/assets/svg/icons/drission-page.svg
new file mode 100644
index 00000000..6ca7090e
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/drission-page.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/elasticsearch.svg b/frontend/crawlab-ui/src/assets/svg/icons/elasticsearch.svg
new file mode 100644
index 00000000..9fc689ef
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/elasticsearch.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/gemini.svg b/frontend/crawlab-ui/src/assets/svg/icons/gemini.svg
new file mode 100644
index 00000000..878eb627
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/gemini.svg
@@ -0,0 +1 @@
+Gemini
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/gmail.svg b/frontend/crawlab-ui/src/assets/svg/icons/gmail.svg
new file mode 100644
index 00000000..40b7175c
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/gmail.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/go.svg b/frontend/crawlab-ui/src/assets/svg/icons/go.svg
new file mode 100644
index 00000000..c60a234a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/go.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/grok.svg b/frontend/crawlab-ui/src/assets/svg/icons/grok.svg
new file mode 100644
index 00000000..efb1a618
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/grok.svg
@@ -0,0 +1 @@
+Grok
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/hive.svg b/frontend/crawlab-ui/src/assets/svg/icons/hive.svg
new file mode 100644
index 00000000..e68b9094
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/hive.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/kafka.svg b/frontend/crawlab-ui/src/assets/svg/icons/kafka.svg
new file mode 100644
index 00000000..403ed158
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/kafka.svg
@@ -0,0 +1,6 @@
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/lark.svg b/frontend/crawlab-ui/src/assets/svg/icons/lark.svg
new file mode 100644
index 00000000..b07867c7
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/lark.svg
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/maven.svg b/frontend/crawlab-ui/src/assets/svg/icons/maven.svg
new file mode 100644
index 00000000..6a5e1b02
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/maven.svg
@@ -0,0 +1,2 @@
+
+file_type_maven
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/mistral.svg b/frontend/crawlab-ui/src/assets/svg/icons/mistral.svg
new file mode 100644
index 00000000..8e03e244
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/mistral.svg
@@ -0,0 +1 @@
+Mistral
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/mongodb.svg b/frontend/crawlab-ui/src/assets/svg/icons/mongodb.svg
new file mode 100644
index 00000000..8ca38b61
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/mongodb.svg
@@ -0,0 +1,6 @@
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/ms_teams.svg b/frontend/crawlab-ui/src/assets/svg/icons/ms_teams.svg
new file mode 100644
index 00000000..3409e6cf
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/ms_teams.svg
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/mssql.svg b/frontend/crawlab-ui/src/assets/svg/icons/mssql.svg
new file mode 100644
index 00000000..7a137dfb
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/mssql.svg
@@ -0,0 +1,12 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/mysql.svg b/frontend/crawlab-ui/src/assets/svg/icons/mysql.svg
new file mode 100644
index 00000000..44fd4dbf
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/mysql.svg
@@ -0,0 +1,6 @@
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/netease.svg b/frontend/crawlab-ui/src/assets/svg/icons/netease.svg
new file mode 100644
index 00000000..fcdea27f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/netease.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/openai.svg b/frontend/crawlab-ui/src/assets/svg/icons/openai.svg
new file mode 100644
index 00000000..50d94d6c
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/openai.svg
@@ -0,0 +1 @@
+OpenAI
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/oracle.svg b/frontend/crawlab-ui/src/assets/svg/icons/oracle.svg
new file mode 100644
index 00000000..3f4e051f
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/oracle.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/outlook.svg b/frontend/crawlab-ui/src/assets/svg/icons/outlook.svg
new file mode 100644
index 00000000..2cde22d4
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/outlook.svg
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/playwright.svg b/frontend/crawlab-ui/src/assets/svg/icons/playwright.svg
new file mode 100644
index 00000000..3dcb4ac0
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/playwright.svg
@@ -0,0 +1 @@
+Playwright Streamline Icon: https://streamlinehq.com
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/postgres.svg b/frontend/crawlab-ui/src/assets/svg/icons/postgres.svg
new file mode 100644
index 00000000..8bed16d4
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/postgres.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/proton.svg b/frontend/crawlab-ui/src/assets/svg/icons/proton.svg
new file mode 100644
index 00000000..dd39b390
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/proton.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/puppeteer.svg b/frontend/crawlab-ui/src/assets/svg/icons/puppeteer.svg
new file mode 100644
index 00000000..dd734b18
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/puppeteer.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/qwen.svg b/frontend/crawlab-ui/src/assets/svg/icons/qwen.svg
new file mode 100644
index 00000000..e1199f82
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/qwen.svg
@@ -0,0 +1 @@
+Qwen
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/redis.svg b/frontend/crawlab-ui/src/assets/svg/icons/redis.svg
new file mode 100644
index 00000000..6b8d45ae
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/redis.svg
@@ -0,0 +1,6 @@
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/scrapy.svg b/frontend/crawlab-ui/src/assets/svg/icons/scrapy.svg
new file mode 100644
index 00000000..224fee19
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/scrapy.svg
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/selenium.svg b/frontend/crawlab-ui/src/assets/svg/icons/selenium.svg
new file mode 100644
index 00000000..e9d6043d
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/selenium.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/snowflake.svg b/frontend/crawlab-ui/src/assets/svg/icons/snowflake.svg
new file mode 100644
index 00000000..f491c273
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/snowflake.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/webmagic.svg b/frontend/crawlab-ui/src/assets/svg/icons/webmagic.svg
new file mode 100644
index 00000000..2b286d1a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/webmagic.svg
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/wechat_work.svg b/frontend/crawlab-ui/src/assets/svg/icons/wechat_work.svg
new file mode 100644
index 00000000..714dd1a6
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/wechat_work.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/xxl.svg b/frontend/crawlab-ui/src/assets/svg/icons/xxl.svg
new file mode 100644
index 00000000..96728a5c
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/xxl.svg
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/icons/zoho.svg b/frontend/crawlab-ui/src/assets/svg/icons/zoho.svg
new file mode 100644
index 00000000..d5c9087a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/icons/zoho.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/assets/svg/logo-icon-white.svg b/frontend/crawlab-ui/src/assets/svg/logo-icon-white.svg
new file mode 100644
index 00000000..997c9db6
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/logo-icon-white.svg
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/logo-icon.svg b/frontend/crawlab-ui/src/assets/svg/logo-icon.svg
new file mode 100644
index 00000000..e095990a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/logo-icon.svg
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/logo-main.svg b/frontend/crawlab-ui/src/assets/svg/logo-main.svg
new file mode 100644
index 00000000..aed3ae3a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/logo-main.svg
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/logo-white.svg b/frontend/crawlab-ui/src/assets/svg/logo-white.svg
new file mode 100644
index 00000000..afc7439a
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/logo-white.svg
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/assets/svg/logo.ts b/frontend/crawlab-ui/src/assets/svg/logo.ts
new file mode 100644
index 00000000..c57b7d0e
--- /dev/null
+++ b/frontend/crawlab-ui/src/assets/svg/logo.ts
@@ -0,0 +1 @@
+export default `data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxnIGZpbGw9Im5vbmUiPgogICAgICAgIDxjaXJjbGUgY3g9IjE1MCIgY3k9IjE1MCIgcj0iMTMwIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjQwIiBzdHJva2U9IiM0MDllZmYiPgogICAgICAgIDwvY2lyY2xlPgogICAgICAgIDxjaXJjbGUgY3g9IjE1MCIgY3k9IjE1MCIgcj0iMTEwIiBmaWxsPSJ3aGl0ZSI+CiAgICAgICAgPC9jaXJjbGU+CiAgICAgICAgPGNpcmNsZSBjeD0iMTUwIiBjeT0iMTUwIiByPSI3MCIgZmlsbD0iIzQwOWVmZiI+CiAgICAgICAgPC9jaXJjbGU+CiAgICAgICAgPHBhdGggZD0iCiAgICAgICAgICAgIE0gMTUwLDE1MAogICAgICAgICAgICBMIDI4MCwyMjUKICAgICAgICAgICAgQSAxNTAsMTUwIDkwIDAgMCAyODAsNzUKICAgICAgICAgICAgIiBmaWxsPSIjNDA5ZWZmIj4KICAgICAgICA8L3BhdGg+CiAgICA8L2c+Cjwvc3ZnPgo=`;
diff --git a/frontend/crawlab-ui/src/components/core/ai/AssistantConsole.vue b/frontend/crawlab-ui/src/components/core/ai/AssistantConsole.vue
new file mode 100644
index 00000000..a5970ff8
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/ai/AssistantConsole.vue
@@ -0,0 +1,342 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/ai/useAssistantConsole.ts b/frontend/crawlab-ui/src/components/core/ai/useAssistantConsole.ts
new file mode 100644
index 00000000..b7898462
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/ai/useAssistantConsole.ts
@@ -0,0 +1,456 @@
+import { computed, ref, watch, reactive, onMounted } from 'vue';
+import { useI18n } from 'vue-i18n';
+import useRequest from '@/services/request';
+import { getRequestBaseUrl } from '@/utils';
+import { debounce } from 'lodash';
+import { ElMessage } from 'element-plus';
+import { AxiosError } from 'axios';
+import { ClChatInput } from '@/components';
+
+const useAssistantConsole = () => {
+ const { t } = useI18n();
+ const { get } = useRequest();
+
+ // Refs
+ const messageListRef = ref<{ scrollToBottom: () => Promise } | null>(
+ null
+ );
+ const chatInputRef = ref | null>(null);
+
+ // State management
+ const currentConversation = ref(null);
+ const currentConversationId = ref('');
+ const conversations = ref([]);
+ const chatHistory = reactive([]);
+ const isGenerating = ref(false);
+ const streamError = ref('');
+ const isLoadingConversations = ref(false);
+ const isLoadingMessages = ref(false);
+ const historyDialogVisible = ref(false);
+ const configDialogVisible = ref(false);
+ const abortController = ref(null);
+ const availableProviders = ref([]);
+
+ // Computed properties
+ const currentConversationTitle = computed(() => {
+ if (!currentConversationId.value) return t('components.ai.chatbot.newChat');
+ return (
+ currentConversation.value?.title || t('components.ai.chatbot.newChat')
+ );
+ });
+
+ const chatbotConfig = ref({
+ systemPrompt:
+ 'You are a helpful AI assistant for Crawlab, a web crawling and data extraction platform.',
+ temperature: 0.7,
+ maxTokens: 1000,
+ });
+
+ // Load conversations
+ const loadConversations = async () => {
+ isLoadingConversations.value = true;
+ try {
+ const res = await get('/ai/chat/conversations', {
+ page: 1,
+ size: 500,
+ sort: '-last_message_at',
+ });
+ conversations.value = res.data || [];
+ } catch (error) {
+ console.error('Failed to load conversations:', error);
+ } finally {
+ isLoadingConversations.value = false;
+ }
+ };
+
+ // Load messages for a conversation
+ const loadConversationMessages = debounce(async (conversationId: string) => {
+ if (!conversationId) return;
+
+ isLoadingMessages.value = true;
+ try {
+ const res = await get(
+ `/ai/chat/conversations/${conversationId}/messages`
+ );
+ const messages = (res.data || []).map((msg: any) => {
+ const message: ChatMessage = {
+ ...msg,
+ timestamp: new Date(msg.created_ts || Date.now()),
+ };
+ return message;
+ });
+
+ messages.sort(
+ (a: ChatMessage, b: ChatMessage) =>
+ (a.timestamp?.getTime() || 0) - (b.timestamp?.getTime() || 0)
+ );
+
+ chatHistory.splice(0, chatHistory.length, ...messages);
+ currentConversationId.value = conversationId;
+ } catch (error) {
+ console.error('Failed to load conversation messages:', error);
+ } finally {
+ isLoadingMessages.value = false;
+ }
+ });
+
+ // Load current conversation details
+ const loadCurrentConversation = debounce(async (conversationId: string) => {
+ if (!conversationId) {
+ currentConversation.value = null;
+ return;
+ }
+ try {
+ const res = await get(`/ai/chat/conversations/${conversationId}`);
+ currentConversation.value = res.data;
+ } catch (error) {
+ if ((error as AxiosError)?.response?.status === 404) {
+ currentConversationId.value = '';
+ return;
+ }
+ console.error('Failed to load conversation details:', error);
+ currentConversation.value = null;
+ }
+ });
+
+ // Load LLM providers
+ const loadLLMProviders = debounce(async () => {
+ try {
+ const res = await get('/ai/llm/providers', { available: true });
+ availableProviders.value = res.data || [];
+
+ if (!availableProviders.value.length) {
+ resetChatbotConfig();
+ }
+
+ if (!chatbotConfig.value.provider || !chatbotConfig.value.model) {
+ if (availableProviders.value.length > 0) {
+ chatbotConfig.value.provider = availableProviders.value[0].key!;
+ chatbotConfig.value.model = availableProviders.value[0].models![0];
+ localStorage.setItem(
+ 'chatbotConfig',
+ JSON.stringify(chatbotConfig.value)
+ );
+ }
+ }
+ } catch (error) {
+ console.error('Failed to load LLM providers:', error);
+ }
+ });
+
+ // Config management
+ const loadChatbotConfig = () => {
+ const storedConfig = localStorage.getItem('chatbotConfig');
+ if (storedConfig) {
+ try {
+ const parsedConfig = JSON.parse(storedConfig);
+ chatbotConfig.value = { ...chatbotConfig.value, ...parsedConfig };
+ } catch (e) {
+ console.error('Failed to parse stored chatbot config', e);
+ }
+ }
+ };
+
+ const saveChatbotConfig = (config: ChatbotConfig) => {
+ configDialogVisible.value = false;
+ chatbotConfig.value = { ...chatbotConfig.value, ...config };
+ localStorage.setItem('chatbotConfig', JSON.stringify(chatbotConfig.value));
+ ElMessage.success(t('common.message.success.save'));
+ };
+
+ const resetChatbotConfig = () => {
+ chatbotConfig.value = {};
+ localStorage.removeItem('chatbotConfig');
+ };
+
+ // Message handling
+ const extractErrorMessage = (errorData: string): string => {
+ try {
+ const parsed = JSON.parse(errorData);
+ if (parsed.error_detail?.message) return parsed.error_detail.message;
+ if (parsed.error)
+ return typeof parsed.error === 'string'
+ ? parsed.error
+ : JSON.stringify(parsed.error);
+ if (parsed.text?.startsWith('Error:')) return parsed.text;
+ if (typeof parsed === 'object') return JSON.stringify(parsed, null, 2);
+ return errorData;
+ } catch (e) {
+ return errorData;
+ }
+ };
+
+ // Message stream handling
+ const createChatRequest = (message: string): ChatRequest => {
+ const { provider, model, systemPrompt, temperature, maxTokens } =
+ chatbotConfig.value;
+
+ if (!provider || !model) {
+ throw new Error(
+ 'Please select a provider and model before sending a message'
+ );
+ }
+
+ return {
+ provider,
+ model,
+ query: message,
+ system_prompt: systemPrompt,
+ temperature,
+ max_tokens: maxTokens,
+ conversation_id: currentConversationId.value,
+ };
+ };
+
+ const getStreamHeaders = (): HeadersInit => {
+ const token = localStorage.getItem('token');
+ return {
+ 'Content-Type': 'application/json',
+ ...(token ? { Authorization: token } : {}),
+ };
+ };
+
+ const handleStreamChunk = (
+ chunk: ChatbotStreamMessage,
+ currentMessage: ChatMessage
+ ): void => {
+ // Handle initial conversation ID update
+ if (chunk.is_initial) {
+ currentConversationId.value = chunk.conversation_id!;
+ return;
+ }
+
+ // Update conversation title
+ if (chunk.conversation_title) {
+ if (!currentConversation.value) {
+ currentConversation.value = {};
+ }
+ currentConversation.value.title = chunk.conversation_title;
+ }
+
+ // Update usage
+ if (chunk.usage) {
+ if (!currentMessage.usage) {
+ currentMessage.usage = {
+ prompt_tokens: 0,
+ completion_tokens: 0,
+ total_tokens: 0,
+ };
+ }
+ currentMessage.usage.prompt_tokens! += chunk.usage.prompt_tokens || 0;
+ currentMessage.usage.completion_tokens! +=
+ chunk.usage.completion_tokens || 0;
+ currentMessage.usage.total_tokens! += chunk.usage.total_tokens || 0;
+ }
+
+ // Initialize contents array if needed
+ if (!currentMessage.contents?.length) {
+ currentMessage.contents = [];
+ }
+
+ // Find content index
+ const contentKey = chunk.key || '';
+ const contentIndex = currentMessage.contents.findIndex(
+ c => c.key === contentKey
+ );
+
+ if (contentIndex >= 0) {
+ // Update existing content
+ const content = currentMessage.contents[contentIndex];
+ if (chunk.type === 'text') {
+ content.content += chunk.content || '';
+ if (chunk.is_text_done) {
+ content.isStreaming = false;
+ }
+ } else if (chunk.type === 'action') {
+ currentMessage.contents[contentIndex] = {
+ ...content,
+ ...chunk,
+ };
+ }
+ } else {
+ // Add new content
+ if (chunk.type === 'text') {
+ // Create new content object
+ const newContent: ChatMessageContent = {
+ ...chunk,
+ content: chunk.content || '',
+ isStreaming: true,
+ };
+ // Add new content to the message
+ currentMessage.contents.push(newContent);
+ } else if (chunk.type === 'action') {
+ // Create new action content object
+ currentMessage.contents.push({
+ ...chunk,
+ });
+ }
+ }
+ };
+
+ const processStreamData = async (
+ reader: ReadableStreamDefaultReader,
+ responseIndex: number,
+ onMessageUpdate: (index: number) => void
+ ): Promise => {
+ const decoder = new TextDecoder();
+ let buffer = '';
+
+ while (true) {
+ const { value, done } = await reader.read();
+ const currentMessage = chatHistory[responseIndex];
+
+ if (done) {
+ if (currentMessage) {
+ currentMessage.isStreaming = false;
+ onMessageUpdate(responseIndex);
+ }
+ return;
+ }
+
+ buffer += decoder.decode(value, { stream: true });
+ const lines = buffer.split('\n');
+ buffer = lines.pop() || '';
+
+ for (const line of lines) {
+ if (line.startsWith('data:')) {
+ const eventData = line.slice(5).trim();
+ if (eventData === '') continue;
+
+ try {
+ const chunk: ChatbotStreamMessage = JSON.parse(eventData);
+ handleStreamChunk(chunk, currentMessage);
+
+ if (chunk.is_done) {
+ if (chatHistory[responseIndex]) {
+ chatHistory[responseIndex].isStreaming = false;
+ onMessageUpdate(responseIndex);
+ }
+ return;
+ }
+ } catch (e) {
+ console.error('Error parsing event data:', e);
+ }
+ } else if (line.startsWith('event: error')) {
+ const errorLine = lines.find(l => l.startsWith('data:'));
+ if (errorLine) {
+ const errorData = errorLine.slice(5).trim();
+ throw new Error(extractErrorMessage(errorData));
+ }
+ }
+ }
+ }
+ };
+
+ const sendStreamingRequest = async (
+ message: string,
+ responseIndex: number,
+ onMessageUpdate: (index: number) => void
+ ): Promise => {
+ try {
+ const chatRequest = createChatRequest(message);
+ const baseUrl = getRequestBaseUrl();
+ const url = `${baseUrl}/ai/chat/stream`;
+
+ const response = await fetch(url, {
+ method: 'POST',
+ headers: getStreamHeaders(),
+ body: JSON.stringify(chatRequest),
+ signal: abortController.value?.signal,
+ });
+
+ if (!response.ok) {
+ const text = await response.text();
+ throw new Error(extractErrorMessage(text));
+ }
+
+ await processStreamData(
+ response.body!.getReader(),
+ responseIndex,
+ onMessageUpdate
+ );
+ } catch (error) {
+ if (error instanceof Error) {
+ throw error;
+ }
+ throw new Error('An error occurred while processing the stream');
+ }
+ };
+
+ // Conversation management
+ const selectConversation = async (conversationId: string) => {
+ if (currentConversationId.value === conversationId) return;
+
+ chatInputRef.value?.focus();
+ currentConversationId.value = conversationId;
+ streamError.value = '';
+ await loadConversationMessages(conversationId);
+ };
+
+ const createNewConversation = () => {
+ currentConversationId.value = '';
+ localStorage.removeItem('currentConversationId');
+ streamError.value = '';
+ chatHistory.splice(0, chatHistory.length);
+ chatInputRef.value?.focus();
+ };
+
+ // Watch conversation ID changes
+ watch(currentConversationId, async newId => {
+ if (newId) {
+ localStorage.setItem('currentConversationId', newId);
+ await loadCurrentConversation(newId);
+ } else {
+ localStorage.removeItem('currentConversationId');
+ currentConversation.value = null;
+ }
+
+ if (newId && !currentConversationId.value) {
+ await loadConversations();
+ currentConversationId.value = newId;
+ }
+ });
+
+ onMounted(() => {
+ setTimeout(() => {
+ chatInputRef.value?.focus();
+ }, 200);
+ });
+
+ return {
+ // Refs
+ messageListRef,
+ chatInputRef,
+
+ // State
+ currentConversation,
+ currentConversationId,
+ conversations,
+ chatHistory,
+ isGenerating,
+ streamError,
+ isLoadingConversations,
+ isLoadingMessages,
+ historyDialogVisible,
+ configDialogVisible,
+ abortController,
+ availableProviders,
+ chatbotConfig,
+ currentConversationTitle,
+
+ // Methods
+ loadConversations,
+ loadConversationMessages,
+ loadCurrentConversation,
+ loadLLMProviders,
+ loadChatbotConfig,
+ saveChatbotConfig,
+ resetChatbotConfig,
+ selectConversation,
+ createNewConversation,
+ sendStreamingRequest,
+ extractErrorMessage,
+ };
+};
+
+export default useAssistantConsole;
diff --git a/frontend/crawlab-ui/src/components/core/database/CreateEditDatabaseDialog.vue b/frontend/crawlab-ui/src/components/core/database/CreateEditDatabaseDialog.vue
new file mode 100644
index 00000000..a4f57c00
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/CreateEditDatabaseDialog.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/CreateEditDatabaseTableDialog.vue b/frontend/crawlab-ui/src/components/core/database/CreateEditDatabaseTableDialog.vue
new file mode 100644
index 00000000..e08f6350
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/CreateEditDatabaseTableDialog.vue
@@ -0,0 +1,82 @@
+
+
+
+
+ (activeTabName = id)"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/DatabaseDataSource.vue b/frontend/crawlab-ui/src/components/core/database/DatabaseDataSource.vue
new file mode 100644
index 00000000..2a85c063
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/DatabaseDataSource.vue
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/DatabaseDatabaseDetail.vue b/frontend/crawlab-ui/src/components/core/database/DatabaseDatabaseDetail.vue
new file mode 100644
index 00000000..92bfee2c
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/DatabaseDatabaseDetail.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/DatabaseForm.vue b/frontend/crawlab-ui/src/components/core/database/DatabaseForm.vue
new file mode 100644
index 00000000..bbc12155
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/DatabaseForm.vue
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ op.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/DatabaseSidebar.vue b/frontend/crawlab-ui/src/components/core/database/DatabaseSidebar.vue
new file mode 100644
index 00000000..4f27edf4
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/DatabaseSidebar.vue
@@ -0,0 +1,935 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/DatabaseStatus.vue b/frontend/crawlab-ui/src/components/core/database/DatabaseStatus.vue
new file mode 100644
index 00000000..5fa990cc
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/DatabaseStatus.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/DatabaseTableDetail.vue b/frontend/crawlab-ui/src/components/core/database/DatabaseTableDetail.vue
new file mode 100644
index 00000000..8b1c9889
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/DatabaseTableDetail.vue
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ internalTable = val;
+ }
+ "
+ />
+
+
+ {
+ internalTable = val;
+ }
+ "
+ />
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/nav/DatabaseNavTabs.vue b/frontend/crawlab-ui/src/components/core/database/nav/DatabaseNavTabs.vue
new file mode 100644
index 00000000..7ab56bb6
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/nav/DatabaseNavTabs.vue
@@ -0,0 +1,65 @@
+
+
+
+ (activeTabName = key)"
+ >
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/tables/DatabaseTableDetailColumns.vue b/frontend/crawlab-ui/src/components/core/database/tables/DatabaseTableDetailColumns.vue
new file mode 100644
index 00000000..16f3ddf6
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/tables/DatabaseTableDetailColumns.vue
@@ -0,0 +1,357 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/tables/DatabaseTableDetailData.vue b/frontend/crawlab-ui/src/components/core/database/tables/DatabaseTableDetailData.vue
new file mode 100644
index 00000000..31bca8c3
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/tables/DatabaseTableDetailData.vue
@@ -0,0 +1,375 @@
+
+
+
+ selectedRows = selection"
+ >
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/tables/DatabaseTableDetailIndexes.vue b/frontend/crawlab-ui/src/components/core/database/tables/DatabaseTableDetailIndexes.vue
new file mode 100644
index 00000000..c4422292
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/tables/DatabaseTableDetailIndexes.vue
@@ -0,0 +1,427 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/database/useDatabase.ts b/frontend/crawlab-ui/src/components/core/database/useDatabase.ts
new file mode 100644
index 00000000..aff9f80f
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/database/useDatabase.ts
@@ -0,0 +1,118 @@
+import { computed } from 'vue';
+import { Store } from 'vuex';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import useDatabaseService from '@/services/database/databaseService';
+import {
+ EMPTY_OBJECT_ID,
+ getDefaultFormComponentData,
+ plainClone,
+ translate,
+} from '@/utils';
+import useForm from '@/components/ui/form/useForm';
+import { databaseDefaults } from '@/utils/database';
+
+// i18n
+const t = translate;
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+export const useDatabase = (store: Store) => {
+ // store
+ const ns = 'database' as ListStoreNamespace;
+ const { database: state } = store.state as RootStoreState;
+
+ // form rules
+ const formRules: FormRules = {};
+
+ const availableDatabaseNames: DatabaseDataSource[] = [
+ 'mongo',
+ 'mysql',
+ 'postgres',
+ 'mssql',
+ 'elasticsearch',
+ ];
+
+ // type options
+ const dataSourceOptions = computed[]>(() =>
+ databaseDefaults.map(item => {
+ const disabled = !availableDatabaseNames.includes(item.data_source!);
+ const label = !disabled
+ ? item.name
+ : `${item.name} (${t('common.status.currentlyUnavailable')})`;
+ const value = item.data_source;
+ return {
+ label,
+ value,
+ disabled,
+ };
+ })
+ );
+ const getTypeOptionsWithDefault = (): SelectOption[] => {
+ return [
+ { label: t('components.database.dataSources.default'), value: undefined },
+ ...dataSourceOptions.value,
+ ];
+ };
+
+ // on change password function
+ const onChangePasswordFunc = async (id?: string) => {
+ if (!id) return;
+
+ const { value } = await ElMessageBox.prompt(
+ t('components.user.messageBox.prompt.changePassword'),
+ t('components.user.form.changePassword'),
+ {
+ inputType: 'password',
+ inputPlaceholder: t('components.user.form.newPassword'),
+ }
+ );
+
+ await store.dispatch(`${ns}/changePassword`, { id, password: value });
+ ElMessage.success(t('common.message.success.save'));
+ };
+
+ // on hosts add
+ const onHostsAdd = (index: number) => {
+ const form = plainClone(state.form);
+ if (!form.hosts) form.hosts = [];
+ form.hosts?.splice(index + 1, 0, '');
+ store.commit(`${ns}/setForm`, { ...form });
+ };
+
+ // on hosts delete
+ const onHostsDelete = (index: number) => {
+ const form = plainClone(state.form);
+ form.hosts?.splice(index, 1);
+ if (form.hosts?.length === 0) form.hosts?.push('');
+ store.commit(`${ns}/setForm`, { ...form });
+ };
+
+ const allListSelectOptions = computed(
+ () =>
+ store.getters[`${ns}/allListSelectOptions`]?.map((op: SelectOption) => {
+ if (op.value === EMPTY_OBJECT_ID) {
+ return { ...op, label: t('components.database.default.name') };
+ }
+ return op;
+ }) || []
+ );
+
+ return {
+ ...useForm(
+ ns,
+ store,
+ useDatabaseService(store),
+ formComponentData
+ ),
+ formRules,
+ dataSourceOptions,
+ allListSelectOptions,
+ getTypeOptionsWithDefault,
+ onChangePasswordFunc,
+ onHostsAdd,
+ onHostsDelete,
+ };
+};
+
+export default useDatabase;
diff --git a/frontend/crawlab-ui/src/components/core/dependency/DependencyConfigDialog.vue b/frontend/crawlab-ui/src/components/core/dependency/DependencyConfigDialog.vue
new file mode 100644
index 00000000..2edcd5fe
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/dependency/DependencyConfigDialog.vue
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/dependency/DependencyInstallDialog.vue b/frontend/crawlab-ui/src/components/core/dependency/DependencyInstallDialog.vue
new file mode 100644
index 00000000..63c70be3
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/dependency/DependencyInstallDialog.vue
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ n.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/dependency/DependencyLogsDialog.vue b/frontend/crawlab-ui/src/components/core/dependency/DependencyLogsDialog.vue
new file mode 100644
index 00000000..b421efdb
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/dependency/DependencyLogsDialog.vue
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+ {{ t('common.tabs.logs') }} - {{ activeTargetName }}
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/dependency/DependencySetupDialog.vue b/frontend/crawlab-ui/src/components/core/dependency/DependencySetupDialog.vue
new file mode 100644
index 00000000..fc19339f
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/dependency/DependencySetupDialog.vue
@@ -0,0 +1,243 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ n.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ t('views.env.deps.config.alert.browser.nodeSetupRequired.content')
+ }}
+
+
+ {
+ store.commit(`${ns}/setLang`, 'node');
+ store.commit(`${ns}/setRepoTabName`, 'nodes');
+ store.commit(`${ns}/hideDialog`);
+ }
+ "
+ />
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/dependency/DependencyStatusTag.vue b/frontend/crawlab-ui/src/components/core/dependency/DependencyStatusTag.vue
new file mode 100644
index 00000000..fed49593
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/dependency/DependencyStatusTag.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/dependency/DependencyUninstallDialog.vue b/frontend/crawlab-ui/src/components/core/dependency/DependencyUninstallDialog.vue
new file mode 100644
index 00000000..01d1bea7
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/dependency/DependencyUninstallDialog.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ n.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/dependency/DependencyVersions.vue b/frontend/crawlab-ui/src/components/core/dependency/DependencyVersions.vue
new file mode 100644
index 00000000..689b88d2
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/dependency/DependencyVersions.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/environment/CreateEditEnvironmentDialog.vue b/frontend/crawlab-ui/src/components/core/environment/CreateEditEnvironmentDialog.vue
new file mode 100644
index 00000000..b9f77634
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/environment/CreateEditEnvironmentDialog.vue
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/environment/EnvironmentForm.vue b/frontend/crawlab-ui/src/components/core/environment/EnvironmentForm.vue
new file mode 100644
index 00000000..e775049e
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/environment/EnvironmentForm.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/environment/useEnvironment.ts b/frontend/crawlab-ui/src/components/core/environment/useEnvironment.ts
new file mode 100644
index 00000000..4378ff60
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/environment/useEnvironment.ts
@@ -0,0 +1,26 @@
+import { Store } from 'vuex';
+import useEnvironmentService from '@/services/environment/environmentService';
+import { getDefaultFormComponentData, translate } from '@/utils';
+import useForm from '@/components/ui/form/useForm';
+
+// i18n
+const t = translate;
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+export const useEnvironment: any = (store: Store) => {
+ // store
+ const ns = 'environment' as ListStoreNamespace;
+
+ return {
+ ...useForm(
+ ns,
+ store,
+ useEnvironmentService(store),
+ formComponentData
+ ),
+ };
+};
+
+export default useEnvironment;
diff --git a/frontend/crawlab-ui/src/components/core/git/CreateEditGitDialog.vue b/frontend/crawlab-ui/src/components/core/git/CreateEditGitDialog.vue
new file mode 100644
index 00000000..10684bac
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/CreateEditGitDialog.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/CreateGitBranchDialog.vue b/frontend/crawlab-ui/src/components/core/git/CreateGitBranchDialog.vue
new file mode 100644
index 00000000..927fd69d
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/CreateGitBranchDialog.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/CreateGitSpiderDialog.vue b/frontend/crawlab-ui/src/components/core/git/CreateGitSpiderDialog.vue
new file mode 100644
index 00000000..b6d01933
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/CreateGitSpiderDialog.vue
@@ -0,0 +1,135 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ label }}
+
+
+ {{ value }}
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/GitBranchSelect.vue b/frontend/crawlab-ui/src/components/core/git/GitBranchSelect.vue
new file mode 100644
index 00000000..2e8f38af
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/GitBranchSelect.vue
@@ -0,0 +1,322 @@
+
+
+
+
+
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+ {{ op.label }}
+
+
+ {{ op.branch?.remote_track }}
+
+
+ onDeleteBranch(op.value as string, event)
+ "
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ {{ t('components.git.branches.pull') }}
+
+
+
+ -
+
+
+
+ {{ t('components.git.branches.commit') }}
+
+
+
+ -
+
+
+
+ {{ t('components.git.branches.push') }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/GitCloneLogsDialog.vue b/frontend/crawlab-ui/src/components/core/git/GitCloneLogsDialog.vue
new file mode 100644
index 00000000..e133a17f
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/GitCloneLogsDialog.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/GitFileDiffDialog.vue b/frontend/crawlab-ui/src/components/core/git/GitFileDiffDialog.vue
new file mode 100644
index 00000000..6b62f393
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/GitFileDiffDialog.vue
@@ -0,0 +1,93 @@
+
+
+
+
+
+
+
+
+
+ {{ state.activeFilePath }}
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/GitFileStatus.vue b/frontend/crawlab-ui/src/components/core/git/GitFileStatus.vue
new file mode 100644
index 00000000..92126037
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/GitFileStatus.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+ {{ fileStatus.name }}
+
+
+ {{ fileStatus.path }}
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/GitForm.vue b/frontend/crawlab-ui/src/components/core/git/GitForm.vue
new file mode 100644
index 00000000..6e325aea
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/GitForm.vue
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/GitLogsBox.vue b/frontend/crawlab-ui/src/components/core/git/GitLogsBox.vue
new file mode 100644
index 00000000..5a091601
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/GitLogsBox.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/GitPath.vue b/frontend/crawlab-ui/src/components/core/git/GitPath.vue
new file mode 100644
index 00000000..201f38ca
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/GitPath.vue
@@ -0,0 +1,17 @@
+
+
+
+ {{ path }}
+
+ {{ FILE_ROOT }} ({{ t('components.file.rootDirectory') }})
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/GitRepo.vue b/frontend/crawlab-ui/src/components/core/git/GitRepo.vue
new file mode 100644
index 00000000..80041760
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/GitRepo.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+ {{ gitRootPath }}
+ ~ ({{ t('components.file.rootDirectory') }})
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/GitStatus.vue b/frontend/crawlab-ui/src/components/core/git/GitStatus.vue
new file mode 100644
index 00000000..38569c87
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/GitStatus.vue
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/UploadGitFilesDialog.vue b/frontend/crawlab-ui/src/components/core/git/UploadGitFilesDialog.vue
new file mode 100644
index 00000000..2202cfe1
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/UploadGitFilesDialog.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/git/useGit.ts b/frontend/crawlab-ui/src/components/core/git/useGit.ts
new file mode 100644
index 00000000..b35d3a78
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/git/useGit.ts
@@ -0,0 +1,34 @@
+import { Store } from 'vuex';
+import useForm from '@/components/ui/form/useForm';
+import useGitService from '@/services/git/gitService';
+import { getDefaultFormComponentData } from '@/utils/form';
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const getGitIcon = (row: Git): { icon: Icon; color?: string; name: string } => {
+ if (row.url?.includes('github')) {
+ return { icon: ['fab', 'github'], color: '#0d1117', name: 'github' };
+ } else if (row.url?.includes('bitbucket')) {
+ return { icon: ['fab', 'bitbucket'], color: '#0052cc', name: 'bitbucket' };
+ } else if (row.url?.includes('gitlab')) {
+ return { icon: ['fab', 'gitlab'], color: '#E24329', name: 'gitlab' };
+ } else if (row.url?.includes('amazonaws')) {
+ return { icon: ['fab', 'aws'], color: '#232f3e', name: 'aws' };
+ } else {
+ return {
+ icon: ['fab', 'git'],
+ color: 'var(--cl-info-medium-dark-color)',
+ name: 'git',
+ };
+ }
+};
+
+const useGit = (store: Store) => {
+ return {
+ ...useForm('git', store, useGitService(store), formComponentData),
+ getGitIcon,
+ };
+};
+
+export default useGit;
diff --git a/frontend/crawlab-ui/src/components/core/metric/CurrentMetrics.vue b/frontend/crawlab-ui/src/components/core/metric/CurrentMetrics.vue
new file mode 100644
index 00000000..f7937f15
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/metric/CurrentMetrics.vue
@@ -0,0 +1,193 @@
+
+
+
+
+
+ emit('click', event)"
+ >
+
+
+
+
+ {{ Math.round(metric?.cpu_usage_percent || 0) }}%
+
+
+
+
+ emit('click', event)"
+ >
+
+
+
+
+ {{ formatBytes(metric?.used_memory) }} ({{
+ Math.round(metric?.used_memory_percent || 0)
+ }}%)
+
+
+
+
+
+ {{ formatBytes(metric?.total_memory) }}
+
+
+
+
+ emit('click', event)"
+ >
+
+
+
+
+ {{ formatBytes(metric?.used_disk) }} ({{
+ Math.round(metric?.used_disk_percent || 0)
+ }}%)
+
+
+
+
+
+ {{ formatBytes(metric?.total_disk) }}
+
+
+
+
+ emit('click', event)"
+ >
+
+
+
+
+ {{ formatBytes(metric?.used_memory) }}
+
+
+
+
+ emit('click', event)"
+ >
+
+
+
+
+ {{ formatBytes(metric?.used_disk) }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/metric/MetricMonitoringDetail.vue b/frontend/crawlab-ui/src/components/core/metric/MetricMonitoringDetail.vue
new file mode 100644
index 00000000..940646d2
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/metric/MetricMonitoringDetail.vue
@@ -0,0 +1,379 @@
+
+
+
+
+
+
+ emit('time-range-change', value)"
+ >
+
+
+
+
+
+
+
+ emit('metric-groups-change', value)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getMetricGroupLabel(name) }}
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/node/CreateEditNodeDialog.vue b/frontend/crawlab-ui/src/components/core/node/CreateEditNodeDialog.vue
new file mode 100644
index 00000000..687b3cb1
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/node/CreateEditNodeDialog.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/node/NodeActive.vue b/frontend/crawlab-ui/src/components/core/node/NodeActive.vue
new file mode 100644
index 00000000..ebe85b36
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/node/NodeActive.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+ {{ label }}
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/node/NodeForm.vue b/frontend/crawlab-ui/src/components/core/node/NodeForm.vue
new file mode 100644
index 00000000..81247986
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/node/NodeForm.vue
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('common.mode.unlimited') }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/node/NodeRunners.vue b/frontend/crawlab-ui/src/components/core/node/NodeRunners.vue
new file mode 100644
index 00000000..3af79876
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/node/NodeRunners.vue
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/node/NodeStatus.vue b/frontend/crawlab-ui/src/components/core/node/NodeStatus.vue
new file mode 100644
index 00000000..5bc80175
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/node/NodeStatus.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/node/NodeTag.vue b/frontend/crawlab-ui/src/components/core/node/NodeTag.vue
new file mode 100644
index 00000000..846db936
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/node/NodeTag.vue
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/node/NodeType.vue b/frontend/crawlab-ui/src/components/core/node/NodeType.vue
new file mode 100644
index 00000000..6052912c
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/node/NodeType.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/node/useNode.ts b/frontend/crawlab-ui/src/components/core/node/useNode.ts
new file mode 100644
index 00000000..98fcbeb8
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/node/useNode.ts
@@ -0,0 +1,37 @@
+import { Store } from 'vuex';
+import useForm from '@/components/ui/form/useForm';
+import useNodeService from '@/services/node/nodeService';
+import { getDefaultFormComponentData } from '@/utils/form';
+import { computed } from 'vue';
+
+type Node = CNode;
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useNode = (store: Store) => {
+ // store
+ const ns = 'node';
+ const { node: state } = store.state;
+
+ // form rules
+ const formRules: FormRules = {};
+
+ const activeNodesSorted = computed(() => {
+ return state.allList
+ .filter(n => n.active)
+ .sort((a, b) => {
+ if (a.is_master) return -1;
+ if (b.is_master) return 1;
+ return a.name!.localeCompare(b.name!);
+ });
+ });
+
+ return {
+ ...useForm(ns, store, useNodeService(store), formComponentData),
+ formRules,
+ activeNodesSorted,
+ };
+};
+
+export default useNode;
diff --git a/frontend/crawlab-ui/src/components/core/notification/alert/CreateEditNotificationAlertDialog.vue b/frontend/crawlab-ui/src/components/core/notification/alert/CreateEditNotificationAlertDialog.vue
new file mode 100644
index 00000000..948639d6
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/alert/CreateEditNotificationAlertDialog.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/notification/alert/NotificationAlertForm.vue b/frontend/crawlab-ui/src/components/core/notification/alert/NotificationAlertForm.vue
new file mode 100644
index 00000000..be79a6ae
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/alert/NotificationAlertForm.vue
@@ -0,0 +1,228 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('views.notification.alerts.form.hasMetricTarget') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ form.target_value = parseFloat(val);
+ }
+ "
+ >
+
+ {{ getMetricUnitLabel(form.metric_name || '') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/notification/alert/useNotificationAlert.ts b/frontend/crawlab-ui/src/components/core/notification/alert/useNotificationAlert.ts
new file mode 100644
index 00000000..4404adc9
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/alert/useNotificationAlert.ts
@@ -0,0 +1,99 @@
+import { computed } from 'vue';
+import { Store } from 'vuex';
+import { useRoute } from 'vue-router';
+import {
+ EMPTY_OBJECT_ID,
+ getDefaultFormComponentData,
+ translate,
+} from '@/utils';
+import useNotificationAlertService from '@/services/notification/useNotificationAlertService';
+import useForm from '@/components/ui/form/useForm';
+import { getAllMetricGroups } from '@/utils/metric';
+
+const t = translate;
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useNotificationAlert = (store: Store) => {
+ const { notificationAlert: state, node: nodeState } =
+ store.state as RootStoreState;
+
+ // route
+ const route = useRoute();
+
+ // notification id
+ const id = computed(() => route.params.id);
+
+ const form = computed(() => state.form);
+
+ const metricTargetOptions = computed(
+ () =>
+ [
+ { label: t('common.mode.all'), value: EMPTY_OBJECT_ID },
+ ...nodeState.allList
+ .filter(node => node.active)
+ .map(node => ({
+ label: node.name,
+ value: node._id,
+ })),
+ ] as SelectOption[]
+ );
+
+ const metricNameOptions = computed(() => {
+ const options = [] as SelectOption[];
+ getAllMetricGroups().forEach(group => {
+ group.metrics.forEach(metric => {
+ options.push({
+ label: t(`components.metric.metrics.${metric}`),
+ value: metric as string,
+ });
+ });
+ });
+ return options;
+ });
+
+ const operatorOptions = computed[]>(
+ () => [
+ { value: 'ge', label: '>=' },
+ { value: 'le', label: '<=' },
+ { value: 'gt', label: '>' },
+ { value: 'lt', label: '<' },
+ ]
+ );
+
+ const lastingSecondsOptions = computed[]>(() => [
+ { value: 60, label: t('views.notification.alerts.lastingDuration.1m') },
+ { value: 300, label: t('views.notification.alerts.lastingDuration.5m') },
+ { value: 600, label: t('views.notification.alerts.lastingDuration.10m') },
+ { value: 1800, label: t('views.notification.alerts.lastingDuration.30m') },
+ { value: 3600, label: t('views.notification.alerts.lastingDuration.1h') },
+ ]);
+
+ const levelOptions = computed[]>(() => [
+ { value: 'info', label: t('views.notification.alerts.levels.info') },
+ { value: 'warning', label: t('views.notification.alerts.levels.warning') },
+ {
+ value: 'critical',
+ label: t('views.notification.alerts.levels.critical'),
+ },
+ ]);
+
+ return {
+ ...useForm(
+ 'notificationAlert',
+ store,
+ useNotificationAlertService(store),
+ formComponentData
+ ),
+ id,
+ form,
+ metricTargetOptions,
+ metricNameOptions,
+ operatorOptions,
+ lastingSecondsOptions,
+ levelOptions,
+ };
+};
+
+export default useNotificationAlert;
diff --git a/frontend/crawlab-ui/src/components/core/notification/channel/CreateEditNotificationChannelDialog.vue b/frontend/crawlab-ui/src/components/core/notification/channel/CreateEditNotificationChannelDialog.vue
new file mode 100644
index 00000000..ec0f39c9
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/channel/CreateEditNotificationChannelDialog.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/notification/channel/NotificationChannelForm.vue b/frontend/crawlab-ui/src/components/core/notification/channel/NotificationChannelForm.vue
new file mode 100644
index 00000000..8e62d5fc
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/channel/NotificationChannelForm.vue
@@ -0,0 +1,385 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ op.label }}
+
+
+
+
+
+
+
+
+
+
+ {{ activeProviderOption.label }}
+
+
+
+
+
+
+ {{ op.label }}
+
+
+
+
+
+
+ {{ t('views.notification.channels.providers.custom') }}
+
+
+
+
+
+
+
+
+
+ {{ t('views.notification.channels.providerDocs.label') }}
+
+ -
+
+ {{
+ t(`views.notification.channels.providers.${activeProvider?.name}`)
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/notification/channel/useNotificationChannel.ts b/frontend/crawlab-ui/src/components/core/notification/channel/useNotificationChannel.ts
new file mode 100644
index 00000000..e96225a9
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/channel/useNotificationChannel.ts
@@ -0,0 +1,273 @@
+import { computed, watch } from 'vue';
+import { Store } from 'vuex';
+import { useRoute } from 'vue-router';
+import { translate, getDefaultFormComponentData } from '@/utils';
+import useNotificationChannelService from '@/services/notification/useNotificationChannelService';
+import useForm from '@/components/ui/form/useForm';
+import { useI18n } from 'vue-i18n';
+
+const t = translate;
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useNotificationChannel = (store: Store) => {
+ const { notificationChannel: state } = store.state as RootStoreState;
+
+ const { locale } = useI18n();
+
+ // route
+ const route = useRoute();
+
+ // notification id
+ const id = computed(() => route.params.id);
+
+ const form = computed(() => state.form);
+
+ const typeOptions = computed(() => [
+ {
+ value: 'mail',
+ label: t('views.notification.channels.types.mail'),
+ icon: ['fa', 'at'],
+ },
+ {
+ value: 'im',
+ label: t('views.notification.channels.types.im'),
+ icon: ['far', 'comment'],
+ },
+ ]);
+
+ const allProviders: NotificationChannelProvider[] = [
+ {
+ type: 'mail',
+ name: 'gmail',
+ icon: ['svg', 'gmail'],
+ smtpServer: 'smtp.gmail.com',
+ smtpPort: 587,
+ docUrl: () =>
+ `https://support.google.com/a/answer/2956491?hl=${locale.value}`,
+ locale: 'en',
+ disabled: true,
+ },
+ {
+ type: 'mail',
+ name: 'outlook',
+ icon: ['svg', 'outlook'],
+ smtpServer: 'smtp-mail.outlook.com',
+ smtpPort: 587,
+ docUrl: () => {
+ if (locale.value === 'zh') {
+ return 'https://support.microsoft.com/zh-cn/office/outlook-com-%E7%9A%84-pop-imap-%E5%92%8C-smtp-%E8%AE%BE%E7%BD%AE-d088b986-291d-42b8-9564-9c414e2aa040';
+ } else {
+ return 'https://support.microsoft.com/en-us/office/pop-imap-and-smtp-settings-for-outlook-com-d088b986-291d-42b8-9564-9c414e2aa040';
+ }
+ },
+ locale: 'en',
+ },
+ {
+ type: 'mail',
+ name: 'qq',
+ icon: ['fab', 'qq'],
+ smtpServer: 'smtp.qq.com',
+ smtpPort: 587,
+ docUrl: 'https://cloud.tencent.com/developer/article/2177098',
+ locale: 'zh',
+ },
+ {
+ type: 'mail',
+ name: '163',
+ icon: ['svg', 'netease'],
+ smtpServer: 'smtp.163.com',
+ smtpPort: 465,
+ docUrl: 'https://www.bilibili.com/read/cv32056718/',
+ locale: 'zh',
+ },
+ {
+ type: 'mail',
+ name: 'icloud',
+ icon: ['fab', 'apple'],
+ smtpServer: 'smtp.mail.me.com',
+ smtpPort: 587,
+ docUrl: () => {
+ if (locale.value === 'zh') {
+ return 'https://support.apple.com/zh-cn/102525';
+ } else {
+ return 'https://support.apple.com/en-us/102525';
+ }
+ },
+ locale: 'en',
+ },
+ {
+ type: 'mail',
+ name: 'yahoo',
+ icon: ['fab', 'yahoo'],
+ smtpServer: 'smtp.mail.yahoo.com',
+ smtpPort: 587,
+ docUrl:
+ 'https://smartreach.io/blog/masterclass/smtp/yahoo-smtp-settings/',
+ locale: 'en',
+ },
+ {
+ type: 'mail',
+ name: 'zoho',
+ icon: ['svg', 'zoho'],
+ smtpServer: 'smtp.zoho.com',
+ smtpPort: 587,
+ docUrl: 'https://www.zoho.com/mail/help/zoho-smtp.html',
+ locale: 'en',
+ },
+ {
+ type: 'mail',
+ name: 'aol',
+ icon: ['svg', 'aol'],
+ smtpServer: 'smtp.aol.com',
+ smtpPort: 587,
+ docUrl: 'https://smartreach.io/blog/masterclass/smtp/aol-mail-settings/',
+ locale: 'en',
+ },
+ {
+ type: 'im',
+ name: 'dingtalk',
+ icon: ['svg', 'dingtalk'],
+ docUrl: 'https://open.dingtalk.com/document/orgapp/custom-robot-access',
+ locale: 'zh',
+ },
+ {
+ type: 'im',
+ name: 'lark',
+ icon: ['svg', 'lark'],
+ docUrl: () => {
+ if (locale.value === 'zh') {
+ return 'https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot';
+ } else {
+ return 'https://open.larksuite.com/document/client-docs/bot-v3/add-custom-bot';
+ }
+ },
+ locale: 'zh',
+ },
+ {
+ type: 'im',
+ name: 'wechat_work',
+ icon: ['svg', 'wechat_work'],
+ docUrl: 'https://developer.work.weixin.qq.com/document/path/91770',
+ locale: 'zh',
+ },
+ {
+ type: 'im',
+ name: 'slack',
+ icon: ['fab', 'slack'],
+ docUrl: 'https://api.slack.com/messaging/webhooks',
+ locale: 'en',
+ },
+ {
+ type: 'im',
+ name: 'ms_teams',
+ icon: ['svg', 'ms_teams'],
+ docUrl: () =>
+ `https://learn.microsoft.com/${locale.value === 'zh' ? 'zh-cn' : 'en-us'}/microsoftteams/platform/webhooks-and-connectors/how-to/add-incoming-webhook`,
+ locale: 'en',
+ },
+ {
+ type: 'im',
+ name: 'telegram',
+ icon: ['fab', 'telegram'],
+ docUrl: 'https://core.telegram.org/bots/api',
+ locale: 'en',
+ },
+ {
+ type: 'im',
+ name: 'discord',
+ icon: ['fab', 'discord'],
+ docUrl: 'https://discord.com/developers/docs/resources/webhook',
+ locale: 'en',
+ },
+ ];
+
+ const providerOptionGroups = computed(() => {
+ const map: Record = {};
+ allProviders
+ .sort((a, b) => {
+ if (a.disabled !== b.disabled) {
+ return a.disabled ? 1 : -1;
+ }
+ if (a.locale === b.locale) return 0;
+ return a.locale === locale.value ? -1 : 1;
+ })
+ .forEach(p => {
+ const op: SelectOption = {
+ value: p.name,
+ label: t(`views.notification.channels.providers.${p.name}`),
+ icon: p.icon,
+ disabled: p.disabled,
+ };
+ if (!map[p.type]) {
+ map[p.type] = [];
+ }
+ map[p.type].push(op);
+ });
+ return Object.keys(map).map(key => {
+ return {
+ label: t(`views.notification.channels.types.${key}`),
+ children: map[key],
+ };
+ });
+ });
+
+ const activeProvider = computed(() => {
+ const provider = allProviders.find(p => p.name === form.value.provider);
+ if (!provider) {
+ return null;
+ }
+ return provider;
+ });
+
+ const activeProviderOption = computed(() => {
+ if (form.value.provider === 'custom') {
+ return {
+ value: form.value.provider,
+ label: t('views.notification.channels.providers.custom'),
+ icon: ['fa', 'edit'],
+ };
+ }
+ if (!activeProvider.value) {
+ return {
+ value: '',
+ label: '',
+ icon: [],
+ };
+ }
+ const { name, icon, disabled } = activeProvider.value;
+ return {
+ value: name,
+ label: t(`views.notification.channels.providers.${name}`),
+ icon,
+ disabled,
+ };
+ });
+
+ const allProviderNames = computed(() => allProviders.map(p => p.name));
+
+ const getProviderIcon = (provider: string): Icon | undefined => {
+ return allProviders.find(p => p.name === provider)?.icon;
+ };
+
+ return {
+ ...useForm(
+ 'notificationChannel',
+ store,
+ useNotificationChannelService(store),
+ formComponentData
+ ),
+ id,
+ form,
+ typeOptions,
+ providerOptionGroups,
+ activeProvider,
+ activeProviderOption,
+ allProviders,
+ allProviderNames,
+ getProviderIcon,
+ };
+};
+
+export default useNotificationChannel;
diff --git a/frontend/crawlab-ui/src/components/core/notification/request/NotificationRequestStatus.vue b/frontend/crawlab-ui/src/components/core/notification/request/NotificationRequestStatus.vue
new file mode 100644
index 00000000..7e08ad66
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/request/NotificationRequestStatus.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/notification/setting/CreateEditNotificationSettingDialog.vue b/frontend/crawlab-ui/src/components/core/notification/setting/CreateEditNotificationSettingDialog.vue
new file mode 100644
index 00000000..efc873f2
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/setting/CreateEditNotificationSettingDialog.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/notification/setting/NotificationSettingForm.vue b/frontend/crawlab-ui/src/components/core/notification/setting/NotificationSettingForm.vue
new file mode 100644
index 00000000..b37ec7f8
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/setting/NotificationSettingForm.vue
@@ -0,0 +1,271 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('views.notification.settings.form.useCustomSetting.label') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('common.actions.cancel') }}
+
+
+ {{ t('common.actions.confirm') }}
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/notification/setting/NotificationSettingTriggerSelect.vue b/frontend/crawlab-ui/src/components/core/notification/setting/NotificationSettingTriggerSelect.vue
new file mode 100644
index 00000000..b709ab49
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/setting/NotificationSettingTriggerSelect.vue
@@ -0,0 +1,88 @@
+
+
+
+ emit('trigger-change', val)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ label }}
+
+
+
+
+ {{ data.label }}
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/notification/setting/useNotificationSetting.ts b/frontend/crawlab-ui/src/components/core/notification/setting/useNotificationSetting.ts
new file mode 100644
index 00000000..a55267f3
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/notification/setting/useNotificationSetting.ts
@@ -0,0 +1,37 @@
+import { useRoute } from 'vue-router';
+import { computed, watch } from 'vue';
+import { Store } from 'vuex';
+import useForm from '@/components/ui/form/useForm';
+import useNotificationSettingService from '@/services/notification/useNotificationSettingService';
+import { getDefaultFormComponentData } from '@/utils/form';
+import { setupGetAllList } from '@/utils';
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useNotificationSetting = (store: Store) => {
+ const { notificationSetting: state } = store.state as RootStoreState;
+
+ // route
+ const route = useRoute();
+
+ // notification id
+ const id = computed(() => route.params.id);
+
+ const form = computed(() => state.form);
+
+ setupGetAllList(store, ['notificationAlert']);
+
+ return {
+ ...useForm(
+ 'notificationSetting',
+ store,
+ useNotificationSettingService(store),
+ formComponentData
+ ),
+ id,
+ form,
+ };
+};
+
+export default useNotificationSetting;
diff --git a/frontend/crawlab-ui/src/components/core/project/CreateEditProjectDialog.vue b/frontend/crawlab-ui/src/components/core/project/CreateEditProjectDialog.vue
new file mode 100644
index 00000000..d19064f7
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/project/CreateEditProjectDialog.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/project/ProjectForm.vue b/frontend/crawlab-ui/src/components/core/project/ProjectForm.vue
new file mode 100644
index 00000000..3bc2999a
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/project/ProjectForm.vue
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/project/useProject.ts b/frontend/crawlab-ui/src/components/core/project/useProject.ts
new file mode 100644
index 00000000..9e1784c6
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/project/useProject.ts
@@ -0,0 +1,46 @@
+import { computed, readonly } from 'vue';
+import { Store } from 'vuex';
+import { isDuplicated } from '@/utils/array';
+import useForm from '@/components/ui/form/useForm';
+import useProjectService from '@/services/project/projectService';
+import { getDefaultFormComponentData } from '@/utils/form';
+import {
+ FORM_FIELD_TYPE_INPUT,
+ FORM_FIELD_TYPE_INPUT_TEXTAREA,
+} from '@/constants/form';
+import { translate } from '@/utils/i18n';
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useProject = (store: Store) => {
+ // store
+ const ns = 'project';
+ const state = store.state[ns];
+
+ // form rules
+ const formRules: FormRules = {};
+
+ // all project select options
+ const allProjectSelectOptions = computed(() =>
+ state.allList.map(d => {
+ return {
+ label: d.name,
+ value: d._id,
+ };
+ })
+ );
+
+ return {
+ ...useForm(
+ 'project',
+ store,
+ useProjectService(store),
+ formComponentData
+ ),
+ formRules,
+ allProjectSelectOptions,
+ };
+};
+
+export default useProject;
diff --git a/frontend/crawlab-ui/src/components/core/result/ResultCell.vue b/frontend/crawlab-ui/src/components/core/result/ResultCell.vue
new file mode 100644
index 00000000..ec2f8e0a
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/result/ResultCell.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ value }}
+
+
+
+
+ {{ value }}
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/result/ResultCellDialog.vue b/frontend/crawlab-ui/src/components/core/result/ResultCellDialog.vue
new file mode 100644
index 00000000..b3b063a7
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/result/ResultCellDialog.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/result/ResultDedupFieldsDialog.vue b/frontend/crawlab-ui/src/components/core/result/ResultDedupFieldsDialog.vue
new file mode 100644
index 00000000..88eacd25
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/result/ResultDedupFieldsDialog.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/role/CreateEditRoleDialog.vue b/frontend/crawlab-ui/src/components/core/role/CreateEditRoleDialog.vue
new file mode 100644
index 00000000..9a4843c7
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/role/CreateEditRoleDialog.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/role/RoleForm.vue b/frontend/crawlab-ui/src/components/core/role/RoleForm.vue
new file mode 100644
index 00000000..7f275fd2
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/role/RoleForm.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/role/useRole.ts b/frontend/crawlab-ui/src/components/core/role/useRole.ts
new file mode 100644
index 00000000..f710b836
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/role/useRole.ts
@@ -0,0 +1,20 @@
+import { Store } from 'vuex';
+import useForm from '@/components/ui/form/useForm';
+import useRoleService from '@/services/role/roleService';
+import { getDefaultFormComponentData } from '@/utils/form';
+import { computed } from 'vue';
+import { getRouteSelectOptions } from '@/utils';
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useRole = (store: Store) => {
+ const routesOptions = computed(() => getRouteSelectOptions());
+
+ return {
+ ...useForm('role', store, useRoleService(store), formComponentData),
+ routesOptions,
+ };
+};
+
+export default useRole;
diff --git a/frontend/crawlab-ui/src/components/core/schedule/CreateEditScheduleDialog.vue b/frontend/crawlab-ui/src/components/core/schedule/CreateEditScheduleDialog.vue
new file mode 100644
index 00000000..92fa49f1
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/schedule/CreateEditScheduleDialog.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/schedule/RunScheduleDialog.vue b/frontend/crawlab-ui/src/components/core/schedule/RunScheduleDialog.vue
new file mode 100644
index 00000000..f8df4c93
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/schedule/RunScheduleDialog.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/schedule/ScheduleCron.vue b/frontend/crawlab-ui/src/components/core/schedule/ScheduleCron.vue
new file mode 100644
index 00000000..7b57fac3
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/schedule/ScheduleCron.vue
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isValid ? description : t('common.status.invalid') }}
+
+
+
+
+
+
+
+
+
+ {{ isValid ? next : t('common.status.invalid') }}
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/schedule/ScheduleForm.vue b/frontend/crawlab-ui/src/components/core/schedule/ScheduleForm.vue
new file mode 100644
index 00000000..ca23946f
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/schedule/ScheduleForm.vue
@@ -0,0 +1,229 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ n.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/schedule/useSchedule.ts b/frontend/crawlab-ui/src/components/core/schedule/useSchedule.ts
new file mode 100644
index 00000000..42d84134
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/schedule/useSchedule.ts
@@ -0,0 +1,94 @@
+import { computed, watch } from 'vue';
+import { Store } from 'vuex';
+import useForm from '@/components/ui/form/useForm';
+import useScheduleService from '@/services/schedule/scheduleService';
+import { getDefaultFormComponentData } from '@/utils/form';
+import { parseExpression } from 'cron-parser';
+import { getModeOptions } from '@/utils/task';
+import useSpider from '@/components/core/spider/useSpider';
+import { translate } from '@/utils/i18n';
+import useScheduleDetail from '@/views/schedule/detail/useScheduleDetail';
+
+// i18n
+const t = translate;
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useSchedule = (store: Store) => {
+ // store
+ const ns = 'schedule';
+ const state = store.state[ns];
+
+ const { allDict: allSpiderDict } = useSpider(store);
+
+ // form
+ const form = computed(() => state.form);
+
+ // options for default mode
+ const modeOptions = getModeOptions();
+
+ // form rules
+ const formRules: FormRules = {
+ cron: {
+ trigger: 'blur',
+ validator: (_, value: string, callback) => {
+ const invalidMessage = t(
+ 'components.schedule.rules.message.invalidCronExpression'
+ );
+ if (!value) return callback(invalidMessage);
+ if (value.trim().split(' ').length != 5)
+ return callback(invalidMessage);
+ try {
+ parseExpression(value);
+ callback();
+ } catch (e: any) {
+ callback(e.message);
+ }
+ },
+ },
+ };
+
+ // all schedule select options
+ const allScheduleSelectOptions = computed(() =>
+ state.allList.map(d => {
+ return {
+ label: d.name,
+ value: d._id,
+ };
+ })
+ );
+
+ const { activeId } = useScheduleDetail();
+
+ watch(
+ () => form.value?.spider_id,
+ () => {
+ if (activeId.value) return;
+ if (!form.value?.spider_id) return;
+ const spider = allSpiderDict.value.get(form.value?.spider_id);
+ if (!spider) return;
+ const payload = { ...form.value } as Schedule;
+ if (spider.cmd) payload.cmd = spider.cmd;
+ if (spider.param) payload.param = spider.param;
+ if (spider.mode) payload.mode = spider.mode;
+ if (spider.node_ids?.length) payload.node_ids = spider.node_ids;
+ if (spider.node_tags?.length) payload.node_tags = spider.node_tags;
+ store.commit(`${ns}/setForm`, payload);
+ }
+ );
+
+ return {
+ ...useForm(
+ 'schedule',
+ store,
+ useScheduleService(store),
+ formComponentData
+ ),
+ modeOptions,
+ formRules,
+ allScheduleSelectOptions,
+ };
+};
+
+export default useSchedule;
diff --git a/frontend/crawlab-ui/src/components/core/spider/CreateEditSpiderDialog.vue b/frontend/crawlab-ui/src/components/core/spider/CreateEditSpiderDialog.vue
new file mode 100644
index 00000000..768be7e6
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/spider/CreateEditSpiderDialog.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/spider/RunSpiderDialog.vue b/frontend/crawlab-ui/src/components/core/spider/RunSpiderDialog.vue
new file mode 100644
index 00000000..746ad244
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/spider/RunSpiderDialog.vue
@@ -0,0 +1,197 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ n.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/spider/SpiderForm.vue b/frontend/crawlab-ui/src/components/core/spider/SpiderForm.vue
new file mode 100644
index 00000000..608086c2
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/spider/SpiderForm.vue
@@ -0,0 +1,347 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ op.label }}
+
+
+
+
+
+
+ {{ activeTemplateOption?.label }}
+
+
+
+
+
+ {{ activeTemplateOption?.doc_label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ n.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/spider/SpiderResultDataWithDatabase.vue b/frontend/crawlab-ui/src/components/core/spider/SpiderResultDataWithDatabase.vue
new file mode 100644
index 00000000..fd66d9fd
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/spider/SpiderResultDataWithDatabase.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/spider/SpiderStat.vue b/frontend/crawlab-ui/src/components/core/spider/SpiderStat.vue
new file mode 100644
index 00000000..6f3ab4b3
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/spider/SpiderStat.vue
@@ -0,0 +1,106 @@
+
+
+
+
+
$emit('tasks-click')"
+ />
+ $emit('results-click')"
+ />
+ $emit('duration-click')"
+ >
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/spider/UploadSpiderFilesDialog.vue b/frontend/crawlab-ui/src/components/core/spider/UploadSpiderFilesDialog.vue
new file mode 100644
index 00000000..c705c719
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/spider/UploadSpiderFilesDialog.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/spider/useSpider.ts b/frontend/crawlab-ui/src/components/core/spider/useSpider.ts
new file mode 100644
index 00000000..73b3cd82
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/spider/useSpider.ts
@@ -0,0 +1,37 @@
+import { useRoute } from 'vue-router';
+import { computed } from 'vue';
+import { Store } from 'vuex';
+import useForm from '@/components/ui/form/useForm';
+import useSpiderService from '@/services/spider/spiderService';
+import { getDefaultFormComponentData } from '@/utils/form';
+import useRequest from '@/services/request';
+import { FILTER_OP_CONTAINS } from '@/constants/filter';
+import { getModeOptions } from '@/utils/task';
+import { translate } from '@/utils/i18n';
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useSpider = (store: Store) => {
+ // options for default mode
+ const modeOptions = getModeOptions();
+
+ // route
+ const route = useRoute();
+
+ // spider id
+ const id = computed(() => route.params.id);
+
+ return {
+ ...useForm(
+ 'spider',
+ store,
+ useSpiderService(store),
+ formComponentData
+ ),
+ id,
+ modeOptions,
+ };
+};
+
+export default useSpider;
diff --git a/frontend/crawlab-ui/src/components/core/task/CreateTaskDialog.vue b/frontend/crawlab-ui/src/components/core/task/CreateTaskDialog.vue
new file mode 100644
index 00000000..1d8c114f
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/task/CreateTaskDialog.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/task/TaskCommand.vue b/frontend/crawlab-ui/src/components/core/task/TaskCommand.vue
new file mode 100644
index 00000000..696b6816
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/task/TaskCommand.vue
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/task/TaskForm.vue b/frontend/crawlab-ui/src/components/core/task/TaskForm.vue
new file mode 100644
index 00000000..487c6e59
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/task/TaskForm.vue
@@ -0,0 +1,337 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ n.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/task/TaskMode.vue b/frontend/crawlab-ui/src/components/core/task/TaskMode.vue
new file mode 100644
index 00000000..3279ca99
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/task/TaskMode.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/task/TaskPriority.vue b/frontend/crawlab-ui/src/components/core/task/TaskPriority.vue
new file mode 100644
index 00000000..38f2f140
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/task/TaskPriority.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/task/TaskResultDataWithDatabase.vue b/frontend/crawlab-ui/src/components/core/task/TaskResultDataWithDatabase.vue
new file mode 100644
index 00000000..08ff8251
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/task/TaskResultDataWithDatabase.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/task/TaskResults.vue b/frontend/crawlab-ui/src/components/core/task/TaskResults.vue
new file mode 100644
index 00000000..f9223211
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/task/TaskResults.vue
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/task/TaskStatus.vue b/frontend/crawlab-ui/src/components/core/task/TaskStatus.vue
new file mode 100644
index 00000000..59381606
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/task/TaskStatus.vue
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/task/useTask.ts b/frontend/crawlab-ui/src/components/core/task/useTask.ts
new file mode 100644
index 00000000..31fe9200
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/task/useTask.ts
@@ -0,0 +1,56 @@
+import { useRoute } from 'vue-router';
+import { computed } from 'vue';
+import { Store } from 'vuex';
+import useForm from '@/components/ui/form/useForm';
+import useTaskService from '@/services/task/taskService';
+import { getDefaultFormComponentData } from '@/utils/form';
+import useSpider from '@/components/core/spider/useSpider';
+import {
+ getModeOptions,
+ getModeOptionsDict,
+ getPriorityLabel,
+} from '@/utils/task';
+import { formatTimeAgo } from '@/utils/time';
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useTask = (store: Store) => {
+ const ns = 'task' as ListStoreNamespace;
+ const { task: state } = store.state as RootStoreState;
+
+ // options for default mode
+ const modeOptions = getModeOptions();
+ const modeOptionsDict = computed(() => getModeOptionsDict());
+
+ const { allDict: allSpiderDict } = useSpider(store);
+
+ // route
+ const route = useRoute();
+
+ // task id
+ const id = computed(() => route.params.id);
+
+ const allListSelectOptions = computed(() =>
+ state.allList.map(task => {
+ const spider = allSpiderDict.value.get(task.spider_id!);
+ const timeAgo = formatTimeAgo(task.created_ts!);
+ return {
+ label: `${spider?.name} (${timeAgo})`,
+ value: task._id,
+ };
+ })
+ );
+
+ return {
+ ...useForm('task', store, useTaskService(store), formComponentData),
+ allSpiderDict,
+ id,
+ modeOptions,
+ modeOptionsDict,
+ getPriorityLabel,
+ allListSelectOptions,
+ };
+};
+
+export default useTask;
diff --git a/frontend/crawlab-ui/src/components/core/user/CreateEditUserDialog.vue b/frontend/crawlab-ui/src/components/core/user/CreateEditUserDialog.vue
new file mode 100644
index 00000000..12f5dff6
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/user/CreateEditUserDialog.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/user/UserForm.vue b/frontend/crawlab-ui/src/components/core/user/UserForm.vue
new file mode 100644
index 00000000..47a5ad09
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/user/UserForm.vue
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/user/UserRole.vue b/frontend/crawlab-ui/src/components/core/user/UserRole.vue
new file mode 100644
index 00000000..e1aa6636
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/user/UserRole.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+ {{ computedLabel }}
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/core/user/useUser.ts b/frontend/crawlab-ui/src/components/core/user/useUser.ts
new file mode 100644
index 00000000..37c5a0f0
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/core/user/useUser.ts
@@ -0,0 +1,87 @@
+import { computed } from 'vue';
+import { Store } from 'vuex';
+import useForm from '@/components/ui/form/useForm';
+import useUserService from '@/services/user/userService';
+import { getDefaultFormComponentData } from '@/utils/form';
+import { getModeOptions } from '@/utils/task';
+import { ROLE_ADMIN, ROLE_NORMAL } from '@/constants/user';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { translate } from '@/utils/i18n';
+
+// i18n
+const t = translate;
+
+// form component data
+const formComponentData = getDefaultFormComponentData();
+
+const useUser = (store: Store) => {
+ // store
+ const ns = 'user';
+ const state = store.state[ns];
+
+ // options for default mode
+ const modeOptions = getModeOptions();
+
+ // form rules
+ const formRules: FormRules = {
+ password: {
+ trigger: 'blur',
+ validator: (_, value: string, callback) => {
+ const invalidMessage =
+ 'Invalid password. Length must be no less than 5.';
+ if (0 < value.length && value.length < 5)
+ return callback(invalidMessage);
+ return callback();
+ },
+ },
+ };
+
+ // all user select options
+ const allUserSelectOptions = computed(() =>
+ state.allList.map(d => {
+ return {
+ label: d.username,
+ value: d._id,
+ };
+ })
+ );
+
+ // on change password
+ const onChangePasswordFunc = async (id?: string) => {
+ if (!id) return;
+
+ const { value } = await ElMessageBox.prompt(
+ t('components.user.messageBox.prompt.changePassword'),
+ t('components.user.form.changePassword'),
+ {
+ inputType: 'password',
+ inputPlaceholder: t('components.user.form.newPassword'),
+ inputValidator: (value: string) => {
+ return value?.length < 5
+ ? t('components.user.rules.invalidPassword')
+ : true;
+ },
+ confirmButtonClass: 'edit-user-password-confirm-btn',
+ cancelButtonClass: 'edit-user-password-cancel-btn',
+ }
+ );
+ await store.dispatch(`${ns}/changePassword`, { id, password: value });
+ ElMessage.success(t('common.message.success.save'));
+ };
+
+ const rolesOptions: SelectOption[] = [
+ { label: t('components.user.role.admin'), value: ROLE_ADMIN },
+ { label: t('components.user.role.normal'), value: ROLE_NORMAL },
+ ];
+
+ return {
+ ...useForm('user', store, useUserService(store), formComponentData),
+ modeOptions,
+ formRules,
+ allUserSelectOptions,
+ onChangePasswordFunc,
+ rolesOptions,
+ };
+};
+
+export default useUser;
diff --git a/frontend/crawlab-ui/src/components/index.ts b/frontend/crawlab-ui/src/components/index.ts
new file mode 100644
index 00000000..e85ecaf3
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/index.ts
@@ -0,0 +1,487 @@
+import * as action from './ui/table/action';
+import * as autoLink from './ui/lexical/utils/autoLink';
+import * as column from './ui/table/column';
+import * as data from './ui/table/data';
+import * as date from './ui/date/date';
+import * as fileEditorDropZone from './ui/file/fileEditorDropZone';
+import * as filter from './ui/filter/filter';
+import * as formTable from './ui/form/formTable';
+import * as getSelectedNode from './ui/lexical/utils/getSelectedNode';
+import * as header from './ui/table/header';
+import * as icon from './ui/icon/icon';
+import * as ImageNode from './ui/lexical/nodes/ImageNode';
+import * as LexicalDecoratedTeleports from './ui/lexical/plugins/LexicalDecoratedTeleports';
+import * as markdownTransformers from './ui/lexical/utils/markdownTransformers';
+import * as node from './ui/lexical/utils/node';
+import * as pagination from './ui/table/pagination';
+import * as store from './ui/table/store';
+import * as theme from './ui/lexical/utils/theme';
+import * as VariableNode from './ui/lexical/nodes/VariableNode';
+import AssistantConsole from './core/ai/AssistantConsole.vue';
+import AtomMaterialIcon from './ui/icon/AtomMaterialIcon.vue';
+import BlockOptionsDropdownList from './ui/lexical/components/BlockOptionsDropdownList.vue';
+import Box from './ui/box/Box.vue';
+import Button from './ui/button/Button.vue';
+import ButtonGroup from './ui/button/ButtonGroup.vue';
+import Chart from './ui/chart/Chart.vue';
+import ChatConfigDialog from './ui/chat/ChatConfigDialog.vue';
+import ChatHistory from './ui/chat/ChatHistory.vue';
+import ChatInput from './ui/chat/ChatInput.vue';
+import ChatMessage from './ui/chat/ChatMessage.vue';
+import ChatMessageAction from './ui/chat/ChatMessageAction.vue';
+import ChatMessageList from './ui/chat/ChatMessageList.vue';
+import ChatSidebar from './ui/chat/ChatSidebar.vue';
+import CheckboxTree from './ui/checkbox/CheckboxTree.vue';
+import CheckboxTreeGroup from './ui/checkbox/CheckboxTreeGroup.vue';
+import CheckTag from './ui/tag/CheckTag.vue';
+import CheckTagGroup from './ui/tag/CheckTagGroup.vue';
+import ConfirmDialog from './ui/dialog/ConfirmDialog.vue';
+import ContextMenu from './ui/context-menu/ContextMenu.vue';
+import ContextMenuList from './ui/context-menu/ContextMenuList.vue';
+import CreateEditDatabaseDialog from './core/database/CreateEditDatabaseDialog.vue';
+import CreateEditDatabaseTableDialog from './core/database/CreateEditDatabaseTableDialog.vue';
+import CreateEditDialog from './ui/dialog/CreateEditDialog.vue';
+import CreateEditEnvironmentDialog from './core/environment/CreateEditEnvironmentDialog.vue';
+import CreateEditGitDialog from './core/git/CreateEditGitDialog.vue';
+import CreateEditNodeDialog from './core/node/CreateEditNodeDialog.vue';
+import CreateEditNotificationAlertDialog from './core/notification/alert/CreateEditNotificationAlertDialog.vue';
+import CreateEditNotificationChannelDialog from './core/notification/channel/CreateEditNotificationChannelDialog.vue';
+import CreateEditNotificationSettingDialog from './core/notification/setting/CreateEditNotificationSettingDialog.vue';
+import CreateEditProjectDialog from './core/project/CreateEditProjectDialog.vue';
+import CreateEditRoleDialog from './core/role/CreateEditRoleDialog.vue';
+import CreateEditScheduleDialog from './core/schedule/CreateEditScheduleDialog.vue';
+import CreateEditSpiderDialog from './core/spider/CreateEditSpiderDialog.vue';
+import CreateEditUserDialog from './core/user/CreateEditUserDialog.vue';
+import CreateGitBranchDialog from './core/git/CreateGitBranchDialog.vue';
+import CreateGitSpiderDialog from './core/git/CreateGitSpiderDialog.vue';
+import CreateTaskDialog from './core/task/CreateTaskDialog.vue';
+import CurrentMetrics from './core/metric/CurrentMetrics.vue';
+import DatabaseDatabaseDetail from './core/database/DatabaseDatabaseDetail.vue';
+import DatabaseDataSource from './core/database/DatabaseDataSource.vue';
+import DatabaseForm from './core/database/DatabaseForm.vue';
+import DatabaseNavTabs from './core/database/nav/DatabaseNavTabs.vue';
+import DatabaseSidebar from './core/database/DatabaseSidebar.vue';
+import DatabaseStatus from './core/database/DatabaseStatus.vue';
+import DatabaseTableDetail from './core/database/DatabaseTableDetail.vue';
+import DatabaseTableDetailColumns from './core/database/tables/DatabaseTableDetailColumns.vue';
+import DatabaseTableDetailData from './core/database/tables/DatabaseTableDetailData.vue';
+import DatabaseTableDetailIndexes from './core/database/tables/DatabaseTableDetailIndexes.vue';
+import DateRangePicker from './ui/date/DateRangePicker.vue';
+import DateTimeRangePicker from './ui/date/DateTimeRangePicker.vue';
+import DependencyConfigDialog from './core/dependency/DependencyConfigDialog.vue';
+import DependencyInstallDialog from './core/dependency/DependencyInstallDialog.vue';
+import DependencyLogsDialog from './core/dependency/DependencyLogsDialog.vue';
+import DependencySetupDialog from './core/dependency/DependencySetupDialog.vue';
+import DependencyStatusTag from './core/dependency/DependencyStatusTag.vue';
+import DependencyUninstallDialog from './core/dependency/DependencyUninstallDialog.vue';
+import DependencyVersions from './core/dependency/DependencyVersions.vue';
+import DetailTabList from './ui/list/DetailTabList.vue';
+import Dialog from './ui/dialog/Dialog.vue';
+import DraggableItem from './ui/drag/DraggableItem.vue';
+import DraggableList from './ui/drag/DraggableList.vue';
+import DropdownButton from './ui/lexical/components/DropdownButton.vue';
+import Duration from './ui/time/Duration.vue';
+import EditInput from './ui/input/EditInput.vue';
+import EditorOptionsDropdownList from './ui/editor/EditorOptionsDropdownList.vue';
+import EditTable from './ui/table/EditTable.vue';
+import EditTableActionCell from './ui/table/EditTableActionCell.vue';
+import Empty from './ui/empty/Empty.vue';
+import EnvironmentForm from './core/environment/EnvironmentForm.vue';
+import ExportButton from './ui/button/ExportButton.vue';
+import ExportForm from './ui/export/ExportForm.vue';
+import FaIconButton from './ui/button/FaIconButton.vue';
+import FileActions from './ui/file/FileActions.vue';
+import FileDiff from './ui/file/FileDiff.vue';
+import FileEditor from './ui/file/FileEditor.vue';
+import FileEditorCreateWithAiDialog from './ui/file/FileEditorCreateWithAiDialog.vue';
+import FileEditorNavMenu from './ui/file/FileEditorNavMenu.vue';
+import FileEditorNavMenuContextMenu from './ui/file/FileEditorNavMenuContextMenu.vue';
+import FileEditorNavTabs from './ui/file/FileEditorNavTabs.vue';
+import FileEditorNavTabsContextMenu from './ui/file/FileEditorNavTabsContextMenu.vue';
+import FileEditorNavTabsShowMoreContextMenu from './ui/file/FileEditorNavTabsShowMoreContextMenu.vue';
+import FileEditorSettingsDialog from './ui/file/FileEditorSettingsDialog.vue';
+import FileTab from './ui/file/FileTab.vue';
+import FileUpload from './ui/file/FileUpload.vue';
+import FilterCondition from './ui/filter/FilterCondition.vue';
+import FilterConditionList from './ui/filter/FilterConditionList.vue';
+import FilterInput from './ui/filter/FilterInput.vue';
+import FilterSelect from './ui/filter/FilterSelect.vue';
+import FloatLinkEditor from './ui/lexical/components/FloatLinkEditor.vue';
+import Form from './ui/form/Form.vue';
+import FormItem from './ui/form/FormItem.vue';
+import FormTableField from './ui/form/FormTableField.vue';
+import GitBranchSelect from './core/git/GitBranchSelect.vue';
+import GitCloneLogsDialog from './core/git/GitCloneLogsDialog.vue';
+import GitFileDiffDialog from './core/git/GitFileDiffDialog.vue';
+import GitFileStatus from './core/git/GitFileStatus.vue';
+import GitForm from './core/git/GitForm.vue';
+import GitHubStarBadge from './ui/badge/GitHubStarBadge.vue';
+import GitLogsBox from './core/git/GitLogsBox.vue';
+import GitPath from './core/git/GitPath.vue';
+import GitRepo from './core/git/GitRepo.vue';
+import GitStatus from './core/git/GitStatus.vue';
+import Icon from './ui/icon/Icon.vue';
+import IconButton from './ui/button/IconButton.vue';
+import ImageComponent from './ui/lexical/components/ImageComponent.vue';
+import ImgEmpty from './ui/empty/ImgEmpty.vue';
+import InputList from './ui/input/InputList.vue';
+import InputSelect from './ui/select/InputSelect.vue';
+import InsertImageDialog from './ui/lexical/components/InsertImageDialog.vue';
+import InsertOptionsDropdownList from './ui/lexical/components/InsertOptionsDropdownList.vue';
+import InsertTableDialog from './ui/lexical/components/InsertTableDialog.vue';
+import InsertVariableDialog from './ui/lexical/components/InsertVariableDialog.vue';
+import LabelButton from './ui/button/LabelButton.vue';
+import LexicalAutoFocusPlugin from './ui/lexical/plugins/LexicalAutoFocusPlugin.vue';
+import LexicalAutoLinkPlugin from './ui/lexical/plugins/LexicalAutoLinkPlugin.vue';
+import LexicalClickableLinkPlugin from './ui/lexical/plugins/LexicalClickableLinkPlugin.vue';
+import LexicalContentEditable from './ui/lexical/plugins/LexicalContentEditable.vue';
+import LexicalEditor from './ui/lexical/LexicalEditor.vue';
+import LexicalImagePlugin from './ui/lexical/plugins/LexicalImagePlugin.vue';
+import LexicalLinkPlugin from './ui/lexical/plugins/LexicalLinkPlugin.vue';
+import LexicalListPlugin from './ui/lexical/plugins/LexicalListPlugin.vue';
+import LexicalRichTextPlugin from './ui/lexical/plugins/LexicalRichTextPlugin.vue';
+import LexicalTablePlugin from './ui/lexical/plugins/LexicalTablePlugin.vue';
+import LexicalToolbarPlugin from './ui/lexical/plugins/LexicalToolbarPlugin.vue';
+import LexicalVariablePlugin from './ui/lexical/plugins/LexicalVariablePlugin.vue';
+import LinkTag from './ui/tag/LinkTag.vue';
+import LoadingText from './ui/loading/LoadingText.vue';
+import LogsView from './ui/logs/LogsView.vue';
+import MarkdownEditor from './ui/markdown/MarkdownEditor.vue';
+import MarkdownEditorToolbar from './ui/markdown/MarkdownEditorToolbar.vue';
+import MenuItemIcon from './ui/icon/MenuItemIcon.vue';
+import Metric from './ui/chart/Metric.vue';
+import MetricMonitoringDetail from './core/metric/MetricMonitoringDetail.vue';
+import NavActionBack from './ui/nav/NavActionBack.vue';
+import NavActionButton from './ui/nav/NavActionButton.vue';
+import NavActionFaIcon from './ui/nav/NavActionFaIcon.vue';
+import NavActionGroup from './ui/nav/NavActionGroup.vue';
+import NavActionGroupDetailCommon from './ui/nav/NavActionGroupDetailCommon.vue';
+import NavActionItem from './ui/nav/NavActionItem.vue';
+import NavActions from './ui/nav/NavActions.vue';
+import NavLink from './ui/nav/NavLink.vue';
+import NavSidebar from './ui/nav/NavSidebar.vue';
+import NavSidebarList from './ui/nav/NavSidebarList.vue';
+import NavSidebarTree from './ui/nav/NavSidebarTree.vue';
+import NavTabs from './ui/nav/NavTabs.vue';
+import NodeActive from './core/node/NodeActive.vue';
+import NodeForm from './core/node/NodeForm.vue';
+import NodeRunners from './core/node/NodeRunners.vue';
+import NodeStatus from './core/node/NodeStatus.vue';
+import NodeTag from './core/node/NodeTag.vue';
+import NodeType from './core/node/NodeType.vue';
+import NotificationAlertForm from './core/notification/alert/NotificationAlertForm.vue';
+import NotificationChannelForm from './core/notification/channel/NotificationChannelForm.vue';
+import NotificationRequestStatus from './core/notification/request/NotificationRequestStatus.vue';
+import NotificationSettingForm from './core/notification/setting/NotificationSettingForm.vue';
+import NotificationSettingTriggerSelect from './core/notification/setting/NotificationSettingTriggerSelect.vue';
+import Option from './ui/select/Option.vue';
+import ProjectForm from './core/project/ProjectForm.vue';
+import RangePicker from './ui/date/RangePicker.vue';
+import ResizeHandle from './ui/resize/ResizeHandle.vue';
+import ResultCell from './core/result/ResultCell.vue';
+import ResultCellDialog from './core/result/ResultCellDialog.vue';
+import ResultDedupFieldsDialog from './core/result/ResultDedupFieldsDialog.vue';
+import RoleForm from './core/role/RoleForm.vue';
+import RunScheduleDialog from './core/schedule/RunScheduleDialog.vue';
+import RunSpiderDialog from './core/spider/RunSpiderDialog.vue';
+import ScheduleCron from './core/schedule/ScheduleCron.vue';
+import ScheduleForm from './core/schedule/ScheduleForm.vue';
+import Select from './ui/select/Select.vue';
+import SpiderForm from './core/spider/SpiderForm.vue';
+import SpiderResultDataWithDatabase from './core/spider/SpiderResultDataWithDatabase.vue';
+import SpiderStat from './core/spider/SpiderStat.vue';
+import Switch from './ui/switch/Switch.vue';
+import Table from './ui/table/Table.vue';
+import TableActions from './ui/table/TableActions.vue';
+import TableCell from './ui/table/TableCell.vue';
+import TableColumnsTransfer from './ui/table/TableColumnsTransfer.vue';
+import TableEditCell from './ui/table/TableEditCell.vue';
+import TableHeader from './ui/table/TableHeader.vue';
+import TableHeaderAction from './ui/table/TableHeaderAction.vue';
+import TableHeaderDialog from './ui/table/TableHeaderDialog.vue';
+import TableHeaderDialogFilter from './ui/table/TableHeaderDialogFilter.vue';
+import TableHeaderDialogSort from './ui/table/TableHeaderDialogSort.vue';
+import Tag from './ui/tag/Tag.vue';
+import TaskCommand from './core/task/TaskCommand.vue';
+import TaskForm from './core/task/TaskForm.vue';
+import TaskMode from './core/task/TaskMode.vue';
+import TaskPriority from './core/task/TaskPriority.vue';
+import TaskResultDataWithDatabase from './core/task/TaskResultDataWithDatabase.vue';
+import TaskResults from './core/task/TaskResults.vue';
+import TaskStatus from './core/task/TaskStatus.vue';
+import Time from './ui/time/Time.vue';
+import Tip from './ui/tip/Tip.vue';
+import Transfer from './ui/transfer/Transfer.vue';
+import TransferPanel from './ui/transfer/TransferPanel.vue';
+import UploadFilesDialog from './ui/file/UploadFilesDialog.vue';
+import UploadGitFilesDialog from './core/git/UploadGitFilesDialog.vue';
+import UploadSpiderFilesDialog from './core/spider/UploadSpiderFilesDialog.vue';
+import useCanShowPlaceholder from './ui/lexical/composables/useCanShowPlaceholder';
+import useDatabase from './core/database/useDatabase';
+import useDecorators from './ui/lexical/composables/useDecorators';
+import useEnvironment from './core/environment/useEnvironment';
+import useForm from './ui/form/useForm';
+import useGit from './core/git/useGit';
+import useLexicalEffect from './ui/lexical/composables/useLexicalEffect';
+import useLexicalList from './ui/lexical/composables/useLexicalList';
+import useLexicalMounted from './ui/lexical/composables/useLexicalMounted';
+import useNode from './core/node/useNode';
+import useNotificationAlert from './core/notification/alert/useNotificationAlert';
+import useNotificationChannel from './core/notification/channel/useNotificationChannel';
+import useNotificationSetting from './core/notification/setting/useNotificationSetting';
+import useProject from './core/project/useProject';
+import UserAvatar from './ui/avatar/UserAvatar.vue';
+import UserForm from './core/user/UserForm.vue';
+import useRichTextSetup from './ui/lexical/composables/useRichTextSetup';
+import useRole from './core/role/useRole';
+import UserRole from './core/user/UserRole.vue';
+import useSchedule from './core/schedule/useSchedule';
+import useSpider from './core/spider/useSpider';
+import useTask from './core/task/useTask';
+import useUser from './core/user/useUser';
+import useVariableSetup from './ui/lexical/composables/useVariableSetup';
+
+export {
+ action as action,
+ autoLink as autoLink,
+ column as column,
+ data as data,
+ date as date,
+ fileEditorDropZone as fileEditorDropZone,
+ filter as filter,
+ formTable as formTable,
+ getSelectedNode as getSelectedNode,
+ header as header,
+ icon as icon,
+ ImageNode as ImageNode,
+ LexicalDecoratedTeleports as LexicalDecoratedTeleports,
+ markdownTransformers as markdownTransformers,
+ node as node,
+ pagination as pagination,
+ store as store,
+ theme as theme,
+ VariableNode as VariableNode,
+ AssistantConsole as ClAssistantConsole,
+ AtomMaterialIcon as ClAtomMaterialIcon,
+ BlockOptionsDropdownList as ClBlockOptionsDropdownList,
+ Box as ClBox,
+ Button as ClButton,
+ ButtonGroup as ClButtonGroup,
+ Chart as ClChart,
+ ChatConfigDialog as ClChatConfigDialog,
+ ChatHistory as ClChatHistory,
+ ChatInput as ClChatInput,
+ ChatMessage as ClChatMessage,
+ ChatMessageAction as ClChatMessageAction,
+ ChatMessageList as ClChatMessageList,
+ ChatSidebar as ClChatSidebar,
+ CheckboxTree as ClCheckboxTree,
+ CheckboxTreeGroup as ClCheckboxTreeGroup,
+ CheckTag as ClCheckTag,
+ CheckTagGroup as ClCheckTagGroup,
+ ConfirmDialog as ClConfirmDialog,
+ ContextMenu as ClContextMenu,
+ ContextMenuList as ClContextMenuList,
+ CreateEditDatabaseDialog as ClCreateEditDatabaseDialog,
+ CreateEditDatabaseTableDialog as ClCreateEditDatabaseTableDialog,
+ CreateEditDialog as ClCreateEditDialog,
+ CreateEditEnvironmentDialog as ClCreateEditEnvironmentDialog,
+ CreateEditGitDialog as ClCreateEditGitDialog,
+ CreateEditNodeDialog as ClCreateEditNodeDialog,
+ CreateEditNotificationAlertDialog as ClCreateEditNotificationAlertDialog,
+ CreateEditNotificationChannelDialog as ClCreateEditNotificationChannelDialog,
+ CreateEditNotificationSettingDialog as ClCreateEditNotificationSettingDialog,
+ CreateEditProjectDialog as ClCreateEditProjectDialog,
+ CreateEditRoleDialog as ClCreateEditRoleDialog,
+ CreateEditScheduleDialog as ClCreateEditScheduleDialog,
+ CreateEditSpiderDialog as ClCreateEditSpiderDialog,
+ CreateEditUserDialog as ClCreateEditUserDialog,
+ CreateGitBranchDialog as ClCreateGitBranchDialog,
+ CreateGitSpiderDialog as ClCreateGitSpiderDialog,
+ CreateTaskDialog as ClCreateTaskDialog,
+ CurrentMetrics as ClCurrentMetrics,
+ DatabaseDatabaseDetail as ClDatabaseDatabaseDetail,
+ DatabaseDataSource as ClDatabaseDataSource,
+ DatabaseForm as ClDatabaseForm,
+ DatabaseNavTabs as ClDatabaseNavTabs,
+ DatabaseSidebar as ClDatabaseSidebar,
+ DatabaseStatus as ClDatabaseStatus,
+ DatabaseTableDetail as ClDatabaseTableDetail,
+ DatabaseTableDetailColumns as ClDatabaseTableDetailColumns,
+ DatabaseTableDetailData as ClDatabaseTableDetailData,
+ DatabaseTableDetailIndexes as ClDatabaseTableDetailIndexes,
+ DateRangePicker as ClDateRangePicker,
+ DateTimeRangePicker as ClDateTimeRangePicker,
+ DependencyConfigDialog as ClDependencyConfigDialog,
+ DependencyInstallDialog as ClDependencyInstallDialog,
+ DependencyLogsDialog as ClDependencyLogsDialog,
+ DependencySetupDialog as ClDependencySetupDialog,
+ DependencyStatusTag as ClDependencyStatusTag,
+ DependencyUninstallDialog as ClDependencyUninstallDialog,
+ DependencyVersions as ClDependencyVersions,
+ DetailTabList as ClDetailTabList,
+ Dialog as ClDialog,
+ DraggableItem as ClDraggableItem,
+ DraggableList as ClDraggableList,
+ DropdownButton as ClDropdownButton,
+ Duration as ClDuration,
+ EditInput as ClEditInput,
+ EditorOptionsDropdownList as ClEditorOptionsDropdownList,
+ EditTable as ClEditTable,
+ EditTableActionCell as ClEditTableActionCell,
+ Empty as ClEmpty,
+ EnvironmentForm as ClEnvironmentForm,
+ ExportButton as ClExportButton,
+ ExportForm as ClExportForm,
+ FaIconButton as ClFaIconButton,
+ FileActions as ClFileActions,
+ FileDiff as ClFileDiff,
+ FileEditor as ClFileEditor,
+ FileEditorCreateWithAiDialog as ClFileEditorCreateWithAiDialog,
+ FileEditorNavMenu as ClFileEditorNavMenu,
+ FileEditorNavMenuContextMenu as ClFileEditorNavMenuContextMenu,
+ FileEditorNavTabs as ClFileEditorNavTabs,
+ FileEditorNavTabsContextMenu as ClFileEditorNavTabsContextMenu,
+ FileEditorNavTabsShowMoreContextMenu as ClFileEditorNavTabsShowMoreContextMenu,
+ FileEditorSettingsDialog as ClFileEditorSettingsDialog,
+ FileTab as ClFileTab,
+ FileUpload as ClFileUpload,
+ FilterCondition as ClFilterCondition,
+ FilterConditionList as ClFilterConditionList,
+ FilterInput as ClFilterInput,
+ FilterSelect as ClFilterSelect,
+ FloatLinkEditor as ClFloatLinkEditor,
+ Form as ClForm,
+ FormItem as ClFormItem,
+ FormTableField as ClFormTableField,
+ GitBranchSelect as ClGitBranchSelect,
+ GitCloneLogsDialog as ClGitCloneLogsDialog,
+ GitFileDiffDialog as ClGitFileDiffDialog,
+ GitFileStatus as ClGitFileStatus,
+ GitForm as ClGitForm,
+ GitHubStarBadge as ClGitHubStarBadge,
+ GitLogsBox as ClGitLogsBox,
+ GitPath as ClGitPath,
+ GitRepo as ClGitRepo,
+ GitStatus as ClGitStatus,
+ Icon as ClIcon,
+ IconButton as ClIconButton,
+ ImageComponent as ClImageComponent,
+ ImgEmpty as ClImgEmpty,
+ InputList as ClInputList,
+ InputSelect as ClInputSelect,
+ InsertImageDialog as ClInsertImageDialog,
+ InsertOptionsDropdownList as ClInsertOptionsDropdownList,
+ InsertTableDialog as ClInsertTableDialog,
+ InsertVariableDialog as ClInsertVariableDialog,
+ LabelButton as ClLabelButton,
+ LexicalAutoFocusPlugin as ClLexicalAutoFocusPlugin,
+ LexicalAutoLinkPlugin as ClLexicalAutoLinkPlugin,
+ LexicalClickableLinkPlugin as ClLexicalClickableLinkPlugin,
+ LexicalContentEditable as ClLexicalContentEditable,
+ LexicalEditor as ClLexicalEditor,
+ LexicalImagePlugin as ClLexicalImagePlugin,
+ LexicalLinkPlugin as ClLexicalLinkPlugin,
+ LexicalListPlugin as ClLexicalListPlugin,
+ LexicalRichTextPlugin as ClLexicalRichTextPlugin,
+ LexicalTablePlugin as ClLexicalTablePlugin,
+ LexicalToolbarPlugin as ClLexicalToolbarPlugin,
+ LexicalVariablePlugin as ClLexicalVariablePlugin,
+ LinkTag as ClLinkTag,
+ LoadingText as ClLoadingText,
+ LogsView as ClLogsView,
+ MarkdownEditor as ClMarkdownEditor,
+ MarkdownEditorToolbar as ClMarkdownEditorToolbar,
+ MenuItemIcon as ClMenuItemIcon,
+ Metric as ClMetric,
+ MetricMonitoringDetail as ClMetricMonitoringDetail,
+ NavActionBack as ClNavActionBack,
+ NavActionButton as ClNavActionButton,
+ NavActionFaIcon as ClNavActionFaIcon,
+ NavActionGroup as ClNavActionGroup,
+ NavActionGroupDetailCommon as ClNavActionGroupDetailCommon,
+ NavActionItem as ClNavActionItem,
+ NavActions as ClNavActions,
+ NavLink as ClNavLink,
+ NavSidebar as ClNavSidebar,
+ NavSidebarList as ClNavSidebarList,
+ NavSidebarTree as ClNavSidebarTree,
+ NavTabs as ClNavTabs,
+ NodeActive as ClNodeActive,
+ NodeForm as ClNodeForm,
+ NodeRunners as ClNodeRunners,
+ NodeStatus as ClNodeStatus,
+ NodeTag as ClNodeTag,
+ NodeType as ClNodeType,
+ NotificationAlertForm as ClNotificationAlertForm,
+ NotificationChannelForm as ClNotificationChannelForm,
+ NotificationRequestStatus as ClNotificationRequestStatus,
+ NotificationSettingForm as ClNotificationSettingForm,
+ NotificationSettingTriggerSelect as ClNotificationSettingTriggerSelect,
+ Option as ClOption,
+ ProjectForm as ClProjectForm,
+ RangePicker as ClRangePicker,
+ ResizeHandle as ClResizeHandle,
+ ResultCell as ClResultCell,
+ ResultCellDialog as ClResultCellDialog,
+ ResultDedupFieldsDialog as ClResultDedupFieldsDialog,
+ RoleForm as ClRoleForm,
+ RunScheduleDialog as ClRunScheduleDialog,
+ RunSpiderDialog as ClRunSpiderDialog,
+ ScheduleCron as ClScheduleCron,
+ ScheduleForm as ClScheduleForm,
+ Select as ClSelect,
+ SpiderForm as ClSpiderForm,
+ SpiderResultDataWithDatabase as ClSpiderResultDataWithDatabase,
+ SpiderStat as ClSpiderStat,
+ Switch as ClSwitch,
+ Table as ClTable,
+ TableActions as ClTableActions,
+ TableCell as ClTableCell,
+ TableColumnsTransfer as ClTableColumnsTransfer,
+ TableEditCell as ClTableEditCell,
+ TableHeader as ClTableHeader,
+ TableHeaderAction as ClTableHeaderAction,
+ TableHeaderDialog as ClTableHeaderDialog,
+ TableHeaderDialogFilter as ClTableHeaderDialogFilter,
+ TableHeaderDialogSort as ClTableHeaderDialogSort,
+ Tag as ClTag,
+ TaskCommand as ClTaskCommand,
+ TaskForm as ClTaskForm,
+ TaskMode as ClTaskMode,
+ TaskPriority as ClTaskPriority,
+ TaskResultDataWithDatabase as ClTaskResultDataWithDatabase,
+ TaskResults as ClTaskResults,
+ TaskStatus as ClTaskStatus,
+ Time as ClTime,
+ Tip as ClTip,
+ Transfer as ClTransfer,
+ TransferPanel as ClTransferPanel,
+ UploadFilesDialog as ClUploadFilesDialog,
+ UploadGitFilesDialog as ClUploadGitFilesDialog,
+ UploadSpiderFilesDialog as ClUploadSpiderFilesDialog,
+ useCanShowPlaceholder as useCanShowPlaceholder,
+ useDatabase as useDatabase,
+ useDecorators as useDecorators,
+ useEnvironment as useEnvironment,
+ useForm as useForm,
+ useGit as useGit,
+ useLexicalEffect as useLexicalEffect,
+ useLexicalList as useLexicalList,
+ useLexicalMounted as useLexicalMounted,
+ useNode as useNode,
+ useNotificationAlert as useNotificationAlert,
+ useNotificationChannel as useNotificationChannel,
+ useNotificationSetting as useNotificationSetting,
+ useProject as useProject,
+ UserAvatar as ClUserAvatar,
+ UserForm as ClUserForm,
+ useRichTextSetup as useRichTextSetup,
+ useRole as useRole,
+ UserRole as ClUserRole,
+ useSchedule as useSchedule,
+ useSpider as useSpider,
+ useTask as useTask,
+ useUser as useUser,
+ useVariableSetup as useVariableSetup,
+};
diff --git a/frontend/crawlab-ui/src/components/ui/avatar/UserAvatar.vue b/frontend/crawlab-ui/src/components/ui/avatar/UserAvatar.vue
new file mode 100644
index 00000000..01067191
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/avatar/UserAvatar.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+ emit('click', e)"
+ >
+
+
+
+ {{ userLabel }}
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/badge/GitHubStarBadge.vue b/frontend/crawlab-ui/src/components/ui/badge/GitHubStarBadge.vue
new file mode 100644
index 00000000..0da131d3
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/badge/GitHubStarBadge.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/box/Box.vue b/frontend/crawlab-ui/src/components/ui/box/Box.vue
new file mode 100644
index 00000000..6e8a3182
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/box/Box.vue
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/button/Button.vue b/frontend/crawlab-ui/src/components/ui/button/Button.vue
new file mode 100644
index 00000000..329b9201
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/button/Button.vue
@@ -0,0 +1,57 @@
+
+
+
+
+ emit('click', event)"
+ @mouseenter="(event: Event) => emit('mouseenter', event)"
+ @mouseleave="(event: Event) => emit('mouseleave', event)"
+ >
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/button/ButtonGroup.vue b/frontend/crawlab-ui/src/components/ui/button/ButtonGroup.vue
new file mode 100644
index 00000000..02c9232f
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/button/ButtonGroup.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+ (visible = false)"
+ />
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/button/ExportButton.vue b/frontend/crawlab-ui/src/components/ui/button/ExportButton.vue
new file mode 100644
index 00000000..eafa4c13
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/button/ExportButton.vue
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/button/FaIconButton.vue b/frontend/crawlab-ui/src/components/ui/button/FaIconButton.vue
new file mode 100644
index 00000000..e44e9f2f
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/button/FaIconButton.vue
@@ -0,0 +1,50 @@
+
+
+
+ emit('click', event)"
+ @mouseenter="(event: Event) => emit('mouseenter', event)"
+ @mouseleave="(event: Event) => emit('mouseleave', event)"
+ >
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/button/IconButton.vue b/frontend/crawlab-ui/src/components/ui/button/IconButton.vue
new file mode 100644
index 00000000..0f589ec4
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/button/IconButton.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+ emit('click', event)"
+ @mouseenter="(event: Event) => emit('mouseenter', event)"
+ @mouseleave="(event: Event) => emit('mouseleave', event)"
+ />
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/button/LabelButton.vue b/frontend/crawlab-ui/src/components/ui/button/LabelButton.vue
new file mode 100644
index 00000000..c5e0029c
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/button/LabelButton.vue
@@ -0,0 +1,39 @@
+
+
+
+ emit('click', event)"
+ @mouseenter="(event: Event) => emit('mouseenter', event)"
+ @mouseleave="(event: Event) => emit('mouseleave', event)"
+ >
+
+
+
+ {{ label }}
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/button/types.d.ts b/frontend/crawlab-ui/src/components/ui/button/types.d.ts
new file mode 100644
index 00000000..2a47f9c8
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/button/types.d.ts
@@ -0,0 +1,59 @@
+import type { TooltipTriggerType } from 'element-plus/es/components/tooltip/src/trigger';
+import type { ContextMenuItem } from '@/components/ui/context-menu/types';
+
+export interface ButtonProps {
+ tooltip?: string;
+ type?: BasicType;
+ size?: BasicSize;
+ round?: boolean;
+ circle?: boolean;
+ plain?: boolean;
+ disabled?: boolean;
+ isIcon?: boolean;
+ loading?: boolean;
+ onClick?: () => void;
+ className?: string;
+ id?: string;
+ noMargin?: boolean;
+}
+
+export interface IconButtonProps extends ButtonProps {
+ icon: Icon;
+}
+
+export interface FaIconButtonProps extends IconButtonProps {
+ badgeIcon?: Icon;
+ spin?: boolean;
+}
+
+export interface LabelButtonProps extends ButtonProps {
+ label?: string;
+ icon?: Icon;
+}
+
+type GenericButtonProps = (
+ | ButtonProps
+ | IconButtonProps
+ | FaIconButtonProps
+ | LabelButtonProps
+) & {
+ buttonType?: ButtonType;
+};
+
+export interface ButtonGroupProps {
+ buttons?: GenericButtonProps[];
+ dropdownItems?: ContextMenuItem[];
+ dropdownTrigger?: TooltipTriggerType;
+ type?: BasicType;
+ size?: BasicSize;
+}
+
+export interface ButtonEmits {
+ (e: 'click', event: Event): void;
+
+ (e: 'mouseenter', event: Event): void;
+
+ (e: 'mouseleave', event: Event): void;
+}
+
+export type ButtonType = 'button' | 'fa-icon' | 'icon' | 'label';
diff --git a/frontend/crawlab-ui/src/components/ui/chart/Chart.vue b/frontend/crawlab-ui/src/components/ui/chart/Chart.vue
new file mode 100644
index 00000000..b69c8c11
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/chart/Chart.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/chart/Metric.vue b/frontend/crawlab-ui/src/components/ui/chart/Metric.vue
new file mode 100644
index 00000000..8ba4a164
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/chart/Metric.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+ {{ t(title || '') }}
+
+
+ {{ value }}
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/chat/ChatConfigDialog.vue b/frontend/crawlab-ui/src/components/ui/chat/ChatConfigDialog.vue
new file mode 100644
index 00000000..66e300d1
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/chat/ChatConfigDialog.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/chat/ChatHistory.vue b/frontend/crawlab-ui/src/components/ui/chat/ChatHistory.vue
new file mode 100644
index 00000000..94108282
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/chat/ChatHistory.vue
@@ -0,0 +1,177 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getConversationTitle(conversation) }}
+
+
+
+
+
+
+ {{ conversation.model }}
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/crawlab-ui/src/components/ui/chat/ChatInput.vue b/frontend/crawlab-ui/src/components/ui/chat/ChatInput.vue
new file mode 100644
index 00000000..fddea95c
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/chat/ChatInput.vue
@@ -0,0 +1,447 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/chat/ChatMessage.vue b/frontend/crawlab-ui/src/components/ui/chat/ChatMessage.vue
new file mode 100644
index 00000000..c8bb34b7
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/chat/ChatMessage.vue
@@ -0,0 +1,446 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/chat/ChatMessageAction.vue b/frontend/crawlab-ui/src/components/ui/chat/ChatMessageAction.vue
new file mode 100644
index 00000000..27b0bb7b
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/chat/ChatMessageAction.vue
@@ -0,0 +1,274 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ content }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/chat/ChatMessageList.vue b/frontend/crawlab-ui/src/components/ui/chat/ChatMessageList.vue
new file mode 100644
index 00000000..66eca4ee
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/chat/ChatMessageList.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/chat/ChatSidebar.vue b/frontend/crawlab-ui/src/components/ui/chat/ChatSidebar.vue
new file mode 100644
index 00000000..cc687722
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/chat/ChatSidebar.vue
@@ -0,0 +1,165 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/checkbox/CheckboxTree.vue b/frontend/crawlab-ui/src/components/ui/checkbox/CheckboxTree.vue
new file mode 100644
index 00000000..2e32e510
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/checkbox/CheckboxTree.vue
@@ -0,0 +1,250 @@
+
+
+
+
+
+ updateState(op.id, checked, op)"
+ />
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/checkbox/CheckboxTreeGroup.vue b/frontend/crawlab-ui/src/components/ui/checkbox/CheckboxTreeGroup.vue
new file mode 100644
index 00000000..d14a9c02
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/checkbox/CheckboxTreeGroup.vue
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+ {{ option.label }}
+
+
+
+
+ updateState(op.id!, checked, op)"
+ />
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/context-menu/ContextMenu.vue b/frontend/crawlab-ui/src/components/ui/context-menu/ContextMenu.vue
new file mode 100644
index 00000000..146ffcc2
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/context-menu/ContextMenu.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/context-menu/ContextMenuList.vue b/frontend/crawlab-ui/src/components/ui/context-menu/ContextMenuList.vue
new file mode 100644
index 00000000..9274b97e
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/context-menu/ContextMenuList.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/context-menu/types.d.ts b/frontend/crawlab-ui/src/components/ui/context-menu/types.d.ts
new file mode 100644
index 00000000..13439203
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/context-menu/types.d.ts
@@ -0,0 +1,29 @@
+import { TooltipTriggerType } from 'element-plus/es/components/tooltip/src/trigger';
+import { Placement } from '@popperjs/core';
+import { StyleValue } from 'vue';
+
+export interface ContextMenuProps {
+ trigger?: TooltipTriggerType;
+ visible?: boolean;
+ activeItem?: FileNavItem;
+ placement?: Placement;
+ clicking?: boolean;
+ style?: StyleValue;
+}
+
+export interface ContextMenuListProps {
+ items: ContextMenuItem[];
+}
+
+export interface ContextMenuItem {
+ title: string;
+ icon?: Icon;
+ action?: () => void;
+ className?: string;
+ disabled?: boolean;
+}
+
+export type FileEditorContextMenuItemVisibleFn = (
+ contextMenuItem: ContextMenuItem,
+ activeFileNavItem?: FileNavItem
+) => boolean;
diff --git a/frontend/crawlab-ui/src/components/ui/date/DateRangePicker.vue b/frontend/crawlab-ui/src/components/ui/date/DateRangePicker.vue
new file mode 100644
index 00000000..73d9eaf8
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/date/DateRangePicker.vue
@@ -0,0 +1,125 @@
+
+
+
+ emit('change', value)"
+ />
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/date/DateTimeRangePicker.vue b/frontend/crawlab-ui/src/components/ui/date/DateTimeRangePicker.vue
new file mode 100644
index 00000000..1950b70b
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/date/DateTimeRangePicker.vue
@@ -0,0 +1,164 @@
+
+
+
+ emit('change', value)"
+ />
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/date/RangePicker.vue b/frontend/crawlab-ui/src/components/ui/date/RangePicker.vue
new file mode 100644
index 00000000..99a71070
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/date/RangePicker.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/date/date.ts b/frontend/crawlab-ui/src/components/ui/date/date.ts
new file mode 100644
index 00000000..50550ec2
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/date/date.ts
@@ -0,0 +1,16 @@
+export const getRangeItemOption = (
+ label: string,
+ key: RangeItemKey,
+ value?: RangeItemValue
+): RangeItemOption => {
+ if (typeof value === 'function') {
+ value = value();
+ }
+ return {
+ label,
+ value: {
+ key,
+ value,
+ },
+ };
+};
diff --git a/frontend/crawlab-ui/src/components/ui/dialog/ConfirmDialog.vue b/frontend/crawlab-ui/src/components/ui/dialog/ConfirmDialog.vue
new file mode 100644
index 00000000..b4b1ffbe
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/dialog/ConfirmDialog.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+ {{ content }}
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/dialog/CreateEditDialog.vue b/frontend/crawlab-ui/src/components/ui/dialog/CreateEditDialog.vue
new file mode 100644
index 00000000..d5cb66ae
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/dialog/CreateEditDialog.vue
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/dialog/Dialog.vue b/frontend/crawlab-ui/src/components/ui/dialog/Dialog.vue
new file mode 100644
index 00000000..c5a35477
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/dialog/Dialog.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+ {{ t('common.actions.cancel') }}
+
+
+ {{ confirmText || t('common.actions.confirm') }}
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/drag/DraggableItem.vue b/frontend/crawlab-ui/src/components/ui/drag/DraggableItem.vue
new file mode 100644
index 00000000..0f6a8dac
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/drag/DraggableItem.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/drag/DraggableList.vue b/frontend/crawlab-ui/src/components/ui/drag/DraggableList.vue
new file mode 100644
index 00000000..300a4f68
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/drag/DraggableList.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/editor/EditorOptionsDropdownList.vue b/frontend/crawlab-ui/src/components/ui/editor/EditorOptionsDropdownList.vue
new file mode 100644
index 00000000..34de563e
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/editor/EditorOptionsDropdownList.vue
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/empty/Empty.vue b/frontend/crawlab-ui/src/components/ui/empty/Empty.vue
new file mode 100644
index 00000000..af4fa4d7
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/empty/Empty.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ {{ t(description) }}
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/empty/ImgEmpty.vue b/frontend/crawlab-ui/src/components/ui/empty/ImgEmpty.vue
new file mode 100644
index 00000000..39104457
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/empty/ImgEmpty.vue
@@ -0,0 +1,131 @@
+
+
+
+
+ Group 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/export/ExportForm.vue b/frontend/crawlab-ui/src/components/ui/export/ExportForm.vue
new file mode 100644
index 00000000..f3a19da5
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/export/ExportForm.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileActions.vue b/frontend/crawlab-ui/src/components/ui/file/FileActions.vue
new file mode 100644
index 00000000..9d39f804
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileActions.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileDiff.vue b/frontend/crawlab-ui/src/components/ui/file/FileDiff.vue
new file mode 100644
index 00000000..e07cd389
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileDiff.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileEditor.vue b/frontend/crawlab-ui/src/components/ui/file/FileEditor.vue
new file mode 100644
index 00000000..00e78b0d
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileEditor.vue
@@ -0,0 +1,515 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('components.file.editor.empty.placeholder') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileEditorCreateWithAiDialog.vue b/frontend/crawlab-ui/src/components/ui/file/FileEditorCreateWithAiDialog.vue
new file mode 100644
index 00000000..0d720236
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileEditorCreateWithAiDialog.vue
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('common.actions.cancel') }}
+
+ {{ t('common.actions.confirm') }}
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileEditorNavMenu.vue b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavMenu.vue
new file mode 100644
index 00000000..d75fabf9
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavMenu.vue
@@ -0,0 +1,673 @@
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileEditorNavMenuContextMenu.vue b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavMenuContextMenu.vue
new file mode 100644
index 00000000..f67f4eb9
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavMenuContextMenu.vue
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileEditorNavTabs.vue b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavTabs.vue
new file mode 100644
index 00000000..6fed27e2
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavTabs.vue
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getTitle(item) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileEditorNavTabsContextMenu.vue b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavTabsContextMenu.vue
new file mode 100644
index 00000000..290eabf8
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavTabsContextMenu.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileEditorNavTabsShowMoreContextMenu.vue b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavTabsShowMoreContextMenu.vue
new file mode 100644
index 00000000..c96d6673
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileEditorNavTabsShowMoreContextMenu.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileEditorSettingsDialog.vue b/frontend/crawlab-ui/src/components/ui/file/FileEditorSettingsDialog.vue
new file mode 100644
index 00000000..1b3b6ec4
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileEditorSettingsDialog.vue
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('common.actions.cancel') }}
+
+ {{ t('common.actions.confirm') }}
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileTab.vue b/frontend/crawlab-ui/src/components/ui/file/FileTab.vue
new file mode 100644
index 00000000..26e215c2
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileTab.vue
@@ -0,0 +1,278 @@
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/FileUpload.vue b/frontend/crawlab-ui/src/components/ui/file/FileUpload.vue
new file mode 100644
index 00000000..8e6b1279
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/FileUpload.vue
@@ -0,0 +1,320 @@
+
+
+
+
+
+
+ emit('mode-change', value)"
+ >
+
+ {{ label }}
+
+
+
+
+
+ emit('directory-change', value)"
+ @clear="internalDirectory = FILE_ROOT"
+ >
+
+
+ {{ label }}
+
+
+ {{ value }}
+
+
+
+
+
+
+
+
+
+
+ {{
+ t(
+ 'components.file.upload.buttons.folder.clickToSelectFolderToUpload'
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+ {{ t('components.file.upload.buttons.files.dragFilesHereOr') }}
+ {{
+ t('components.file.upload.buttons.files.clickToUpload')
+ }}
+
+
+
+
+
+
+
+ {{ t('components.file.upload.fileList.title') }}
+
+
+
+
+
+
+
+ {{ data.label }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/UploadFilesDialog.vue b/frontend/crawlab-ui/src/components/ui/file/UploadFilesDialog.vue
new file mode 100644
index 00000000..f6fe571e
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/UploadFilesDialog.vue
@@ -0,0 +1,185 @@
+
+
+
+
+ (mode = value)"
+ @directory-change="onTargetDirectoryChange"
+ @files-change="onFilesChange"
+ />
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/file/fileEditorDropZone.ts b/frontend/crawlab-ui/src/components/ui/file/fileEditorDropZone.ts
new file mode 100644
index 00000000..a16e23a6
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/file/fileEditorDropZone.ts
@@ -0,0 +1,13 @@
+import { useDropzone } from 'crawlab-vue3-dropzone';
+
+const useFileEditorDropZone = () => {
+ const { getRootProps, getInputProps, open } = useDropzone({});
+
+ return {
+ getRootProps,
+ getInputProps,
+ open,
+ };
+};
+
+export default useFileEditorDropZone;
diff --git a/frontend/crawlab-ui/src/components/ui/filter/FilterCondition.vue b/frontend/crawlab-ui/src/components/ui/filter/FilterCondition.vue
new file mode 100644
index 00000000..d48f4524
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/filter/FilterCondition.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/filter/FilterConditionList.vue b/frontend/crawlab-ui/src/components/ui/filter/FilterConditionList.vue
new file mode 100644
index 00000000..8f92751a
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/filter/FilterConditionList.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/filter/FilterInput.vue b/frontend/crawlab-ui/src/components/ui/filter/FilterInput.vue
new file mode 100644
index 00000000..0a0cb01d
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/filter/FilterInput.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/filter/FilterSelect.vue b/frontend/crawlab-ui/src/components/ui/filter/FilterSelect.vue
new file mode 100644
index 00000000..3cc4aeaf
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/filter/FilterSelect.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+ {{ activeOption?.label }}
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/filter/filter.ts b/frontend/crawlab-ui/src/components/ui/filter/filter.ts
new file mode 100644
index 00000000..7d844638
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/filter/filter.ts
@@ -0,0 +1,43 @@
+import { plainClone } from '@/utils';
+import {
+ FILTER_OP_CONTAINS,
+ FILTER_OP_EQUAL,
+ FILTER_OP_GREATER_THAN,
+ FILTER_OP_GREATER_THAN_EQUAL,
+ FILTER_OP_LESS_THAN,
+ FILTER_OP_LESS_THAN_EQUAL,
+ FILTER_OP_NOT_CONTAINS,
+ FILTER_OP_NOT_EQUAL,
+ FILTER_OP_NOT_SET,
+ FILTER_OP_REGEX,
+} from '@/constants';
+
+export const defaultFilterCondition: FilterConditionData = {
+ op: FILTER_OP_NOT_SET,
+ value: '',
+};
+
+export const getDefaultFilterCondition = () => {
+ return plainClone(defaultFilterCondition);
+};
+
+export const conditionTypesOptions: SelectOption[] = [
+ { value: FILTER_OP_NOT_SET, label: 'Not Set' },
+ { value: FILTER_OP_CONTAINS, label: 'Contains' },
+ { value: FILTER_OP_NOT_CONTAINS, label: 'Not Contains' },
+ { value: FILTER_OP_REGEX, label: 'Regex' },
+ { value: FILTER_OP_EQUAL, label: 'Equal to' },
+ { value: FILTER_OP_NOT_EQUAL, label: 'Not Equal to' },
+ { value: FILTER_OP_GREATER_THAN, label: 'Greater than' },
+ { value: FILTER_OP_LESS_THAN, label: 'Less than' },
+ { value: FILTER_OP_GREATER_THAN_EQUAL, label: 'Greater than or Equal to' },
+ { value: FILTER_OP_LESS_THAN_EQUAL, label: 'Less than or Equal to' },
+];
+
+export const conditionTypesMap: { [key: string]: string } = (() => {
+ const map: { [key: string]: string } = {};
+ conditionTypesOptions.forEach(d => {
+ map[d.value] = d.label as string;
+ });
+ return map;
+})();
diff --git a/frontend/crawlab-ui/src/components/ui/form/Form.vue b/frontend/crawlab-ui/src/components/ui/form/Form.vue
new file mode 100644
index 00000000..58f12d78
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/form/Form.vue
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/form/FormItem.vue b/frontend/crawlab-ui/src/components/ui/form/FormItem.vue
new file mode 100644
index 00000000..763ca25b
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/form/FormItem.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/form/FormTableField.vue b/frontend/crawlab-ui/src/components/ui/form/FormTableField.vue
new file mode 100644
index 00000000..fbd52682
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/form/FormTableField.vue
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/crawlab-ui/src/components/ui/form/formTable.ts b/frontend/crawlab-ui/src/components/ui/form/formTable.ts
new file mode 100644
index 00000000..a5c2ad95
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/form/formTable.ts
@@ -0,0 +1,67 @@
+import { Store } from 'vuex';
+import { plainClone } from '@/utils/object';
+import { computed, Ref } from 'vue';
+
+export const useFormTable = (
+ ns: ListStoreNamespace,
+ store: Store,
+ data: FormComponentData
+) => {
+ const { formTableFieldRefsMap } = data;
+
+ // state
+ const state = store.state[ns as keyof RootStoreState] as BaseStoreState;
+
+ // form list
+ const formList = computed(() => state.formList);
+
+ // get new form
+ const getNewForm = state.newFormFn;
+
+ const onAdd = (index: number) => {
+ formList.value.splice(index, 0, getNewForm());
+ };
+
+ const onClone = (index: number) => {
+ const form = plainClone(formList.value[index]);
+ formList.value.splice(index, 0, form);
+ };
+
+ const onDelete = (index: number) => {
+ formList.value.splice(index, 1);
+ for (const key of formTableFieldRefsMap.value.keys()) {
+ const rowIndex = key[0];
+ if (rowIndex === index) {
+ formTableFieldRefsMap.value.delete(key);
+ }
+ }
+ };
+
+ const onFieldChange = (rowIndex: number, prop: string, value: any) => {
+ if (rowIndex !== -1) {
+ // one row change
+ const item = formList.value[rowIndex] as BaseModel;
+ item[prop] = value;
+ } else {
+ // all rows change
+ for (let i = 0; i < formList.value.length; i++) {
+ onFieldChange(i, prop, value);
+ }
+ }
+ };
+
+ const onFieldRegister = (rowIndex: number, prop: string, formRef: Ref) => {
+ const key = [rowIndex, prop] as FormTableFieldRefsMapKey;
+ formTableFieldRefsMap.value.set(key, formRef);
+ };
+
+ return {
+ onAdd,
+ onClone,
+ onDelete,
+ onFieldChange,
+ onFieldRegister,
+ };
+};
+
+export default useFormTable;
diff --git a/frontend/crawlab-ui/src/components/ui/form/index.ts b/frontend/crawlab-ui/src/components/ui/form/index.ts
new file mode 100644
index 00000000..45d84816
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/form/index.ts
@@ -0,0 +1,2 @@
+export * from './useForm';
+export * from './formTable';
diff --git a/frontend/crawlab-ui/src/components/ui/form/useForm.ts b/frontend/crawlab-ui/src/components/ui/form/useForm.ts
new file mode 100644
index 00000000..abc4d1be
--- /dev/null
+++ b/frontend/crawlab-ui/src/components/ui/form/useForm.ts
@@ -0,0 +1,241 @@
+import { computed, provide, watch } from 'vue';
+import { Store } from 'vuex';
+import useFormTable from '@/components/ui/form/formTable';
+import { EMPTY_OBJECT_ID } from '@/utils/mongo';
+import { translate } from '@/utils/i18n';
+
+// i18n
+const t = translate;
+
+export const useForm = (
+ ns: ListStoreNamespace,
+ store: Store,
+ services: Services,
+ data: FormComponentData
+) => {
+ const { formRef, formTableFieldRefsMap } = data;
+
+ // state
+ const state = store.state[ns as keyof RootStoreState] as BaseStoreState;
+
+ // get new form
+ const getNewForm = state.newFormFn;
+
+ // get new form list
+ const getNewFormList = () => {
+ const list = [];
+ for (let i = 0; i < 5; i++) {
+ list.push(getNewForm());
+ }
+ return list;
+ };
+
+ // form
+ const form = computed(() => state.form);
+
+ // form list
+ const formList = computed(() => state.formList);
+
+ // active dialog key
+ const activeDialogKey = computed(
+ () => state.activeDialogKey as DialogKey
+ );
+
+ // is selective form
+ const isSelectiveForm = computed(() => state.isSelectiveForm);
+
+ // selected form fields
+ const selectedFormFields = computed(() => state.selectedFormFields);
+
+ // readonly form fields
+ const readonlyFormFields = computed(() => state.readonlyFormFields);
+
+ const validateForm = async () => {
+ return await formRef.value?.validate();
+ };
+
+ const resetForm = () => {
+ if (activeDialogKey.value) {
+ switch (activeDialogKey.value) {
+ case 'create':
+ store.commit(`${ns}/setForm`, getNewForm());
+ store.commit(`${ns}/setFormList`, getNewFormList());
+ break;
+ case 'edit':
+ formRef.value?.clearValidate();
+ break;
+ }
+ } else {
+ formRef.value?.resetFields();
+ formTableFieldRefsMap.value = new Map();
+ }
+ };
+
+ // whether form item is disabled
+ const isFormItemDisabled = (prop: string) => {
+ if (readonlyFormFields.value.includes(prop)) {
+ return true;
+ }
+ if (!isSelectiveForm.value) return false;
+ if (!prop) return false;
+ return !selectedFormFields.value.includes(prop);
+ };
+
+ // whether the form is empty
+ const isEmptyForm = (d: any): boolean => {
+ return JSON.stringify(d) === JSON.stringify(getNewForm());
+ };
+ provide<(d: any) => boolean>('fn:isEmptyForm', isEmptyForm);
+
+ // all list select options
+ const allListSelectOptions = computed(
+ () => store.getters[`${ns}/allListSelectOptions`]
+ );
+
+ // all list select options with empty
+ const allListSelectOptionsWithEmpty = computed(() =>
+ allListSelectOptions.value.concat({
+ label: t('common.status.unassigned'),
+ value: EMPTY_OBJECT_ID,
+ })
+ );
+
+ // all dict
+ const allDict = computed