From 9a0a502de21f85917b1c5245cd19284e2e6a98ec Mon Sep 17 00:00:00 2001 From: zestack Date: Fri, 19 Jan 2024 17:29:46 +0800 Subject: [PATCH] first edition --- .editorconfig | 11 + .gitignore | 24 + .prettierrc | 3 + .vscode/extensions.json | 3 + README.md | 18 + demo.html | 58 + index.html | 13 + package-lock.json | 3402 +++++++++++++++++ package.json | 32 + postcss.config.js | 6 + src/App.vue | 64 + src/backend/api.ts | 203 + src/backend/index.ts | 73 + src/backend/qiniu/api/index.ts | 173 + src/backend/qiniu/config/index.ts | 1 + src/backend/qiniu/config/region.ts | 37 + src/backend/qiniu/errors/index.ts | 62 + src/backend/qiniu/image/index.ts | 195 + src/backend/qiniu/index.ts | 22 + src/backend/qiniu/logger/index.ts | 73 + src/backend/qiniu/logger/report-v3.ts | 48 + src/backend/qiniu/upload/base.ts | 340 ++ src/backend/qiniu/upload/direct.ts | 70 + src/backend/qiniu/upload/hosts.ts | 134 + src/backend/qiniu/upload/index.ts | 86 + src/backend/qiniu/upload/resume.ts | 314 ++ src/backend/qiniu/utils/base64.ts | 274 ++ src/backend/qiniu/utils/compress.ts | 218 ++ src/backend/qiniu/utils/config.ts | 61 + src/backend/qiniu/utils/crc32.ts | 90 + src/backend/qiniu/utils/helper.ts | 346 ++ src/backend/qiniu/utils/index.ts | 8 + src/backend/qiniu/utils/observable.ts | 151 + src/backend/qiniu/utils/pool.ts | 53 + src/backend/qiniu/utils/spark-md5.ts | 348 ++ src/backend/reader.ts | 181 + src/backend/report.ts | 96 + src/backend/socket.ts | 275 ++ src/backend/uploader.ts | 417 ++ src/frontend/assets/logo.png | Bin 0 -> 19195 bytes src/frontend/directives/index.ts | 1 + src/frontend/directives/vTooltip.ts | 69 + src/frontend/hooks/index.ts | 1 + src/frontend/hooks/useTheme.ts | 91 + src/frontend/index.ts | 225 ++ src/frontend/store/events.ts | 38 + src/frontend/store/filter.ts | 112 + src/frontend/store/handlers.ts | 204 + src/frontend/store/index.ts | 4 + src/frontend/store/store.ts | 52 + src/frontend/style.css | 131 + src/frontend/utils/align.ts | 105 + src/frontend/utils/index.ts | 3 + src/frontend/utils/preview.ts | 106 + src/frontend/utils/scroll.ts | 58 + src/frontend/widgets/UiBarrier.vue | 63 + src/frontend/widgets/UiContainer.vue | 103 + src/frontend/widgets/UiContextMenu.vue | 275 ++ .../widgets/UiContextMenuController.ts | 173 + src/frontend/widgets/UiDialog.vue | 159 + src/frontend/widgets/UiDirectoryButton.vue | 85 + src/frontend/widgets/UiDirectoryNaming.vue | 199 + .../widgets/UiDirectoryNamingController.ts | 29 + src/frontend/widgets/UiDirectoryTree.vue | 35 + src/frontend/widgets/UiDirectoryTreeItem.vue | 22 + src/frontend/widgets/UiDropFeedback.vue | 69 + src/frontend/widgets/UiFile.vue | 101 + src/frontend/widgets/UiFileNaming.vue | 157 + .../widgets/UiFileNamingController.ts | 25 + src/frontend/widgets/UiFileThumbnail.vue | 45 + src/frontend/widgets/UiFiles.vue | 83 + src/frontend/widgets/UiMimeSelect.vue | 64 + src/frontend/widgets/UiMistake.vue | 30 + src/frontend/widgets/UiModeTools.vue | 65 + src/frontend/widgets/UiNavbar.vue | 54 + src/frontend/widgets/UiNoData.vue | 40 + src/frontend/widgets/UiPaginateEllipsis.vue | 21 + src/frontend/widgets/UiPaginateItem.vue | 32 + src/frontend/widgets/UiPagination.vue | 83 + src/frontend/widgets/UiSidebar.vue | 90 + src/frontend/widgets/UiSplitter.vue | 63 + src/frontend/widgets/UiTask.vue | 134 + src/frontend/widgets/UiTaskAction.vue | 25 + src/frontend/widgets/UiTasks.vue | 82 + src/frontend/widgets/UiThemeSelect.vue | 40 + src/frontend/widgets/UiToast.vue | 85 + src/frontend/widgets/UiToastController.ts | 24 + src/frontend/widgets/UiToaster.vue | 39 + src/frontend/widgets/VBadge.vue | 14 + src/frontend/widgets/VButton.vue | 37 + src/frontend/widgets/VDropdown.vue | 124 + src/frontend/widgets/VIcon.vue | 21 + src/frontend/widgets/VIconButton.vue | 38 + src/frontend/widgets/VIcons.ts | 516 +++ src/frontend/widgets/VImage.vue | 35 + src/frontend/widgets/VLoading.vue | 24 + src/frontend/widgets/VProgress.vue | 24 + src/frontend/widgets/VText.vue | 50 + src/frontend/widgets/utils.ts | 9 + src/frontend/worker.ts | 70 + src/main.ts | 7 + src/shared/emitter.ts | 60 + src/shared/index.ts | 7 + src/shared/messenger.ts | 97 + src/shared/task.ts | 35 + src/shared/time.ts | 16 + src/shared/ui.ts | 38 + src/shared/utils.ts | 70 + src/style.css | 11 + src/vite-env.d.ts | 200 + tailwind.config.js | 31 + tsconfig.json | 36 + tsconfig.node.json | 10 + vite.config.ts | 34 + 114 files changed, 13391 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .prettierrc create mode 100644 .vscode/extensions.json create mode 100644 README.md create mode 100644 demo.html create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 src/App.vue create mode 100644 src/backend/api.ts create mode 100644 src/backend/index.ts create mode 100644 src/backend/qiniu/api/index.ts create mode 100644 src/backend/qiniu/config/index.ts create mode 100644 src/backend/qiniu/config/region.ts create mode 100644 src/backend/qiniu/errors/index.ts create mode 100644 src/backend/qiniu/image/index.ts create mode 100644 src/backend/qiniu/index.ts create mode 100644 src/backend/qiniu/logger/index.ts create mode 100644 src/backend/qiniu/logger/report-v3.ts create mode 100644 src/backend/qiniu/upload/base.ts create mode 100644 src/backend/qiniu/upload/direct.ts create mode 100644 src/backend/qiniu/upload/hosts.ts create mode 100644 src/backend/qiniu/upload/index.ts create mode 100644 src/backend/qiniu/upload/resume.ts create mode 100644 src/backend/qiniu/utils/base64.ts create mode 100644 src/backend/qiniu/utils/compress.ts create mode 100644 src/backend/qiniu/utils/config.ts create mode 100644 src/backend/qiniu/utils/crc32.ts create mode 100644 src/backend/qiniu/utils/helper.ts create mode 100644 src/backend/qiniu/utils/index.ts create mode 100644 src/backend/qiniu/utils/observable.ts create mode 100644 src/backend/qiniu/utils/pool.ts create mode 100644 src/backend/qiniu/utils/spark-md5.ts create mode 100644 src/backend/reader.ts create mode 100644 src/backend/report.ts create mode 100644 src/backend/socket.ts create mode 100644 src/backend/uploader.ts create mode 100644 src/frontend/assets/logo.png create mode 100644 src/frontend/directives/index.ts create mode 100644 src/frontend/directives/vTooltip.ts create mode 100644 src/frontend/hooks/index.ts create mode 100644 src/frontend/hooks/useTheme.ts create mode 100644 src/frontend/index.ts create mode 100644 src/frontend/store/events.ts create mode 100644 src/frontend/store/filter.ts create mode 100644 src/frontend/store/handlers.ts create mode 100644 src/frontend/store/index.ts create mode 100644 src/frontend/store/store.ts create mode 100644 src/frontend/style.css create mode 100644 src/frontend/utils/align.ts create mode 100644 src/frontend/utils/index.ts create mode 100644 src/frontend/utils/preview.ts create mode 100644 src/frontend/utils/scroll.ts create mode 100644 src/frontend/widgets/UiBarrier.vue create mode 100644 src/frontend/widgets/UiContainer.vue create mode 100644 src/frontend/widgets/UiContextMenu.vue create mode 100644 src/frontend/widgets/UiContextMenuController.ts create mode 100644 src/frontend/widgets/UiDialog.vue create mode 100644 src/frontend/widgets/UiDirectoryButton.vue create mode 100644 src/frontend/widgets/UiDirectoryNaming.vue create mode 100644 src/frontend/widgets/UiDirectoryNamingController.ts create mode 100644 src/frontend/widgets/UiDirectoryTree.vue create mode 100644 src/frontend/widgets/UiDirectoryTreeItem.vue create mode 100644 src/frontend/widgets/UiDropFeedback.vue create mode 100644 src/frontend/widgets/UiFile.vue create mode 100644 src/frontend/widgets/UiFileNaming.vue create mode 100644 src/frontend/widgets/UiFileNamingController.ts create mode 100644 src/frontend/widgets/UiFileThumbnail.vue create mode 100644 src/frontend/widgets/UiFiles.vue create mode 100644 src/frontend/widgets/UiMimeSelect.vue create mode 100644 src/frontend/widgets/UiMistake.vue create mode 100644 src/frontend/widgets/UiModeTools.vue create mode 100644 src/frontend/widgets/UiNavbar.vue create mode 100644 src/frontend/widgets/UiNoData.vue create mode 100644 src/frontend/widgets/UiPaginateEllipsis.vue create mode 100644 src/frontend/widgets/UiPaginateItem.vue create mode 100644 src/frontend/widgets/UiPagination.vue create mode 100644 src/frontend/widgets/UiSidebar.vue create mode 100644 src/frontend/widgets/UiSplitter.vue create mode 100644 src/frontend/widgets/UiTask.vue create mode 100644 src/frontend/widgets/UiTaskAction.vue create mode 100644 src/frontend/widgets/UiTasks.vue create mode 100644 src/frontend/widgets/UiThemeSelect.vue create mode 100644 src/frontend/widgets/UiToast.vue create mode 100644 src/frontend/widgets/UiToastController.ts create mode 100644 src/frontend/widgets/UiToaster.vue create mode 100644 src/frontend/widgets/VBadge.vue create mode 100644 src/frontend/widgets/VButton.vue create mode 100644 src/frontend/widgets/VDropdown.vue create mode 100644 src/frontend/widgets/VIcon.vue create mode 100644 src/frontend/widgets/VIconButton.vue create mode 100644 src/frontend/widgets/VIcons.ts create mode 100644 src/frontend/widgets/VImage.vue create mode 100644 src/frontend/widgets/VLoading.vue create mode 100644 src/frontend/widgets/VProgress.vue create mode 100644 src/frontend/widgets/VText.vue create mode 100644 src/frontend/widgets/utils.ts create mode 100644 src/frontend/worker.ts create mode 100644 src/main.ts create mode 100644 src/shared/emitter.ts create mode 100644 src/shared/index.ts create mode 100644 src/shared/messenger.ts create mode 100644 src/shared/task.ts create mode 100644 src/shared/time.ts create mode 100644 src/shared/ui.ts create mode 100644 src/shared/utils.ts create mode 100644 src/style.css create mode 100644 src/vite-env.d.ts create mode 100644 tailwind.config.js create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d4631d6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = false +trim_trailing_whitespace = true +max_line_length = 80 +tab_width = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..6e1cd92 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleAttributePerLine": true +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..c0a6e5a --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..ef72fd5 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Vue 3 + TypeScript + Vite + +This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..04d4866 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + Vue + TS + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..d7df48b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3402 @@ +{ + "name": "wfs", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wfs", + "version": "0.0.0", + "dependencies": { + "@vueuse/core": "^10.7.2", + "dom-align": "^1.12.4", + "hls.js": "^1.4.14", + "viewerjs": "^1.11.6", + "vue": "^3.3.8" + }, + "devDependencies": { + "@tailwindcss/container-queries": "^0.1.1", + "@tailwindcss/forms": "^0.5.7", + "@types/node": "^20.10.4", + "@vitejs/plugin-basic-ssl": "^1.0.2", + "@vitejs/plugin-vue": "^4.5.0", + "@vitejs/plugin-vue-jsx": "^3.1.0", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.32", + "tailwindcss": "^3.4.0", + "typescript": "^5.2.2", + "vite": "^5.0.0", + "vue-tsc": "^1.8.22" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz", + "integrity": "sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", + "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", + "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.23.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.23.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz", + "integrity": "sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.11.tgz", + "integrity": "sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz", + "integrity": "sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.11.tgz", + "integrity": "sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz", + "integrity": "sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz", + "integrity": "sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz", + "integrity": "sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz", + "integrity": "sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz", + "integrity": "sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz", + "integrity": "sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz", + "integrity": "sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz", + "integrity": "sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz", + "integrity": "sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz", + "integrity": "sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz", + "integrity": "sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz", + "integrity": "sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz", + "integrity": "sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz", + "integrity": "sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz", + "integrity": "sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz", + "integrity": "sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz", + "integrity": "sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz", + "integrity": "sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz", + "integrity": "sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.2.tgz", + "integrity": "sha512-RKzxFxBHq9ysZ83fn8Iduv3A283K7zPPYuhL/z9CQuyFrjwpErJx0h4aeb/bnJ+q29GRLgJpY66ceQ/Wcsn3wA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.2.tgz", + "integrity": "sha512-yZ+MUbnwf3SHNWQKJyWh88ii2HbuHCFQnAYTeeO1Nb8SyEiWASEi5dQUygt3ClHWtA9My9RQAYkjvrsZ0WK8Xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.2.tgz", + "integrity": "sha512-vqJ/pAUh95FLc/G/3+xPqlSBgilPnauVf2EXOQCZzhZJCXDXt/5A8mH/OzU6iWhb3CNk5hPJrh8pqJUPldN5zw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.2.tgz", + "integrity": "sha512-otPHsN5LlvedOprd3SdfrRNhOahhVBwJpepVKUN58L0RnC29vOAej1vMEaVU6DadnpjivVsNTM5eNt0CcwTahw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.2.tgz", + "integrity": "sha512-ewG5yJSp+zYKBYQLbd1CUA7b1lSfIdo9zJShNTyc2ZP1rcPrqyZcNlsHgs7v1zhgfdS+kW0p5frc0aVqhZCiYQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.2.tgz", + "integrity": "sha512-pL6QtV26W52aCWTG1IuFV3FMPL1m4wbsRG+qijIvgFO/VBsiXJjDPE/uiMdHBAO6YcpV4KvpKtd0v3WFbaxBtg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.2.tgz", + "integrity": "sha512-On+cc5EpOaTwPSNetHXBuqylDW+765G/oqB9xGmWU3npEhCh8xu0xqHGUA+4xwZLqBbIZNcBlKSIYfkBm6ko7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.2.tgz", + "integrity": "sha512-Wnx/IVMSZ31D/cO9HSsU46FjrPWHqtdF8+0eyZ1zIB5a6hXaZXghUKpRrC4D5DcRTZOjml2oBhXoqfGYyXKipw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.2.tgz", + "integrity": "sha512-ym5x1cj4mUAMBummxxRkI4pG5Vht1QMsJexwGP8547TZ0sox9fCLDHw9KCH9c1FO5d9GopvkaJsBIOkTKxksdw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.2.tgz", + "integrity": "sha512-m0hYELHGXdYx64D6IDDg/1vOJEaiV8f1G/iO+tejvRCJNSwK4jJ15e38JQy5Q6dGkn1M/9KcyEOwqmlZ2kqaZg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.2.tgz", + "integrity": "sha512-x1CWburlbN5JjG+juenuNa4KdedBdXLjZMp56nHFSHTOsb/MI2DYiGzLtRGHNMyydPGffGId+VgjOMrcltOksA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.2.tgz", + "integrity": "sha512-VVzCB5yXR1QlfsH1Xw1zdzQ4Pxuzv+CPr5qpElpKhVxlxD3CRdfubAG9mJROl6/dmj5gVYDDWk8sC+j9BI9/kQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.2.tgz", + "integrity": "sha512-SYRedJi+mweatroB+6TTnJYLts0L0bosg531xnQWtklOI6dezEagx4Q0qDyvRdK+qgdA3YZpjjGuPFtxBmddBA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/container-queries": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/container-queries/-/container-queries-0.1.1.tgz", + "integrity": "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==", + "dev": true, + "peerDependencies": { + "tailwindcss": ">=3.2.0" + } + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz", + "integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==", + "dev": true, + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + } + }, + "node_modules/@types/node": { + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", + "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/web-bluetooth": { + "version": "0.0.20", + "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz", + "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==" + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.0.2.tgz", + "integrity": "sha512-DKHKVtpI+eA5fvObVgQ3QtTGU70CcCnedalzqmGSR050AzKZMdUzgC8KmlOneHWH8dF2hJ3wkC9+8FDVAaDRCw==", + "dev": true, + "engines": { + "node": ">=14.6.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz", + "integrity": "sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==", + "dev": true, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.0.0 || ^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitejs/plugin-vue-jsx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-3.1.0.tgz", + "integrity": "sha512-w9M6F3LSEU5kszVb9An2/MmXNxocAnUb3WhRr8bHlimhDrXNt6n6D2nJQR3UXpGlZHh/EsgouOHCsM8V3Ln+WA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3", + "@vue/babel-plugin-jsx": "^1.1.5" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.0.0 || ^5.0.0", + "vue": "^3.0.0" + } + }, + "node_modules/@volar/language-core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz", + "integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==", + "dev": true, + "dependencies": { + "@volar/source-map": "1.11.1" + } + }, + "node_modules/@volar/source-map": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz", + "integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==", + "dev": true, + "dependencies": { + "muggle-string": "^0.3.1" + } + }, + "node_modules/@volar/typescript": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz", + "integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==", + "dev": true, + "dependencies": { + "@volar/language-core": "1.11.1", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.1.5.tgz", + "integrity": "sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w==", + "dev": true + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.5.tgz", + "integrity": "sha512-nKs1/Bg9U1n3qSWnsHhCVQtAzI6aQXqua8j/bZrau8ywT1ilXQbK4FwEJGmU8fV7tcpuFvWmmN7TMmV1OBma1g==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", + "@vue/babel-helper-vue-transform-on": "^1.1.5", + "camelcase": "^6.3.0", + "html-tags": "^3.3.1", + "svg-tags": "^1.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.5.tgz", + "integrity": "sha512-Daka7P1z2AgKjzuueWXhwzIsKu0NkLB6vGbNVEV2iJ8GJTrzraZo/Sk4GWCMRtd/qVi3zwnk+Owbd/xSZbwHtQ==", + "dependencies": { + "@babel/parser": "^7.23.6", + "@vue/shared": "3.4.5", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.0.2" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.5.tgz", + "integrity": "sha512-J8YlxknJVd90SXFJ4HwGANSAXsx5I0lK30sO/zvYV7s5gXf7gZR7r/1BmZ2ju7RGH1lnc6bpBc6nL61yW+PsAQ==", + "dependencies": { + "@vue/compiler-core": "3.4.5", + "@vue/shared": "3.4.5" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.5.tgz", + "integrity": "sha512-jauvkDuSSUbP0ebhfNqljhShA90YEfX/0wZ+w40oZF43IjGyWYjqYaJbvMJwGOd+9+vODW6eSvnk28f0SGV7OQ==", + "dependencies": { + "@babel/parser": "^7.23.6", + "@vue/compiler-core": "3.4.5", + "@vue/compiler-dom": "3.4.5", + "@vue/compiler-ssr": "3.4.5", + "@vue/shared": "3.4.5", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.5", + "postcss": "^8.4.32", + "source-map-js": "^1.0.2" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.5.tgz", + "integrity": "sha512-DDdEcDzj2lWTMfUMMtEpLDhURai9LhM0zSZ219jCt7b2Vyl0/jy3keFgCPMitG0V1S1YG4Cmws3lWHWdxHQOpg==", + "dependencies": { + "@vue/compiler-dom": "3.4.5", + "@vue/shared": "3.4.5" + } + }, + "node_modules/@vue/language-core": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz", + "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==", + "dev": true, + "dependencies": { + "@volar/language-core": "~1.11.1", + "@volar/source-map": "~1.11.1", + "@vue/compiler-dom": "^3.3.0", + "@vue/shared": "^3.3.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.3.1", + "path-browserify": "^1.0.1", + "vue-template-compiler": "^2.7.14" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.5.tgz", + "integrity": "sha512-BcWkKvjdvqJwb7BhhFkXPLDCecX4d4a6GATvCduJQDLv21PkPowAE5GKuIE5p6RC07/Lp9FMkkq4AYCTVF5KlQ==", + "dependencies": { + "@vue/shared": "3.4.5" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.5.tgz", + "integrity": "sha512-wh9ELIOQKeWT9SaUPdLrsxRkZv14jp+SJm9aiQGWio+/MWNM3Lib0wE6CoKEqQ9+SCYyGjDBhTOTtO47kCgbkg==", + "dependencies": { + "@vue/reactivity": "3.4.5", + "@vue/shared": "3.4.5" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.5.tgz", + "integrity": "sha512-n5ewvOjyG3IEpqGBahdPXODFSpVlSz3H4LF76Sx0XAqpIOqyJ5bIb2PrdYuH2ogBMAQPh+o5tnoH4nJpBr8U0Q==", + "dependencies": { + "@vue/runtime-core": "3.4.5", + "@vue/shared": "3.4.5", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.5.tgz", + "integrity": "sha512-jOFc/VE87yvifQpNju12VcqimH8pBLxdcT+t3xMeiED1K6DfH9SORyhFEoZlW5TG2Vwfn3Ul5KE+1aC99xnSBg==", + "dependencies": { + "@vue/compiler-ssr": "3.4.5", + "@vue/shared": "3.4.5" + }, + "peerDependencies": { + "vue": "3.4.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.5.tgz", + "integrity": "sha512-6XptuzlMvN4l4cDnDw36pdGEV+9njYkQ1ZE0Q6iZLwrKefKaOJyiFmcP3/KBDHbt72cJZGtllAc1GaHe6XGAyg==" + }, + "node_modules/@vueuse/core": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.7.2.tgz", + "integrity": "sha512-AOyAL2rK0By62Hm+iqQn6Rbu8bfmbgaIMXcE3TSr7BdQ42wnSFlwIdPjInO62onYsEMK/yDMU8C6oGfDAtZ2qQ==", + "dependencies": { + "@types/web-bluetooth": "^0.0.20", + "@vueuse/metadata": "10.7.2", + "@vueuse/shared": "10.7.2", + "vue-demi": ">=0.14.6" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/core/node_modules/vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/@vueuse/metadata": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.7.2.tgz", + "integrity": "sha512-kCWPb4J2KGrwLtn1eJwaJD742u1k5h6v/St5wFe8Quih90+k2a0JP8BS4Zp34XUuJqS2AxFYMb1wjUL8HfhWsQ==", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared": { + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.7.2.tgz", + "integrity": "sha512-qFbXoxS44pi2FkgFjPvF4h7c9oMDutpyBdcJdMYIMg9XyXli2meFMuaKn+UMgsClo//Th6+beeCgqweT/79BVA==", + "dependencies": { + "vue-demi": ">=0.14.6" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@vueuse/shared/node_modules/vue-demi": { + "version": "0.14.6", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.6.tgz", + "integrity": "sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.16", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001574", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz", + "integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/chalk/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/dom-align": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz", + "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.620", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.620.tgz", + "integrity": "sha512-a2fcSHOHrqBJsPNXtf6ZCEZpXrFCcbK1FBxfX3txoqWzNgtEDG1f3M59M98iwxhRW4iMKESnSjbJ310/rkrp0g==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.19.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.11.tgz", + "integrity": "sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.11", + "@esbuild/android-arm": "0.19.11", + "@esbuild/android-arm64": "0.19.11", + "@esbuild/android-x64": "0.19.11", + "@esbuild/darwin-arm64": "0.19.11", + "@esbuild/darwin-x64": "0.19.11", + "@esbuild/freebsd-arm64": "0.19.11", + "@esbuild/freebsd-x64": "0.19.11", + "@esbuild/linux-arm": "0.19.11", + "@esbuild/linux-arm64": "0.19.11", + "@esbuild/linux-ia32": "0.19.11", + "@esbuild/linux-loong64": "0.19.11", + "@esbuild/linux-mips64el": "0.19.11", + "@esbuild/linux-ppc64": "0.19.11", + "@esbuild/linux-riscv64": "0.19.11", + "@esbuild/linux-s390x": "0.19.11", + "@esbuild/linux-x64": "0.19.11", + "@esbuild/netbsd-x64": "0.19.11", + "@esbuild/openbsd-x64": "0.19.11", + "@esbuild/sunos-x64": "0.19.11", + "@esbuild/win32-arm64": "0.19.11", + "@esbuild/win32-ia32": "0.19.11", + "@esbuild/win32-x64": "0.19.11" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hls.js": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.4.14.tgz", + "integrity": "sha512-UppQjyvPVclg+6t2KY/Rv03h0+bA5u6zwqVoz4LAC/L0fgYmIaCD7ZCrwe8WI1Gv01be1XL0QFsRbSdIHV/Wbw==" + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/muggle-string": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", + "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", + "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.2.tgz", + "integrity": "sha512-66RB8OtFKUTozmVEh3qyNfH+b+z2RXBVloqO2KCC/pjFaGaHtxP9fVfOQKPSGXg2mElmjmxjW/fZ7iKrEpMH5Q==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.2", + "@rollup/rollup-android-arm64": "4.9.2", + "@rollup/rollup-darwin-arm64": "4.9.2", + "@rollup/rollup-darwin-x64": "4.9.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.2", + "@rollup/rollup-linux-arm64-gnu": "4.9.2", + "@rollup/rollup-linux-arm64-musl": "4.9.2", + "@rollup/rollup-linux-riscv64-gnu": "4.9.2", + "@rollup/rollup-linux-x64-gnu": "4.9.2", + "@rollup/rollup-linux-x64-musl": "4.9.2", + "@rollup/rollup-win32-arm64-msvc": "4.9.2", + "@rollup/rollup-win32-ia32-msvc": "4.9.2", + "@rollup/rollup-win32-x64-msvc": "4.9.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/tailwindcss": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", + "integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/viewerjs": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.6.tgz", + "integrity": "sha512-TlhdSp2oEOLFXvEp4psKaeTjR5zBjTRcM/sHUN8PkV1UWuY8HKC8n7GaVdW5Xqnwdr/F1OmzLik1QwDjI4w/nw==" + }, + "node_modules/vite": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz", + "integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==", + "dev": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.5.tgz", + "integrity": "sha512-VH6nHFhLPjgu2oh5vEBXoNZxsGHuZNr3qf4PHClwJWw6IDqw6B3x+4J+ABdoZ0aJuT8Zi0zf3GpGlLQCrGWHrw==", + "dependencies": { + "@vue/compiler-dom": "3.4.5", + "@vue/compiler-sfc": "3.4.5", + "@vue/runtime-dom": "3.4.5", + "@vue/server-renderer": "3.4.5", + "@vue/shared": "3.4.5" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "1.8.27", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz", + "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==", + "dev": true, + "dependencies": { + "@volar/typescript": "~1.11.1", + "@vue/language-core": "1.8.27", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..db32444 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "wfs", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vue-tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "@vueuse/core": "^10.7.2", + "dom-align": "^1.12.4", + "hls.js": "^1.4.14", + "viewerjs": "^1.11.6", + "vue": "^3.3.8" + }, + "devDependencies": { + "@tailwindcss/container-queries": "^0.1.1", + "@tailwindcss/forms": "^0.5.7", + "@types/node": "^20.10.4", + "@vitejs/plugin-basic-ssl": "^1.0.2", + "@vitejs/plugin-vue": "^4.5.0", + "@vitejs/plugin-vue-jsx": "^3.1.0", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.32", + "tailwindcss": "^3.4.0", + "typescript": "^5.2.2", + "vite": "^5.0.0", + "vue-tsc": "^1.8.22" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..b3ffbd2 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,64 @@ + + + \ No newline at end of file diff --git a/src/backend/api.ts b/src/backend/api.ts new file mode 100644 index 0000000..27e38d2 --- /dev/null +++ b/src/backend/api.ts @@ -0,0 +1,203 @@ +import {$on} from '../shared' +import {useSocket} from './socket' + +let apiUrl: string | undefined; +let accessToken: string | undefined; +let artifact: string | undefined; + +export async function configApi(cfg: Partial) { + if ( + cfg.accessToken !== accessToken || + cfg.apiUrl !== apiUrl || + cfg.artifact !== artifact + ) { + await ajax(cfg.apiUrl + "/" + cfg.artifact, { + headers: {"Authorization": `Bearer ${cfg.accessToken}`} + }) + + accessToken = cfg.accessToken; + artifact = cfg.artifact; + apiUrl = cfg.apiUrl; + + useSocket(cfg) + } +} + +export async function ajax(url: string, init: RequestInit = {}): Promise { + return new Promise(async (resolve, reject) => { + if (accessToken) { + const headers = new Headers(init.headers); + headers.set("Authorization", `Bearer ${accessToken}`); + init.headers = headers; + } + try { + const uri = (apiUrl ?? '') + url; + const response = await fetch(uri, init); + const res = await response.json(); + if ( + res != null && + typeof res === "object" && + !Array.isArray(res) && + "ok" in res && + "msg" in res && + typeof res.ok === "boolean" && + typeof res.msg === "string" + ) { + if (res.ok) { + resolve(res.data); + } else { + reject(new Error(res.msg)); + } + } else { + reject(new Error("服务器异常")); + } + } catch (err) { + // 服务器返回的有可能不是 JSON 数据 + if (err instanceof SyntaxError) { + reject(new Error("服务器异常")); + } else if (err instanceof Error && err.message === "Failed to fetch") { + reject(new Error("服务器异常")); + } else { + reject(err); + } + } + }); +} + +export interface UploadArgs { + skip: false; + token: string; + key: string; +} + +export interface UploadSkip { + skip: true; +} + +/** + * 初始化任务 + */ +export function initiateUploadApi( + fileMime: string, + fileHash: string, + signal: AbortSignal, +): Promise { + const url = `/${artifact}/objects/initiate`; + const body = new URLSearchParams({fileMime, fileHash}); + const init = {method: "POST", body, signal}; + return ajax(url, init); +} + +export function completeUploadApi( + fileHash: string, + fileNames: string[], + directories: number[], + signal: AbortSignal, +) { + const targets: Map> = new Map(); + for (let i = 0; i < directories.length; i++) { + const dirid = directories[i]; + const dirs = targets.get(dirid) ?? new Set(); + if (!fileNames[i]) return Promise.reject("参数错误"); + dirs.add(fileNames[i]); + targets.set(dirid, dirs); + } + const body = new URLSearchParams({fileHash}); + targets.forEach((names, dir) => { + names.forEach((name) => { + body.append("directoryId", `${dir}`); + body.append("fileName", name); + }); + }); + const url = `/${artifact}/objects/complete`; + return ajax< + Array<{ + id: TaskId; + directoryId: number; + fileName: string; + fileHash: string; + createdAt: string; + updatedAt: string; + }> + >(url, {method: "POST", body, signal}); +} + +interface SimulateQiniuCallbackOptions { + key: string; + name: string; + mime: string; + md5: FileHash; + size: number; +} + +export function simulateQiniuCallback( + data: SimulateQiniuCallbackOptions, + signal: AbortSignal, +) { + const url = `/${artifact}/objects/uploaded`; + const body = JSON.stringify({...data, etag: "", bucket: ""}); + return ajax(url, { + method: "POST", + body, + signal, + headers: {"Content-Type": "application/json"}, + }); +} + +// 新建文件夹 +$on('dir', 'create', (data: NewDirEventData): Promise => { + const {title, pid = 0} = data + const uri = `/${artifact}/directories`; + const body = new URLSearchParams({title}); + if (pid > 0) body.set("parentId", `${pid}`); + return ajax(uri, {method: "POST", body}); +}) + +// 所有文件夹 +$on('dir', 'all', (): Promise => { + return ajax(`/${artifact}/directories`); +}) + +// 删除文件夹 +$on('dir', 'delete', (id: number): Promise => { + const url = `/${artifact}/directories/${id}`; + return ajax(url, {method: "DELETE"}); +}) + +// 重命名文件夹 +$on('dir', 'rename', (data: RenameDirEventData): Promise => { + const url = `/${artifact}/directories/${data.id}`; + const body = new URLSearchParams({title: data.title}); + return ajax(url, {method: "PUT", body}); +}) + +// 文件重命名 +$on('file', 'rename', async (data: RenameFileEventData): Promise => { + const url = `/${artifact}/files/${data.id}`; + const body = new URLSearchParams({name: data.name}); + return await ajax(url, {method: "PUT", body}); +}) + +// 删除文件 +$on('file', 'delete', (files: number[] | number) => { + let url = `/${artifact}/files`; + if (!Array.isArray(files)) { + url += `/${files}`; + } else { + const body = new URLSearchParams(); + files.forEach((file) => body!.append("files", `${file}`)); + url += "?" + body + } + return ajax(url, {method: "DELETE"}); +}) + +// 文件列表(分页) +$on('file', 'list', (data: FilesEventData) => { + const url = `/${artifact}/files`; + const query = new URLSearchParams(); + query.set("page", `${data.page}`) + query.set("limit", `${data.limit}`) + if (data.directoryId != null) query.set("directoryId", `${data.directoryId}`); + if (data.mime != null) query.set("mime", data.mime); + return ajax(url + "?" + query.toString()); +}) diff --git a/src/backend/index.ts b/src/backend/index.ts new file mode 100644 index 0000000..7be0bb0 --- /dev/null +++ b/src/backend/index.ts @@ -0,0 +1,73 @@ +import {$on} from '../shared' +import {configApi} from './api' +import { + cleanReadTasks, + getReadTasks, + pauseReadTask, + Reader, + removeReadTask, + resumeReadTask, +} from "./reader"; +import {handleNetworkEvent} from "./socket"; +import { + cleanUploadTasks, + getUploadTasks, + pauseUploadTask, + removeUploadTask, + resumeUploadTask, +} from "./uploader"; + +// 取消前端任务 +async function cancel(data: TaskEventData): Promise { + const {fileHash, taskId} = data; + await pauseReadTask(taskId); + await pauseUploadTask(fileHash, taskId); +} + +// 恢复任务 +async function resume(data: TaskEventData): Promise { + const {fileHash, taskId} = data; + await resumeReadTask(taskId); + await resumeUploadTask(fileHash, taskId); +} + +// 清理任务 +async function cleanup() { + await cleanReadTasks(); + await cleanUploadTasks(); +} + +// 创建任务 +function create(data: TaskCreateData) { + Reader.create(data.file, data.dirid); +} + +// 删除任务 +async function remove(data: TaskEventData): Promise { + const {fileHash, taskId} = data; + await removeReadTask(taskId); + await removeUploadTask(fileHash, taskId); +} + +function allTasks(): Task[] { + return [ + ...getReadTasks(), + ...getUploadTasks(), + ] +} + +// 配置接口事件 +$on("cfg", "init", configApi); + +// 监听前端任务事件 +$on("task", "all", allTasks) +$on("task", "cancel", cancel); +$on("task", "cleanup", cleanup); +$on("task", "create", create); +$on("task", "remove", remove); +$on("task", "resume", resume); + +// 网络变化事件 +$on("net", "status", handleNetworkEvent); + + diff --git a/src/backend/qiniu/api/index.ts b/src/backend/qiniu/api/index.ts new file mode 100644 index 0000000..1d7d6e1 --- /dev/null +++ b/src/backend/qiniu/api/index.ts @@ -0,0 +1,173 @@ +import * as utils from '../utils' +import {normalizeUploadConfig} from '../utils' +import {Config, InternalConfig, UploadInfo} from '../upload' + +interface UpHosts { + data: { + up: { + acc: { + main: string[] + backup: string[] + } + } + } +} + +export async function getUpHosts(accessKey: string, bucketName: string, protocol: InternalConfig['upprotocol']): Promise { + const params = new URLSearchParams({ak: accessKey, bucket: bucketName}) + const url = `${protocol}://api.qiniu.com/v2/query?${params}` + return utils.request(url, {method: 'GET'}) +} + +/** + * @param bucket 空间名 + * @param key 目标文件名 + * @param uploadInfo 上传信息 + */ +function getBaseUrl(bucket: string, key: string | null | undefined, uploadInfo: UploadInfo) { + const {url, id} = uploadInfo + return `${url}/buckets/${bucket}/objects/${key != null ? utils.urlSafeBase64Encode(key) : '~'}/uploads/${id}` +} + +export interface InitPartsData { + /** 该文件的上传 id, 后续该文件其他各个块的上传,已上传块的废弃,已上传块的合成文件,都需要该 id */ + uploadId: string + /** uploadId 的过期时间 */ + expireAt: number +} + +/** + * @param token 上传鉴权凭证 + * @param bucket 上传空间 + * @param key 目标文件名 + * @param uploadUrl 上传地址 + */ +export function initUploadParts( + token: string, + bucket: string, + key: string | null | undefined, + uploadUrl: string +): utils.Response { + const url = `${uploadUrl}/buckets/${bucket}/objects/${key != null ? utils.urlSafeBase64Encode(key) : '~'}/uploads` + return utils.request( + url, + { + method: 'POST', + headers: utils.getAuthHeaders(token) + } + ) +} + +export interface UploadChunkData { + etag: string + md5: string +} + +/** + * @param token 上传鉴权凭证 + * @param index 当前 chunk 的索引 + * @param uploadInfo 上传信息 + * @param options 请求参数 + */ +export function uploadChunk( + token: string, + key: string | null | undefined, + index: number, + uploadInfo: UploadInfo, + options: Partial +): utils.Response { + const bucket = utils.getPutPolicy(token).bucketName + const url = getBaseUrl(bucket, key, uploadInfo) + `/${index}` + const headers = utils.getHeadersForChunkUpload(token) + if (options.md5) headers['Content-MD5'] = options.md5 + + return utils.request(url, { + ...options, + method: 'PUT', + headers + }) +} + +export type UploadCompleteData = any + +/** + * @param token 上传鉴权凭证 + * @param key 目标文件名 + * @param uploadInfo 上传信息 + * @param options 请求参数 + */ +export function uploadComplete( + token: string, + key: string | null | undefined, + uploadInfo: UploadInfo, + options: Partial +): utils.Response { + const bucket = utils.getPutPolicy(token).bucketName + const url = getBaseUrl(bucket, key, uploadInfo) + return utils.request(url, { + ...options, + method: 'POST', + headers: utils.getHeadersForMkFile(token) + }) +} + +/** + * @param token 上传鉴权凭证 + * @param key 目标文件名 + * @param uploadInfo 上传信息 + */ +export function deleteUploadedChunks( + token: string, + key: string | null | undefined, + uploadinfo: UploadInfo +): utils.Response { + const bucket = utils.getPutPolicy(token).bucketName + const url = getBaseUrl(bucket, key, uploadinfo) + return utils.request( + url, + { + method: 'DELETE', + headers: utils.getAuthHeaders(token) + } + ) +} + +/** + * @param {string} url + * @param {FormData} data + * @param {Partial} options + * @returns Promise + * @description 直传接口 + */ +export function direct( + url: string, + data: FormData, + options: Partial +): Promise { + return utils.request(url, { + method: 'POST', + body: data, + ...options + }) +} + +export type UploadUrlConfig = Partial> + +/** + * @param {UploadUrlConfig} config + * @param {string} token + * @returns Promise + * @description 获取上传 url + */ +export async function getUploadUrl(_config: UploadUrlConfig, token: string): Promise { + const config = normalizeUploadConfig(_config) + const protocol = config.upprotocol + + if (config.uphost.length > 0) { + return `${protocol}://${config.uphost[0]}` + } + const putPolicy = utils.getPutPolicy(token) + const res = await getUpHosts(putPolicy.assessKey, putPolicy.bucketName, protocol) + const hosts = res.data.up.acc.main + return `${protocol}://${hosts[0]}` +} diff --git a/src/backend/qiniu/config/index.ts b/src/backend/qiniu/config/index.ts new file mode 100644 index 0000000..9ceff9b --- /dev/null +++ b/src/backend/qiniu/config/index.ts @@ -0,0 +1 @@ +export * from './region' diff --git a/src/backend/qiniu/config/region.ts b/src/backend/qiniu/config/region.ts new file mode 100644 index 0000000..2bce5c9 --- /dev/null +++ b/src/backend/qiniu/config/region.ts @@ -0,0 +1,37 @@ +/** 上传区域 */ +export const region = { + z0: 'z0', + z1: 'z1', + z2: 'z2', + na0: 'na0', + as0: 'as0', + cnEast2: 'cn-east-2' +} as const + +/** 上传区域对应的 host */ +export const regionUphostMap = { + [region.z0]: { + srcUphost: ['up.qiniup.com'], + cdnUphost: ['upload.qiniup.com'] + }, + [region.z1]: { + srcUphost: ['up-z1.qiniup.com'], + cdnUphost: ['upload-z1.qiniup.com'] + }, + [region.z2]: { + srcUphost: ['up-z2.qiniup.com'], + cdnUphost: ['upload-z2.qiniup.com'] + }, + [region.na0]: { + srcUphost: ['up-na0.qiniup.com'], + cdnUphost: ['upload-na0.qiniup.com'] + }, + [region.as0]: { + srcUphost: ['up-as0.qiniup.com'], + cdnUphost: ['upload-as0.qiniup.com'] + }, + [region.cnEast2]: { + srcUphost: ['up-cn-east-2.qiniup.com'], + cdnUphost: ['upload-cn-east-2.qiniup.com'] + } +} as const diff --git a/src/backend/qiniu/errors/index.ts b/src/backend/qiniu/errors/index.ts new file mode 100644 index 0000000..6fe6010 --- /dev/null +++ b/src/backend/qiniu/errors/index.ts @@ -0,0 +1,62 @@ +export enum QiniuErrorName { + // 输入错误 + InvalidFile = 'InvalidFile', + InvalidToken = 'InvalidToken', + InvalidMetadata = 'InvalidMetadata', + InvalidChunkSize = 'InvalidChunkSize', + InvalidCustomVars = 'InvalidCustomVars', + NotAvailableUploadHost = 'NotAvailableUploadHost', + + // 缓存相关 + ReadCacheFailed = 'ReadCacheFailed', + InvalidCacheData = 'InvalidCacheData', + WriteCacheFailed = 'WriteCacheFailed', + RemoveCacheFailed = 'RemoveCacheFailed', + + // 图片压缩模块相关 + GetCanvasContextFailed = 'GetCanvasContextFailed', + UnsupportedFileType = 'UnsupportedFileType', + + // 运行环境相关 + FileReaderReadFailed = 'FileReaderReadFailed', + NotAvailableXMLHttpRequest = 'NotAvailableXMLHttpRequest', + InvalidProgressEventTarget = 'InvalidProgressEventTarget', + + // 请求错误 + RequestError = 'RequestError' +} + +export class QiniuError implements Error { + public stack: string | undefined + constructor(public name: QiniuErrorName, public message: string) { + this.stack = new Error().stack + } +} + +export class QiniuRequestError extends QiniuError { + + /** + * @description 标记当前的 error 类型是一个 QiniuRequestError + * @deprecated 下一个大版本将会移除,不推荐使用,推荐直接使用 instanceof 进行判断 + */ + public isRequestError = true + + /** + * @description 发生错误时服务端返回的错误信息,如果返回不是一个合法的 json、则该字段为 undefined + */ + public data?: any + + constructor(public code: number, public reqId: string, message: string, data?: any) { + super(QiniuErrorName.RequestError, message) + this.data = data + } +} + +/** + * @description 由于跨域、证书错误、断网、host 解析失败、系统拦截等原因导致的错误 + */ +export class QiniuNetworkError extends QiniuRequestError { + constructor(message: string, reqId = '') { + super(0, reqId, message) + } +} diff --git a/src/backend/qiniu/image/index.ts b/src/backend/qiniu/image/index.ts new file mode 100644 index 0000000..7e2ee5f --- /dev/null +++ b/src/backend/qiniu/image/index.ts @@ -0,0 +1,195 @@ +import { request, urlSafeBase64Encode } from '../utils' + +export interface ImageViewOptions { + mode: number + format?: string + w?: number + h?: number + q?: number +} + +export interface ImageWatermark { + image: string + mode: number + fontsize?: number + dissolve?: number + dx?: number + dy?: number + gravity?: string + text?: string + font?: string + fill?: string +} + +export interface ImageMogr2 { + 'auto-orient'?: boolean + strip?: boolean + thumbnail?: number + crop?: number + gravity?: number + format?: number + blur?: number + quality?: number + rotate?: number +} + +type Pipeline = + | (ImageWatermark & { fop: 'watermark' }) + | (ImageViewOptions & { fop: 'imageView2' }) + | (ImageMogr2 & { fop: 'imageMogr2' }) + +export interface Entry { + domain: string + key: string +} + +function getImageUrl(key: string, domain: string) { + key = encodeURIComponent(key) + if (domain.slice(domain.length - 1) !== '/') { + domain += '/' + } + + return domain + key +} + +export function imageView2(op: ImageViewOptions, key?: string, domain?: string) { + if (!/^\d$/.test(String(op.mode))) { + throw 'mode should be number in imageView2' + } + + const { mode, w, h, q, format } = op + + if (!w && !h) { + throw 'param w and h is empty in imageView2' + } + + let imageUrl = 'imageView2/' + encodeURIComponent(mode) + imageUrl += w ? '/w/' + encodeURIComponent(w) : '' + imageUrl += h ? '/h/' + encodeURIComponent(h) : '' + imageUrl += q ? '/q/' + encodeURIComponent(q) : '' + imageUrl += format ? '/format/' + encodeURIComponent(format) : '' + if (key && domain) { + imageUrl = getImageUrl(key, domain) + '?' + imageUrl + } + return imageUrl +} + +// invoke the imageMogr2 api of Qiniu +export function imageMogr2(op: ImageMogr2, key?: string, domain?: string) { + const autoOrient = op['auto-orient'] + const { thumbnail, strip, gravity, crop, quality, rotate, format, blur } = op + + let imageUrl = 'imageMogr2' + + imageUrl += autoOrient ? '/auto-orient' : '' + imageUrl += thumbnail ? '/thumbnail/' + encodeURIComponent(thumbnail) : '' + imageUrl += strip ? '/strip' : '' + imageUrl += gravity ? '/gravity/' + encodeURIComponent(gravity) : '' + imageUrl += quality ? '/quality/' + encodeURIComponent(quality) : '' + imageUrl += crop ? '/crop/' + encodeURIComponent(crop) : '' + imageUrl += rotate ? '/rotate/' + encodeURIComponent(rotate) : '' + imageUrl += format ? '/format/' + encodeURIComponent(format) : '' + imageUrl += blur ? '/blur/' + encodeURIComponent(blur) : '' + if (key && domain) { + imageUrl = getImageUrl(key, domain) + '?' + imageUrl + } + return imageUrl +} + +// invoke the watermark api of Qiniu +export function watermark(op: ImageWatermark, key?: string, domain?: string) { + const mode = op.mode + if (!mode) { + throw "mode can't be empty in watermark" + } + + let imageUrl = 'watermark/' + mode + if (mode !== 1 && mode !== 2) { + throw 'mode is wrong' + } + + if (mode === 1) { + const image = op.image + if (!image) { + throw "image can't be empty in watermark" + } + imageUrl += image ? '/image/' + urlSafeBase64Encode(image) : '' + } + + if (mode === 2) { + const { text, font, fontsize, fill } = op + if (!text) { + throw "text can't be empty in watermark" + } + imageUrl += text ? '/text/' + urlSafeBase64Encode(text) : '' + imageUrl += font ? '/font/' + urlSafeBase64Encode(font) : '' + imageUrl += fontsize ? '/fontsize/' + fontsize : '' + imageUrl += fill ? '/fill/' + urlSafeBase64Encode(fill) : '' + } + + const { dissolve, gravity, dx, dy } = op + + imageUrl += dissolve ? '/dissolve/' + encodeURIComponent(dissolve) : '' + imageUrl += gravity ? '/gravity/' + encodeURIComponent(gravity) : '' + imageUrl += dx ? '/dx/' + encodeURIComponent(dx) : '' + imageUrl += dy ? '/dy/' + encodeURIComponent(dy) : '' + if (key && domain) { + imageUrl = getImageUrl(key, domain) + '?' + imageUrl + } + return imageUrl +} + +// invoke the imageInfo api of Qiniu +export function imageInfo(key: string, domain: string) { + const url = getImageUrl(key, domain) + '?imageInfo' + return request(url, { method: 'GET' }) +} + +// invoke the exif api of Qiniu +export function exif(key: string, domain: string) { + const url = getImageUrl(key, domain) + '?exif' + return request(url, { method: 'GET' }) +} + +export function pipeline(arr: Pipeline[], key?: string, domain?: string) { + const isArray = Object.prototype.toString.call(arr) === '[object Array]' + let option: Pipeline + let errOp = false + let imageUrl = '' + if (isArray) { + for (let i = 0, len = arr.length; i < len; i++) { + option = arr[i] + if (!option.fop) { + throw "fop can't be empty in pipeline" + } + switch (option.fop) { + case 'watermark': + imageUrl += watermark(option) + '|' + break + case 'imageView2': + imageUrl += imageView2(option) + '|' + break + case 'imageMogr2': + imageUrl += imageMogr2(option) + '|' + break + default: + errOp = true + break + } + if (errOp) { + throw 'fop is wrong in pipeline' + } + } + + if (key && domain) { + imageUrl = getImageUrl(key, domain) + '?' + imageUrl + const length = imageUrl.length + if (imageUrl.slice(length - 1) === '|') { + imageUrl = imageUrl.slice(0, length - 1) + } + } + return imageUrl + } + + throw "pipeline's first param should be array" +} diff --git a/src/backend/qiniu/index.ts b/src/backend/qiniu/index.ts new file mode 100644 index 0000000..098bea5 --- /dev/null +++ b/src/backend/qiniu/index.ts @@ -0,0 +1,22 @@ +export { + QiniuErrorName, + QiniuError, + QiniuRequestError, + QiniuNetworkError +} from './errors' +export {imageMogr2, watermark, imageInfo, exif, pipeline} from './image' +export {deleteUploadedChunks, getUploadUrl} from './api' +export { + upload, + type UploadProgress +} from './upload' +export {region} from './config' + +export { + // compressImage, + // type CompressResult, + urlSafeBase64Encode, + urlSafeBase64Decode, + getHeadersForMkFile, + getHeadersForChunkUpload +} from './utils' diff --git a/src/backend/qiniu/logger/index.ts b/src/backend/qiniu/logger/index.ts new file mode 100644 index 0000000..3152304 --- /dev/null +++ b/src/backend/qiniu/logger/index.ts @@ -0,0 +1,73 @@ +import {reportV3, V3LogInfo} from './report-v3' + +export type LogLevel = 'INFO' | 'WARN' | 'ERROR' | 'OFF' + +export default class Logger { + private static id = 0 + + // 为每个类分配一个 id + // 用以区分不同的上传任务 + private id = ++Logger.id + + constructor( + private token: string, + private disableReport = true, + private level: LogLevel = 'OFF', + private prefix = 'UPLOAD' + ) { + } + + /** + * @param {V3LogInfo} data 上报的数据。 + * @param {boolean} retry 重试次数,可选,默认为 3。 + * @description 向服务端上报统计信息。 + */ + report(data: V3LogInfo, retry?: number) { + if (this.disableReport) return + try { + reportV3(this.token, data, retry) + } catch (error) { + this.warn(error) + } + } + + /** + * 输出 info 级别的调试信息。 + * @param {unknown[]} args + */ + info(...args: unknown[]) { + const allowLevel: LogLevel[] = ['INFO'] + if (allowLevel.includes(this.level)) { + // eslint-disable-next-line no-console + console.log(this.getPrintPrefix('INFO'), ...args) + } + } + + /** + * 输出 warn 级别的调试信息。 + * @param {unknown[]} args + */ + warn(...args: unknown[]) { + const allowLevel: LogLevel[] = ['INFO', 'WARN'] + if (allowLevel.includes(this.level)) { + // eslint-disable-next-line no-console + console.warn(this.getPrintPrefix('WARN'), ...args) + } + } + + /** + * 输出 error 级别的调试信息。 + * @param {unknown[]} args + */ + error(...args: unknown[]) { + const allowLevel: LogLevel[] = ['INFO', 'WARN', 'ERROR'] + if (allowLevel.includes(this.level)) { + // eslint-disable-next-line no-console + console.error(this.getPrintPrefix('ERROR'), ...args) + } + } + + private getPrintPrefix(level: LogLevel) { + return `Qiniu-JS-SDK [${level}][${this.prefix}#${this.id}]:` + } +} diff --git a/src/backend/qiniu/logger/report-v3.ts b/src/backend/qiniu/logger/report-v3.ts new file mode 100644 index 0000000..32acc65 --- /dev/null +++ b/src/backend/qiniu/logger/report-v3.ts @@ -0,0 +1,48 @@ +import { createXHR, getAuthHeaders } from '../utils' + +export interface V3LogInfo { + code: number + reqId: string + host: string + remoteIp: string + port: string + duration: number + time: number + bytesSent: number + upType: 'jssdk-h5' + size: number +} + +/** + * @param {string} token 上传使用的 token + * @param {V3LogInfo} data 上报的统计数据 + * @param {number} retry 重试的次数,默认值 3 + * @description v3 版本的日志上传接口,参考文档 https://github.com/qbox/product/blob/master/kodo/uplog.md#%E7%89%88%E6%9C%AC-3。 + */ +export function reportV3(token: string, data: V3LogInfo, retry = 3) { + const xhr = createXHR() + xhr.open('POST', 'https://uplog.qbox.me/log/3') + xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') + xhr.setRequestHeader('Authorization', getAuthHeaders(token).Authorization) + xhr.onreadystatechange = () => { + if (xhr.readyState === 4 && xhr.status !== 200 && retry > 0) { + reportV3(token, data, retry - 1) + } + } + + // 顺序参考:https://github.com/qbox/product/blob/master/kodo/uplog.md#%E7%89%88%E6%9C%AC-3 + const stringifyData = [ + data.code || '', + data.reqId || '', + data.host || '', + data.remoteIp || '', + data.port || '', + data.duration || '', + data.time || '', + data.bytesSent || '', + data.upType || '', + data.size || '' + ].join(',') + + xhr.send(stringifyData) +} diff --git a/src/backend/qiniu/upload/base.ts b/src/backend/qiniu/upload/base.ts new file mode 100644 index 0000000..5277bb4 --- /dev/null +++ b/src/backend/qiniu/upload/base.ts @@ -0,0 +1,340 @@ +import {QiniuError, QiniuErrorName, QiniuRequestError} from '../errors' +import Logger, {LogLevel} from '../logger' +import {region} from '../config' +import * as utils from '../utils' + +import {Host, HostPool} from './hosts' + +export const DEFAULT_CHUNK_SIZE = 4 // 单位 MB + +// code 信息地址 https://developer.qiniu.com/kodo/3928/error-responses +export const FREEZE_CODE_LIST = [0, 502, 503, 504, 599] // 将会冻结当前 host 的 code +export const RETRY_CODE_LIST = [...FREEZE_CODE_LIST, 612] // 会进行重试的 code + +/** 上传文件的资源信息配置 */ +export interface Extra { + /** 文件原文件名 */ + fname: string + /** 用来放置自定义变量 */ + customVars?: { [key: string]: string } + /** 自定义元信息 */ + metadata?: { [key: string]: string } + /** 文件类型设置 */ + mimeType?: string // +} + +export interface InternalConfig { + /** 是否开启 cdn 加速 */ + useCdnDomain: boolean + /** 是否开启服务端校验 */ + checkByServer: boolean + /** 是否对分片进行 md5校验 */ + checkByMD5: boolean + /** 强制直传 */ + forceDirect: boolean + /** 上传失败后重试次数 */ + retryCount: number + /** 自定义上传域名 */ + uphost: string[] + /** 自定义分片上传并发请求量 */ + concurrentRequestLimit: number + /** 分片大小,单位为 MB */ + chunkSize: number + /** 上传域名协议 */ + upprotocol: 'https' | 'http' + /** 上传区域 */ + region?: typeof region[keyof typeof region] + /** 是否禁止统计日志上报 */ + disableStatisticsReport: boolean + /** 设置调试日志输出模式,默认 `OFF`,不输出任何日志 */ + debugLogLevel?: LogLevel +} + +/** 上传任务的配置信息 */ +export interface Config extends Partial> { + /** 上传域名协议 */ + upprotocol?: InternalConfig['upprotocol'] | 'https:' | 'http:' + /** 自定义上传域名 */ + uphost?: InternalConfig['uphost'] | string +} + +export interface UploadOptions { + file: File + key: string | null | undefined + token: string + config: InternalConfig + putExtra?: Partial +} + +export interface UploadInfo { + id: string + url: string +} + +/** 传递给外部的上传进度信息 */ +export interface UploadProgress { + total: ProgressCompose + uploadInfo?: UploadInfo + chunks?: ProgressCompose[] +} + +export interface UploadHandlers { + onData: (data: UploadProgress) => void + onError: (err: QiniuError) => void + onComplete: (res: any) => void +} + +export interface Progress { + total: number + loaded: number +} + +export interface ProgressCompose { + size: number + loaded: number + percent: number + fromCache?: boolean +} + +export type XHRHandler = (xhr: XMLHttpRequest) => void + +const GB = 1024 ** 3 + +export default abstract class Base { + protected config: InternalConfig + protected putExtra: Extra + + protected aborted = false + protected retryCount = 0 + + protected uploadHost?: Host + protected xhrList: XMLHttpRequest[] = [] + + protected file: File + protected key: string | null | undefined + + protected token: string + protected assessKey: string = '' + protected bucketName: string = '' + + protected uploadAt?: number + protected progress?: UploadProgress + + protected onData: (data: UploadProgress) => void + protected onError: (err: QiniuError) => void + protected onComplete: (res: any) => void + + constructor( + options: UploadOptions, + handlers: UploadHandlers, + protected hostPool: HostPool, + protected logger: Logger + ) { + + this.config = options.config + logger.info('config inited.', this.config) + + this.putExtra = { + fname: '', + ...options.putExtra + } + + logger.info('putExtra inited.', this.putExtra) + + this.key = options.key + this.file = options.file + this.token = options.token + + this.onData = handlers.onData + this.onError = handlers.onError + this.onComplete = handlers.onComplete + + try { + const putPolicy = utils.getPutPolicy(this.token) + this.bucketName = putPolicy.bucketName + this.assessKey = putPolicy.assessKey + } catch (error) { + logger.error('get putPolicy from token failed.', error) + this.onError(error as QiniuError) + } + } + + /** + * @returns Promise 返回结果与上传最终状态无关,状态信息请通过 [Subscriber] 获取。 + * @description 上传文件,状态信息请通过 [Subscriber] 获取。 + */ + public async putFile(): Promise { + this.aborted = false + if (!this.putExtra.fname) { + this.logger.info('use file.name as fname.') + this.putExtra.fname = this.file.name + } + + if (this.file.size > 10000 * GB) { + this.handleError(new QiniuError( + QiniuErrorName.InvalidFile, + 'file size exceed maximum value 10000G' + )) + return + } + + if (this.putExtra.customVars) { + if (!utils.isCustomVarsValid(this.putExtra.customVars)) { + this.handleError(new QiniuError( + QiniuErrorName.InvalidCustomVars, + 'customVars key should start with x:' + )) + return + } + } + + if (this.putExtra.metadata) { + if (!utils.isMetaDataValid(this.putExtra.metadata)) { + this.handleError(new QiniuError( + QiniuErrorName.InvalidMetadata, + 'metadata key should start with x-qn-meta-' + )) + return + } + } + + try { + this.uploadAt = new Date().getTime() + await this.checkAndUpdateUploadHost() + const result = await this.run() + this.onComplete(result.data) + this.checkAndUnfreezeHost() + this.sendLog(result.reqId, 200) + return + } catch (err) { + if (this.aborted) { + this.logger.warn('upload is aborted.') + this.sendLog('', -2) + return + } + + this.clear() + this.logger.error(err) + if (err instanceof QiniuRequestError) { + this.sendLog(err.reqId, err.code) + + // 检查并冻结当前的 host + this.checkAndFreezeHost(err) + + const notReachRetryCount = ++this.retryCount <= this.config.retryCount + const needRetry = RETRY_CODE_LIST.includes(err.code) + + // 以下条件满足其中之一则会进行重新上传: + // 1. 满足 needRetry 的条件且 retryCount 不为 0 + // 2. uploadId 无效时在 resume 里会清除本地数据,并且这里触发重新上传 + if (needRetry && notReachRetryCount) { + this.logger.warn(`error auto retry: ${this.retryCount}/${this.config.retryCount}.`) + this.putFile() + return + } + } + + this.onError(err as QiniuError) + } + } + + public stop() { + this.logger.info('aborted.') + this.clear() + this.aborted = true + } + + public addXhr(xhr: XMLHttpRequest) { + this.xhrList.push(xhr) + } + + public getProgressInfoItem(loaded: number, size: number, fromCache?: boolean): ProgressCompose { + return { + size, + loaded, + percent: loaded / size * 100, + ...(fromCache == null ? {} : {fromCache}) + } + } + + /** + * @returns utils.Response + * @description 子类通过该方法实现具体的任务处理 + */ + protected abstract run(): utils.Response + + // 检查并更新 upload host + protected async checkAndUpdateUploadHost() { + // 从 hostPool 中获取一个可用的 host 挂载在 this + this.logger.info('get available upload host.') + const newHost = await this.hostPool.getUp( + this.assessKey, + this.bucketName, + this.config.upprotocol + ) + + if (newHost == null) { + throw new QiniuError( + QiniuErrorName.NotAvailableUploadHost, + 'no available upload host.' + ) + } + + if (this.uploadHost != null && this.uploadHost.host !== newHost.host) { + this.logger.warn(`host switches from ${this.uploadHost.host} to ${newHost.host}.`) + } else { + this.logger.info(`use host ${newHost.host}.`) + } + + this.uploadHost = newHost + } + + // 检查并解冻当前的 host + protected checkAndUnfreezeHost() { + this.logger.info('check unfreeze host.') + if (this.uploadHost != null && this.uploadHost.isFrozen()) { + this.logger.warn(`${this.uploadHost.host} will be unfrozen.`) + this.uploadHost.unfreeze() + } + } + + // 检查并更新冻结当前的 host + private checkAndFreezeHost(error: QiniuError) { + this.logger.info('check freeze host.') + if (error instanceof QiniuRequestError && this.uploadHost != null) { + if (FREEZE_CODE_LIST.includes(error.code)) { + this.logger.warn(`${this.uploadHost.host} will be temporarily frozen.`) + this.uploadHost.freeze() + } + } + } + + private handleError(error: QiniuError) { + this.logger.error(error.message) + this.onError(error) + } + + private clear() { + this.xhrList.forEach(xhr => { + xhr.onreadystatechange = null + xhr.abort() + }) + this.xhrList = [] + this.logger.info('cleanup uploading xhr.') + } + + private sendLog(reqId: string, code: number) { + this.logger.report({ + code, + reqId, + remoteIp: '', + upType: 'jssdk-h5', + size: this.file.size, + time: Math.floor(this.uploadAt! / 1000), + port: utils.getPortFromUrl(this.uploadHost?.getUrl()), + host: utils.getDomainFromUrl(this.uploadHost?.getUrl()), + bytesSent: this.progress ? this.progress.total.loaded : 0, + duration: Math.floor((new Date().getTime() - this.uploadAt!) / 1000) + }) + } +} diff --git a/src/backend/qiniu/upload/direct.ts b/src/backend/qiniu/upload/direct.ts new file mode 100644 index 0000000..717f236 --- /dev/null +++ b/src/backend/qiniu/upload/direct.ts @@ -0,0 +1,70 @@ +import { CRC32 } from '../utils/crc32' + +import { direct } from '../api' + +import Base from './base' + +export default class Direct extends Base { + + protected async run() { + this.logger.info('start run Direct.') + + const formData = new FormData() + formData.append('file', this.file) + formData.append('token', this.token) + if (this.key != null) { + formData.append('key', this.key) + } + formData.append('fname', this.putExtra.fname) + + if (this.config.checkByServer) { + const crcSign = await CRC32.file(this.file) + formData.append('crc32', crcSign.toString()) + } + + if (this.putExtra.customVars) { + this.logger.info('init customVars.') + const { customVars } = this.putExtra + Object.keys(customVars).forEach(key => formData.append(key, customVars[key].toString())) + this.logger.info('customVars inited.') + } + + if (this.putExtra.metadata) { + this.logger.info('init metadata.') + const { metadata } = this.putExtra + Object.keys(metadata).forEach(key => formData.append(key, metadata[key].toString())) + } + + this.logger.info('formData inited.') + const result = await direct(this.uploadHost!.getUrl(), formData, { + onProgress: data => { + this.updateDirectProgress(data.loaded, data.total) + }, + onCreate: xhr => this.addXhr(xhr) + }) + + this.logger.info('Direct progress finish.') + this.finishDirectProgress() + return result + } + + private updateDirectProgress(loaded: number, total: number) { + // 当请求未完成时可能进度会达到100,所以total + 1来防止这种情况出现 + this.progress = { total: this.getProgressInfoItem(loaded, total + 1) } + this.onData(this.progress) + } + + private finishDirectProgress() { + // 在某些浏览器环境下,xhr 的 progress 事件无法被触发,progress 为 null,这里 fake 下 + if (!this.progress) { + this.logger.warn('progress is null.') + this.progress = { total: this.getProgressInfoItem(this.file.size, this.file.size) } + this.onData(this.progress) + return + } + + const { total } = this.progress + this.progress = { total: this.getProgressInfoItem(total.loaded + 1, total.size) } + this.onData(this.progress) + } +} diff --git a/src/backend/qiniu/upload/hosts.ts b/src/backend/qiniu/upload/hosts.ts new file mode 100644 index 0000000..a5c3277 --- /dev/null +++ b/src/backend/qiniu/upload/hosts.ts @@ -0,0 +1,134 @@ +import {getUpHosts} from '../api' +import {InternalConfig} from './base' + +/** + * @description 解冻时间,key 是 host,value 为解冻时间 + */ +const unfreezeTimeMap = new Map() + +export class Host { + constructor(public host: string, public protocol: InternalConfig['upprotocol']) { + } + + /** + * @description 当前 host 是否为冻结状态 + */ + isFrozen() { + const currentTime = new Date().getTime() + const unfreezeTime = unfreezeTimeMap.get(this.host) + return unfreezeTime != null && unfreezeTime >= currentTime + } + + /** + * @param {number} time 单位秒,默认 20s + * @description 冻结该 host 对象,该 host 将在指定时间内不可用 + */ + freeze(time = 20) { + const unfreezeTime = new Date().getTime() + (time * 1000) + unfreezeTimeMap.set(this.host, unfreezeTime) + } + + /** + * @description 解冻该 host + */ + unfreeze() { + unfreezeTimeMap.delete(this.host) + } + + /** + * @description 获取当前 host 的完整 url + */ + getUrl() { + return `${this.protocol}://${this.host}` + } + + /** + * @description 获取解冻时间 + */ + getUnfreezeTime() { + return unfreezeTimeMap.get(this.host) + } +} + +export class HostPool { + /** + * @description 缓存的 host 表,以 bucket 和 accessKey 作为 key + */ + private cachedHostsMap = new Map() + + /** + * @param {string[]} initHosts + * @description 如果在构造时传入 initHosts,则该 host 池始终使用传入的 initHosts 做为可用的数据 + */ + constructor(private initHosts: string[] = []) { + } + + /** + * @param {string} accessKey + * @param {string} bucketName + * @param {InternalConfig['upprotocol']} protocol + * @returns {Promise} + * @description 获取一个可用的上传 Host,排除已冻结的 + */ + public async getUp(accessKey: string, bucketName: string, protocol: InternalConfig['upprotocol']): Promise { + await this.refresh(accessKey, bucketName, protocol) + const cachedHostList = this.cachedHostsMap.get(`${accessKey}@${bucketName}`) || [] + + if (cachedHostList.length === 0) { + return null + } + + const availableHostList = cachedHostList.filter(host => !host.isFrozen()) + if (availableHostList.length > 0) { + return availableHostList[0] + } + + // 无可用的,去取离解冻最近的 host + const priorityQueue = cachedHostList + .slice() + .sort((hostA, hostB) => (hostA.getUnfreezeTime() || 0) - (hostB.getUnfreezeTime() || 0)) + + return priorityQueue[0] + } + + /** + * @param {string} accessKey + * @param {string} bucketName + * @param {string[]} hosts + * @param {InternalConfig['upprotocol']} protocol + * @returns {void} + * @description 注册可用 host + */ + private register(accessKey: string, bucketName: string, hosts: string[], protocol: InternalConfig['upprotocol']): void { + this.cachedHostsMap.set( + `${accessKey}@${bucketName}`, + hosts.map(host => new Host(host, protocol)) + ) + } + + /** + * @param {string} accessKey + * @param {string} bucketName + * @param {InternalConfig['upprotocol']} protocol + * @returns {Promise} + * @description 刷新最新的 host 数据,如果用户在构造时该类时传入了 host 或者已经存在缓存则不会发起请求 + */ + private async refresh(accessKey: string, bucketName: string, protocol: InternalConfig['upprotocol']): Promise { + const cachedHostList = this.cachedHostsMap.get(`${accessKey}@${bucketName}`) || [] + if (cachedHostList.length > 0) return + + if (this.initHosts.length > 0) { + this.register(accessKey, bucketName, this.initHosts, protocol) + return + } + + const response = await getUpHosts(accessKey, bucketName, protocol) + if (response?.data != null) { + const stashHosts: string[] = [ + ...(response.data.up?.acc?.main || []), + ...(response.data.up?.acc?.backup || []) + ] + this.register(accessKey, bucketName, stashHosts, protocol) + } + } +} diff --git a/src/backend/qiniu/upload/index.ts b/src/backend/qiniu/upload/index.ts new file mode 100644 index 0000000..389da09 --- /dev/null +++ b/src/backend/qiniu/upload/index.ts @@ -0,0 +1,86 @@ +import Resume from './resume' +import Direct from './direct' +import Logger from '../logger' +import {UploadCompleteData} from '../api' +import {IObserver, MB, normalizeUploadConfig, Observable} from '../utils' +import {QiniuError, QiniuNetworkError, QiniuRequestError} from '../errors' +import { + Config, + Extra, + UploadHandlers, + UploadOptions, + UploadProgress +} from './base' +import {HostPool} from './hosts' + +export * from './base' +export * from './resume' + +export type { + UploadProgress +} + +export function createUploadManager( + options: UploadOptions, + handlers: UploadHandlers, + hostPool: HostPool, + logger: Logger +) { + if (options.config && options.config.forceDirect) { + logger.info('ues forceDirect mode.') + return new Direct(options, handlers, hostPool, logger) + } + + if (options.file.size > 4 * MB) { + logger.info('file size over 4M, use Resume.') + return new Resume(options, handlers, hostPool, logger) + } + + logger.info('file size less or equal than 4M, use Direct.') + return new Direct(options, handlers, hostPool, logger) +} + +/** + * @param file 上传文件 + * @param key 目标文件名 + * @param token 上传凭证 + * @param putExtra 上传文件的相关资源信息配置 + * @param config 上传任务的配置 + * @returns 返回用于上传任务的可观察对象 + */ +export function upload( + file: File, + key: string | null | undefined, + token: string, + putExtra?: Partial, + config?: Config +): Observable { + + // 为每个任务创建单独的 Logger + const logger = new Logger(token, config?.disableStatisticsReport, config?.debugLogLevel, file.name) + + const options: UploadOptions = { + file, + key, + token, + putExtra, + config: normalizeUploadConfig(config, logger) + } + + // 创建 host 池 + const hostPool = new HostPool(options.config.uphost) + + return new Observable((observer: IObserver< + UploadProgress, + QiniuError | QiniuRequestError | QiniuNetworkError, + UploadCompleteData + >) => { + const manager = createUploadManager(options, { + onData: (data: UploadProgress) => observer.next(data), + onError: (err: QiniuError) => observer.error(err), + onComplete: (res: any) => observer.complete(res) + }, hostPool, logger) + manager.putFile() + return manager.stop.bind(manager) + }) +} diff --git a/src/backend/qiniu/upload/resume.ts b/src/backend/qiniu/upload/resume.ts new file mode 100644 index 0000000..c9279c7 --- /dev/null +++ b/src/backend/qiniu/upload/resume.ts @@ -0,0 +1,314 @@ +import { + initUploadParts, + uploadChunk, + UploadChunkData, + uploadComplete +} from '../api' +import {QiniuError, QiniuErrorName, QiniuRequestError} from '../errors' +import * as utils from '../utils' + +import Base, {Extra, Progress, UploadInfo} from './base' + +export interface UploadedChunkStorage extends UploadChunkData { + size: number +} + +export interface ChunkLoaded { + mkFileProgress: 0 | 1 + chunks: number[] +} + +export interface ChunkInfo { + chunk: Blob + index: number +} + +export interface LocalInfo { + data: UploadedChunkStorage[] + id: string +} + +export interface ChunkPart { + etag: string + partNumber: number +} + +export interface UploadChunkBody extends Extra { + parts: ChunkPart[] +} + +/** 是否为正整数 */ +function isPositiveInteger(n: number) { + const re = /^[1-9]\d*$/ + return re.test(String(n)) +} + +export default class Resume extends Base { + /** + * @description 文件的分片 chunks + */ + private chunks: Blob[] = [] + + /** + * @description 使用缓存的 chunks + */ + private usedCacheList: boolean[] = [] + + /** + * @description 来自缓存的上传信息 + */ + private cachedUploadedList: UploadedChunkStorage[] = [] + + /** + * @description 当前上传过程中已完成的上传信息 + */ + private uploadedList: UploadedChunkStorage[] = [] + + /** + * @description 当前上传片进度信息 + */ + private loaded?: ChunkLoaded + + /** + * @description 当前上传任务的 id + */ + private uploadId?: string + + /** + * @returns {Promise>} + * @description 实现了 Base 的 run 接口,处理具体的分片上传事务,并抛出过程中的异常。 + */ + protected async run() { + this.logger.info('start run Resume.') + if (!this.config.chunkSize || !isPositiveInteger(this.config.chunkSize)) { + throw new QiniuError( + QiniuErrorName.InvalidChunkSize, + 'chunkSize must be a positive integer' + ) + } + + if (this.config.chunkSize > 1024) { + throw new QiniuError( + QiniuErrorName.InvalidChunkSize, + 'chunkSize maximum value is 1024' + ) + } + + await this.initBeforeUploadChunks() + + const pool = new utils.Pool( + async (chunkInfo: ChunkInfo) => { + if (this.aborted) { + pool.abort() + throw new Error('pool is aborted') + } + + await this.uploadChunk(chunkInfo) + }, + this.config.concurrentRequestLimit + ) + + let mkFileResponse = null + const localKey = this.getLocalKey() + const uploadChunks = this.chunks.map((chunk, index) => pool.enqueue({ + chunk, + index + })) + + try { + await Promise.all(uploadChunks) + mkFileResponse = await this.mkFileReq() + } catch (error) { + // uploadId 无效,上传参数有误(多由于本地存储信息的 uploadId 失效) + if (error instanceof QiniuRequestError && (error.code === 612 || error.code === 400)) { + utils.removeLocalFileInfo(localKey, this.logger) + } + + throw error + } + + // 上传成功,清理本地缓存数据 + utils.removeLocalFileInfo(localKey, this.logger) + return mkFileResponse + } + + private async uploadChunk(chunkInfo: ChunkInfo) { + const {index, chunk} = chunkInfo + const cachedInfo = this.cachedUploadedList[index] + this.logger.info(`upload part ${index}, cache:`, cachedInfo) + + const shouldCheckMD5 = this.config.checkByMD5 + const reuseSaved = () => { + this.usedCacheList[index] = true + this.updateChunkProgress(chunk.size, index) + this.uploadedList[index] = cachedInfo + this.updateLocalCache() + } + + // FIXME: 至少判断一下 size + if (cachedInfo && !shouldCheckMD5) { + reuseSaved() + return + } + + const md5 = await utils.computeMd5(chunk) + this.logger.info('computed part md5.', md5) + + if (cachedInfo && md5 === cachedInfo.md5) { + reuseSaved() + return + } + + // 没有使用缓存设置标记为 false + this.usedCacheList[index] = false + + const onProgress = (data: Progress) => { + this.updateChunkProgress(data.loaded, index) + } + + const requestOptions = { + body: chunk, + md5: this.config.checkByServer ? md5 : undefined, + onProgress, + onCreate: (xhr: XMLHttpRequest) => this.addXhr(xhr) + } + + this.logger.info(`part ${index} start uploading.`) + const response = await uploadChunk( + this.token, + this.key, + chunkInfo.index + 1, + this.getUploadInfo(), + requestOptions + ) + this.logger.info(`part ${index} upload completed.`) + + // 在某些浏览器环境下,xhr 的 progress 事件无法被触发,progress 为 null,这里在每次分片上传完成后都手动更新下 progress + onProgress({ + loaded: chunk.size, + total: chunk.size + }) + + this.uploadedList[index] = { + etag: response.data.etag, + md5: response.data.md5, + size: chunk.size + } + + this.updateLocalCache() + } + + private async mkFileReq() { + const data: UploadChunkBody = { + parts: this.uploadedList.map((value, index) => ({ + etag: value.etag, + // 接口要求 index 需要从 1 开始,所以需要整体 + 1 + partNumber: index + 1 + })), + fname: this.putExtra.fname, + ...this.putExtra.mimeType && {mimeType: this.putExtra.mimeType}, + ...this.putExtra.customVars && {customVars: this.putExtra.customVars}, + ...this.putExtra.metadata && {metadata: this.putExtra.metadata} + } + + this.logger.info('parts upload completed, make file.', data) + const result = await uploadComplete( + this.token, + this.key, + this.getUploadInfo(), + { + onCreate: xhr => this.addXhr(xhr), + body: JSON.stringify(data) + } + ) + + this.logger.info('finish Resume Progress.') + this.updateMkFileProgress(1) + return result + } + + private async initBeforeUploadChunks() { + this.uploadedList = [] + this.usedCacheList = [] + const cachedInfo = utils.getLocalFileInfo(this.getLocalKey(), this.logger) + + // 分片必须和当时使用的 uploadId 配套,所以断点续传需要把本地存储的 uploadId 拿出来 + // 假如没有 cachedInfo 本地信息并重新获取 uploadId + if (!cachedInfo) { + this.logger.info('init upload parts from api.') + const res = await initUploadParts( + this.token, + this.bucketName, + this.key, + this.uploadHost!.getUrl() + ) + this.logger.info(`initd upload parts of id: ${res.data.uploadId}.`) + this.uploadId = res.data.uploadId + this.cachedUploadedList = [] + } else { + const infoMessage = [ + 'resume upload parts from local cache,', + `total ${cachedInfo.data.length} part,`, + `id is ${cachedInfo.id}.` + ] + + this.logger.info(infoMessage.join(' ')) + this.cachedUploadedList = cachedInfo.data + this.uploadId = cachedInfo.id + } + + this.chunks = utils.getChunks(this.file, this.config.chunkSize) + this.loaded = { + mkFileProgress: 0, + chunks: this.chunks.map(_ => 0) + } + this.notifyResumeProgress() + } + + private getUploadInfo(): UploadInfo { + return { + id: this.uploadId!, + url: this.uploadHost!.getUrl() + } + } + + private getLocalKey() { + return utils.createLocalKey(this.file.name, this.key, this.file.size) + } + + private updateLocalCache() { + utils.setLocalFileInfo(this.getLocalKey(), { + id: this.uploadId!, + data: this.uploadedList + }, this.logger) + } + + private updateChunkProgress(loaded: number, index: number) { + this.loaded!.chunks[index] = loaded + this.notifyResumeProgress() + } + + private updateMkFileProgress(progress: 0 | 1) { + this.loaded!.mkFileProgress = progress + this.notifyResumeProgress() + } + + private notifyResumeProgress() { + this.progress = { + total: this.getProgressInfoItem( + utils.sum(this.loaded!.chunks) + this.loaded!.mkFileProgress, + // FIXME: 不准确的 fileSize + this.file.size + 1 // 防止在 complete 未调用的时候进度显示 100% + ), + chunks: this.chunks.map((chunk, index) => { + const fromCache = this.usedCacheList[index] + return this.getProgressInfoItem(this.loaded!.chunks[index], chunk.size, fromCache) + }), + uploadInfo: { + id: this.uploadId!, + url: this.uploadHost!.getUrl() + } + } + this.onData(this.progress!) + } +} diff --git a/src/backend/qiniu/utils/base64.ts b/src/backend/qiniu/utils/base64.ts new file mode 100644 index 0000000..4f6d75e --- /dev/null +++ b/src/backend/qiniu/utils/base64.ts @@ -0,0 +1,274 @@ +/* eslint-disable */ + +// https://github.com/locutusjs/locutus/blob/master/src/php/xml/utf8_encode.js +function utf8Encode(argString: string) { + // http://kevin.vanzonneveld.net + // + original by: Webtoolkit.info (http://www.webtoolkit.info/) + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + improved by: sowberry + // + tweaked by: Jack + // + bugfixed by: Onno Marsman + // + improved by: Yves Sucaet + // + bugfixed by: Onno Marsman + // + bugfixed by: Ulrich + // + bugfixed by: Rafal Kukawski + // + improved by: kirilloid + // + bugfixed by: kirilloid + // * example 1: this.utf8Encode('Kevin van Zonneveld') + // * returns 1: 'Kevin van Zonneveld' + + if (argString === null || typeof argString === 'undefined') { + return '' + } + + let string = argString + '' // .replace(/\r\n/g, '\n').replace(/\r/g, '\n') + let utftext = '', + start, + end, + stringl = 0 + + start = end = 0 + stringl = string.length + for (let n = 0; n < stringl; n++) { + let c1 = string.charCodeAt(n) + let enc = null + + if (c1 < 128) { + end++ + } else if (c1 > 127 && c1 < 2048) { + enc = String.fromCharCode((c1 >> 6) | 192, (c1 & 63) | 128) + } else if ((c1 & 0xf800 ^ 0xd800) > 0) { + enc = String.fromCharCode( + (c1 >> 12) | 224, + ((c1 >> 6) & 63) | 128, + (c1 & 63) | 128 + ) + } else { + // surrogate pairs + if ((c1 & 0xfc00 ^ 0xd800) > 0) { + throw new RangeError('Unmatched trail surrogate at ' + n) + } + let c2 = string.charCodeAt(++n) + if ((c2 & 0xfc00 ^ 0xdc00) > 0) { + throw new RangeError('Unmatched lead surrogate at ' + (n - 1)) + } + c1 = ((c1 & 0x3ff) << 10) + (c2 & 0x3ff) + 0x10000 + enc = String.fromCharCode( + (c1 >> 18) | 240, + ((c1 >> 12) & 63) | 128, + ((c1 >> 6) & 63) | 128, + (c1 & 63) | 128 + ) + } + if (enc !== null) { + if (end > start) { + utftext += string.slice(start, end) + } + utftext += enc + start = end = n + 1 + } + } + + if (end > start) { + utftext += string.slice(start, stringl) + } + + return utftext +} + +// https://github.com/locutusjs/locutus/blob/master/src/php/xml/utf8_decode.js +function utf8Decode(strData: string) { + // eslint-disable-line camelcase + // discuss at: https://locutus.io/php/utf8_decode/ + // original by: Webtoolkit.info (https://www.webtoolkit.info/) + // input by: Aman Gupta + // input by: Brett Zamir (https://brett-zamir.me) + // improved by: Kevin van Zonneveld (https://kvz.io) + // improved by: Norman "zEh" Fuchs + // bugfixed by: hitwork + // bugfixed by: Onno Marsman (https://twitter.com/onnomarsman) + // bugfixed by: Kevin van Zonneveld (https://kvz.io) + // bugfixed by: kirilloid + // bugfixed by: w35l3y (https://www.wesley.eti.br) + // example 1: utf8_decode('Kevin van Zonneveld') + // returns 1: 'Kevin van Zonneveld' + + const tmpArr = [] + let i = 0 + let c1 = 0 + let seqlen = 0 + + strData += '' + + while (i < strData.length) { + c1 = strData.charCodeAt(i) & 0xFF + seqlen = 0 + + // https://en.wikipedia.org/wiki/UTF-8#Codepage_layout + if (c1 <= 0xBF) { + c1 = (c1 & 0x7F) + seqlen = 1 + } else if (c1 <= 0xDF) { + c1 = (c1 & 0x1F) + seqlen = 2 + } else if (c1 <= 0xEF) { + c1 = (c1 & 0x0F) + seqlen = 3 + } else { + c1 = (c1 & 0x07) + seqlen = 4 + } + + for (let ai = 1; ai < seqlen; ++ai) { + c1 = ((c1 << 0x06) | (strData.charCodeAt(ai + i) & 0x3F)) + } + + if (seqlen === 4) { + c1 -= 0x10000 + tmpArr.push(String.fromCharCode(0xD800 | ((c1 >> 10) & 0x3FF))) + tmpArr.push(String.fromCharCode(0xDC00 | (c1 & 0x3FF))) + } else { + tmpArr.push(String.fromCharCode(c1)) + } + + i += seqlen + } + + return tmpArr.join('') +} + +function base64Encode(data: any) { + // http://kevin.vanzonneveld.net + // + original by: Tyler Akins (http://rumkin.com) + // + improved by: Bayron Guevara + // + improved by: Thunder.m + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Pellentesque Malesuada + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // - depends on: this.utf8Encode + // * example 1: this.base64Encode('Kevin van Zonneveld') + // * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA==' + // mozilla has this native + // - but breaks in 2.0.0.12! + // if (typeof this.window['atob'] == 'function') { + // return atob(data) + // } + let b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + let o1, + o2, + o3, + h1, + h2, + h3, + h4, + bits, + i = 0, + ac = 0, + enc = '', + tmp_arr = [] + + if (!data) { + return data + } + + data = utf8Encode(data + '') + + do { + // pack three octets into four hexets + o1 = data.charCodeAt(i++) + o2 = data.charCodeAt(i++) + o3 = data.charCodeAt(i++) + + bits = (o1 << 16) | (o2 << 8) | o3 + + h1 = (bits >> 18) & 0x3f + h2 = (bits >> 12) & 0x3f + h3 = (bits >> 6) & 0x3f + h4 = bits & 0x3f + + // use hexets to index into b64, and append result to encoded string + tmp_arr[ac++] = + b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4) + } while (i < data.length) + + enc = tmp_arr.join('') + + switch (data.length % 3) { + case 1: + enc = enc.slice(0, -2) + '==' + break + case 2: + enc = enc.slice(0, -1) + '=' + break + } + + return enc +} + +function base64Decode(data: string) { + // http://kevin.vanzonneveld.net + // + original by: Tyler Akins (http://rumkin.com) + // + improved by: Thunder.m + // + input by: Aman Gupta + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + bugfixed by: Onno Marsman + // + bugfixed by: Pellentesque Malesuada + // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // + input by: Brett Zamir (http://brett-zamir.me) + // + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + // * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==') + // * returns 1: 'Kevin van Zonneveld' + // mozilla has this native + // - but breaks in 2.0.0.12! + // if (typeof this.window['atob'] == 'function') { + // return atob(data) + // } + let b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' + let o1, o2, o3, h1, h2, h3, h4, bits, i = 0, + ac = 0, + dec = '', + tmp_arr = [] + + if (!data) { + return data + } + + data += '' + + do { // unpack four hexets into three octets using index points in b64 + h1 = b64.indexOf(data.charAt(i++)) + h2 = b64.indexOf(data.charAt(i++)) + h3 = b64.indexOf(data.charAt(i++)) + h4 = b64.indexOf(data.charAt(i++)) + + bits = h1 << 18 | h2 << 12 | h3 << 6 | h4 + + o1 = bits >> 16 & 0xff + o2 = bits >> 8 & 0xff + o3 = bits & 0xff + + if (h3 === 64) { + tmp_arr[ac++] = String.fromCharCode(o1) + } else if (h4 === 64) { + tmp_arr[ac++] = String.fromCharCode(o1, o2) + } else { + tmp_arr[ac++] = String.fromCharCode(o1, o2, o3) + } + } while (i < data.length) + + dec = tmp_arr.join('') + + return utf8Decode(dec) +} + +export function urlSafeBase64Encode(v: any) { + v = base64Encode(v) + + // 参考 https://tools.ietf.org/html/rfc4648#section-5 + return v.replace(/\//g, '_').replace(/\+/g, '-') +} + +export function urlSafeBase64Decode(v: any) { + v = v.replace(/_/g, '/').replace(/-/g, '+') + return base64Decode(v) +} diff --git a/src/backend/qiniu/utils/compress.ts b/src/backend/qiniu/utils/compress.ts new file mode 100644 index 0000000..17184f0 --- /dev/null +++ b/src/backend/qiniu/utils/compress.ts @@ -0,0 +1,218 @@ +// import {QiniuError, QiniuErrorName} from '../errors' +// +// import {createObjectURL} from './helper' +// +// export interface CompressOptions { +// quality?: number +// noCompressIfLarger?: boolean +// maxWidth?: number +// maxHeight?: number +// } +// +// export interface Dimension { +// width?: number +// height?: number +// } +// +// export interface CompressResult { +// dist: Blob | File +// width: number +// height: number +// } +// +// const mimeTypes = { +// PNG: 'image/png', +// JPEG: 'image/jpeg', +// WEBP: 'image/webp', +// BMP: 'image/bmp' +// } as const +// +// const maxSteps = 4 +// const scaleFactor = Math.log(2) +// // @ts-ignore +// const supportMimeTypes = Object.keys(mimeTypes).map(type => mimeTypes[type]) +// const defaultType = mimeTypes.JPEG +// +// type MimeKey = keyof typeof mimeTypes +// +// function isSupportedType(type: string): type is typeof mimeTypes[MimeKey] { +// return supportMimeTypes.includes(type) +// } +// +// class Compress { +// private outputType: string +// private config: CompressOptions +// +// constructor(private file: File, config: CompressOptions) { +// this.outputType = this.file.type +// this.config = { +// quality: 0.92, +// noCompressIfLarger: false, +// ...config +// } +// } +// +// async process(): Promise { +// this.outputType = this.file.type +// const srcDimension: Dimension = {} +// if (!isSupportedType(this.file.type)) { +// throw new QiniuError( +// QiniuErrorName.UnsupportedFileType, +// `unsupported file type: ${this.file.type}` +// ) +// } +// +// const originImage = await this.getOriginImage() +// const canvas = await this.getCanvas(originImage) +// let scale = 1 +// if (this.config.maxWidth) { +// scale = Math.min(1, this.config.maxWidth / canvas.width) +// } +// if (this.config.maxHeight) { +// scale = Math.min(1, scale, this.config.maxHeight / canvas.height) +// } +// srcDimension.width = canvas.width +// srcDimension.height = canvas.height +// +// const scaleCanvas = await this.doScale(canvas, scale) +// const distBlob = this.toBlob(scaleCanvas) +// if (distBlob.size > this.file.size && this.config.noCompressIfLarger) { +// return { +// dist: this.file, +// width: srcDimension.width, +// height: srcDimension.height +// } +// } +// +// return { +// dist: distBlob, +// width: scaleCanvas.width, +// height: scaleCanvas.height +// } +// } +// +// clear(ctx: CanvasRenderingContext2D, width: number, height: number) { +// // jpeg 没有 alpha 通道,透明区间会被填充成黑色,这里把透明区间填充为白色 +// if (this.outputType === defaultType) { +// ctx.fillStyle = '#fff' +// ctx.fillRect(0, 0, width, height) +// } else { +// ctx.clearRect(0, 0, width, height) +// } +// } +// +// /** 通过 file 初始化 image 对象 */ +// getOriginImage(): Promise { +// return new Promise((resolve, reject) => { +// const url = createObjectURL(this.file) +// const img = new Image() +// img.onload = () => { +// resolve(img) +// } +// img.onerror = () => { +// reject('image load error') +// } +// img.src = url +// }) +// } +// +// getCanvas(img: HTMLImageElement): Promise { +// return new Promise((resolve, reject) => { +// const canvas = document.createElement('canvas') +// const context = canvas.getContext('2d') +// +// if (!context) { +// reject(new QiniuError( +// QiniuErrorName.GetCanvasContextFailed, +// 'context is null' +// )) +// return +// } +// +// const {width, height} = img +// canvas.height = height +// canvas.width = width +// +// this.clear(context, width, height) +// context.drawImage(img, 0, 0) +// resolve(canvas) +// }) +// } +// +// async doScale(source: HTMLCanvasElement, scale: number) { +// if (scale === 1) { +// return source +// } +// // 不要一次性画图,通过设定的 step 次数,渐进式的画图,这样可以增加图片的清晰度,防止一次性画图导致的像素丢失严重 +// const sctx = source.getContext('2d') +// const steps = Math.min(maxSteps, Math.ceil((1 / scale) / scaleFactor)) +// +// const factor = scale ** (1 / steps) +// +// const mirror = document.createElement('canvas') +// const mctx = mirror.getContext('2d') +// +// let {width, height} = source +// const originWidth = width +// const originHeight = height +// mirror.width = width +// mirror.height = height +// if (!mctx || !sctx) { +// throw new QiniuError( +// QiniuErrorName.GetCanvasContextFailed, +// "mctx or sctx can't be null" +// ) +// } +// +// let src!: CanvasImageSource +// let context!: CanvasRenderingContext2D +// for (let i = 0; i < steps; i++) { +// +// let dw = width * factor | 0 // eslint-disable-line no-bitwise +// let dh = height * factor | 0 // eslint-disable-line no-bitwise +// // 到最后一步的时候 dw, dh 用目标缩放尺寸,否则会出现最后尺寸偏小的情况 +// if (i === steps - 1) { +// dw = originWidth * scale +// dh = originHeight * scale +// } +// +// if (i % 2 === 0) { +// src = source +// context = mctx +// } else { +// src = mirror +// context = sctx +// } +// // 每次画前都清空,避免图像重叠 +// this.clear(context, width, height) +// context.drawImage(src, 0, 0, width, height, 0, 0, dw, dh) +// width = dw +// height = dh +// } +// +// const canvas = src === source ? mirror : source +// // save data +// const data = context.getImageData(0, 0, width, height) +// +// // resize +// canvas.width = width +// canvas.height = height +// +// // store image data +// context.putImageData(data, 0, 0) +// +// return canvas +// } +// +// /** 这里把 base64 字符串转为 blob 对象 */ +// toBlob(result: HTMLCanvasElement) { +// const dataURL = result.toDataURL(this.outputType, this.config.quality) +// const buffer = atob(dataURL.split(',')[1]).split('').map(char => char.charCodeAt(0)) +// const blob = new Blob([new Uint8Array(buffer)], {type: this.outputType}) +// return blob +// } +// } +// +// const compressImage = (file: File, options: CompressOptions) => new Compress(file, options).process() +// +// export default compressImage diff --git a/src/backend/qiniu/utils/config.ts b/src/backend/qiniu/utils/config.ts new file mode 100644 index 0000000..0a7d698 --- /dev/null +++ b/src/backend/qiniu/utils/config.ts @@ -0,0 +1,61 @@ +import Logger from '../logger' +import { regionUphostMap } from '../config' +import { Config, DEFAULT_CHUNK_SIZE, InternalConfig } from '../upload' + +export function normalizeUploadConfig(config?: Partial, logger?: Logger): InternalConfig { + const { upprotocol, uphost, ...otherConfig } = { ...config } + + const normalizeConfig: InternalConfig = { + uphost: [], + retryCount: 3, + + checkByMD5: false, + forceDirect: false, + useCdnDomain: true, + checkByServer: false, + concurrentRequestLimit: 3, + chunkSize: DEFAULT_CHUNK_SIZE, + + upprotocol: 'https', + + debugLogLevel: 'OFF', + disableStatisticsReport: false, + + ...otherConfig + } + + // 兼容原来的 http: https: 的写法 + if (upprotocol) { + normalizeConfig.upprotocol = upprotocol + .replace(/:$/, '') as InternalConfig['upprotocol'] + } + + const hostList: string[] = [] + + if (logger && config?.uphost != null && config?.region != null) { + logger.warn('do not use both the uphost and region config.') + } + + // 如果同时指定了 uphost 参数,添加到可用 host 列表 + if (uphost) { + if (Array.isArray(uphost)) { + hostList.push(...uphost) + } else { + hostList.push(uphost) + } + + // 否则如果用户传了 region,添加指定 region 的 host 到可用 host 列表 + } else if (normalizeConfig?.region) { + const hostMap = regionUphostMap[normalizeConfig?.region] + if (normalizeConfig.useCdnDomain) { + hostList.push(...hostMap.cdnUphost) + } else { + hostList.push(...hostMap.srcUphost) + } + } + + return { + ...normalizeConfig, + uphost: hostList.filter(Boolean) + } +} diff --git a/src/backend/qiniu/utils/crc32.ts b/src/backend/qiniu/utils/crc32.ts new file mode 100644 index 0000000..85f2082 --- /dev/null +++ b/src/backend/qiniu/utils/crc32.ts @@ -0,0 +1,90 @@ +/* eslint-disable no-bitwise */ + +import { MB } from './helper' + +/** + * 以下 class 实现参考 + * https://github.com/Stuk/jszip/blob/d4702a70834bd953d4c2d0bc155fad795076631a/lib/crc32.js + * 该实现主要针对大文件优化、对计算的值进行了 `>>> 0` 运算(为与服务端保持一致) + */ +export class CRC32 { + private crc = -1 + private table = this.makeTable() + + private makeTable() { + const table = new Array() + for (let i = 0; i < 256; i++) { + let t = i + for (let j = 0; j < 8; j++) { + if (t & 1) { + // IEEE 标准 + t = (t >>> 1) ^ 0xEDB88320 + } else { + t >>>= 1 + } + } + table[i] = t + } + + return table + } + + private append(data: Uint8Array) { + let crc = this.crc + for (let offset = 0; offset < data.byteLength; offset++) { + crc = (crc >>> 8) ^ this.table[(crc ^ data[offset]) & 0xFF] + } + this.crc = crc + } + + private compute() { + return (this.crc ^ -1) >>> 0 + } + + private async readAsUint8Array(file: File | Blob): Promise { + if (typeof file.arrayBuffer === 'function') { + return new Uint8Array(await file.arrayBuffer()) + } + + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = () => { + if (reader.result == null) { + reject() + return + } + + if (typeof reader.result === 'string') { + reject() + return + } + + resolve(new Uint8Array(reader.result)) + } + reader.readAsArrayBuffer(file) + }) + } + + async file(file: File): Promise { + if (file.size <= MB) { + this.append(await this.readAsUint8Array(file)) + return this.compute() + } + + const count = Math.ceil(file.size / MB) + for (let index = 0; index < count; index++) { + const start = index * MB + const end = index === (count - 1) ? file.size : start + MB + // eslint-disable-next-line no-await-in-loop + const chuck = await this.readAsUint8Array(file.slice(start, end)) + this.append(new Uint8Array(chuck)) + } + + return this.compute() + } + + static file(file: File): Promise { + const crc = new CRC32() + return crc.file(file) + } +} diff --git a/src/backend/qiniu/utils/helper.ts b/src/backend/qiniu/utils/helper.ts new file mode 100644 index 0000000..38f022e --- /dev/null +++ b/src/backend/qiniu/utils/helper.ts @@ -0,0 +1,346 @@ +import {SparkMD5} from './spark-md5' +import { + QiniuError, + QiniuErrorName, + QiniuNetworkError, + QiniuRequestError +} from '../errors' +import {LocalInfo, Progress} from '../upload' +import Logger from '../logger' + +import {urlSafeBase64Decode} from './base64' + +export const MB = 1024 ** 2 + +// 文件分块 +export function getChunks(file: File, blockSize: number): Blob[] { + + let chunkByteSize = blockSize * MB // 转换为字节 + // 如果 chunkByteSize 比文件大,则直接取文件的大小 + if (chunkByteSize > file.size) { + chunkByteSize = file.size + } else { + // 因为最多 10000 chunk,所以如果 chunkSize 不符合则把每片 chunk 大小扩大两倍 + while (file.size > chunkByteSize * 10000) { + chunkByteSize *= 2 + } + } + + const chunks: Blob[] = [] + const count = Math.ceil(file.size / chunkByteSize) + for (let i = 0; i < count; i++) { + const chunk = file.slice( + chunkByteSize * i, + i === count - 1 ? file.size : chunkByteSize * (i + 1) + ) + chunks.push(chunk) + } + return chunks +} + +export function isMetaDataValid(params: { [key: string]: string }) { + return Object.keys(params).every(key => key.indexOf('x-qn-meta-') === 0) +} + +export function isCustomVarsValid(params: { [key: string]: string }) { + return Object.keys(params).every(key => key.indexOf('x:') === 0) +} + +export function sum(list: number[]) { + return list.reduce((data, loaded) => data + loaded, 0) +} + +export function setLocalFileInfo(localKey: string, info: LocalInfo, logger: Logger) { + try { + localStorage.setItem(localKey, JSON.stringify(info)) + } catch (err) { + logger.warn(new QiniuError( + QiniuErrorName.WriteCacheFailed, + `setLocalFileInfo failed: ${localKey}` + )) + } +} + +export function createLocalKey(name: string, key: string | null | undefined, size: number): string { + const localKey = key == null ? '_' : `_key_${key}_` + return `qiniu_js_sdk_upload_file_name_${name}${localKey}size_${size}` +} + +export function removeLocalFileInfo(localKey: string, logger: Logger) { + try { + localStorage.removeItem(localKey) + } catch (err) { + logger.warn(new QiniuError( + QiniuErrorName.RemoveCacheFailed, + `removeLocalFileInfo failed. key: ${localKey}` + )) + } +} + +export function getLocalFileInfo(localKey: string, logger: Logger): LocalInfo | null { + let localInfoString: string | null = null + try { + localInfoString = localStorage.getItem(localKey) + } catch { + logger.warn(new QiniuError( + QiniuErrorName.ReadCacheFailed, + `getLocalFileInfo failed. key: ${localKey}` + )) + } + + if (localInfoString == null) { + return null + } + + let localInfo: LocalInfo | null = null + try { + localInfo = JSON.parse(localInfoString) + } catch { + // 本地信息已被破坏,直接删除 + removeLocalFileInfo(localKey, logger) + logger.warn(new QiniuError( + QiniuErrorName.InvalidCacheData, + `getLocalFileInfo failed to parse. key: ${localKey}` + )) + } + + return localInfo +} + +export function getAuthHeaders(token: string) { + const auth = 'UpToken ' + token + return {Authorization: auth} +} + +export function getHeadersForChunkUpload(token: string): Record { + const header = getAuthHeaders(token) + return { + 'content-type': 'application/octet-stream', + ...header + } +} + +export function getHeadersForMkFile(token: string) { + const header = getAuthHeaders(token) + return { + 'content-type': 'application/json', + ...header + } +} + +export function createXHR(): XMLHttpRequest { + if (typeof XMLHttpRequest === "function") { + return new XMLHttpRequest() + } + throw new QiniuError( + QiniuErrorName.NotAvailableXMLHttpRequest, + 'the current environment does not support.' + ) +} + +export async function computeMd5(data: Blob): Promise { + const buffer = await readAsArrayBuffer(data) + const spark = new SparkMD5() + spark.append(buffer) + return spark.end() +} + +export function readAsArrayBuffer(data: Blob): Promise { + return new Promise((resolve, reject) => { + const reader = new FileReader() + // evt 类型目前存在问题 https://github.com/Microsoft/TypeScript/issues/4163 + reader.onload = (evt: ProgressEvent) => { + if (evt.target) { + const body = evt.target.result + resolve(body as ArrayBuffer) + } else { + reject(new QiniuError( + QiniuErrorName.InvalidProgressEventTarget, + 'progress event target is undefined' + )) + } + } + + reader.onerror = () => { + reject(new QiniuError( + QiniuErrorName.FileReaderReadFailed, + 'fileReader read failed' + )) + } + + reader.readAsArrayBuffer(data) + }) +} + +export interface ResponseSuccess { + data: T + reqId: string +} + +export type XHRHandler = (xhr: XMLHttpRequest) => void + +export interface RequestOptions { + method: string + onProgress?: (data: Progress) => void + onCreate?: XHRHandler + body?: XMLHttpRequestBodyInit | null + headers?: { [key: string]: string } +} + +export type Response = Promise> + +export function request(url: string, options: RequestOptions): Response { + return new Promise((resolve, reject) => { + const xhr = createXHR() + xhr.open(options.method, url) + + if (options.onCreate) { + options.onCreate(xhr) + } + + if (options.headers) { + const headers = options.headers + Object.keys(headers).forEach(k => { + xhr.setRequestHeader(k, headers[k]) + }) + } + + xhr.upload.addEventListener('progress', (evt: ProgressEvent) => { + if (evt.lengthComputable && options.onProgress) { + options.onProgress({ + loaded: evt.loaded, + total: evt.total + }) + } + }) + + xhr.onreadystatechange = () => { + const responseText = xhr.responseText + if (xhr.readyState !== 4) { + return + } + + const reqId = xhr.getResponseHeader('x-reqId') || '' + + if (xhr.status === 0) { + // 发生 0 基本都是网络错误,常见的比如跨域、断网、host 解析失败、系统拦截等等 + reject(new QiniuNetworkError('network error.', reqId)) + return + } + + if (xhr.status !== 200) { + let message = `xhr request failed, code: ${xhr.status}` + if (responseText) { + message += ` response: ${responseText}` + } + + let data + try { + data = JSON.parse(responseText) + } catch { + // 无需处理该错误、可能拿到非 json 格式的响应是预期的 + } + + reject(new QiniuRequestError(xhr.status, reqId, message, data)) + return + } + + try { + resolve({ + data: JSON.parse(responseText), + reqId + }) + } catch (err) { + reject(err) + } + } + + xhr.send(options.body) + }) +} + +export function getPortFromUrl(url: string | undefined) { + if (url && url.match) { + let groups = url.match(/(^https?)/) + + if (!groups) { + return '' + } + + const type = groups[1] + groups = url.match(/^https?:\/\/([^:^/]*):(\d*)/) + + if (groups) { + return groups[2] + } + + if (type === 'http') { + return '80' + } + + return '443' + } + + return '' +} + +export function getDomainFromUrl(url: string | undefined): string { + if (url && url.match) { + const groups = url.match(/^https?:\/\/([^:^/]*)/) + return groups ? groups[1] : '' + } + + return '' +} + +// 非标准的 PutPolicy +interface PutPolicy { + assessKey: string + bucketName: string + scope: string +} + +/** + * @param token + * @throws {QiniuError} + */ +export function getPutPolicy(token: string): PutPolicy { + if (!token) throw new QiniuError(QiniuErrorName.InvalidToken, 'invalid token.') + + const segments = token.split(':') + if (segments.length === 1) throw new QiniuError(QiniuErrorName.InvalidToken, 'invalid token segments.') + + // token 构造的差异参考:https://github.com/qbox/product/blob/master/kodo/auths/UpToken.md#admin-uptoken-authorization + const assessKey = segments.length > 3 ? segments[1] : segments[0] + if (!assessKey) throw new QiniuError(QiniuErrorName.InvalidToken, 'missing assess key field.') + + let putPolicy: PutPolicy | null = null + + try { + putPolicy = JSON.parse(urlSafeBase64Decode(segments[segments.length - 1])) + } catch (error) { + throw new QiniuError(QiniuErrorName.InvalidToken, 'token parse failed.') + } + + if (putPolicy == null) { + throw new QiniuError(QiniuErrorName.InvalidToken, 'putPolicy is null.') + } + + if (putPolicy.scope == null) { + throw new QiniuError(QiniuErrorName.InvalidToken, 'scope field is null.') + } + + const bucketName = putPolicy.scope.split(':')[0] + if (!bucketName) { + throw new QiniuError(QiniuErrorName.InvalidToken, 'resolve bucketName failed.') + } + + return {assessKey, bucketName, scope: putPolicy.scope} +} + +export function createObjectURL(file: File) { + // @ts-ignore + const URL = window.URL || window.webkitURL || window.mozURL + // FIXME: 需要 revokeObjectURL + return URL.createObjectURL(file) +} diff --git a/src/backend/qiniu/utils/index.ts b/src/backend/qiniu/utils/index.ts new file mode 100644 index 0000000..03f8d2c --- /dev/null +++ b/src/backend/qiniu/utils/index.ts @@ -0,0 +1,8 @@ +export * from './pool' +export * from './observable' + +export * from './base64' +export * from './helper' +export * from './config' + +// export {default as compressImage, type CompressResult} from './compress' diff --git a/src/backend/qiniu/utils/observable.ts b/src/backend/qiniu/utils/observable.ts new file mode 100644 index 0000000..f11b04a --- /dev/null +++ b/src/backend/qiniu/utils/observable.ts @@ -0,0 +1,151 @@ +/** 消费者接口 */ +export interface IObserver { + /** 用来接收 Observable 中的 next 类型通知 */ + next: (value: T) => void + /** 用来接收 Observable 中的 error 类型通知 */ + error: (err: E) => void + /** 用来接收 Observable 中的 complete 类型通知 */ + complete: (res: C) => void +} + +export interface NextObserver { + next: (value: T) => void + error?: (err: E) => void + complete?: (res: C) => void +} + +export interface ErrorObserver { + next?: (value: T) => void + error: (err: E) => void + complete?: (res: C) => void +} + +export interface CompletionObserver { + next?: (value: T) => void + error?: (err: E) => void + complete: (res: C) => void +} + +export type PartialObserver = NextObserver | ErrorObserver | CompletionObserver + +export interface IUnsubscribable { + /** 取消 observer 的订阅 */ + unsubscribe(): void +} + +/** Subscription 的接口 */ +export interface ISubscriptionLike extends IUnsubscribable { + readonly closed: boolean +} + +export type TeardownLogic = () => void + +export interface ISubscribable { + subscribe( + observer?: PartialObserver | ((value: T) => void), + error?: (error: any) => void, + complete?: () => void + ): IUnsubscribable +} + +/** 表示可清理的资源,比如 Observable 的执行 */ +class Subscription implements ISubscriptionLike { + /** 用来标示该 Subscription 是否被取消订阅的标示位 */ + public closed = false + + /** 清理 subscription 持有的资源 */ + private _unsubscribe: TeardownLogic | undefined + + /** 取消 observer 的订阅 */ + unsubscribe() { + if (this.closed) { + return + } + + this.closed = true + if (this._unsubscribe) { + this._unsubscribe() + } + } + + /** 添加一个 tear down 在该 Subscription 的 unsubscribe() 期间调用 */ + add(teardown: TeardownLogic) { + this._unsubscribe = teardown + } +} + +/** + * 实现 Observer 接口并且继承 Subscription 类,Observer 是消费 Observable 值的公有 API + * 所有 Observers 都转化成了 Subscriber,以便提供类似 Subscription 的能力,比如 unsubscribe +*/ +export class Subscriber extends Subscription implements IObserver { + protected isStopped = false + protected destination: Partial> + + constructor( + observerOrNext?: PartialObserver | ((value: T) => void) | null, + error?: ((err: E) => void) | null, + complete?: ((res: C) => void) | null + ) { + super() + + if (observerOrNext && typeof observerOrNext === 'object') { + this.destination = observerOrNext + } else { + this.destination = { + ...observerOrNext && { next: observerOrNext }, + ...error && { error }, + ...complete && { complete } + } + } + } + + unsubscribe(): void { + if (this.closed) { + return + } + + this.isStopped = true + super.unsubscribe() + } + + next(value: T) { + if (!this.isStopped && this.destination.next) { + this.destination.next(value) + } + } + + error(err: E) { + if (!this.isStopped && this.destination.error) { + this.isStopped = true + this.destination.error(err) + } + } + + complete(result: C) { + if (!this.isStopped && this.destination.complete) { + this.isStopped = true + this.destination.complete(result) + } + } +} + +/** 可观察对象,当前的上传事件的集合 */ +export class Observable implements ISubscribable { + + constructor(private _subscribe: (subscriber: Subscriber) => TeardownLogic) {} + + subscribe(observer: PartialObserver): Subscription + subscribe(next: null | undefined, error: null | undefined, complete: (res: C) => void): Subscription + subscribe(next: null | undefined, error: (error: E) => void, complete?: (res: C) => void): Subscription + subscribe(next: (value: T) => void, error: null | undefined, complete: (res: C) => void): Subscription + subscribe( + observerOrNext?: PartialObserver | ((value: T) => void) | null, + error?: ((err: E) => void) | null, + complete?: ((res: C) => void) | null + ): Subscription { + const sink = new Subscriber(observerOrNext, error, complete) + sink.add(this._subscribe(sink)) + return sink + } +} diff --git a/src/backend/qiniu/utils/pool.ts b/src/backend/qiniu/utils/pool.ts new file mode 100644 index 0000000..5172c7a --- /dev/null +++ b/src/backend/qiniu/utils/pool.ts @@ -0,0 +1,53 @@ +export type RunTask = (...args: T[]) => Promise + +export interface QueueContent { + task: T + resolve: () => void + reject: (err?: any) => void +} + +export class Pool { + aborted = false + queue: Array> = [] + processing: Array> = [] + + constructor(private runTask: RunTask, private limit: number) {} + + enqueue(task: T) { + return new Promise((resolve, reject) => { + this.queue.push({ + task, + resolve, + reject + }) + this.check() + }) + } + + private run(item: QueueContent) { + this.queue = this.queue.filter(v => v !== item) + this.processing.push(item) + this.runTask(item.task).then( + () => { + this.processing = this.processing.filter(v => v !== item) + item.resolve() + this.check() + }, + err => item.reject(err) + ) + } + + private check() { + if (this.aborted) return + const processingNum = this.processing.length + const availableNum = this.limit - processingNum + this.queue.slice(0, availableNum).forEach(item => { + this.run(item) + }) + } + + abort() { + this.queue = [] + this.aborted = true + } +} diff --git a/src/backend/qiniu/utils/spark-md5.ts b/src/backend/qiniu/utils/spark-md5.ts new file mode 100644 index 0000000..9d20ce7 --- /dev/null +++ b/src/backend/qiniu/utils/spark-md5.ts @@ -0,0 +1,348 @@ +// https://www.npmjs.com/package/spark-md5 + +export interface State { + buff: string; + length: number; + hash: number[]; +} + +/** + * SparkMD5 OOP implementation for array buffers. + * + * Use this class to perform an incremental md5 ONLY for array buffers. + */ +export class SparkMD5 { + _buff: Uint8Array = new Uint8Array(0); + _length: number = 0; + _hash: number[] = []; + + constructor() { + this.reset(); + } + + /** + * Appends an array buffer. + * + * @param {ArrayBuffer} arr The array to be appended + * + * @return {SparkMD5.ArrayBuffer} The instance itself + */ + append(arr: ArrayBufferLike) { + const buff = concatenateArrayBuffers(this._buff.buffer, arr); + const length = buff.length; + this._length += arr.byteLength; + + let i; + for (i = 64; i <= length; i += 64) { + md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i))); + } + this._buff = + i - 64 < length + ? new Uint8Array(buff.buffer.slice(i - 64)) + : new Uint8Array(0); + + return this; + } + + /** + * Finishes the incremental computation, resetting the internal state and + * returning the result. + * + * @param {Boolean} raw True to get the raw string, false to get the hex string + * + * @return {String} The result + */ + end(raw?: boolean) { + const buff = this._buff; + const length = buff.length; + const tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + for (let i = 0; i < length; i += 1) { + tail[i >> 2] |= buff[i] << (i % 4 << 3); + } + + this._finish(tail, length); + + let ret = hex(this._hash); + if (raw) { + ret = hexToBinaryString(ret); + } + + this.reset(); + + return ret; + } + + /** + * Resets the internal state of the computation. + */ + reset() { + this._buff = new Uint8Array(0); + this._length = 0; + this._hash = [1732584193, -271733879, -1732584194, 271733878]; + } + + /** + * Gets the internal state of the computation. + * + * @return {Object} The state + */ + getState(): State { + return { + buff: arrayBuffer2Utf8Str(this._buff), // Convert buffer to a string + length: this._length, + hash: this._hash.slice(), + }; + } + + /** + * Releases memory used by the incremental buffer and other additional + * resources. If you plan to use the instance again, use reset instead. + */ + destroy() { + // delete this._hash; + // delete this._buff; + // delete this._length; + this.reset(); + } + + /** + * Finish the final calculation based on the tail. + * + * @param {Array} tail The tail (will be modified) + * @param {Number} length The length of the remaining buffer + */ + _finish(tail: number[], length: number) { + let i = length; + tail[i >> 2] |= 0x80 << (i % 4 << 3); + if (i > 55) { + md5cycle(this._hash, tail); + for (i = 0; i < 16; i += 1) { + tail[i] = 0; + } + } + + // Do the final computation based on the tail and length + // Beware that the final length may not fit in 32 bits so we take care of that + const tmp = this._length * 8; + const arr = tmp.toString(16).match(/(.*?)(.{0,8})$/)!; + const lo = parseInt(arr[2], 16); + const hi = parseInt(arr[1], 16) || 0; + + tail[14] = lo; + tail[15] = hi; + md5cycle(this._hash, tail); + } +} + +function md5cycle(x: number[], k: number[]) { + let a = x[0]; + let b = x[1]; + let c = x[2]; + let d = x[3]; + + a += (((b & c) | (~b & d)) + k[0] - 680876936) | 0; + a = (((a << 7) | (a >>> 25)) + b) | 0; + d += (((a & b) | (~a & c)) + k[1] - 389564586) | 0; + d = (((d << 12) | (d >>> 20)) + a) | 0; + c += (((d & a) | (~d & b)) + k[2] + 606105819) | 0; + c = (((c << 17) | (c >>> 15)) + d) | 0; + b += (((c & d) | (~c & a)) + k[3] - 1044525330) | 0; + b = (((b << 22) | (b >>> 10)) + c) | 0; + a += (((b & c) | (~b & d)) + k[4] - 176418897) | 0; + a = (((a << 7) | (a >>> 25)) + b) | 0; + d += (((a & b) | (~a & c)) + k[5] + 1200080426) | 0; + d = (((d << 12) | (d >>> 20)) + a) | 0; + c += (((d & a) | (~d & b)) + k[6] - 1473231341) | 0; + c = (((c << 17) | (c >>> 15)) + d) | 0; + b += (((c & d) | (~c & a)) + k[7] - 45705983) | 0; + b = (((b << 22) | (b >>> 10)) + c) | 0; + a += (((b & c) | (~b & d)) + k[8] + 1770035416) | 0; + a = (((a << 7) | (a >>> 25)) + b) | 0; + d += (((a & b) | (~a & c)) + k[9] - 1958414417) | 0; + d = (((d << 12) | (d >>> 20)) + a) | 0; + c += (((d & a) | (~d & b)) + k[10] - 42063) | 0; + c = (((c << 17) | (c >>> 15)) + d) | 0; + b += (((c & d) | (~c & a)) + k[11] - 1990404162) | 0; + b = (((b << 22) | (b >>> 10)) + c) | 0; + a += (((b & c) | (~b & d)) + k[12] + 1804603682) | 0; + a = (((a << 7) | (a >>> 25)) + b) | 0; + d += (((a & b) | (~a & c)) + k[13] - 40341101) | 0; + d = (((d << 12) | (d >>> 20)) + a) | 0; + c += (((d & a) | (~d & b)) + k[14] - 1502002290) | 0; + c = (((c << 17) | (c >>> 15)) + d) | 0; + b += (((c & d) | (~c & a)) + k[15] + 1236535329) | 0; + b = (((b << 22) | (b >>> 10)) + c) | 0; + + a += (((b & d) | (c & ~d)) + k[1] - 165796510) | 0; + a = (((a << 5) | (a >>> 27)) + b) | 0; + d += (((a & c) | (b & ~c)) + k[6] - 1069501632) | 0; + d = (((d << 9) | (d >>> 23)) + a) | 0; + c += (((d & b) | (a & ~b)) + k[11] + 643717713) | 0; + c = (((c << 14) | (c >>> 18)) + d) | 0; + b += (((c & a) | (d & ~a)) + k[0] - 373897302) | 0; + b = (((b << 20) | (b >>> 12)) + c) | 0; + a += (((b & d) | (c & ~d)) + k[5] - 701558691) | 0; + a = (((a << 5) | (a >>> 27)) + b) | 0; + d += (((a & c) | (b & ~c)) + k[10] + 38016083) | 0; + d = (((d << 9) | (d >>> 23)) + a) | 0; + c += (((d & b) | (a & ~b)) + k[15] - 660478335) | 0; + c = (((c << 14) | (c >>> 18)) + d) | 0; + b += (((c & a) | (d & ~a)) + k[4] - 405537848) | 0; + b = (((b << 20) | (b >>> 12)) + c) | 0; + a += (((b & d) | (c & ~d)) + k[9] + 568446438) | 0; + a = (((a << 5) | (a >>> 27)) + b) | 0; + d += (((a & c) | (b & ~c)) + k[14] - 1019803690) | 0; + d = (((d << 9) | (d >>> 23)) + a) | 0; + c += (((d & b) | (a & ~b)) + k[3] - 187363961) | 0; + c = (((c << 14) | (c >>> 18)) + d) | 0; + b += (((c & a) | (d & ~a)) + k[8] + 1163531501) | 0; + b = (((b << 20) | (b >>> 12)) + c) | 0; + a += (((b & d) | (c & ~d)) + k[13] - 1444681467) | 0; + a = (((a << 5) | (a >>> 27)) + b) | 0; + d += (((a & c) | (b & ~c)) + k[2] - 51403784) | 0; + d = (((d << 9) | (d >>> 23)) + a) | 0; + c += (((d & b) | (a & ~b)) + k[7] + 1735328473) | 0; + c = (((c << 14) | (c >>> 18)) + d) | 0; + b += (((c & a) | (d & ~a)) + k[12] - 1926607734) | 0; + b = (((b << 20) | (b >>> 12)) + c) | 0; + + a += ((b ^ c ^ d) + k[5] - 378558) | 0; + a = (((a << 4) | (a >>> 28)) + b) | 0; + d += ((a ^ b ^ c) + k[8] - 2022574463) | 0; + d = (((d << 11) | (d >>> 21)) + a) | 0; + c += ((d ^ a ^ b) + k[11] + 1839030562) | 0; + c = (((c << 16) | (c >>> 16)) + d) | 0; + b += ((c ^ d ^ a) + k[14] - 35309556) | 0; + b = (((b << 23) | (b >>> 9)) + c) | 0; + a += ((b ^ c ^ d) + k[1] - 1530992060) | 0; + a = (((a << 4) | (a >>> 28)) + b) | 0; + d += ((a ^ b ^ c) + k[4] + 1272893353) | 0; + d = (((d << 11) | (d >>> 21)) + a) | 0; + c += ((d ^ a ^ b) + k[7] - 155497632) | 0; + c = (((c << 16) | (c >>> 16)) + d) | 0; + b += ((c ^ d ^ a) + k[10] - 1094730640) | 0; + b = (((b << 23) | (b >>> 9)) + c) | 0; + a += ((b ^ c ^ d) + k[13] + 681279174) | 0; + a = (((a << 4) | (a >>> 28)) + b) | 0; + d += ((a ^ b ^ c) + k[0] - 358537222) | 0; + d = (((d << 11) | (d >>> 21)) + a) | 0; + c += ((d ^ a ^ b) + k[3] - 722521979) | 0; + c = (((c << 16) | (c >>> 16)) + d) | 0; + b += ((c ^ d ^ a) + k[6] + 76029189) | 0; + b = (((b << 23) | (b >>> 9)) + c) | 0; + a += ((b ^ c ^ d) + k[9] - 640364487) | 0; + a = (((a << 4) | (a >>> 28)) + b) | 0; + d += ((a ^ b ^ c) + k[12] - 421815835) | 0; + d = (((d << 11) | (d >>> 21)) + a) | 0; + c += ((d ^ a ^ b) + k[15] + 530742520) | 0; + c = (((c << 16) | (c >>> 16)) + d) | 0; + b += ((c ^ d ^ a) + k[2] - 995338651) | 0; + b = (((b << 23) | (b >>> 9)) + c) | 0; + + a += ((c ^ (b | ~d)) + k[0] - 198630844) | 0; + a = (((a << 6) | (a >>> 26)) + b) | 0; + d += ((b ^ (a | ~c)) + k[7] + 1126891415) | 0; + d = (((d << 10) | (d >>> 22)) + a) | 0; + c += ((a ^ (d | ~b)) + k[14] - 1416354905) | 0; + c = (((c << 15) | (c >>> 17)) + d) | 0; + b += ((d ^ (c | ~a)) + k[5] - 57434055) | 0; + b = (((b << 21) | (b >>> 11)) + c) | 0; + a += ((c ^ (b | ~d)) + k[12] + 1700485571) | 0; + a = (((a << 6) | (a >>> 26)) + b) | 0; + d += ((b ^ (a | ~c)) + k[3] - 1894986606) | 0; + d = (((d << 10) | (d >>> 22)) + a) | 0; + c += ((a ^ (d | ~b)) + k[10] - 1051523) | 0; + c = (((c << 15) | (c >>> 17)) + d) | 0; + b += ((d ^ (c | ~a)) + k[1] - 2054922799) | 0; + b = (((b << 21) | (b >>> 11)) + c) | 0; + a += ((c ^ (b | ~d)) + k[8] + 1873313359) | 0; + a = (((a << 6) | (a >>> 26)) + b) | 0; + d += ((b ^ (a | ~c)) + k[15] - 30611744) | 0; + d = (((d << 10) | (d >>> 22)) + a) | 0; + c += ((a ^ (d | ~b)) + k[6] - 1560198380) | 0; + c = (((c << 15) | (c >>> 17)) + d) | 0; + b += ((d ^ (c | ~a)) + k[13] + 1309151649) | 0; + b = (((b << 21) | (b >>> 11)) + c) | 0; + a += ((c ^ (b | ~d)) + k[4] - 145523070) | 0; + a = (((a << 6) | (a >>> 26)) + b) | 0; + d += ((b ^ (a | ~c)) + k[11] - 1120210379) | 0; + d = (((d << 10) | (d >>> 22)) + a) | 0; + c += ((a ^ (d | ~b)) + k[2] + 718787259) | 0; + c = (((c << 15) | (c >>> 17)) + d) | 0; + b += ((d ^ (c | ~a)) + k[9] - 343485551) | 0; + b = (((b << 21) | (b >>> 11)) + c) | 0; + + x[0] = (a + x[0]) | 0; + x[1] = (b + x[1]) | 0; + x[2] = (c + x[2]) | 0; + x[3] = (d + x[3]) | 0; +} + +function md5blk_array(a: Uint8Array) { + const md5blks: number[] = []; + for (let i = 0; i < 64; i += 4) { + md5blks[i >> 2] = + a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24); + } + return md5blks; +} + +const hex_chr = [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "a", + "b", + "c", + "d", + "e", + "f", +]; + +function rhex(n: number): string { + let s = ""; + for (let j = 0; j < 4; j += 1) { + s += hex_chr[(n >> (j * 8 + 4)) & 0x0f] + hex_chr[(n >> (j * 8)) & 0x0f]; + } + return s; +} + +function hex(x: number[]) { + const r = new Array(x.length); + for (let i = 0; i < x.length; i += 1) { + r[i] = rhex(x[i]); + } + return r.join(""); +} + +function arrayBuffer2Utf8Str(buff: ArrayBufferLike) { + // @ts-ignore + return String.fromCharCode.apply(null, new Uint8Array(buff)); +} + +function concatenateArrayBuffers( + first: ArrayBufferLike, + second: ArrayBufferLike, +): Uint8Array { + const result = new Uint8Array(first.byteLength + second.byteLength); + result.set(new Uint8Array(first)); + result.set(new Uint8Array(second), first.byteLength); + return result; +} + +function hexToBinaryString(hex: string) { + const bytes = []; + const length = hex.length; + for (let x = 0; x < length - 1; x += 2) { + bytes.push(parseInt(hex.substring(x, 2), 16)); + } + return String.fromCharCode.apply(String, bytes); +} diff --git a/src/backend/reader.ts b/src/backend/reader.ts new file mode 100644 index 0000000..bf117d4 --- /dev/null +++ b/src/backend/reader.ts @@ -0,0 +1,181 @@ +import { + abortion, + type AbortionContext, + coerce, + isInvalid, + isValid, + SparkMD5, + uniqueId +} from "../shared"; +import {report} from "./report"; +import {Uploader} from "./uploader"; + +const registry: Map = new Map(); + +export function getReadTasks(): Array { + return Array.from(registry.values()).map((t) => ({...t.data})); +} + +export async function pauseReadTask(id: TaskId, reason?: any): Promise { + return registry.get(id)?.pause(reason); +} + +export async function resumeReadTask(id: TaskId): Promise { + return registry.get(id)?.resume(); +} + +export async function removeReadTask(id: TaskId): Promise { + return registry.get(id)?.destroy(); +} + +export async function cleanReadTasks(): Promise { + for (const task of registry.values()) { + await task.cleanup(); + } +} + +/** + * 读取文件,有效的 5 种任务状态: + * initial、reading、readend、failed、aborted + */ +export class Reader implements AbortionContext { + ctrl: AbortController; + + protected constructor( + readonly file: File, + readonly data: Task, + ) { + this.ctrl = abortion(this); + this.file = file; + } + + get id(): TaskId { + return this.data.id; + } + + get status(): TaskStatus { + return this.data.status; + } + + get signal(): AbortSignal { + return this.ctrl.signal; + } + + static create(file: File, dirid: number): void { + if (!file.type) { + // 有些文件是没有类型的 + // TODO 需要更加明确的信息来指明是哪一个文件 + void report("app", "error", "未知文件类型"); + } else if (!/^(image|video|audio)\//.test(file.type)) { + // 仅支持图片、视频和音频这三种类型 + // TODO 需要更加明确的信息来指明是哪一个文件 + void report("app", "error", "不支持的文件类型"); + } else { + const task = new Reader(file, { + id: uniqueId(), + dirid, + name: file.name, + path: URL.createObjectURL(file), + hash: "", + mime: file.type, + size: file.size, + status: "initial", + }); + registry.set(task.id, task); + queueMicrotask(task.start.bind(task)); + } + } + + /** 暂停任务 */ + pause(reason?: any): void { + if (isValid(this) && !this.signal.aborted) { + this.ctrl.abort(reason ?? new Error("已暂停")); + } + } + + /** 恢复任务 */ + async resume(): Promise { + if (isInvalid(this)) { + if (this.signal.aborted) { + this.ctrl = abortion(this); + } + await this.start(); + } + } + + /** 清理任务 */ + async cleanup(): Promise { + if (isInvalid(this)) { + await this.destroy(); + } + } + + /** 直接删除 */ + async destroy(): Promise { + registry.delete(this.id); + if (!this.signal.aborted) { + this.ctrl.abort(new Error("删除任务")); + } + if (this.status !== "readend") { + // 如果任务已经完成,文件路径还会被继续使用, + // 所以只有不在该状态下才能够释放内存。 + URL.revokeObjectURL(this.data.path); + } + await report("task", "delete", [this.id]); + } + + async onabort(_: Event): Promise { + if (isValid(this)) { + await this.report("aborted", { + error: this.signal.reason, + }); + } + } + + /** 开始解析文件 */ + private async start(): Promise { + await this.report("initial"); + if (this.data.hash === "") { + await this.report("reading", {progress: 0}); + const spark = new SparkMD5(); + let read = 0; + try { + await this.file.stream().pipeTo( + new WritableStream({ + write: async (chunk) => { + spark.append(chunk); + read += chunk.length; + await this.report("reading", { + progress: Math.min(read / this.file.size, 1), + }); + }, + }), + {signal: this.signal}, + ); + } catch (error) { + if (!this.signal.aborted && isValid(this)) { + await this.report("failed", {error}); + } + return; + } + this.data.hash = spark.end(); + } + await this.report("readend", {progress: 1}); + Uploader.create(this.file, this.data, (duplicated: boolean) => { + registry.delete(this.id); + if (!duplicated) return; + return report("app", "error", "任务已经存在了,切勿重复上传哦"); + }); + } + + private async report( + status: TaskStatus, + info?: { progress?: number; error?: any }, + ): Promise { + const error = coerce(info?.error); + this.data.status = status; + this.data.progress = info?.progress; + this.data.error = error != null ? String(error) : undefined; + await report("task", "sync", {...this.data}); + } +} diff --git a/src/backend/report.ts b/src/backend/report.ts new file mode 100644 index 0000000..9727e37 --- /dev/null +++ b/src/backend/report.ts @@ -0,0 +1,96 @@ +import { + call, + coerce, + createReplier, + emit, + isRecvData, + isSendData, + on, + once, + uniqueId, +} from "../shared"; + +const ports: Array = []; +const resolvers: Map = new Map(); + +let online = false + +// 处理来自客户端的数据 +// @ts-ignore +self.onconnect = (evt: MessageEvent): void => { + const port = evt.ports[0]; + + // 客户端卸载事件,无需回复 + once("app:close", () => { + const index = ports.findIndex((p) => p === port); + index > -1 && ports.splice(index, 1); + }) + + // 接受客户端消息 + port.onmessage = (evt: MessageEvent): void => { + if (isRecvData(evt.data)) { + call(resolvers.get(evt.data.id)); // 忽略来自客户端的结果 + } else if (isSendData(evt.data)) { + const {scope, action, data} = evt.data + const reply = createReplier(port, evt.data) + emit(`${scope}:${action}`, data, reply); + } else { + console.warn("[wfs] unknown message event", evt) + } + }; + + ports.push(port); + + void report('app', 'status', online ? 'online' : 'offline') +} + +// WebSocket 事件 +on("ws:open", () => { + online = true + void report("app", "status", "online") +}); + +on("ws:close", () => { + online = false + void report("app", "status", "offline") +}); + +on('ws:file:create', (data: CloudFile) => { + void report('file', 'create', data); +}) + +on('ws:file:update', (data: CloudFile) => { + void report('file', 'update', data); +}) + +export function report(scope: "app", action: "config", config: Partial): Promise +export function report(scope: "app", action: "status", data: "online" | "offline"): Promise; +export function report(scope: "app", action: "init", tasks: Array): Promise; +export function report(scope: "app", action: "error", error: any): Promise; +export function report(scope: "task", action: "sync", task: Task): Promise; +export function report(scope: "task", action: "delete", tasks: number[]): Promise; +export function report(scope: 'file', action: "create", file: CloudFile): Promise +export function report(scope: 'file', action: "update", file: CloudFile): Promise + +export function report(scope: string, action: string, data?: any): Promise { + const id = uniqueId() + if (scope === "app" && action === "error") { + data = String(coerce(data)); + } + return new Promise((resolve) => { + let sends = 0; // 发送次数 + let count = 0; // 接收次数 + resolvers.set(id, () => { + if (++count >= sends) { + resolvers.delete(id); + resolve(); + } + }); + queueMicrotask(() => { + for (const port of ports) { + sends++ + port.postMessage({id, scope, action, data}); + } + }); + }); +} diff --git a/src/backend/socket.ts b/src/backend/socket.ts new file mode 100644 index 0000000..7bdff17 --- /dev/null +++ b/src/backend/socket.ts @@ -0,0 +1,275 @@ +// 参考文档链接 +// https://www.zhihu.com/tardis/zm/art/162808604?source_id=1003 +// https://www.ruanyifeng.com/blog/2017/05/websocket.html +// https://web.dev/articles/websockets-basics?hl=zh-cn + +import {emit} from "../shared"; + +const HEARTBEAT = "heartbeat"; +const CLOSE = "close"; + +const closeTimeout = 5000; // 等待关闭超时 +const reconnectInterval = 3000; // 重连间隔 +const heartbeatInterval = 60000; // 心跳间隔 +const heartbeatTimeout = 50000; // 心跳超时 + +// WebSocket 对象引用 +let socket: WebSocket | undefined; + +// 自动重连开关。 +// +// 如果客户端网络不可用,我们就只能等到网络可用时 +// 才去连接服务器。所以该变量是用来配合网络变化事件, +// 当连接不可用时能否自动重连。 +let isReconnect = navigator.onLine; + +// 心跳机制 +type Heartbeat = { + boot: VoidFunction; + next: VoidFunction; + stop: VoidFunction; +}; + +let heartbeat: Heartbeat | undefined; + +/** + * 用于监听网络状态编号时的回调函数 + */ +export function handleNetworkEvent(status: "on" | "off"): void { + // 只有在网络可用的情况下才支持自动重连 + isReconnect = status === "on"; + + if (status === "on") { + // 理论上,切换成在线状态时,立即发送一个心跳包, + // 这样就可以检测连接是否可以;但是当网络可用时, + // 打开的连接可能还不能立即恢复,所以延迟发送。 + setTimeout(() => heartbeat?.boot(), reconnectInterval); + } else { + heartbeat?.stop(); + } +} + +export function useSocket(cfg: Partial): void { + if (cfg.apiUrl && cfg.artifact && cfg.accessToken) { + const uri = new URL(cfg.apiUrl); + uri.protocol = uri.protocol.replace(/^http/, "ws"); + uri.pathname = uri.pathname.replace(/\/+$/, "") + `/${cfg.artifact}/notifies`; + openSocket(uri.toString(), cfg.accessToken); + } else if (socket != null) { + closeSocket(socket); + } +} + +function createHeartbeat(ws: WebSocket): Heartbeat { + let timeoutTimer: ReturnType | undefined; // 心跳回复超时器 + let heartbeatTimer: ReturnType | undefined; // 心跳定时器 + + const boot = () => { + // 引用不存在或被重置 + if (socket !== ws) { + closeSocket(ws); + return; + } + // 如果还存在定时器,就说明没有收到心跳回复, + // 我们就可以直接关闭连接。 + if (timeoutTimer != null) { + closeSocket(ws); + socket = undefined; + return; + } + // 只有在连接可用时才能发送心跳包 + if (ws.readyState === ws.OPEN) { + // 发送心跳包 + ws.send(HEARTBEAT); + // 心跳回复超时检测 + timeoutTimer = setTimeout(() => { + timeoutTimer = undefined; + closeSocket(ws); + }, heartbeatTimeout); + } + }; + + const next = () => { + // 引用不存在或被重置 + if (socket !== ws) { + closeSocket(ws); + return; + } + // 取消心跳回复超时事件 + if (timeoutTimer) { + clearTimeout(timeoutTimer); + timeoutTimer = undefined; + } + // 下次心跳 + heartbeatTimer = setTimeout(() => { + heartbeatTimer = undefined; + boot(); + }, heartbeatInterval); + }; + + const stop = () => { + // 取消心跳回复超时事件 + if (timeoutTimer) { + clearTimeout(timeoutTimer); + timeoutTimer = undefined; + } + // 取消心跳事件 + if (heartbeatTimer) { + clearTimeout(heartbeatTimer); + heartbeatTimer = undefined; + } + }; + + return {boot, next, stop}; +} + +// 关闭 WebSocket 连接 +function closeSocket(ws: WebSocket, force = false): void { + console.log("closeSocket") + switch (ws.readyState) { + case WebSocket.CONNECTING: + ws.close(); + return; + case WebSocket.CLOSING: + case WebSocket.CLOSED: + return; + case WebSocket.OPEN: + if (force) { + ws.close(); + return; + } + // 我们尝试通知服务器关闭, + // 如果超过指定时限就强制关闭。 + ws.send(CLOSE); + setTimeout(() => { + closeSocket(ws, true); + }, closeTimeout); + } +} + +interface NotifyData { + scope: string; + action: string; + data: Record | Array; +} + +function isNotifyData(v: any): v is NotifyData { + return ( + typeof v === "object" && + !Array.isArray(v) && + v != null && + "scope" in v && + "action" in v && + "data" in v && + typeof v.scope === "string" && + typeof v.action === "string" && + typeof v.data === "object" && + v.data != null + ); +} + +// 打开 WebSocket 连接 +function openSocket(url: string, protocol: string): void { + if (socket != null) { + if ( + socket.url === url && + socket.protocol === protocol && + socket.readyState < WebSocket.CLOSING + ) { + // 如果关键参数一致而且连接是可用的, + // 我们就不需要重新创建连接。 + return; + } + // 关闭之前的连接 + closeSocket(socket); + socket = undefined; + } + + // 创建新的连接 + const ws = new WebSocket(url, protocol); + const hb = createHeartbeat(ws); + + heartbeat = hb; + + // Event: WebSocket opened + ws.onopen = (): void => { + if (socket === ws) { + emit("ws:open"); // 通知连接可用 + hb.boot(); // 启动心跳检测 + } else { + // 运行到这里,说明在 ws 可用之前就被弃用了, + // 我们就发送关闭事件通知服务器关闭这个连接。 + closeSocket(ws); + } + }; + + // Event: WebSocket message received + ws.onmessage = (evt: MessageEvent): void => { + // 假定我们只接受字符串消息,服务器不会发送二进制数据 + const lines = (evt.data as string).split("\n"); + if (socket !== ws) { + // 代码运行到这里,就说明 ws 被弃用了,此刻若收到关闭命令, + // 我们就主动关闭客户端,相反则通知服务器关闭连接。 + if (lines.includes(CLOSE)) { + ws.close(); + } else { + closeSocket(ws); + } + return; + } + // 处理消息 + for (const line of lines) { + if (line === CLOSE) { + // 当我们收到的消息里面包含了关闭命令时, + // 我们就主动关闭客户端并销毁连接引用。 + // 同时抛弃后续数据 + ws.close(); + socket = undefined; + return; + } + if (line === HEARTBEAT) { + hb.next(); + continue; + } + try { + const item = JSON.parse(line); + if (isNotifyData(item)) { + // 派发事件 + const {scope, action, data} = item; + emit(`ws:${scope}:${action}`, data); + } else { + emit("ws:message", item); + } + } catch { + emit("ws:message", line); + } + } + }; + + // Event: WebSocket error + ws.onerror = (error) => { + console.error("WebSocket error:", error); + socket === ws && emit("ws:error", error); + closeSocket(ws); // Close the socket if an error occurs + }; + + // Event: WebSocket closed + ws.onclose = () => { + console.log("WebSocket connection is closed."); + if (socket === ws) { + emit("ws:close"); + } + socket = undefined + // 停止心跳 + hb.stop(); + // 断线后自动重连 + if (isReconnect) { + console.log("Reconnecting..."); + setTimeout(function () { + openSocket(url, protocol); + }, reconnectInterval); + } + }; + + socket = ws; +} diff --git a/src/backend/uploader.ts b/src/backend/uploader.ts new file mode 100644 index 0000000..34f97a1 --- /dev/null +++ b/src/backend/uploader.ts @@ -0,0 +1,417 @@ +// import {QiniuNetworkError, QiniuRequestError, upload} from './qiniu-js'; +// import {type UploadProgress} from "./qiniu-js/upload"; +import { + QiniuNetworkError, + QiniuRequestError, + upload, + type UploadProgress, +} from './qiniu' +import { + abortion, + type AbortionContext, + coerce, + isInvalid, + isSameTask, + isValid, + promisify, +} from "../shared"; +import type {UploadArgs} from './api' +import { + completeUploadApi, + initiateUploadApi, + simulateQiniuCallback +} from './api' +import {report} from "./report"; + +const registry: Map = new Map(); + +export function getUploadTasks(): Array { + return Array.from(registry.values()).reduce( + (l, u) => l.concat(...u.tasks.map((t) => ({...t}))), + [], + ); +} + +export async function pauseUploadTask( + hash: FileHash, + id: TaskId, +): Promise { + await registry.get(hash)?.pause(id); +} + +export async function resumeUploadTask( + hash: FileHash, + id: TaskId, +): Promise { + await registry.get(hash)?.resume(id); +} + +export async function removeUploadTask( + hash: FileHash, + id: TaskId, +): Promise { + await registry.get(hash)?.destroy(id); +} + +export async function cleanUploadTasks(): Promise { + for (const tu of registry.values()) { + await tu.cleanup(); + } +} + +/** + * 后端任务状态 + */ +type JobStatus = + | "initial" // 初始化状态 + | "pending" // 等待上传 + | "uploading" // 正在上传 + | "uploaded" // 上传完成 + | "errored" // 上传失败 + | "aborted"; // 终止上传 + +const statuses: Array = [ + "initial", // 初始化状态 + "pending", // 等待上传 + "uploading", // 正在上传 + "uploaded", // 上传完成 + "errored", // 上传失败 + "aborted", // 终止上传 +]; + +export class Uploader implements AbortionContext { + ctrl: AbortController; + + skipUpload?: boolean; + uploadToken?: string; + key?: string; + + status: JobStatus; + + protected constructor( + readonly file: File, + readonly hash: FileHash, + readonly tasks: Array, + ) { + this.ctrl = abortion(this); + this.status = "initial"; + } + + get signal(): AbortSignal { + return this.ctrl.signal; + } + + get isUploaded(): boolean { + return this.status === "uploaded"; + } + + get isValid(): boolean { + return statuses.indexOf(this.status) < statuses.indexOf("uploaded"); + } + + get isInvalid(): boolean { + return statuses.indexOf(this.status) > statuses.indexOf("uploaded"); + } + + static create( + file: File, + task: Task, + callback: (duplicated: boolean) => void, + ): void { + let job = registry.get(task.hash); + if (job == null) { + job = new Uploader(file, task.hash, [task]); + registry.set(job.hash, job); + callback(false); + queueMicrotask(job.start.bind(job)); + return; + } + const exists = job.tasks.some((t) => isSameTask(t, task)); + if (exists) { + callback(true); // 任务重复 + return; + } + job.tasks.push(task); + callback(false); + if (!job.isValid) { + void job.resume(task.id); + } + } + + async pause(taskId: TaskId): Promise { + const task = this.tasks.find((t) => t.id === taskId); + if (!task) { + return; + } + // 只能中止状态正常的任务 + if (isValid(task)) { + task.status = "aborted"; + task.error = "已暂停"; + await report("task", "sync", {...task}); + } + // 如果没有正常的任务,就中止文件上传 + if (this.isValid && !this.signal.aborted && !this.tasks.some(isValid)) { + this.ctrl.abort(); + } + } + + // 恢复上传任务 + async resume(taskId: TaskId): Promise { + // 获取前端任务 + const task = this.tasks.find((t) => t.id === taskId); + if (task == null) { + return; + } + // 恢复任务 + if (isInvalid(task)) { + task.status = "readend"; + task.error = undefined; + task.progress = 1; + await report("task", "sync", {...task}); + } + // 确保中断器可用 + if (this.signal.aborted) { + this.ctrl = abortion(this); + } + // 启动任务 + if (this.isInvalid) { + await this.start(); + } + } + + // 移除任务 + async destroy(taskId: TaskId): Promise { + const index = this.tasks.findIndex((t) => t.id === taskId); + if (index > -1) { + const task = this.tasks.splice(index, 1)[0]; + URL.revokeObjectURL(task.path); // 释放内存 + await report("task", "delete", [taskId]); + } + // 如果没有正常的任务,就中止文件上传 + if (!this.signal.aborted && !this.tasks.some(isValid)) { + this.ctrl.abort(); + } + // 若没有前端任务,我们就删除文件上传任务 + if (!this.tasks.length) { + registry.delete(this.hash); + } + } + + async cleanup() { + let hasValidTask = false; + const deletes: Array = []; + for (let i = this.tasks.length - 1; i >= 0; i--) { + const task = this.tasks[i]; + if (!isValid(task)) { + this.tasks.splice(i, 1); // 删除任务 + URL.revokeObjectURL(task.path); // 释放内存 + deletes.push(task.id); + } else { + hasValidTask = true; + } + } + // 如果没有正常的前端任务,就中止后端任务 + if (!this.signal.aborted && !hasValidTask && !this.isInvalid) { + this.ctrl.abort(); + } + // 如果所有的前端任务都被删除了,就同时把后端任务也删除掉。 + if (!this.tasks.length) { + registry.delete(this.hash); + } + // 广播:通知前端删除任务。 + await report("task", "delete", deletes); + } + + async onabort(_?: Event): Promise { + await this.report("aborted", { + status: "aborted", + error: this.signal.reason || "已暂停", + }); + } + + async report( + status: JobStatus, + info: { error?: any; progress?: number; status: TaskStatus }, + ): Promise { + this.status = status; + let error = coerce(info.error); + if (error != null) error = String(error); + for (const task of this.tasks) { + if (isValid(task)) { + task.status = info.status; + task.error = error; + task.progress = info.progress; + await report("task", "sync", task); + } + } + } + + private async start(): Promise { + await this.report("initial", {status: "queuing"}); + await this.getUploadOptions(); + await this.startUpload(); + await this.completeUpload(); + } + + private async getUploadOptions(): Promise { + if (this.skipUpload || this.uploadToken || this.signal.aborted) { + return; + } + try { + const result = await initiateUploadApi( + this.file.type, + this.hash, + this.signal, + ); + this.skipUpload = result.skip; + this.uploadToken = (result as UploadArgs).token; + this.key = (result as UploadArgs).key; + this.signal.throwIfAborted(); + } catch (error) { + if (!this.signal.aborted) { + await this.report("errored", { + status: "failed", + error, + }); + } + } + } + + private async startUpload(): Promise { + // 任务被中止或者状态错误 + if (this.signal.aborted || this.isUploaded) { + return; + } + // 跳过文件上传 + if (this.skipUpload) { + await this.report("uploaded", { + status: "uploaded", + progress: 1, + }); + return; + } + // 没有状态正常的任务,就不需要上传 + if (!this.tasks.some(isValid)) { + return; + } + const {resolve, promise} = promisify(); + const observable = upload( + this.file, + this.key, + this.uploadToken!, + { + fname: this.file.name, // 文件原文件名 + customVars: {"x:md5": this.hash}, // 自定义变量 + mimeType: this.file.type, // 文件类型设置 + }, + { + useCdnDomain: false, + checkByServer: true, + checkByMD5: true, + }, + ); + const subscription = observable.subscribe({ + next: (res: UploadProgress) => { + this.report("uploading", { + status: "uploading", + progress: res.total.percent / 100, + }); + }, + error: async (error: any) => { + if (this.signal.aborted) { + resolve(); + return; + } + // 如果文件已经存在,则模拟七牛云回调, + // 这样就可以保证文件被正确上传。 + if ( + error instanceof QiniuRequestError && + error.data?.error === "file exists" + ) { + try { + await simulateQiniuCallback( + { + md5: this.hash, + name: this.file.name, + mime: this.file.type, + size: this.file.size, + key: this.key!, + }, + this.signal, + ); + this.signal.throwIfAborted(); + await this.report("uploaded", { + status: "uploaded", + progress: 1, + }); + resolve(); + return; + } catch (err) { + error = err; + } + } + if (!this.signal.aborted) { + if (error instanceof QiniuNetworkError) { + await this.report("errored", { + status: "failed", + error: "网络异常", + }); + } else { + await this.report("errored", { + status: "failed", + error + }); + } + } + resolve(); + }, + complete: async (_: any) => { + // 参数 _ 是七牛云返回我们服务器返回的结果 + await this.report("uploaded", { + status: "uploaded", + progress: 1, + }); + resolve(); + }, + }); + const dispose = (): void => { + if (!subscription.closed) { + subscription.unsubscribe(); + } + }; + // 当信号被中止时取消文件上传 + this.signal.addEventListener("abort", dispose); + try { + await promise; + } finally { + this.signal.removeEventListener("abort", dispose); + } + } + + private async completeUpload(): Promise { + if (this.signal.aborted || !this.isUploaded) { + return; + } + try { + const fileNames: Array = []; + const directories: Array = []; + // const removes: Array = []; + for (const task of this.tasks) { + fileNames.push(task.name); + directories.push(task.dirid); + // removes.push(task.id); + } + await completeUploadApi(this.hash, fileNames, directories, this.signal); + await this.report("uploaded", {status: "completed"}); + } catch (error) { + if (!this.signal.aborted) { + await this.report("errored", {status: "failed", error}); + } + return; + } + await this.report("uploaded", { + status: "completed", + progress: 1, + }); + } +} diff --git a/src/frontend/assets/logo.png b/src/frontend/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..27991f6b1498e6425173bf7ba5cfa74f00a12796 GIT binary patch literal 19195 zcmd3N`#+QKAOAKR!!R1nA&sVHDCa}S%pr$pPIHWrLpcr+rEJ5^%z3IyzJ1#n6s0;>F2y?y*xH#3|vsU{) zbKalHLSA45znFa-zB_nrXDYaa&i0N+On$krJ)0Xdd5&xH*}3~_9}lN1wuVQ#{?tbVJc=B? za%;EP{cpq7XDZldyRs!0y$P+qXSFZhdbTobGpSbj`*Y())!_?`Pyg3-9)Zu>Bw9KLGvOSN+UMIpzkxs0sjF~=Rw8Fhhl+vZvjvQFs1pHFo%Vyl5X$c|_@B$K3!A7K4_+^Ez+y$BEFJb9{kMD3O9og6$=UIK`;)XH|+%s*bK03|BH zp)U0yd+jTtaGw=7!BK&99j%Xrb>1ybR%Y7*1L?jLmsf{C` zPypk1GxzJgF#xna(^#{iC=nlVrRZAxfWCVu{j~^FZ7ko78nTz2Z`rjZ-{YM!W$E4v zY!}rRR0Od*)q|zm@cI>#jZv0O15`#fcr+Wld(kWt&8G*3)1;uEXN+&1tiSl7+xU7o zb}^Jp zbm8#QWw5E3=7D=Ik$+fhuTUXoYo)>pVKA)X;xo_`XrR6HxIt#8M*o8o)|&TUyDcCA z+hkiP2JVxskOU&5Hjg^U6(j46p$$3AOyQUb|H?ee*quA%e};gsh7@mqTH(z3JzT!- zYv_0nN&=iVHob`M(Z&z5KVF{UtWc zmh+?Ky$H(-(b%l#e{E#nZ|z(En*8`+kpI#@+xy?=C$I2r^z3%~Tp5<^8?hN5z5ZtR zuqsU#`mx$i`#|M*d)djw)->JC_e|Ez-}lx{eBQF{=T|?b)$K>yC|AvYJnVFPkX&6i z&9gn>W~E!RWUm(HTvefUeX->cb?bKNvxP1=Wb4%Y#JOV<{QugLH7{qIeL-H!c+(vd zcP~U!@dxljbAT>f62^&lmgRlrl&)r|M08dpszAZ%DhH5&nvAQA^zM%33kG<)m z*o51LhWz``jK&|(8=X^@vd%>;hnNL193hdl#L>bLL#kDD&;;}3sDOF+xVzs#2_&^j zL6g3R0h5a8*SEdXKYx*_2da-6e{gy3Wg>~f4J=FXLd$4zJ+g@%8|jP39kswbWgly4 zD1-nNuZgUL+B+&P%b&HGH{PnkuV39@s{#} zh!xKdsqTK-OGFrKm}$@f)#7p#?` z{f}OoPhYa1z@L=OvY20N-sBu=V$u8bz8}XEUWF(jMKZEI7}fx^mU$e*#E4+hhg%|N zn0fKh@ZNsKaA=)v0kUeKLJS1KgZyYTf00S!ieNj=J9Rc`S})96zdZH7=fv<(L=LI@ zB&0L57rv+cskPRZLc73XheglE9_?XKBFvYzH6-r@h zW~jS@5A*eF^%f5F=BX|31DhH8^{M&H&qDHikR=4{7pTV$hA8E9)AoxajShwcgYk@rbDBb#OSz~H2)kCeVMtjE5)a!*w=On1C>++?*V-u| z^Q~hk46~`12CB$E&GSP5M*#Fe+&2PN%v8wU`C`cAa!%rm1 zBdeyJ0-h)OfKG6N7Z{u0$GUy^9;jTKZf9li#6(QTk#iJ0dN;`}9-1Wsuva7IHz(|3 z5?_?vvk{;g#E}mlLSq;k7k(ev#88eBqpG9;SH^3oSwA@C=tL<0Vtz{5SWY5vi&PU5 zOF+S0pm7w|Hq_|}bMUtkroXqyf61%Sd$y&nRb2i4Ync}PV3o`2wOPkCbZHAeAdaPC zlcmxa!mZmeWE9#<;AM5dIw4U%z!?%Zaujsl^NA|@p$eSGNd;CS0Rzj?ZTKJR>k{15QDxt9I^9(U$l24l3 zoy0+TE-jjQ_h^E=tlM*EKA%TA0K-wYr4lAIk<;f+DU-CzS`N?~*6@5^=0g=oJj+-p1KRhb6_ z5sF1}mgG2C|L{>e8N^nqA zZkrR`C|1`1a|XWu^Q*Gn#wvLDm2(*qO1w8^wzNQkcegM23lA|BJ^>^f{nLHFqUJn& z%vgfUd_hk1U;zsY^MP9bdHOncN>x(CQEPk8E7G?*q zhEk55qV<+1R>b^LI44c}cBGUuhxT5Ls*fY>6Cq^T2P!QASwXXAfHU_}o?VxC&xU0X z^eEC$4p-!puNPt_3MAZzM{{JW5g|E2(|tLmnm%Ocq!}w; z+pX4vd>!jkY+`h68Lk~iLBZLRh4WIi1n0IQk@;^ez&ponK*YsX{dF{FxSvY^dvVCM zzHWTubeakdc;Ij*7Vq{lk#($;pwvB4$%VIlQWI9rB2b_(XhNe+p@?!&3&8W^L za58DvCQE$yoM|T+_u|_^Wtzl~tC~@E);yI+2sy#GBr$*U7*_iTLg#2>z+wmq!KSLV zPP%w;qeQ7ma4b=P9^+k73FuGW9`I%vHSQf`IP#ixFXYs5&pd*Sd-Cdf z94&6b97hOG#A^)k^K*8G6{n?mVA?~Vh^d@9#~$J4fXWUb#j1JKI)bB04YgX0u|JC1 zde`)GDxt~pY!t7=y^shbeu+#O<&JoY*arXQxmfYxJ;+;kz#R}i~3yk?4Yc1PVC5o;$>UI^!0Md(Tv)YEUPzWB#5rL}aC(ogtKpH(br_Z>YU1*Ww> zT}wmEg$`KdYp1^!owJU_Nn-%eN@NNkb}&Vb^9ZV?b3;#H5AP zJ)}hLtDrD1caS&DP?m>^BgLLt(9-s5v+*o$2qCpifbMDX@UuV3G)QxT1I7a@ zQ7-4|3ZC?`d(-=a)N(;^sze7B+*eYF95PcoIJ55|CllF|;d%rOinE4k@7rvah!PaJfSFz{!~z1{T0|Byry z9tM&Od-}nV6H7n%EKFq+P%t!CsofXMBP7R_-Zh^OMfAi4mW$othb*eMh)NX|10I*y0#Umpq+10_Q>w#uUwBm9JpFzBgWf9ne{ zjN8O1_+Q6rehBqt$>hii#Jg!!HBl-=#-Z-+!rbHdHCsTuj@RlK-Yc1>$!yIwTO|rU zN!Xii4y>#c8Yi=y^@Tk7s>M8t!Hko!9$GO{;|&MkddS*@+i;ay=$VqDFU=X3tkuh< z9#lZgV@85!hfa-WTfo8ep6N68kg^5JXq+$gH?7S^)la*~xjEk>FC804nvc3_L5!oA zG>wwyLv4wsP#Bm3yh>h89eoB=kM8kx0~vATT%F3O(*2mwX6r7l5melMBSO44)W1ka z!v6_g(*t*H^Dc`_xq=GO;?TAu%6t{e-}Kd|m;jL}a+W9@IfZdS*yJ?Ui_~0~o`?23 z%^~Bykt>A}-<-dzzj<*=2qW>08}3y^&@BmArL_!*T55eAO~q?2!74?XR1t+JQZ<~% zZJeR+GK^`m`+*4L0I~=Y4nw`G_gjfM{=cfv_OH1`S}PuR2$KflRVrIuCe6BAa+a<- z-+G)MN)MbJa5R3S?Fz|$v+WR|O-YQd- zvU+7|p~Gjge(T9*%IL*i0A={8`FAz8Xvt>PJR0TRO$QtHb*0^6QzYK;y2dY}Q9{E! zpVr2fG*u+{;dR!MJe-D9_sa2Sx#OOG(L`Y$W;}ktfc49)r#6?OIo-~IYOnv{Ssw(M zFeRLNRxn9^D#klXKIWh%f}(au3gg||?38(tU>Ysf3{jZpRes!bB&_h0N{Zs;Jx8u! z>Iq`9V(b?{?Ew3UF~RNSC(Eb}eHWCM&U>kdqa1(U4{1U~M?n}Btq2|sv=6Ascp`iu zs`^&VDCko?T>Uf0c!Lgs%3&_!rjgD~Ue@tP4%1D+Xn)#=3 z^#E%VsZ+eT_Kxh;O0QG+l%n4OE@zJM?gr~-_#Ow;1^q@o`sk>|i4(nS2a(e6h3~*` z3WpbeaL?uYpalCXaRPjT&JmCn!)o-?a&D|uv1SgUZ$2v3D;x68wf|&ueT~dI2_~Sjy6A?lW2V8{mQWs0UgEF{!96V}#7Fd* zitrL2S6mlaww#XP#7jl?^sv6wK0S+gQOa zoZ-9c@%Uq2By3!=<@S-~=8U^QLf^v<0(3xIu7w|kA;9hge*9Ena2=e`Wc!Wl!a}RD zs}dMqiHsv(5%*%f3M&Dz5$;5TjYp9VN|GN~82lShHuP`q-G#sI(}QOP)+XY-Vglxc z8RjoSW+NlD+TI0z+nETzcb0V1L4JH>i810N3o#xodX_wHFszz5CS5E>)Pge1TRt6* zEz1r#nr31tE0#`G5+Pa7HxHJ{=M@N@a)Xxka3wWHXJ)om~Gc7W3_)3Y@wT|+@ zhRD*}2%O45`!iBnoEL(koT%@3c3{m}eW1NQ3V~DRL5V_qr5OMin9JZ(OvGQEo(C9k z&w^!lZHYzs!l?Q{868sKF`O{Jsb#QCP=kWMxtJ~!25MpXY?WP#;SZ9@{MqBAvfmy; z9|=$kGEF$BQJi+{YA7;g+YDKq}{{npiyLN$F!sv_Gj5Rl+D!^GWo;Vy z(emPjBNIOsUjY)S1?Wsy_0P_#8+4QH}H02uS>5zl>HslzO zQ{4YvoR_HqB$RzH`sSXSRhN`uBZva^!)08OQtQfxrC! z)I|pGT=jl=>{O=05&FY`Wsa+ z$36dMxDYt@b{0!Y=Ya|~n{v~n8!nmc<)9eic;aYhqi;>f-XM#W#m;*9d1|eQ3K2uT z3)K*zY*8i~V05$g4(l?5E&B&`pZkma1&8z-<)Qa#P6STmrdwIK#NR=oWLQ1ML}9L> zrC+)wB9>Zv^>TzOgQ`pXbGE$OojBQMa)bN#y{z)>==DuK$9^T-PwdJH=ikS?$cw zar3g;Bes`crU};cpMR*(4AU5qKa>XqKjV;g^C|m%A#|QqedI#cDVVx|rG$rMmO7#{ z6j{;_-BvE?U*$djP&euPqvnic#hVZAQd~fJ^2dQ@G^{eMOmG~W4}E4cac;VTha5B4e zhx!7UOh-z9k}65oEMyz z3;AdW_&&i}zE^56^e*q@nL$Z!*D5X(5g3<@D8HFS5WGh6E0XPae%Vnvg z<$U^(Ur&dexH2mZI2(I@z>t!@OB>P>NH5G*0I|pO$(hxX@72SL2F0F+KxEme-GeiRN`?x=>Byq7i{&_d z6D?E_N+P0)>LWIle}i*A!hgnua=E4TOJ6=isq?Ke(x^;|gy<0XR@(~bkJVqU7GZwT zH+6j!&wy#P9p&dRp$nq-Gx*qTCSADXB%q%M+)Xs<-PG!#E~Ir2zwVi@+=j%E783Xm zK$Teu~FGu5jJ#ZF-JasNh z&@`c(T$Ey!fs6_VE73jOV4y6PBW8Y2c9KW?<$mc9Uz9xfV%tZDWCfQRJ4D}ktE}+- z7Yp7!d&whAUJ8(sgJOT7S&9YP!xG3iN_|ds39SuJyPB|?-79~2skT;TX zH^JPj*z^&j#)W7Zp!DMikp}*D*((0?zgTThXi(`Vz`A_;(6NxF{B(}VslIWd*%%%r z*f{@f!y6o1y?+GVe5+bL>(_~MVb%LeI8X-fh- zA+Lt?HLOHTP1K&d?87hMn;0=q-Q-7Q&i+70rdjy+nQv)22b78QnI;ef0WzRdx2G;lHjRh2}AvmP!A)cms4wrW{b`Lgs1{FTaWiJW8_l)0F4@|MZf;@z+YyLNJZw#D%n&F4T?#RqyAs z5*}qJmJ9OZY?EDTOyv#IC&fCuGnFaI@l94+%c-lRjXSP4N#BsK&@W?{*`##OnrXG` zP`Q3u{<4BKGd~Y)FmyZC^EZL_j%TXk<noESV*!qGBT3!Lf=akb(f zN~v^KSyC6kU8g4`k(xSp-#WrsrZF3Mb)Vn6LCrt)g^7}*XSm0&c2?Q%7+=`#ZEg)^AcDk%T+ha4^QS-{( z|5EHTOnyC%?B{04f$hu1`5RoL^^3zco2}gOLcU+fcv}c)UzG(-ucla zyAuRhKv4)=^yanVr}hA)$)xg(2^gd);Ig7Bf{*G8McfkQdA7b8HFK*F-RRvn zO==#N^9uWN1U-#EaI@z?mz;cU#YYa$OB02`e*Jp{5clBd&v&GeLncLdahETO5A)bC6??^G&)g+Q*wdZGrra*uY|0#^Spk zepr!Yo`gBDp=dl0b*3WXIN8=Zp}==&AKUrmf?qRd*8WKV`ab8w92LvF3qQVN{{5=XRL~HpW55(g29Z}>AF7SEt6oP< zDbPQEH+pG6J};v+w-)aU$7RI%NPuFmvCA8k{h~?;Y|tJ+(u*$)v~Fi07fX@KBg3l; zMXo%FNTHDLgeamqzJ~?znkwjn!sW6e!}|xdnmw0u?4{LME_ItPi537rISdTJ@&aQv z6DmQNnhes{YJMEne;}g%8(&mSOn1TC+|=jJ&rjXHRJj{?$W^~Ur!N1^5@aq84zIxa zUx!+ma@Nf{B`_p5P+9=}cGlm5R4NWP<+PG4=#2fn;#VO;O|b?ZkhBbr|NM=5JR$}% zM)qmNenCk=-juP(s;AF&t4@pZO0_N|*JbX_GBf|gFk2Vb>M{eQN}DrYl#18&8H0x(inHk)+jM(w-0`2o-an6VPrzHF&bGprbd|IO}r$=&dGc}e4 z&@xhOjS(oE@~DJ^uT-$!Ex!Eu#x8=RT8NR94jm1CGHM3-Egj3zX6?XsD$Tqv;pB}i zF)WdggxM$YYpnh&N^1T4QwV3B6fW1s(Y^m6)O^~7ov{%tYTn6AgQMqo_2-Cc1dZ@4^ErNuEQ7s~Z; zc86myixrm9`%`)7!1CP1zSBJ9(b5^&6MI zc}rmfp^UEl@hANv___r*ZJ)_pSM}!G@y9LLVY60Wl?ZD6wl$eZw9vs6N&}z(n??#E zU?L(^q~nz9(Q%e*kcDv6OYz;?Z$8}m{>JSC>!5&5yEw=>Xq5=c0o+MT>_GU7q_$0@<19Y=}7Q_=v@nC zM@wEU*tT@;3*@i@#~(Qlvkgg6&$Hh+a+mb`A0$b=2w8B0>q|*`A9UQY6B)oBP2+}x zW#%ph1ir|sdh0F!q04APJ8jZ=`!VuW-Z0qYoR7N8^@d15+UG6V5;R*L@|N^-&ABO$ z3z5h40|?B;uE1klW+B;HHDKRTfyHOeOOgg>2(ZHh6ZbdnZs+V<+Vi_LxdGuf+YQn#z={OE# zro3`xd$56>fla>@M7OP`a+utWxjo)@q6KH_AcOlhN36IdX7Hxa z-J1DZ)sk~&k>WCB46-CZd@gBR+Wub758R7h3aRp+!6`~%_G}1|8zt+d;vaGJwH=q# z<1s_wQXVR8z`9aom0)gw3N1>xv~t@;$wlGz-wWG6yKM$_NzF|bUq{&Amoj@JOcEa= zKUH~o$eH(UFhjxt1YuLCZ%RLru84)pmLCVQ2% zn=&N$%t9I>eFQ-?U!flAD#i_*?<^Z%Z|&ZiykJ{U^1|}Y4UD4gFrz5|-o2RZzJMVn zOS*_8KAm^HZq01_drF^ap~OC=g?MvER|Kp;|?7xMt9FLp_^2HP!q ziiTgd)Th-6;z397!fxNc)4|ix>NAx$|9u$IiQvV*{iwssSOb~3X7Vz|VwhvczoVv8 zeeq`9WYzh0G|xOy?UM?u9|=krMpAPUrGi_#@lp&!GeHa;6Bipeo?bA`Vv$tegcVAF z?!**nSF~-n*NEepm$rA9qS40vAO6ldsVv=;iD~sb8AEI06^>Il`nPE2%Tw6fAM~`h z>SbT+(<>aUj;-yO?KnhqgerM6p9~s+P4#M=Vg2Qy`!UR)TryfSYnKu-!BPun0VGuv zn%c-Vj>NK-lM4V|ec*jJ^-}KbL z2*i=2&E{<AZS&kM z%PM}AOQz1HKH7GeVDVoG!8p*S@o7U?S>f4;OQs5m^7aRhW9;61NnF=I446RHZ;jj< zbPIx*oILLJ*gz&TGA%O9QjfsA4KUTCd%8#0=b~62W6bk&-cFw&!lIupuyOVkQfrAY zF`!Gr%wo78b8^;v4HuXSXt$E=g$mmeq)~=h&zsLx`2a-h=Wo^NLn(U)A~p3z5%&rfuoOY0snv%A4JkqSqje-sa7o+<{Q-=!*_ z8CvHGe~q_}M&Lr&mo4}X|5Bvp=I*?c7OlccU+sfI+@F4Dph4Kv5Q1$5Cc@lqP)w3X z0Bj&NX(7VYj{YdMzi4j`=05jGd8N&Z4wj!hre|`gB1$;m#okWA?hAl$NA*|N1Fb_> z-R1%~b4VkP`BUwp<+?eeJ()46HPEiKfuVc;wSvz<*m*tWNk!uP3F#jXp?FHuz;cqz z-Zh1Dbjg{rcBd=6OSui>7eDYw@Ii=WaGpy_D1M${aXzdM6kY2}4zmh9T8!3+;$53? zEb{+x-F(_MN@wXNH@qt1NZ;f?yYZRmyG=KYa_c95UJsmAhAa2q1DGD)9!u+lf7qwZ ze&b;LRBPjAb`nkhIGGS+qSoi%UYzPPv+^urSIr1fUnk0>5(RxykTB<}OG#$=6GW{? zw)+`U2?6Q$+@0$@03~?DNOkM_pIiHD->N4cZ57*)M+{B>H$jq~bKMIo((7H^J+}5Z zYI6T~=dR74n`?UYcTie${+*r8zgHD*;Y|NLJo|r&--iS2s$b0PgMNOl9YC zmoz;VcV|R3LlOSt#8V_srDVkD(d?^JJY&+DYjS0vUT8%cE^ktVr_QQLdi;vhSK$+G zITnit!+GfMeE5(fxDejI6NlR4oF;-2m8E7IDH|^*jf8t_R#ej0U;8Pv-!-d?O9P`? zcT5P(bIYw)ZvmK{08_XBKxYnetAdF_UwJZRw({Jg7@D|ee@knLfo-solBn`p9afDJ z4lDVQcZFBju~Y&PGOHCE16VQKeirTPqj*1AuLI1mg`gl=a-H@oo&JUu5tm=u{rnv> zzW@8eLF=COni{@ttMBq)#%d~=p_6K+%qz9R-mdx?gL-e*I0(PatZ{_%0vypzX8sxf zc!fZ{3C>Mo{1kv)PY%W2I;BLrXhanckC;VJSfVkNwi<1}d#!(0yit6PQy_EpUU^Hw z5a3L|PGg2G2fGt(MS4`Z*)wH(H+QZ#{2WQpJ06`HRb?&iShcwS=H_OQ*>uF>8}HYA z9Yc5D|9O~W^CpnW96T$8(qf__%O3+HeOUMM(Cl!0j%l&ytCn1diFMVwb{%`}kU#%F zH^ZNr)KEGx5uTg}8h^zVT>3Ol3c*Lz5<5Eo;Z8c(cTVPeop`5XRUO0is=>mo4%07HG8xk}h@}ixzvUA=J=ra&rcR`9EoS}l}F*(keQ(0%&hY<3R>GJ8cCpZPo z=xekQWwZtF*+=w0{NBAmGj>4KmT_6l9jA(k_Fcl7Bz15RFJP*5e4C&*Nwg5cJB~dRxr{h{QOjB zV5t+W@~h<7U#?CN<0prVmc-h`!t&KR=MwgaSHb;)BFqywQvf6GJOC|{*!?l?3wv7( zp<5?q#6%Q|%;1Ifg-Xw}-3$d4?&;klAbJ(QZ3*fo%e$AYOQ$4?|3O86YWk*j_=nYb z=61(d-FWLi&&%IlUy0UmhW6`D9=p2!e2cu@?0f@=j?X&EMy>NWGMC9Wejk&(UvehM zv^_yV&O&aV-PLovhS@4ujPbCg(ZGVhfyWZuISy|7N|dN1fx`MQZj7nG)3N_?dxRu{ zNPAl3%MI>Wkn5=0u>136@$l>Q>h`pEEG2pOgmvhf-nhqH%f8Vv zZN!jpcg?z`Dck>k2f#h{7Xs~H$U7;?Xfa6VT^KeqA|NF9;)Hd?gkr3$l%$v7e!}H+ zkU@D|`JZ2a3pbksvvcJ|9t1WMXV$iSc8u?8kj-=V70cdU7oNRNo>BwQw%=zxzp~!? z3mW>5PZ!8^UCt~$&&IYLkO{v=^$B$7&06NzE=XntlJ^s2vZ*njA7U~wKd%s4>q}~4 zoPTqR8|E7@qS9hsWP4&jv^E5+UIvkqDSHz;Nu@Q!-;})j4On<(F^cg=vGWX_XZ0{D z=#7sAqDgXx)bjhD%AhO7^MtJF#cd4k$J&@HfcybkHhbi+8phz=U)Is?-YfLW1?rvt+_vH_8Ds_1%Lhd+>4lsfuM*BxU9DcZ+BBQM_^GO0{8RairJe+8 z1rlC<6k~dkQdm6gBc(N;_QDxUXJL)Y>x{v%3fvzLQ0@@65%2!l&+pWI+2w->MF)kb zRpNX?WjehO*mb20i~D3@_p`XU{mktkm#tML)KzXC?SGdh+1l)A{!e5bli8w=Y zi=|y>K+>-}29XaHHf!Ncw%E%h{;Y3Q6h_lTMh9xCRFT8e1z$?|-*o$|Zq;6UE_qUJ zHfUw1V94!f=icoD$wSB5i%?UlFA5Hhi=zX#7!}W-wMCMnE^hA`zCz!>Nn!p(n}qYC zw{O7F8)LgD6Mle%!;c52@|a&=juw5`P`$QK%p29}8QyYt+Z;?I@?8>vaT=zvdh{a= zP*!oi3zV8Ns}2gu&N?;XU9Y&+`pez95kG~1y@@n>cX~%tq(ON+k*`=nlLKiD9jH$_ z=R5JQHY;+zPjb2T{j)5erzo^;SKv;K+1q2N6@JD~KpTWCKkw^6Nu`sQ6BeNI={gsX zp2-+D!+`~-CNVg$yEM+8&#gdm!rF+mH&AYT`Br85eeI*bYC2;YMJ&4QT9xP!;o{*nSbLAHtyw0Od=dU+%meh_&TA))^|wE=iG!uq-P- zv2QvO<3N-4H&{DClLjE~oVb1awDYqJV#vXZ2oZwQz+b7XUM-$!Q>-|)|4SGktxv z{=uHwVI~qvleT5^4-uXwYisq(Y-=%v@<$HuzR=ZJS-hO$ZW0KR0k-X|jY%#SiRuE_ zuinjGUmnr|m|nVGU(GMI!pXR%!c}5B2k&M3e$CgvRJrP=rSQym-JCF6%7gO?%W{Sl zdiZ&z)AHEbG#4k$L+(EW{H*Ts=A@28e_!&(LmVT<_EqsF#G-CylI}p?(E>07)hXi zow-PE!oEUEbZsX}k;tnV6}A%H!tkk_0a-c@p6&B>Ra$#V_(>_??l;iqn2fphbamwn z&Xe>n1amoh#gY>{+cL0?QSY$h=l>`uF=Hv)`)a)BW8RHt{HWGHire!k@l@tDl@ZHx zgYDq(Rkb?jc>R<5ZR1001~>^c(P(U%?SZmg@hSGeh-|WQr(}nb_d>v0J!SFrcAf1x zJPUB`UP>D6f5nX3PjYrx>On9gzpBNXTdP~Thpm7Fs8E(#VSuJ)jCdfX=&uMHUopz>(*$&(h>o<9P-5m)AX z^43__p*J_FT7dZHPzer-qxIx^VYIziP*5w8{jiE#(wbA29X>2JWTeIGVm5SthLyp- z1-;|UvY3?gmV8!P9vSkYl3h!)k$JeB3ib{Afu)umH`S+Pk$}YJQ!1^u7+uyU;_UKgLs_)~hx8wM?y=#xh zh0_Ylr?p4$){~I2*lkXrv<%5%r>mXy%M^FVj$`@jy`OtE)tZ+Plzb9glb{fB$#xuI z(k{C=)r}^~Xa86XetZsaoc&^yJXVOUa z8aKQ^>`$J+#tEnrQooOc!dy@Ay@IJom*y?n(85Gi+8NB}%?xj6i0?nNi2ep*T$3_k z$D?=c(L@|7G6uB*3NM$#fv}*kyiS$(3cNg=A?voK6$|`(5yOWJ*#{a^W{sZ3cwEGH zbB^wCY=iygpLSnR;gxrNyK)_0dgQrcIAa!G$;D&(MAf6@FotThWGeuj$j=vdnksb~ za=z)9FnB%SU)j|Uf4u6$qTFbnFJn(Hm);>YpS%ui8y|0HpA%G|71mE{RI5xW017+r z+X=xwJ-aNtoQzkL9)D#Z2-}#z(bIG}*XEYO12gkGx&X{m^H3HPoJGS0Vjxa+IZkAvt?tc|Q1BIr6NJ z$@`44%UpT{hlt05xB}rP4hmrYTA~sf1nODH&%1&9CigV>^@Bm(=x^MNYu+-g9Z8Yy zShh-U3GQEa%mFYhm%nTM*T84A?=j&LXOq83nx@)uP-{X9MQW{EP=RcFWiiI*bACd; zpvnLIknh{$>)s@leci5A{;HPldIDh$i`S9t_7UR|YOOwN`@`XEkC1_&BWCiMS1)Qj zp$py!%^O_;ydg!&0Kls`H}!(nJDTFBZ(KS>V86Ze{aGdSJ3*#p9y;g$G;=QgOuua$ z|8B;HH6xZ7ZBEacgd{R*bDWjT91`_7Hq@>C?} zQ*&&|A!bQRPtUjhis$vZU-uty-Pd*9_jTQ$_w`yGZf_?>jX9*`aPS9J)=4+mYx`5BY|;A*0o$_LA;Asez$_ z3@p|N1;4Rs*0Im<7+pFVSiB_Ln=l=Q*V;)GgtepcOKI>b1VU1lXoX}? zR22GFgn@FCFT`@id>-0UJJbu&n?TjGnvGWL*IXnSXx)b72CMal0{>BMG-Ks9!KqUcS_lM& z$o}LmO^6)ZREjN&vwC5$8L)%vwC<`R_j3uZEKObjdx-G2Gkn5x$-IPIZRXQQlPyj0=s zBA#YAb4RM1;zqMb1!M4Z?iENMMI+M(&!lf+E8tL{4`f3kOTNMukdy!!EiaNJgdoUX z>2JP&V&~%UWZ3tp1o3iu3}8=cT-8^T&K=(%KhEGb8GRSFG_vY4A0aJIU&{}ZzZa|s zak}`ec_CVGC7y$NU47Ez*Q4prqS>{BOWZUHx)bvhPj{!56wX6LF|;*TJ{C5Bny%MS zZpt4bZ>~)>W6&wqSJ}>4U|SRiBTJrf#e2n({3%R&9V2ek8)vTNwJ`NfJN5E#b5Xy< zc&kay?2y3Rq_QafqyBnVNVCb~ji5k5;po6x(J;ydh7W$hU7k&chdAXK#~Fn-52^}g zub(U@h@v}CBjd;{Fgs&~^$|buaPk?Y>N6Kpdm~NXI^{J$kX%IA46$6nb(+cDuyw?3BXKTrgh2KQoUMKEfnBDOX!xM34t)MYT`oR>^v)2DOlSCSere(Khm(TMKN-n;ALtwxUTrciu0l6a-bqwc~w zz-^c`DMu2!nVZ8cZj>3@an)0TWju~gY}gfhFNZCR_H>*o7s`n`Dy!ykT?2oxHKo$# z`eMsJPue>5BJMZ+&`B%y;jHHoq`G-sb(D)qljX0f)AVXoP$(D=fs$l@0*%w%m@RYS zC1jz|mWPdT;JKSF|665~7 z7v3DIh%vnkpg-Jw(fP#r*?bbH1?$u0kbNn5?> z=`{;??fu2yK6{9TXWh8hqUxJps=BYtu{MP{`IMp)$0^aqw;WxK_e?4~&783x%B%yFMRBV}Pf&RKmxwvREb?`YOB&K=e; z^$k@5wp^Atb;ePbeNJ*I=|X+_6SHJ){Fg7sCio4LQSges zH3b4fLWBEU_0(&O&3rpTC;823%Y{$t!G-6=aA&V!Bf+8I2@22r>bEwYiWu_8x(f0m z-5wz)@d&PA`c>A&ipLqza6`pgYc)7~Py`8ssVPPj28=yHticdHCoTla2A1`?Nh^Cb zggPN1HWUSk`V61Vn9LL=&)wjz$&c83CP`iGZE;+QrEuB4q*QCZypW5A!fgOMzQTN< zOA2GeTw<0K|FivWq;%HtA*yEf|HZc08M2qWoO7#aMagkLIMfQTw>f8BYw4Z(KZtyN A$^ZZW literal 0 HcmV?d00001 diff --git a/src/frontend/directives/index.ts b/src/frontend/directives/index.ts new file mode 100644 index 0000000..50b77e0 --- /dev/null +++ b/src/frontend/directives/index.ts @@ -0,0 +1 @@ +export * from './vTooltip' \ No newline at end of file diff --git a/src/frontend/directives/vTooltip.ts b/src/frontend/directives/vTooltip.ts new file mode 100644 index 0000000..8080ddd --- /dev/null +++ b/src/frontend/directives/vTooltip.ts @@ -0,0 +1,69 @@ +import type {DirectiveBinding, ObjectDirective} from 'vue' +import {alignWithElement} from '../utils' + +interface Elm extends Element { + v$tooltip?: HTMLDivElement +} + +const getTooltip = (el: Elm): HTMLDivElement => { + if (el.v$tooltip != null) { + return el.v$tooltip + } + + const tooltip = document.createElement('div') + tooltip.style.display = "none" + tooltip.classList.add('tooltip') + + el.addEventListener("mouseenter", () => { + if (tooltip.parentNode) { + tooltip.style.display = "block" + alignWithElement(el, tooltip, "top-center", [0, -6]) + } + }) + + el.addEventListener("mouseleave", () => { + if (tooltip.parentNode) { + tooltip.style.display = "none" + } + }) + + el.v$tooltip = tooltip + + return tooltip +} + +const stringify = (value: any) => { + if (value == null) { + return value + } else if (typeof value === 'string') { + return value.trim() + } else { + return value.toString().trim() + } +} + +function destroy(elm: Elm) { + if (elm.v$tooltip) { + elm.v$tooltip.remove() + delete elm.v$tooltip + } +} + +function bind(el: Elm, {value}: DirectiveBinding) { + const text = stringify(value) + if (text) { + const tooltip = getTooltip(el) + if (!tooltip.parentNode) { + el.closest('[data-ui-barrier]')?.appendChild(tooltip) + } + tooltip.innerHTML = text + } else { + destroy(el) + } +} + +export const vTooltip: ObjectDirective = { + mounted: bind, + updated: bind, + beforeUnmount: destroy, +} \ No newline at end of file diff --git a/src/frontend/hooks/index.ts b/src/frontend/hooks/index.ts new file mode 100644 index 0000000..0a71771 --- /dev/null +++ b/src/frontend/hooks/index.ts @@ -0,0 +1 @@ +export * from './useTheme' \ No newline at end of file diff --git a/src/frontend/hooks/useTheme.ts b/src/frontend/hooks/useTheme.ts new file mode 100644 index 0000000..502ebcf --- /dev/null +++ b/src/frontend/hooks/useTheme.ts @@ -0,0 +1,91 @@ +import { + computed, + ComputedRef, + getCurrentInstance, + getCurrentScope, + inject, + InjectionKey, + onScopeDispose, + provide, + Ref, + ref, + watchEffect +} from 'vue' + +export interface UseTheme { + use: Ref<'dark' | 'light' | 'system'> + dark: ComputedRef + light: ComputedRef +} + +const key: InjectionKey = Symbol("useTheme") + +export function useTheme(): UseTheme { + let instance = inject(key, undefined) + if (instance != null) { + return instance + } + + const use = ref<'dark' | 'light' | 'system'>('light') + const matches = ref(false) + const dark = computed(() => { + if (use.value === 'dark') return true + if (use.value === 'light') return false + return matches.value + }) + const light = computed(() => { + if (use.value === 'light') return true + if (use.value === 'dark') return false + return !matches.value + }) + + const handler = (event: MediaQueryListEvent) => { + matches.value = event.matches + } + + let mediaQuery: MediaQueryList | undefined + + const cleanup = () => { + if (mediaQuery) { + if ('removeEventListener' in mediaQuery) { + mediaQuery.removeEventListener('change', handler) + } else { + // @ts-expect-error deprecated API + mediaQuery.removeListener(handler) + } + } + } + + const stopWatch = watchEffect(() => { + if ('matchMedia' in window && typeof window.matchMedia === 'function') { + cleanup() + + mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') + + if ('addEventListener' in mediaQuery) { + mediaQuery.addEventListener('change', handler) + } else { + // @ts-expect-error deprecated API + mediaQuery.addListener(handler) + } + + matches.value = mediaQuery.matches + } + }) + + if (getCurrentScope()) { + onScopeDispose(() => { + stopWatch() + cleanup() + mediaQuery = undefined + }) + } + + instance = {use, dark, light} + + if (getCurrentInstance()) { + provide(key, instance) + } + + return instance +} diff --git a/src/frontend/index.ts b/src/frontend/index.ts new file mode 100644 index 0000000..03ff3bb --- /dev/null +++ b/src/frontend/index.ts @@ -0,0 +1,225 @@ +import {store} from './store' +import {installIcons} from './widgets/VIcons' +import UiContainer from './widgets/UiContainer.vue' +import VDialog from './widgets/UiDialog.vue' +import { + type CSSProperties, + defineCustomElement, + getCurrentInstance, + h, + nextTick, + onBeforeMount, + onMounted, + onUnmounted, + type Prop, + reactive, + ref, + type VNode, + VueElementConstructor, + watch +} from 'vue' +import style from './style.css?inline' + +let FileManager: VueElementConstructor + +if (!customElements.get("file-manager")) { + FileManager = defineCustomElement({ + props: { + select: { + type: String, + validator: v => v == null || v === 'single' || v === 'multiple', + } as Prop<'single' | 'multiple'>, + mime: { + type: String, + validator: v => v == null || v === 'image' || v === 'video' || v === 'all', + } as Prop, + apiUrl: { + type: String, + required: true, + }, + artifact: { + type: String, + required: true, + }, + accessToken: { + type: String, + required: true, + }, + dialog: Boolean, + width: Number, + height: Number, + zIndex: Number + }, + emits: [ + 'close', + 'select', + 'open', + 'fail', + ], + setup(props, ctx) { + const shadowRoot = ref(null) + const visible = ref(false) + const size = reactive({}) + + watch(() => props.select, value => { + store.mode = value ? 'select' : 'manage' + store.multiple = value === 'multiple' + }, {immediate: true}) + + watch(() => props.mime, value => { + store.mime = value ?? 'all' + store.lockMime = value != null + }, {immediate: true}) + + const attachDialog = (root: ShadowRoot) => { + if (!props.dialog) return + const style = document.createElement('style') + style.textContent = `:host{position:fixed;inset:0;z-index:${props.zIndex ?? 100}` + root.insertBefore(style, root.firstChild) + } + + const handleWindowResize = () => { + let {width, height} = props + if (width == null || width < 100) { + width = window.innerWidth * 0.8 + } + if (height == null || height < 100) { + height = window.innerHeight * 0.8 + } + size.width = `${Math.max(width, 875)}px` + size.height = `${Math.max(height, 640)}px` + } + + onBeforeMount(handleWindowResize) + + onMounted(() => { + const el = getCurrentInstance()?.vnode.el as HTMLElement + const root = el.parentNode as ShadowRoot + installIcons(root) + attachDialog(root) + shadowRoot.value = root + void nextTick(() => { + visible.value = true + }) + window.addEventListener('resize', handleWindowResize) + }) + + onUnmounted(() => { + window.removeEventListener('resize', handleWindowResize) + }) + + const createManager = () => h(UiContainer, { + setup() { + return { + apiUrl: props.apiUrl, + accessToken: props.accessToken, + artifact: props.artifact, + } + }, + onClose() { + ctx.emit('close') + }, + onSelect(files: CloudFile[]) { + ctx.emit('select', files) + }, + onFail(message: string) { + ctx.emit('fail', message) + } + }) + + return () => { + let child: VNode + if (props.dialog && shadowRoot.value) { + child = h(VDialog, { + barrier: shadowRoot.value as unknown as Element, + visible: visible.value, + class: 'shadow-2xl', + style: size, + rounded: false, + outside: 'shake', + onDismissed() { + ctx.emit('close') + }, + onCompleted() { + ctx.emit('open') + } + }, { + default: createManager, + }) + } else { + child = createManager() + } + return h('div', { + ref: shadowRoot, + class: ['size-full', {'fixed': props.dialog}] + }, [child]) + } + }, + styles: [style], + }) + customElements.define("file-manager", FileManager) +} else { + FileManager = customElements.get('file-manager') as VueElementConstructor +} + +export { + FileManager +} + +interface ShowFileManagerOptions { + select?: 'single' | 'multiple', + mime?: FileMime + apiUrl: string + accessToken: string + artifact: string + zIndex?: number // with dialog + width?: number // with dialog + height?: number // with dialog + onClose?: VoidFunction // with dialog + onSelect?: (files: CloudFile[]) => void // with dialog + onOpen?: VoidFunction // with dialog + onFail?: (error: string) => void +} + +export function showFileManager(opts: ShowFileManagerOptions) { + let elm: Element | null = new FileManager({ + select: opts.select, + mime: opts.mime, + apiUrl: opts.apiUrl, + accessToken: opts.accessToken, + artifact: opts.artifact, + dialog: true, + zIndex: opts.zIndex, + width: opts.width, + height: opts.height, + }) + + const dismiss = () => { + if (elm != null) { + elm.remove() + elm = null + } + } + + elm.addEventListener('close', () => { + opts.onClose?.() + dismiss() + }) + + elm.addEventListener('select', evt => { + opts.onSelect?.((evt as CustomEvent).detail as CloudFile[]) + dismiss() + }) + + elm.addEventListener('open', () => { + opts.onOpen?.() + }) + + elm.addEventListener('fail', evt => { + opts.onFail?.((evt as CustomEvent).detail) + }) + + document.body.appendChild(elm) + + return dismiss +} diff --git a/src/frontend/store/events.ts b/src/frontend/store/events.ts new file mode 100644 index 0000000..7e01645 --- /dev/null +++ b/src/frontend/store/events.ts @@ -0,0 +1,38 @@ +import {$on} from '../../shared' +import {useFile} from './handlers' +import {store} from './store' +import {toast} from "../widgets/UiToastController.ts"; + +// 接收上传任务的信息同步命令 +$on("task", "sync", (task: Task): void => { + const old = store.tasks.find((t) => t.id === task.id); + if (old == null) { + store.tasks.push(task); + } else { + Object.assign(old, task); + } +}); + +// 接收上传任务的删除命令 +$on("task", "delete", (deletes: Array): void => { + for (const id of deletes) { + const i = store.tasks.findIndex((t) => t.id === id); + i > -1 && store.tasks.splice(i, 1); + } +}); + +$on('file', 'create', useFile) +$on('file', 'update', useFile) + +let online = false + +$on('app', 'status', (status) => { + if (status === 'online' && !online) { + online = true + } else if (status === 'offline' && online) { + online = false + toast('warn', '后台不在线,上传文件无法同步显示') + } +}) + +export {} diff --git a/src/frontend/store/filter.ts b/src/frontend/store/filter.ts new file mode 100644 index 0000000..8f141b4 --- /dev/null +++ b/src/frontend/store/filter.ts @@ -0,0 +1,112 @@ +import {dispatch} from '../worker.ts' +import {coerce, off, on, sleep} from '../../shared' +import {getCurrentScope, nextTick, onScopeDispose, reactive, watch} from 'vue' +import {store} from './store' + +export interface Filer { + loading: boolean + limit: number + page: number + total: number + error?: string + dirId: number + view: + | "error" + | "files" + | "empty" + | "loader" +} + +export function useFiler(directoryId: number): Filer { + const filer = reactive({ + loading: false, + limit: 30, + page: 0, + total: 0, + dirId: directoryId, + error: undefined, + view: "loader", + }) + + const jump = async (to: number) => { + if (filer.total < 1 || filer.view === "error") { + filer.view = "loader" + } + + filer.loading = true + filer.page = to + filer.error = undefined + + const mime = store.mime === "all" ? undefined : store.mime + const {limit, page} = filer + try { + const res = await dispatch('file', "list", { + directoryId, + page, + limit, + mime + }) + if (filer.page === res.page && filer.limit === res.limit) { + filer.loading = false + filer.total = res.total + filer.view = res.items?.length ? 'files' : 'empty' + store.files[directoryId] = res.items ?? [] // 这里会不会是响应式的 + } + } catch (error) { + if (filer.page === page && filer.limit === limit) { + filer.view = "error" + filer.error = String(coerce(error)) + filer.loading = false + } + } + } + + const refresh = async (): Promise => { + filer.loading = true + if (filer.page == 0) { + filer.page = 1 + } + await sleep(300) + await jump(filer.page) + } + + const reload = async (): Promise => { + filer.loading = true + filer.view = "loader" + await sleep(300) + await jump(filer.page) + } + + const reset = async (): Promise => { + filer.page = 0 + filer.total = 0 + delete store.files[directoryId] + await jump(1) + } + + watch([ + () => filer.limit, + () => store.mime, + ], reset) + + if (getCurrentScope()) { + on(`files:${directoryId}:refresh`, refresh) + on(`files:${directoryId}:reload`, reload) + on(`files:${directoryId}:reset`, reset) + on(`files:${directoryId}:jump`, jump) + on("mime:change", reset) + + void nextTick(reset) + + onScopeDispose(() => { + off(`files:${directoryId}:refresh`, refresh) + off(`files:${directoryId}:reload`, reload) + off(`files:${directoryId}:reset`, reset) + off(`files:${directoryId}:jump`, jump) + off("mime:change", reset) + delete store.files[directoryId] + }) + } + + return filer +} diff --git a/src/frontend/store/handlers.ts b/src/frontend/store/handlers.ts new file mode 100644 index 0000000..7ae7c54 --- /dev/null +++ b/src/frontend/store/handlers.ts @@ -0,0 +1,204 @@ +import {toast} from '../widgets/UiToastController' +import {dispatch} from '../worker' +import {store} from './store' + +export function getDirectoryEntry(id: number): CloudDirectory | undefined { + if (id === 0) return store.rootDirectory; + return store.directories.get(id); +} + +export function hasChildDirectoryEntries(id: number) { + return id > 0 && Array.from(store.directories.values()).some((d) => d.pid === id); +} + +export async function refreshDirectories() { + const dirs = await dispatch("dir", "all"); + const set = (dirs: CloudDirectory[] | undefined) => { + if (dirs == null) return; + for (const {children, ...dir} of dirs) { + store.directories.set(dir.id, dir); + set(children); + } + }; + store.directories.clear() + set(dirs); + if (store.activeDirectoryId > 0 && !store.directories.has(store.activeDirectoryId)) { + store.activeDirectoryId = 0; + } +} + +export function reload(): Promise { + return new Promise((resolve) => { + store.activeDirectoryId = 0; + store.files = {}; + store.directories.clear(); + queueMicrotask(() => { + refreshDirectories() + .catch(err => toast("error", err)) + .then(resolve) + }); + }); +} + +export async function uploadFiles(dirid: number, files: FileList | File[] | File): Promise { + if (files instanceof File) { + await dispatch('task', 'create', {dirid, file: files}) + } else { + for (const file of files) { + await dispatch('task', "create", {dirid, file}) + } + } +} + +export function deleteFile(file: CloudFile): void { + const list = store.files[file.directoryId] + const index = list?.findIndex(f => f.id === file.id) + if (list && index != null && index > -1) { + list.splice(index, 1) + } + removeSelected(file.id) +} + +export function updateFile(file: CloudFile): void { + const list = store.files[file.directoryId] + const index = list?.findIndex(f => f.id === file.id) + if (list && index != null && index > -1) { + list.splice(index, 1, file) + } +} + +export function useFile(list: CloudFile | Array) { + if (!Array.isArray(list)) { + list = [list] + } + for (let file of list) { + console.log(file.id, file.object.status, file.name) + const files = store.files[file.directoryId] ?? [] + const index = files.findIndex(f => f.id === file.id) + if (index === -1) { + files.push(file) + } else if (files[index].object.status < file.object.status) { + files.splice(index, 1, file) + } + store.files[file.directoryId] = files + } +} + +export function toggleSelect(file: CloudFile): void { + if (store.mode === 'manage' || store.multiple) { + if (!removeSelected(file.id)) { + store.selected.push({...file}) + } + } else if (store.selected.length && store.selected[0].id === file.id) { + store.selected = [] + } else { + store.selected = [file] + } +} + +export function removeSelected(id: number): boolean { + const index = store.selected.findIndex(f => f.id === id) + if (index > -1) { + store.selected.splice(index, 1) + return true + } + return false +} + +// Feature detection. The API needs to be supported +// and the app not run in an iframe. +const supportsFileSystemAccess = + "showOpenFilePicker" in window && + (() => { + try { + return window.self === window.top; + } catch { + return false; + } + })(); + +export async function pickFiles(directoryId: number) { + // If the File System Access API is supported… + if (supportsFileSystemAccess) { + try { + // Show the file picker, optionally allowing multiple files. + // @ts-ignore + const handles = await self.showOpenFilePicker({ + multiple: true, + excludeAcceptAllOption: true, + types: [ + { + description: "图片和视频", + accept: { + "image/*": [ + ".apng", + ".avif", + ".gif", + ".jpeg", ".jpg", ".jfif", ".pjpeg", ".pjp", + ".png", + ".webp", + ".bmp", + ".ico", ".cur", + ".tif", ".tiff", + ], + "video/*": [ + ".3gp", + ".mpeg", ".mpg", + ".mp4", ".m4v", ".m4p", + ".ogv", ".ogg", + ".mov", ".qt", + ".webm", + ".avi", + ], + } + } + ], + }); + + const files = await Promise.all( + handles.map(async (handle: FileSystemFileHandle) => { + const file = await handle.getFile(); + // Add the `FileSystemFileHandle` as `.handle`. + // @ts-ignore + file.handle = handle; + return file; + }) + ); + + await uploadFiles(directoryId, files) + + } catch (err) { + // Fail silently if the user has simply canceled the dialog. + if ((err as Error).name !== 'AbortError') { + // console.error(err.name, err.message); + throw err + } + } + + return; + } + + // Fallback if the File System Access API is not supported. + return new Promise((resolve) => { + const input = document.createElement('input'); + input.style.display = 'none'; + input.type = 'file'; + input.accept = "image/*,video/*" + document.body.append(input); + input.multiple = true; + input.addEventListener('change', () => { + input.remove(); + if (!input.files) { + return; + } + uploadFiles(directoryId, input.files) + resolve() + }); + // Show the picker. + if ('showPicker' in HTMLInputElement.prototype) { + input.showPicker(); + } else { + input.click(); + } + }); +} diff --git a/src/frontend/store/index.ts b/src/frontend/store/index.ts new file mode 100644 index 0000000..86b45c0 --- /dev/null +++ b/src/frontend/store/index.ts @@ -0,0 +1,4 @@ +export * from './events' +export * from './filter' +export * from './handlers' +export * from './store' diff --git a/src/frontend/store/store.ts b/src/frontend/store/store.ts new file mode 100644 index 0000000..819480e --- /dev/null +++ b/src/frontend/store/store.ts @@ -0,0 +1,52 @@ +import {computed, reactive} from 'vue' + +export interface Store { + showSidebar: boolean; + showTasksPanel: boolean; + isDragging: boolean; + mime: FileMime + lockMime: boolean + mode: FileMode + multiple: boolean + tasks: Array; + rootDirectory: CloudDirectory; + directories: Map; + activeDirectoryId: number; + // files: Map; + files: Record; + selected: CloudFile[]; +} + +export const store = reactive({ + showSidebar: true, + showTasksPanel: false, + isDragging: false, + mime: "all", + lockMime: false, + tasks: [], + mode: 'manage', + multiple: true, + directories: new Map(), + rootDirectory: rootDirectoryEntry(), + activeDirectoryId: 0, + files: {}, + selected: [], +}); + +/** 当前文件视图的关联目录 */ +export const activeDirectory = computed(() => { + if (store.activeDirectoryId === 0) { + return store.rootDirectory; + } + return store.directories.get(store.activeDirectoryId); +}); + +export function rootDirectoryEntry(): CloudDirectory { + return { + id: 0, + pid: -1, + title: "根目录", + createdAt: "", + sort: Number.MAX_SAFE_INTEGER, + }; +} diff --git a/src/frontend/style.css b/src/frontend/style.css new file mode 100644 index 0000000..c8b418a --- /dev/null +++ b/src/frontend/style.css @@ -0,0 +1,131 @@ +:host, +:root { + font-size: 14px; +} + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer utilities { + .flex-center { + @apply flex flex-col justify-center items-center ; + } + + .shadow-pop { + @apply shadow-[0_2px_11px_rgba(0,0,0,0.1),0_3px_6px_rgba(0,0,0,0.05)]; + } + + .pseudo-border { + @apply before:absolute before:z-10 before:inset-0 ring-1 ring-gray-900/[0.18] before:rounded-2xl before:pointer-events-none; + } + + .popup-panel { + @apply rounded-lg overflow-hidden shadow-pop bg-[#f2f1f1]/[0.9] backdrop-blur-[8px] pseudo-border; + } + + .tooltip { + @apply text-xs bg-white px-1 py-0.5 fixed top-0 left-0 leading-tight transition-opacity pointer-events-none; + @apply rounded overflow-hidden shadow-pop bg-[#fff]/[0.8] backdrop-blur-[8px] pseudo-border z-[1234]; + } +} + +[hidden] { + display: none; +} + + +.spinner { + position: relative; + transform: translate3d(50%, 50%, 0); +} + +@keyframes v-loading-spin { + 0% { + opacity: 1; + } + 100% { + opacity: 0.15; + } +} + +.loading-bar { + animation: v-loading-spin 1.2s linear infinite; + border-radius: 6px; + height: 8%; + left: -10%; + position: absolute; + top: -3.9%; + width: 24%; +} + +.loading-bar:nth-child(1) { + animation-delay: -1.2s; + transform: rotate(0.0001deg) translate(146%); +} + +.loading-bar:nth-child(2) { + animation-delay: -1.1s; + transform: rotate(30deg) translate(146%); +} + +.loading-bar:nth-child(3) { + animation-delay: -1s; + transform: rotate(60deg) translate(146%); +} + +.loading-bar:nth-child(4) { + animation-delay: -0.9s; + transform: rotate(90deg) translate(146%); +} + +.loading-bar:nth-child(5) { + animation-delay: -0.8s; + transform: rotate(120deg) translate(146%); +} + +.loading-bar:nth-child(6) { + animation-delay: -0.7s; + transform: rotate(150deg) translate(146%); +} + +.loading-bar:nth-child(7) { + animation-delay: -0.6s; + transform: rotate(180deg) translate(146%); +} + +.loading-bar:nth-child(8) { + animation-delay: -0.5s; + transform: rotate(210deg) translate(146%); +} + +.loading-bar:nth-child(9) { + animation-delay: -0.4s; + transform: rotate(240deg) translate(146%); +} + +.loading-bar:nth-child(10) { + animation-delay: -0.3s; + transform: rotate(270deg) translate(146%); +} + +.loading-bar:nth-child(11) { + animation-delay: -0.2s; + transform: rotate(300deg) translate(146%); +} + +.loading-bar:nth-child(12) { + animation-delay: -0.1s; + transform: rotate(330deg) translate(146%); +} + +.v-snake { + --v-snake: 102%; + animation: v-snake 100ms ease-out normal; +} + +@keyframes v-snake { + 50% { + transform: scale(var(--v-snake, 102%)) + } +} \ No newline at end of file diff --git a/src/frontend/utils/align.ts b/src/frontend/utils/align.ts new file mode 100644 index 0000000..55e10ae --- /dev/null +++ b/src/frontend/utils/align.ts @@ -0,0 +1,105 @@ +import domAlign, {alignPoint} from "dom-align"; + +export type Placement = + | 'top-left' + | 'top-center' + | 'top-right' + | 'bottom-left' + | 'bottom-center' + | 'bottom-right' + | 'left-top' + | 'left-center' + | 'left-bottom' + | 'right-top' + | 'right-center' + | 'right-bottom' + +// position-align +// +// +// | top-left top-center top-right | +// -------------+------------------------------------------+-------------- +// left-top | | right-top +// | | +// left-center | | right-center +// | | +// left-bottom | | right-bottom +// -------------+------------------------------------------+-------------- +// | bottom-left bottom-center bottom-right | +// +export const positions: Record = { + // trigger => popup + 'top-left': 'bottom-left', + 'top-center': 'bottom-center', + 'top-right': 'bottom-right', + 'bottom-left': 'top-left', + 'bottom-center': 'top-center', + 'bottom-right': 'top-right', + 'left-top': 'right-top', + 'left-center': 'right-center', + 'left-bottom': 'right-bottom', + 'right-top': 'left-top', + 'right-center': 'left-center', + 'right-bottom': 'left-bottom', +} + +export function alignWithElement( + trigger: Element, + popup: HTMLElement, + placement: Placement, + offset: [number, number] +) { + let triggerPoints: string[] = placement.split('-') + let popupPoints: string[] = positions[placement].split('-') + switch (triggerPoints[0]) { + case 'left': + case 'right': + triggerPoints = triggerPoints.reverse() + popupPoints = popupPoints.reverse() + break + } + domAlign(popup, trigger, { + points: [ + popupPoints.map(c => c[0]).join(''), + triggerPoints.map(c => c[0]).join(''), + ], + offset: offset, + overflow: { + adjustX: true, + adjustY: true, + }, + useCssTransform: true, + ignoreShake: true, + }) +} + +export function alignWithPoint( + popup: HTMLElement, + clientX: number, + clientY: number, + placement: Placement, + offset: [number, number] +) { + let triggerPoints: string[] = placement.split('-') + let popupPoints: string[] = positions[placement].split('-') + switch (triggerPoints[0]) { + case 'left': + case 'right': + triggerPoints = triggerPoints.reverse() + popupPoints = popupPoints.reverse() + break + } + alignPoint(popup, {clientX, clientY}, { + points: [ + popupPoints.map(c => c[0]).join(''), + triggerPoints.map(c => c[0]).join(''), + ], + offset: offset, + overflow: { + adjustX: true, + adjustY: true, + }, + useCssTransform: true, + ignoreShake: true, + }) +} \ No newline at end of file diff --git a/src/frontend/utils/index.ts b/src/frontend/utils/index.ts new file mode 100644 index 0000000..d3b88d9 --- /dev/null +++ b/src/frontend/utils/index.ts @@ -0,0 +1,3 @@ +export * from './align' +export * from './preview' +export * from './scroll' \ No newline at end of file diff --git a/src/frontend/utils/preview.ts b/src/frontend/utils/preview.ts new file mode 100644 index 0000000..9bf7947 --- /dev/null +++ b/src/frontend/utils/preview.ts @@ -0,0 +1,106 @@ +import Hls from "hls.js" +import Viewer from 'viewerjs' +import viewerStyle from "viewerjs/dist/viewer.css?inline" + +const viewerStyleId = "wfs-viewer-style" + +let video: HTMLVideoElement | undefined + +interface PreviewOptions { + path: string + mime: string + thumb: string + name: string +} + +// https://juejin.cn/post/7146947008015089672 +const startVideoPreview = (path: string, img: HTMLImageElement): VoidFunction => { + video = document.createElement('video'); + video.controls = true + video.className = img.className + video.style.cssText = img.style.cssText + .replace("relative", "absolute") + .replace("margin-left", "left") + .replace("margin-top", "top") + + if (Hls.isSupported()) { + const hls = new Hls(); + hls.loadSource(path); + hls.attachMedia(video); + hls.on(Hls.Events.MANIFEST_PARSED, function () { + void video?.play(); + }); + } else if (video.canPlayType('application/vnd.apple.mpegurl')) { + video.src = 'https://video-dev.github.io/streams/x36xhzz/x36xhzz.m3u8'; + video.addEventListener('loadedmetadata', function () { + img.style.opacity = "0" + void video?.play(); + }); + } else { + alert("浏览器不支持该视频") + } + + img.parentNode!.appendChild(video) + + const onResize = () => { + if (video != null) { + video.className = img.className + video.style.cssText = img.style.cssText + .replace("relative", "absolute") + .replace("margin-left", "left") + .replace("margin-top", "top") + } + } + + onResize() + + window.addEventListener("resize", onResize) + + return () => { + video?.pause() + video?.remove() + video = undefined + window.removeEventListener("resize", onResize) + } +} + +export function showPreview(opts: PreviewOptions): void { + if (!document.querySelector(`#${viewerStyleId}`)) { + const style = document.createElement("style") + style.innerHTML = viewerStyle + style.id = viewerStyleId + document.head.appendChild(style) + } + + const isVideo = opts.mime.startsWith("video/") + const img = document.createElement("img") + img.src = isVideo ? opts.thumb : opts.path + let dispose: VoidFunction | undefined + const viewer = new Viewer(img, { + title: [4, (_: any, imageData: any) => `${opts.name} (${imageData.naturalWidth} × ${imageData.naturalHeight})`], + navbar: false, + zoomable: !isVideo, + movable: !isVideo, + backdrop: true, + slideOnTouch: !isVideo, + keyboard: !isVideo, + toolbar: isVideo ? {} : { + flipHorizontal: true, + flipVertical: true, + oneToOne: true, + reset: true, + rotateLeft: true, + rotateRight: true, + zoomIn: true, + zoomOut: true, + }, + viewed(event: CustomEvent) { + const el = event.detail.image as HTMLImageElement + isVideo && (dispose = startVideoPreview(opts.path, el)) + }, + hide() { + dispose?.() + }, + }) + viewer.show() +} diff --git a/src/frontend/utils/scroll.ts b/src/frontend/utils/scroll.ts new file mode 100644 index 0000000..beaa3ca --- /dev/null +++ b/src/frontend/utils/scroll.ts @@ -0,0 +1,58 @@ +export function getScrollParent(el?: HTMLElement, includeHidden = false) { + while (el) { + if (includeHidden ? isPotentiallyScrollable(el) : hasScrollbar(el)) return el + el = el.parentElement! + } + + return document.scrollingElement as HTMLElement +} + +export function getScrollParents(el?: Element | null, stopAt?: Element | null) { + const elements: HTMLElement[] = [] + + if (stopAt && el && !stopAt.contains(el)) return elements + + while (el) { + if (hasScrollbar(el)) elements.push(el as HTMLElement) + if (el === stopAt) break + el = el.parentElement! + } + + return elements +} + +export function hasScrollbar(el?: Element | null) { + if (!el || el.nodeType !== Node.ELEMENT_NODE) return false + + const style = window.getComputedStyle(el) + return style.overflowY === 'scroll' || (style.overflowY === 'auto' && el.scrollHeight > el.clientHeight) +} + +function isPotentiallyScrollable(el?: Element | null) { + if (!el || el.nodeType !== Node.ELEMENT_NODE) return false + + const style = window.getComputedStyle(el) + return ['scroll', 'auto'].includes(style.overflowY) +} + +export function getScrollBarWidth() { + const outer = document.createElement('div'); + outer.style.overflow = 'scroll'; + outer.style.height = '200px'; + outer.style.width = '100px'; + outer.style.position = 'fixed' + outer.style.opacity = '0' + outer.style.pointerEvents = 'none' + + document.body.appendChild(outer); + const inner = document.createElement('div'); + inner.style.width = '100%'; + outer.appendChild(inner); + + const widthNoScroll = outer.offsetWidth; + const widthWithScroll = inner.offsetWidth; + + outer.remove() + + return widthNoScroll - widthWithScroll; +} diff --git a/src/frontend/widgets/UiBarrier.vue b/src/frontend/widgets/UiBarrier.vue new file mode 100644 index 0000000..085169b --- /dev/null +++ b/src/frontend/widgets/UiBarrier.vue @@ -0,0 +1,63 @@ + + + diff --git a/src/frontend/widgets/UiContainer.vue b/src/frontend/widgets/UiContainer.vue new file mode 100644 index 0000000..801bbcd --- /dev/null +++ b/src/frontend/widgets/UiContainer.vue @@ -0,0 +1,103 @@ + + + diff --git a/src/frontend/widgets/UiContextMenu.vue b/src/frontend/widgets/UiContextMenu.vue new file mode 100644 index 0000000..30c2a25 --- /dev/null +++ b/src/frontend/widgets/UiContextMenu.vue @@ -0,0 +1,275 @@ + + + diff --git a/src/frontend/widgets/UiContextMenuController.ts b/src/frontend/widgets/UiContextMenuController.ts new file mode 100644 index 0000000..631498d --- /dev/null +++ b/src/frontend/widgets/UiContextMenuController.ts @@ -0,0 +1,173 @@ +import {copyText} from '../../shared' +import {computed, nextTick, reactive} from "vue"; +import {deleteFile, store} from "../store"; +import {dispatch} from "../worker.ts"; +import {showDirectoryNaming} from "./UiDirectoryNamingController"; +import {toast} from "./UiToastController"; + +export type ContextmenuTarget = + | "directory" // 作用在侧边栏文件夹按钮上 + | "file" // 作用在右侧文件图标上 + | "filename" // 作用在右侧文件标题图标上 + | "sidebar" // 作用在侧边栏上 + | "content"; // 作用在右侧文件容器上 + +export interface ContextMenuOptions { + x: number; + y: number; + directoryId?: number; + fileId?: number; + target?: ContextmenuTarget; +} + +interface ContextMenuState extends ContextMenuOptions { + visible: boolean; +} + +export const controller = reactive({ + x: 0, + y: 0, + directoryId: 0, + fileId: undefined, + visible: false, +}); + +export const file = computed((): CloudFile | undefined => { + return controller.fileId + ? store.files[controller.directoryId ?? 0]?.find(f => f.id === controller.fileId) + : undefined +}) + +export const directory = computed(() => { + const dirId = controller.directoryId + return dirId == 0 + ? store.rootDirectory + : dirId != null + ? store.directories.get(dirId) + : undefined +}) + +export function isTarget(...targets: ContextmenuTarget[]): boolean { + return controller.target != null && targets.includes(controller.target); +} + +export function showContextMenu(opts: ContextMenuOptions): void { + if (opts.target && opts.directoryId != null) { + controller.visible = true; + void nextTick(() => { + controller.x = opts.x; + controller.y = opts.y; + controller.fileId = opts.fileId; + controller.directoryId = opts.directoryId; + controller.target = opts.target; + }); + } else { + hideContextMenu(); + } +} + +export function hideContextMenu() { + if (controller.visible) { + controller.visible = false; + void nextTick(() => { + if (!controller.visible) { + controller.directoryId = undefined; + controller.fileId = undefined; + controller.target = undefined; + } + }); + } +} + +export function toggleContextMenu(evt: MouseEvent): void { + const elm = evt.target as HTMLElement; + if (elm.closest('[data-role="contextmenu"]')) { + return; + } + const dataset = elm.closest("[data-contextmenu]")?.dataset; + if (dataset && dataset.target != null && dataset.directoryId != null) { + showContextMenu({ + x: evt.clientX, + y: evt.clientY, + fileId: dataset.fileId ? Number(dataset.fileId) : undefined, + directoryId: Number(dataset.directoryId), + target: dataset.target as ContextmenuTarget, + }); + } else { + hideContextMenu(); + } +} + +export function handleNewDir(child?: boolean) { + showDirectoryNaming({ + action: "create", + pid: child ? controller.directoryId : undefined, + }); +} + +// 重命名文件或文件夹 +export function renameDir() { + const {directoryId} = controller + if (isTarget("directory") && directoryId) { + showDirectoryNaming({ + id: directoryId, + action: "rename", + }); + } +} + +export function copyDirName() { + const text = directory.value?.title + text?.length && copyText(text) +} + +export function copyFileName() { + const text = file.value?.name + text?.length && copyText(text) +} + +export function copyFilePath() { + const text = file.value?.object.path + text?.length && copyText(text) +} + +export function removeDir() { + const {directoryId} = controller + if (isTarget("directory") && directoryId) { + dispatch("dir", "delete", directoryId) + .then(() => store.directories.delete(directoryId)) + .then(() => toast("success", "删除成功")) + .catch((error) => toast("error", error)); + } +} + +export function removeFile() { + const f = file.value; + if (isTarget("file", "filename") && f) { + dispatch("file", "delete", f.id) + .then(() => deleteFile(f)) + .then(() => toast("success", "删除成功")) + .catch((error) => toast("error", error)); + } +} + +export function canShare() { + return typeof navigator.canShare === 'function' && navigator.canShare({ + title: "MDN", + text: "Learn web development on MDN!", + url: "https://developer.mozilla.org", + }) +} + +export function share() { + if (file.value) { + navigator + .share({ + title: file.value!.name, + text: file.value!.object.path, + url: file.value!.object.path, + }) + .then(() => toast("success", "分享成功")) + .catch((error) => toast("error", error)); + } +} diff --git a/src/frontend/widgets/UiDialog.vue b/src/frontend/widgets/UiDialog.vue new file mode 100644 index 0000000..2c00646 --- /dev/null +++ b/src/frontend/widgets/UiDialog.vue @@ -0,0 +1,159 @@ + + + \ No newline at end of file diff --git a/src/frontend/widgets/UiDirectoryButton.vue b/src/frontend/widgets/UiDirectoryButton.vue new file mode 100644 index 0000000..2259b34 --- /dev/null +++ b/src/frontend/widgets/UiDirectoryButton.vue @@ -0,0 +1,85 @@ + + + diff --git a/src/frontend/widgets/UiDirectoryNaming.vue b/src/frontend/widgets/UiDirectoryNaming.vue new file mode 100644 index 0000000..7ecd43f --- /dev/null +++ b/src/frontend/widgets/UiDirectoryNaming.vue @@ -0,0 +1,199 @@ + + + diff --git a/src/frontend/widgets/UiDirectoryNamingController.ts b/src/frontend/widgets/UiDirectoryNamingController.ts new file mode 100644 index 0000000..41175f8 --- /dev/null +++ b/src/frontend/widgets/UiDirectoryNamingController.ts @@ -0,0 +1,29 @@ +import { reactive } from 'vue' + +interface DirectoryNamingOptions { + pid?: number + id?: number + action: 'create' | 'rename' +} + +interface DirectoryNamingState extends DirectoryNamingOptions { + visible: boolean +} + +export const controller = reactive({ + visible: false, + pid: undefined, + id: undefined, + action: 'create', +}) + +export function hideDirectoryNaming() { + controller.visible = false +} + +export function showDirectoryNaming(opts: DirectoryNamingOptions): void { + controller.visible = true + controller.action = opts.action + controller.pid = opts.pid + controller.id = opts.id +} diff --git a/src/frontend/widgets/UiDirectoryTree.vue b/src/frontend/widgets/UiDirectoryTree.vue new file mode 100644 index 0000000..56da9c2 --- /dev/null +++ b/src/frontend/widgets/UiDirectoryTree.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/frontend/widgets/UiDirectoryTreeItem.vue b/src/frontend/widgets/UiDirectoryTreeItem.vue new file mode 100644 index 0000000..151af5c --- /dev/null +++ b/src/frontend/widgets/UiDirectoryTreeItem.vue @@ -0,0 +1,22 @@ + + + diff --git a/src/frontend/widgets/UiDropFeedback.vue b/src/frontend/widgets/UiDropFeedback.vue new file mode 100644 index 0000000..5b5eca1 --- /dev/null +++ b/src/frontend/widgets/UiDropFeedback.vue @@ -0,0 +1,69 @@ + + + diff --git a/src/frontend/widgets/UiFile.vue b/src/frontend/widgets/UiFile.vue new file mode 100644 index 0000000..9cb560a --- /dev/null +++ b/src/frontend/widgets/UiFile.vue @@ -0,0 +1,101 @@ + + + diff --git a/src/frontend/widgets/UiFileNaming.vue b/src/frontend/widgets/UiFileNaming.vue new file mode 100644 index 0000000..c01252d --- /dev/null +++ b/src/frontend/widgets/UiFileNaming.vue @@ -0,0 +1,157 @@ + + + diff --git a/src/frontend/widgets/UiFileNamingController.ts b/src/frontend/widgets/UiFileNamingController.ts new file mode 100644 index 0000000..202d166 --- /dev/null +++ b/src/frontend/widgets/UiFileNamingController.ts @@ -0,0 +1,25 @@ +import { reactive } from 'vue' + +interface FileNamingOptions { + dirId?: number + id?: number +} + +interface FileNamingState extends FileNamingOptions { + visible: boolean +} + +export const controller = reactive({ + visible: false, + dirId: undefined, + id: undefined, +}) + +export function hideFileNaming(): void { + controller.visible = false +} + +export function showFileNaming(opts: Required): void { + Object.assign(controller, opts) + controller.visible = true +} diff --git a/src/frontend/widgets/UiFileThumbnail.vue b/src/frontend/widgets/UiFileThumbnail.vue new file mode 100644 index 0000000..dd8e61d --- /dev/null +++ b/src/frontend/widgets/UiFileThumbnail.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/frontend/widgets/UiFiles.vue b/src/frontend/widgets/UiFiles.vue new file mode 100644 index 0000000..9295f17 --- /dev/null +++ b/src/frontend/widgets/UiFiles.vue @@ -0,0 +1,83 @@ + + + diff --git a/src/frontend/widgets/UiMimeSelect.vue b/src/frontend/widgets/UiMimeSelect.vue new file mode 100644 index 0000000..1f58329 --- /dev/null +++ b/src/frontend/widgets/UiMimeSelect.vue @@ -0,0 +1,64 @@ + + + + diff --git a/src/frontend/widgets/UiMistake.vue b/src/frontend/widgets/UiMistake.vue new file mode 100644 index 0000000..39eef83 --- /dev/null +++ b/src/frontend/widgets/UiMistake.vue @@ -0,0 +1,30 @@ + + + diff --git a/src/frontend/widgets/UiModeTools.vue b/src/frontend/widgets/UiModeTools.vue new file mode 100644 index 0000000..fcb6d3a --- /dev/null +++ b/src/frontend/widgets/UiModeTools.vue @@ -0,0 +1,65 @@ + + + diff --git a/src/frontend/widgets/UiNavbar.vue b/src/frontend/widgets/UiNavbar.vue new file mode 100644 index 0000000..19b5de3 --- /dev/null +++ b/src/frontend/widgets/UiNavbar.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/frontend/widgets/UiNoData.vue b/src/frontend/widgets/UiNoData.vue new file mode 100644 index 0000000..703d37f --- /dev/null +++ b/src/frontend/widgets/UiNoData.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/frontend/widgets/UiPaginateEllipsis.vue b/src/frontend/widgets/UiPaginateEllipsis.vue new file mode 100644 index 0000000..4444ff4 --- /dev/null +++ b/src/frontend/widgets/UiPaginateEllipsis.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/frontend/widgets/UiPaginateItem.vue b/src/frontend/widgets/UiPaginateItem.vue new file mode 100644 index 0000000..5391373 --- /dev/null +++ b/src/frontend/widgets/UiPaginateItem.vue @@ -0,0 +1,32 @@ + + + + diff --git a/src/frontend/widgets/UiPagination.vue b/src/frontend/widgets/UiPagination.vue new file mode 100644 index 0000000..609e5be --- /dev/null +++ b/src/frontend/widgets/UiPagination.vue @@ -0,0 +1,83 @@ + + + diff --git a/src/frontend/widgets/UiSidebar.vue b/src/frontend/widgets/UiSidebar.vue new file mode 100644 index 0000000..3d6dbd4 --- /dev/null +++ b/src/frontend/widgets/UiSidebar.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/frontend/widgets/UiSplitter.vue b/src/frontend/widgets/UiSplitter.vue new file mode 100644 index 0000000..d4a2c1b --- /dev/null +++ b/src/frontend/widgets/UiSplitter.vue @@ -0,0 +1,63 @@ + + + \ No newline at end of file diff --git a/src/frontend/widgets/UiTask.vue b/src/frontend/widgets/UiTask.vue new file mode 100644 index 0000000..30fd942 --- /dev/null +++ b/src/frontend/widgets/UiTask.vue @@ -0,0 +1,134 @@ + + + \ No newline at end of file diff --git a/src/frontend/widgets/UiTaskAction.vue b/src/frontend/widgets/UiTaskAction.vue new file mode 100644 index 0000000..29fc991 --- /dev/null +++ b/src/frontend/widgets/UiTaskAction.vue @@ -0,0 +1,25 @@ + + + diff --git a/src/frontend/widgets/UiTasks.vue b/src/frontend/widgets/UiTasks.vue new file mode 100644 index 0000000..1946827 --- /dev/null +++ b/src/frontend/widgets/UiTasks.vue @@ -0,0 +1,82 @@ + + + diff --git a/src/frontend/widgets/UiThemeSelect.vue b/src/frontend/widgets/UiThemeSelect.vue new file mode 100644 index 0000000..65dafdd --- /dev/null +++ b/src/frontend/widgets/UiThemeSelect.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/frontend/widgets/UiToast.vue b/src/frontend/widgets/UiToast.vue new file mode 100644 index 0000000..a51f000 --- /dev/null +++ b/src/frontend/widgets/UiToast.vue @@ -0,0 +1,85 @@ + + + diff --git a/src/frontend/widgets/UiToastController.ts b/src/frontend/widgets/UiToastController.ts new file mode 100644 index 0000000..377381e --- /dev/null +++ b/src/frontend/widgets/UiToastController.ts @@ -0,0 +1,24 @@ +import {coerce} from "../../shared"; +import {ref} from "vue"; + +export type ToastType = "success" | "error" | "warn" | "info"; + +export interface Toast { + key: number; + type: ToastType; + message: string; + duration: number +} + +export const items = ref>([]); + +let nextKey = 0; + +export function toast(type: ToastType, message: any, duration: number = 3000): void { + items.value.unshift({ + key: ++nextKey, + type, + message: String(coerce(message)), + duration + }); +} diff --git a/src/frontend/widgets/UiToaster.vue b/src/frontend/widgets/UiToaster.vue new file mode 100644 index 0000000..cbd166f --- /dev/null +++ b/src/frontend/widgets/UiToaster.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/frontend/widgets/VBadge.vue b/src/frontend/widgets/VBadge.vue new file mode 100644 index 0000000..cf4be54 --- /dev/null +++ b/src/frontend/widgets/VBadge.vue @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/src/frontend/widgets/VButton.vue b/src/frontend/widgets/VButton.vue new file mode 100644 index 0000000..8855c25 --- /dev/null +++ b/src/frontend/widgets/VButton.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/frontend/widgets/VDropdown.vue b/src/frontend/widgets/VDropdown.vue new file mode 100644 index 0000000..3de25dd --- /dev/null +++ b/src/frontend/widgets/VDropdown.vue @@ -0,0 +1,124 @@ + + + diff --git a/src/frontend/widgets/VIcon.vue b/src/frontend/widgets/VIcon.vue new file mode 100644 index 0000000..bedf361 --- /dev/null +++ b/src/frontend/widgets/VIcon.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/src/frontend/widgets/VIconButton.vue b/src/frontend/widgets/VIconButton.vue new file mode 100644 index 0000000..3460743 --- /dev/null +++ b/src/frontend/widgets/VIconButton.vue @@ -0,0 +1,38 @@ + + + diff --git a/src/frontend/widgets/VIcons.ts b/src/frontend/widgets/VIcons.ts new file mode 100644 index 0000000..0ca85e1 --- /dev/null +++ b/src/frontend/widgets/VIcons.ts @@ -0,0 +1,516 @@ +export const paths = { + 'Folder': 'M7.167 3c.27 0 .535.073.765.21l.135.09l1.6 1.2H15.5a2.5 2.5 0 0 1 2.479 2.174l.016.162L18 7v7.5a2.5 2.5 0 0 1-2.336 2.495L15.5 17h-11a2.5 2.5 0 0 1-2.495-2.336L2 14.5v-9a2.5 2.5 0 0 1 2.336-2.495L4.5 3h2.667zm.99 4.034a1.5 1.5 0 0 1-.933.458l-.153.008L3 7.499V14.5a1.5 1.5 0 0 0 1.356 1.493L4.5 16h11a1.5 1.5 0 0 0 1.493-1.355L17 14.5V7a1.5 1.5 0 0 0-1.355-1.493L15.5 5.5H9.617l-1.46 1.534zM7.168 4H4.5a1.5 1.5 0 0 0-1.493 1.356L3 5.5v.999l4.071.001a.5.5 0 0 0 .302-.101l.06-.054L8.694 5.02L7.467 4.1a.5.5 0 0 0-.22-.093L7.167 4z', + 'FolderSolid': 'M10.565 4.5H15.5a2.5 2.5 0 0 1 2.479 2.174l.016.162L18 7v7.5a2.5 2.5 0 0 1-2.336 2.495L15.5 17h-11a2.5 2.5 0 0 1-2.495-2.336L2 14.5v-7h5.07l.154-.008a1.5 1.5 0 0 0 .823-.353l.111-.106L10.565 4.5zM7.167 3c.27 0 .535.073.765.21l.135.09l1.318.989l-1.952 2.055l-.06.055a.5.5 0 0 1-.221.094l-.081.007H2v-1a2.5 2.5 0 0 1 2.336-2.495L4.5 3h2.667z', + 'FolderAdd': 'M4.5 3A2.5 2.5 0 0 0 2 5.5v9A2.5 2.5 0 0 0 4.5 17h5.1a5.465 5.465 0 0 1-.393-1H4.5A1.5 1.5 0 0 1 3 14.5v-7h4.071a1.5 1.5 0 0 0 1.087-.466L9.619 5.5H15.5A1.5 1.5 0 0 1 17 7v2.6c.358.183.693.404 1 .657V7a2.5 2.5 0 0 0-2.5-2.5H9.667l-1.6-1.2a1.5 1.5 0 0 0-.9-.3H4.5zM3 5.5A1.5 1.5 0 0 1 4.5 4h2.667a.5.5 0 0 1 .3.1l1.227.92l-1.26 1.325a.5.5 0 0 1-.363.155H3v-1zm16 9a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4-2a.5.5 0 0 0-1 0V14h-1.5a.5.5 0 0 0 0 1H14v1.5a.5.5 0 0 0 1 0V15h1.5a.5.5 0 0 0 0-1H15v-1.5z', + 'FolderAddSolid': 'M9.386 4.29l-1.32-.99a1.5 1.5 0 0 0-.9-.3H4.5A2.5 2.5 0 0 0 2 5.5v1h5.07a.5.5 0 0 0 .363-.156L9.386 4.29zm1.179.21L8.158 7.033a1.5 1.5 0 0 1-1.087.467H2v7A2.5 2.5 0 0 0 4.5 17h5.1a5.5 5.5 0 0 1 8.4-6.743V7l-.005-.164A2.5 2.5 0 0 0 15.5 4.5h-4.935zM19 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4-2a.5.5 0 0 0-1 0V14h-1.5a.5.5 0 0 0 0 1H14v1.5a.5.5 0 0 0 1 0V15h1.5a.5.5 0 0 0 0-1H15v-1.5z', + 'FolderArrowRight': 'M4.5 3A2.5 2.5 0 0 0 2 5.5v9A2.5 2.5 0 0 0 4.5 17h5.1a5.465 5.465 0 0 1-.393-1H4.5A1.5 1.5 0 0 1 3 14.5v-7h4.071a1.5 1.5 0 0 0 1.087-.466L9.619 5.5H15.5A1.5 1.5 0 0 1 17 7v2.6c.358.183.693.404 1 .657V7a2.5 2.5 0 0 0-2.5-2.5H9.667l-1.6-1.2a1.5 1.5 0 0 0-.9-.3H4.5zM3 5.5A1.5 1.5 0 0 1 4.5 4h2.667a.5.5 0 0 1 .3.1l1.227.92l-1.26 1.325a.5.5 0 0 1-.363.155H3v-1zM14.5 10a4.5 4.5 0 1 1 0 9a4.5 4.5 0 0 1 0-9zm2.353 4.854l.003-.003a.499.499 0 0 0 .144-.348v-.006a.5.5 0 0 0-.146-.35l-2-2a.5.5 0 0 0-.708.707L15.293 14H12.5a.5.5 0 0 0 0 1h2.793l-1.147 1.146a.5.5 0 0 0 .708.708l2-2z', + 'FolderArrowRightSolid': 'M9.386 4.29l-1.32-.99a1.5 1.5 0 0 0-.9-.3H4.5A2.5 2.5 0 0 0 2 5.5v1h5.07a.5.5 0 0 0 .363-.156L9.386 4.29zm1.179.21L8.158 7.033a1.5 1.5 0 0 1-1.087.467H2v7A2.5 2.5 0 0 0 4.5 17h5.1a5.5 5.5 0 0 1 8.4-6.743V7l-.005-.164A2.5 2.5 0 0 0 15.5 4.5h-4.935zM14.5 10a4.5 4.5 0 1 1 0 9a4.5 4.5 0 0 1 0-9zm2.353 4.854l.003-.003a.499.499 0 0 0 .144-.348v-.006a.5.5 0 0 0-.146-.35l-2-2a.5.5 0 0 0-.708.707L15.293 14H12.5a.5.5 0 0 0 0 1h2.793l-1.147 1.146a.5.5 0 0 0 .708.708l2-2z', + 'FolderArrowUp': 'M4.5 3h2.667c.324 0 .64.105.9.3l1.6 1.2H15.5A2.5 2.5 0 0 1 18 7v3.257a5.503 5.503 0 0 0-1-.657V7a1.5 1.5 0 0 0-1.5-1.5H9.62L8.157 7.034A1.5 1.5 0 0 1 7.07 7.5H3v7A1.5 1.5 0 0 0 4.5 16h4.707c.099.349.23.683.393 1H4.5A2.5 2.5 0 0 1 2 14.5v-9A2.5 2.5 0 0 1 4.5 3zM3 5.5v1h4.071a.5.5 0 0 0 .363-.155l1.26-1.324L7.467 4.1a.5.5 0 0 0-.3-.1H4.5A1.5 1.5 0 0 0 3 5.5zm16 9a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4.146-2.353l-.003-.003a.497.497 0 0 0-.348-.144h-.006a.498.498 0 0 0-.35.146l-2 2a.5.5 0 0 0 .707.708L14 13.707V16.5a.5.5 0 1 0 1 0v-2.793l1.146 1.147a.5.5 0 1 0 .707-.707l-2-2z', + 'FolderArrowUpSolid': 'M8.067 3.3l1.319.99l-1.953 2.054a.5.5 0 0 1-.362.156H2v-1A2.5 2.5 0 0 1 4.5 3h2.667c.324 0 .64.105.9.3zm.091 3.733L10.565 4.5H15.5a2.5 2.5 0 0 1 2.495 2.336L18 7v3.257A5.5 5.5 0 0 0 9.6 17H4.5A2.5 2.5 0 0 1 2 14.5v-7h5.07a1.5 1.5 0 0 0 1.088-.467zM19 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4.146-2.353l-.003-.003a.497.497 0 0 0-.348-.144h-.006a.498.498 0 0 0-.35.146l-2 2a.5.5 0 0 0 .707.708L14 13.707V16.5a.5.5 0 1 0 1 0v-2.793l1.146 1.147a.5.5 0 1 0 .707-.707l-2-2z', + 'FolderOpen': 'M16.996 7.073V7a2.5 2.5 0 0 0-2.5-2.5H9.664l-1.6-1.2a1.5 1.5 0 0 0-.9-.3H4.5A2.5 2.5 0 0 0 2 5.5l.001 8.998a2.5 2.5 0 0 0 2.201 2.482c.085.014.172.022.26.022H15.18a1.5 1.5 0 0 0 1.472-1.214l1.358-7a1.501 1.501 0 0 0-1.014-1.715zM4.5 4h2.664a.5.5 0 0 1 .3.1l1.734 1.3a.5.5 0 0 0 .3.1h4.998a1.5 1.5 0 0 1 1.5 1.5v.002H5.824a1.5 1.5 0 0 0-1.472 1.214l-1.298 6.676A1.502 1.502 0 0 1 3 14.498L3 5.5A1.5 1.5 0 0 1 4.5 4zm.833 4.407a.5.5 0 0 1 .491-.405h10.713a.5.5 0 0 1 .491.595l-1.357 7a.5.5 0 0 1-.491.405H4.463a.5.5 0 0 1-.49-.595l1.36-7z', + 'FolderOpenSolid': 'M2 5.5A2.5 2.5 0 0 1 4.5 3h2.664c.325 0 .64.105.9.3l1.6 1.2h4.832a2.5 2.5 0 0 1 2.5 2.5v.002H5.824A1.5 1.5 0 0 0 4.35 8.215l-1.577 8.09a2.493 2.493 0 0 1-.773-1.807L2 5.5zm1.773 10.907a.5.5 0 0 0 .491.595H15.18a1.5 1.5 0 0 0 1.472-1.214l1.395-7.19a.5.5 0 0 0-.491-.596H5.824a.5.5 0 0 0-.491.404l-1.56 8z', + 'FolderOpenVertical': 'M4 3.5A1.5 1.5 0 0 1 5.5 2h9A1.5 1.5 0 0 1 16 3.5v3.877c0 .123-.015.245-.045.364L15 11.56V14.5a1.5 1.5 0 0 1-1.5 1.5H12v.742a1.5 1.5 0 0 1-2.04 1.4L5.6 16.46A2.5 2.5 0 0 1 4 14.128V3.5zM7.186 3L10.4 4.24A2.5 2.5 0 0 1 12 6.572V15h1.5a.5.5 0 0 0 .5-.5v-3c0-.04.005-.082.015-.121l.97-3.88A.5.5 0 0 0 15 7.376V3.5a.5.5 0 0 0-.5-.5H7.186zM5 3.958v10.17a1.5 1.5 0 0 0 .96 1.4l4.36 1.681a.5.5 0 0 0 .68-.466V6.572a1.5 1.5 0 0 0-.96-1.4L5.68 3.49a.5.5 0 0 0-.68.467z', + 'FolderOpenVerticalSolid': 'M4.137 2.873C4.049 3.063 4 3.276 4 3.5v10.628a2.5 2.5 0 0 0 1.6 2.332l4.36 1.682c.355.137.72.13 1.04.015V6.568a1.5 1.5 0 0 0-.956-1.398L4.137 2.873zm.797-.763l5.472 2.128A2.5 2.5 0 0 1 12 6.568V16h1.5a1.5 1.5 0 0 0 1.5-1.5v-2.938l.955-3.821c.03-.12.045-.241.045-.364V3.5A1.5 1.5 0 0 0 14.5 2h-9c-.2 0-.391.04-.566.11z', + 'FolderProhibited': 'M2 5.5A2.5 2.5 0 0 1 4.5 3h2.667c.324 0 .64.105.9.3l1.6 1.2H15.5A2.5 2.5 0 0 1 18 7v3.257a5.503 5.503 0 0 0-1-.657V7a1.5 1.5 0 0 0-1.5-1.5H9.62L8.157 7.034A1.5 1.5 0 0 1 7.07 7.5H3v7A1.5 1.5 0 0 0 4.5 16h4.707c.099.349.23.683.393 1H4.5A2.5 2.5 0 0 1 2 14.5v-9zM4.5 4A1.5 1.5 0 0 0 3 5.5v1h4.071a.5.5 0 0 0 .363-.155l1.26-1.324L7.467 4.1a.5.5 0 0 0-.3-.1H4.5zM10 14.5a4.5 4.5 0 1 1 9 0a4.5 4.5 0 0 1-9 0zm4.5-3.5a3.5 3.5 0 0 0-2.803 5.596l4.9-4.9A3.484 3.484 0 0 0 14.5 11zm2.803 1.404l-4.9 4.9a3.5 3.5 0 0 0 4.9-4.9z', + 'FolderProhibitedSolid': 'M9.386 4.29l-1.32-.99a1.5 1.5 0 0 0-.9-.3H4.5A2.5 2.5 0 0 0 2 5.5v1h5.07a.5.5 0 0 0 .363-.156L9.386 4.29zm1.179.21L8.158 7.033a1.5 1.5 0 0 1-1.087.467H2v7A2.5 2.5 0 0 0 4.5 17h5.1a5.5 5.5 0 0 1 8.4-6.743V7l-.005-.164A2.5 2.5 0 0 0 15.5 4.5h-4.935zM10 14.5a4.5 4.5 0 1 1 9 0a4.5 4.5 0 0 1-9 0zm4.5-3.5a3.5 3.5 0 0 0-2.803 5.596l4.9-4.9A3.484 3.484 0 0 0 14.5 11zm0 7a3.5 3.5 0 0 0 2.803-5.596l-4.9 4.9c.585.437 1.31.696 2.097.696z', + 'FolderSwap': 'M7.932 3.21A1.5 1.5 0 0 0 7.167 3H4.5l-.164.005A2.5 2.5 0 0 0 2 5.5v9l.005.164A2.5 2.5 0 0 0 4.5 17h4.377l-.436-.434A1.5 1.5 0 0 1 8.085 16H4.5l-.144-.007A1.5 1.5 0 0 1 3 14.5V7.499l4.071.001l.153-.008a1.5 1.5 0 0 0 .934-.458L9.617 5.5H15.5l.145.007A1.5 1.5 0 0 1 17 7v5.883l1 1V7l-.005-.164l-.016-.162A2.5 2.5 0 0 0 15.5 4.5H9.667l-1.6-1.2l-.135-.09zM4.5 4h2.667l.08.006a.5.5 0 0 1 .22.094l1.227.921l-1.26 1.324l-.061.054a.5.5 0 0 1-.302.101L3 6.499V5.5l.007-.144A1.5 1.5 0 0 1 4.5 4zm7.356 9.859a.5.5 0 0 0-.706-.708l-2.003 1.998a.5.5 0 0 0 0 .708l2.003 1.997a.5.5 0 1 0 .706-.708l-1.147-1.144h5.584l-1.146 1.144a.5.5 0 1 0 .706.708l2-1.996a.5.5 0 0 0 0-.708l-2-1.999a.5.5 0 1 0-.707.707l1.145 1.144H10.71l1.146-1.143z', + 'FolderSwapSolid': 'M10.565 4.5H15.5a2.5 2.5 0 0 1 2.479 2.174l.016.162L18 7v6.883l-1.44-1.44a1.5 1.5 0 0 0-2.475 1.56h-1.166a1.5 1.5 0 0 0-2.475-1.56L8.44 14.44a1.5 1.5 0 0 0 0 2.125l.436.434H4.5a2.5 2.5 0 0 1-2.495-2.336L2 14.5v-7h5.07l.154-.008a1.5 1.5 0 0 0 .823-.353l.111-.106L10.565 4.5zM7.167 3c.27 0 .535.073.765.21l.135.09l1.318.989l-1.952 2.055l-.06.055a.5.5 0 0 1-.221.094l-.081.007H2v-1a2.5 2.5 0 0 1 2.336-2.495L4.5 3h2.667zm4.69 10.859a.5.5 0 0 0-.707-.708l-2.003 1.998a.5.5 0 0 0 0 .708l2.003 1.997a.5.5 0 1 0 .706-.708l-1.147-1.144h5.584l-1.146 1.144a.5.5 0 1 0 .706.708l2-1.996a.5.5 0 0 0 0-.708l-2-1.999a.5.5 0 1 0-.707.707l1.145 1.144H10.71l1.146-1.143z', + 'FolderSync': 'M4.5 3A2.5 2.5 0 0 0 2 5.5v9A2.5 2.5 0 0 0 4.5 17h5.1a5.465 5.465 0 0 1-.393-1H4.5A1.5 1.5 0 0 1 3 14.5v-7h4.071a1.5 1.5 0 0 0 1.087-.466L9.619 5.5H15.5A1.5 1.5 0 0 1 17 7v2.6c.358.183.693.404 1 .657V7a2.5 2.5 0 0 0-2.5-2.5H9.667l-1.6-1.2a1.5 1.5 0 0 0-.9-.3H4.5zM3 5.5A1.5 1.5 0 0 1 4.5 4h2.667a.5.5 0 0 1 .3.1l1.227.92l-1.26 1.325a.5.5 0 0 1-.363.155H3v-1zM14.5 19a4.5 4.5 0 1 1 0-9a4.5 4.5 0 0 1 0 9zm1.5-7v.152a3.011 3.011 0 0 0-1.448-.401a2.999 2.999 0 0 0-2.173.878a.5.5 0 0 0 .707.707A2 2 0 0 1 15.468 13H15a.5.5 0 0 0 0 1h1.5a.5.5 0 0 0 .5-.5V12a.5.5 0 0 0-1 0zm-1.552 5.25a2.999 2.999 0 0 0 2.173-.879a.5.5 0 0 0-.707-.707a2 2 0 0 1-2.382.336H14a.5.5 0 0 0 0-1h-1.5a.5.5 0 0 0-.5.5V17a.5.5 0 0 0 1 0v-.152a3.011 3.011 0 0 0 1.448.402z', + 'FolderSyncSolid': 'M9.386 4.29l-1.32-.99a1.5 1.5 0 0 0-.9-.3H4.5A2.5 2.5 0 0 0 2 5.5v1h5.07a.5.5 0 0 0 .363-.156L9.386 4.29zm1.179.21L8.158 7.033a1.5 1.5 0 0 1-1.087.467H2v7A2.5 2.5 0 0 0 4.5 17h5.1a5.5 5.5 0 0 1 8.4-6.743V7l-.005-.164A2.5 2.5 0 0 0 15.5 4.5h-4.935zM14.5 19a4.5 4.5 0 1 1 0-9a4.5 4.5 0 0 1 0 9zm1.5-7v.152a3.011 3.011 0 0 0-1.448-.401a2.999 2.999 0 0 0-2.173.878a.5.5 0 0 0 .707.707A2 2 0 0 1 15.468 13H15a.5.5 0 0 0 0 1h1.5a.5.5 0 0 0 .5-.5V12a.5.5 0 0 0-1 0zm-1.552 5.25a2.999 2.999 0 0 0 2.173-.879a.5.5 0 0 0-.707-.707a2 2 0 0 1-2.382.336H14a.5.5 0 0 0 0-1h-1.5a.5.5 0 0 0-.5.5V17a.5.5 0 0 0 1 0v-.152a3.011 3.011 0 0 0 1.448.402z', + 'FolderZip': 'M4.5 3A2.5 2.5 0 0 0 2 5.5v9A2.5 2.5 0 0 0 4.5 17h5.1a5.465 5.465 0 0 1-.393-1H4.5A1.5 1.5 0 0 1 3 14.5v-7h4.071a1.5 1.5 0 0 0 1.087-.466L9.619 5.5H15.5A1.5 1.5 0 0 1 17 7v2.6c.358.183.693.404 1 .657V7a2.5 2.5 0 0 0-2.5-2.5H9.667l-1.6-1.2a1.5 1.5 0 0 0-.9-.3H4.5zM3 5.5A1.5 1.5 0 0 1 4.5 4h2.667a.5.5 0 0 1 .3.1l1.227.92l-1.26 1.325a.5.5 0 0 1-.363.155H3v-1zM14.5 19a4.5 4.5 0 1 1 0-9a4.5 4.5 0 0 1 0 9zm1.5-7v.152a3.011 3.011 0 0 0-1.448-.401a2.999 2.999 0 0 0-2.173.878a.5.5 0 0 0 .707.707A2 2 0 0 1 15.468 13H15a.5.5 0 0 0 0 1h1.5a.5.5 0 0 0 .5-.5V12a.5.5 0 0 0-1 0zm-1.552 5.25a2.999 2.999 0 0 0 2.173-.879a.5.5 0 0 0-.707-.707a2 2 0 0 1-2.382.336H14a.5.5 0 0 0 0-1h-1.5a.5.5 0 0 0-.5.5V17a.5.5 0 0 0 1 0v-.152a3.011 3.011 0 0 0 1.448.402z', + 'FolderZipSolid': 'M12.005 4.5h-1.44L8.158 7.033l-.111.106a1.5 1.5 0 0 1-.823.353L7.07 7.5H2v7l.005.164A2.5 2.5 0 0 0 4.5 17h8.504v-1.941a.515.515 0 0 1 0-.117L13.002 14h-.498a.5.5 0 0 1 0-1h.497v-2H12.5a.5.5 0 0 1 0-1h.5V9h-.495a.5.5 0 0 1-.5-.5v-4zm2 0h-1V8h1V4.5zm1 0h.495a2.5 2.5 0 0 1 2.479 2.174l.016.162L18 7v7.5a2.5 2.5 0 0 1-2.336 2.495L15.5 17h-1.496v-1.5h.496a.5.5 0 0 0 0-1h-.497v-.955a.478.478 0 0 0 0-.09V12.5h.502a.5.5 0 1 0 0-1h-.503L14 9h.505a.5.5 0 0 0 .5-.5v-4zM7.932 3.21A1.5 1.5 0 0 0 7.167 3H4.5l-.164.005A2.5 2.5 0 0 0 2 5.5v1h5.07l.082-.007a.5.5 0 0 0 .22-.094l.061-.055L9.385 4.29L8.067 3.3l-.135-.09z', + + 'Checkmark': 'M3.374 10.168a.5.5 0 0 0-.748.664l4 4.5a.5.5 0 0 0 .728.022l10.5-10.5a.5.5 0 0 0-.707-.708L7.02 14.271l-3.647-4.103z', + 'CheckmarkSolid': 'M7.032 13.907l-3.471-3.905a.75.75 0 0 0-1.122.996l4 4.5a.75.75 0 0 0 1.091.032l10.5-10.5a.75.75 0 0 0-1.06-1.06l-9.938 9.937z', + 'CheckmarkCircle': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zm0 1a7 7 0 1 0 0 14a7 7 0 0 0 0-14zm3.358 4.646a.5.5 0 0 1 .058.638l-.058.07l-4.004 4.004a.5.5 0 0 1-.638.058l-.07-.058l-2-2a.5.5 0 0 1 .638-.765l.07.058L9 11.298l3.651-3.652a.5.5 0 0 1 .707 0z', + 'CheckmarkCircleSolid': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zm3.358 5.646a.5.5 0 0 0-.637-.057l-.07.057L9 11.298L7.354 9.651l-.07-.058a.5.5 0 0 0-.695.696l.057.07l2 2l.07.057a.5.5 0 0 0 .568 0l.07-.058l4.004-4.004l.058-.07a.5.5 0 0 0-.058-.638z', + 'CheckmarkStarburst': 'M8.46 1.897l.99.39a1.5 1.5 0 0 0 1.099 0l.99-.39a2.418 2.418 0 0 1 3.102 1.285l.424.975a1.5 1.5 0 0 0 .777.777l.975.424a2.418 2.418 0 0 1 1.285 3.103l-.39.99a1.5 1.5 0 0 0 0 1.098l.39.99a2.418 2.418 0 0 1-1.285 3.102l-.975.424a1.499 1.499 0 0 0-.777.777l-.424.975a2.418 2.418 0 0 1-3.103 1.285l-.99-.39a1.5 1.5 0 0 0-1.098 0l-.99.39a2.418 2.418 0 0 1-3.102-1.285l-.424-.975a1.5 1.5 0 0 0-.777-.777l-.975-.424a2.418 2.418 0 0 1-1.285-3.103l.39-.99a1.5 1.5 0 0 0 0-1.098l-.39-.99a2.418 2.418 0 0 1 1.285-3.102l.975-.424a1.5 1.5 0 0 0 .777-.777l.424-.975a2.418 2.418 0 0 1 3.103-1.285zm3.445.93l-.99.39a2.5 2.5 0 0 1-1.831 0l-.99-.39a1.418 1.418 0 0 0-1.819.754l-.424.975a2.5 2.5 0 0 1-1.295 1.295l-.975.424a1.418 1.418 0 0 0-.753 1.82l.389.989a2.5 2.5 0 0 1 0 1.831l-.39.99c-.279.71.054 1.514.754 1.819l.975.424a2.5 2.5 0 0 1 1.295 1.295l.424.975a1.418 1.418 0 0 0 1.82.753l.989-.39a2.5 2.5 0 0 1 1.831 0l.99.39c.71.28 1.514-.053 1.819-.753l.424-.975a2.5 2.5 0 0 1 1.295-1.295l.975-.424a1.418 1.418 0 0 0 .753-1.82l-.39-.989a2.5 2.5 0 0 1 0-1.831l.39-.99a1.418 1.418 0 0 0-.753-1.819l-.975-.424a2.5 2.5 0 0 1-1.295-1.295l-.424-.975a1.418 1.418 0 0 0-1.82-.753zm-2.927 8.944l3.648-4.104a.5.5 0 0 1 .8.592l-.053.073l-4 4.5a.5.5 0 0 1-.655.081l-.072-.06l-2-2a.5.5 0 0 1 .638-.765l.069.058l1.625 1.625l3.648-4.104l-3.648 4.104z', + 'CheckmarkStarburstSolid': 'M8.46 1.897l.99.39a1.5 1.5 0 0 0 1.099 0l.99-.39a2.418 2.418 0 0 1 3.102 1.285l.424.975a1.5 1.5 0 0 0 .777.777l.975.424a2.418 2.418 0 0 1 1.285 3.103l-.39.99a1.5 1.5 0 0 0 0 1.098l.39.99a2.418 2.418 0 0 1-1.285 3.102l-.975.424a1.499 1.499 0 0 0-.777.777l-.424.975a2.418 2.418 0 0 1-3.103 1.285l-.99-.39a1.5 1.5 0 0 0-1.098 0l-.99.39a2.418 2.418 0 0 1-3.102-1.285l-.424-.975a1.5 1.5 0 0 0-.777-.777l-.975-.424a2.418 2.418 0 0 1-1.285-3.103l.39-.99a1.5 1.5 0 0 0 0-1.098l-.39-.99a2.418 2.418 0 0 1 1.285-3.102l.975-.424a1.5 1.5 0 0 0 .777-.777l.424-.975a2.418 2.418 0 0 1 3.103-1.285zm4.166 5.77l-3.648 4.104l-1.625-1.625a.5.5 0 0 0-.707.707l2 2a.5.5 0 0 0 .727-.021l4-4.5a.5.5 0 0 0-.747-.665z', + 'ArrowSyncCheckmark': 'M11.414 3.635a.5.5 0 0 0 0-.707L9.293.807a.5.5 0 0 0-.707.707l.997.997a7.5 7.5 0 0 0-4.075 13.495a.5.5 0 0 0 .6-.8a6.5 6.5 0 0 1 5.29-11.554l.016-.017zM8.586 16.363l.016-.016c.408.09.831.14 1.264.15l-.006.006a.502.502 0 0 1 .074-.004a6.5 6.5 0 0 0 3.959-11.706a.5.5 0 1 1 .6-.8a7.5 7.5 0 0 1-4.075 13.495l.996.996a.5.5 0 1 1-.707.707l-2.121-2.12a.5.5 0 0 1 0-.708zm3.768-8.218a.5.5 0 0 1 0 .708l-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 0 1 .708-.708L9 10.792l2.646-2.647a.5.5 0 0 1 .708 0zM5 10a5 5 0 1 1 10 0a5 5 0 0 1-10 0zm5-4a4 4 0 1 0 0 8a4 4 0 0 0 0-8z', + 'ArrowSyncCheckmarkSolid': 'M11.414 3.635a.5.5 0 0 0 0-.707L9.293.807a.5.5 0 0 0-.707.707l.997.997a7.5 7.5 0 0 0-4.075 13.495a.5.5 0 0 0 .6-.8a6.5 6.5 0 0 1 5.29-11.554l.016-.017zM8.586 16.363l.016-.016c.408.09.831.14 1.264.15l-.006.006a.516.516 0 0 1 .074-.004a6.5 6.5 0 0 0 3.959-11.706a.5.5 0 1 1 .6-.8a7.5 7.5 0 0 1-4.075 13.495l.996.996a.5.5 0 1 1-.707.707l-2.121-2.12a.5.5 0 0 1 0-.708zM15 9.999a5 5 0 1 1-10 0a5 5 0 0 1 10 0zm-2.646-1.854a.5.5 0 0 0-.708 0L9 10.792L7.854 9.645a.5.5 0 1 0-.708.708l1.5 1.5a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0 0-.708z', + + 'Image': 'M14 7.5a1.5 1.5 0 1 1-3 0a1.5 1.5 0 0 1 3 0zm-1 0a.5.5 0 1 0-1 0a.5.5 0 0 0 1 0zM3 6a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6zm3-2a2 2 0 0 0-2 2v8c0 .373.102.722.28 1.02l4.669-4.588a1.5 1.5 0 0 1 2.102 0l4.67 4.588A1.99 1.99 0 0 0 16 14V6a2 2 0 0 0-2-2H6zm0 12h8c.37 0 .715-.1 1.012-.274l-4.662-4.58a.5.5 0 0 0-.7 0l-4.662 4.58A1.99 1.99 0 0 0 6 16z', + 'ImageSolid': 'M12.5 8a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1zM3 6a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v8c0 .65-.206 1.25-.557 1.742l-5.39-5.308a1.5 1.5 0 0 0-2.105 0l-5.39 5.308A2.986 2.986 0 0 1 3 14V6zm9.5 3a1.5 1.5 0 1 0 0-3a1.5 1.5 0 0 0 0 3zm-8.235 7.448C4.755 16.796 5.354 17 6 17h8c.646 0 1.245-.204 1.735-.552l-5.384-5.3a.5.5 0 0 0-.702 0l-5.384 5.3z', + 'DrawImage': 'M14 7.5a1.5 1.5 0 1 1-3 0a1.5 1.5 0 0 1 3 0zm-1 0a.5.5 0 1 0-1 0a.5.5 0 0 0 1 0zM3 6a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v3.003c-.341.016-.68.092-1 .229V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8.826a.2.2 0 0 0 .34.142l.807-.796l-.002-.003l1.048-1.03l.206-.202l2.55-2.505a1.5 1.5 0 0 1 2.102 0l1.745 1.714l-.707.708l-1.739-1.709a.5.5 0 0 0-.7 0l-2.756 2.707l-1.851 1.828c-.758.748-2.043.21-2.043-.854V6zm.4 11.035c.369.184.83.335 1.217.25c.251-.056.577-.193.943-.347c.885-.373 2.003-.843 2.862-.497c.637.256.584.981.405 1.33c-.035.066-.008.16.065.177a4.6 4.6 0 0 0 1.112.088a.917.917 0 0 1 .023-.14l.375-1.498c.096-.386.296-.74.578-1.02l4.83-4.83a1.87 1.87 0 1 1 2.644 2.645l-4.83 4.829a2.197 2.197 0 0 1-1.02.578l-1.222.305c-1.121.328-2.794.222-3.313-.183c-.449-.35-.467-.887-.316-1.244c.034-.08-.026-.183-.111-.17c-.495.07-.9.25-1.3.427c-.585.26-1.156.513-1.976.411c-.711-.088-1.107-.459-1.325-.825c-.122-.204.147-.392.36-.286z', + 'DrawImageSolid': 'M6 3a3 3 0 0 0-3 3v9.076a.51.51 0 0 0 .868.363l1.342-1.325l3.738-3.68a1.5 1.5 0 0 1 2.104 0l1.742 1.715l2.308-2.308A2.86 2.86 0 0 1 17 9.003V6a3 3 0 0 0-3-3H6zm8 4.5a1.5 1.5 0 1 1-3 0a1.5 1.5 0 0 1 3 0zm-1 0a.5.5 0 1 1-1 0a.5.5 0 0 1 1 0zm-2.727 7.17l1.813-1.814l-1.735-1.709a.5.5 0 0 0-.702 0l-4.224 4.159c-.22.236-.008.587.296.478l.327-.117c.705-.253 1.764-.55 2.747-.154c.286.115.512.28.684.472c.154-.495.426-.947.794-1.315zm.707.707l4.83-4.83a1.87 1.87 0 1 1 2.644 2.646l-4.83 4.829a2.197 2.197 0 0 1-1.02.578l-1.221.305c-1.122.328-2.795.222-3.314-.183c-.449-.35-.467-.887-.316-1.244c.034-.08-.026-.183-.111-.17c-.495.07-.9.25-1.3.427c-.584.26-1.156.513-1.976.411c-.711-.088-1.107-.459-1.325-.825c-.122-.204.147-.392.36-.286c.368.184.829.335 1.216.25c.251-.056.577-.193.943-.347c.885-.373 2.003-.843 2.863-.497c.636.256.583.981.404 1.33c-.035.066-.008.16.065.177a4.6 4.6 0 0 0 1.112.088a.917.917 0 0 1 .023-.14l.375-1.498c.096-.386.296-.74.578-1.02z', + 'ImageCopy': 'M8.498 7.497a.998.998 0 1 0 0-1.995a.998.998 0 0 0 0 1.995zM5 6a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H8a3 3 0 0 1-3-3V6zm3-2a2 2 0 0 0-2 2v6c0 .37.101.718.277 1.016L9.79 9.502a1.71 1.71 0 0 1 2.418 0l3.514 3.514C15.9 12.718 16 12.371 16 12V6a2 2 0 0 0-2-2H8zm7.016 9.723l-3.514-3.514a.71.71 0 0 0-1.004 0l-3.514 3.514C7.282 13.9 7.629 14 8 14h6c.37 0 .718-.101 1.016-.277zM12 17c.889 0 1.687-.386 2.236-1H7.5A3.5 3.5 0 0 1 4 12.5V5.764C3.386 6.314 3 7.112 3 8v4.5A4.5 4.5 0 0 0 7.5 17H12z', + 'ImageCopySolid': 'M5 6a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v6c0 .648-.205 1.248-.555 1.738L12.21 9.502a1.71 1.71 0 0 0-2.418 0l-4.236 4.236A2.986 2.986 0 0 1 5 12V6zm3.498 1.497a.998.998 0 1 0 0-1.995a.998.998 0 0 0 0 1.995zm3.004 2.712l4.236 4.236c-.49.35-1.09.555-1.738.555H8a2.986 2.986 0 0 1-1.738-.555l4.236-4.236a.71.71 0 0 1 1.004 0zM14.236 16c-.55.614-1.348 1-2.236 1H7.5A4.5 4.5 0 0 1 3 12.5V8c0-.888.386-1.687 1-2.236V12.5A3.5 3.5 0 0 0 7.5 16h6.736z', + 'ImageEdit': 'M13.999 7.5a1.5 1.5 0 1 1-3 0a1.5 1.5 0 0 1 3 0zm-1 0a.5.5 0 1 0-1 0a.5.5 0 0 0 1 0zM3 6a3 3 0 0 1 3-3h7.999a3 3 0 0 1 3 3v3.002a2.87 2.87 0 0 0-1 .229V6a2 2 0 0 0-2-2h-8a2 2 0 0 0-2 2v7.999c0 .372.103.721.28 1.02l4.669-4.588a1.5 1.5 0 0 1 2.102 0l1.745 1.715l-.707.707l-1.738-1.709a.5.5 0 0 0-.701 0l-4.661 4.58A1.99 1.99 0 0 0 6 16h3.474c-.016.05-.03.103-.043.155l-.211.845H6a3 3 0 0 1-3-3v-8zm7.979 9.376l4.829-4.83a1.87 1.87 0 1 1 2.644 2.646l-4.829 4.828a2.197 2.197 0 0 1-1.02.578l-1.498.375a.89.89 0 0 1-1.078-1.079l.374-1.498c.097-.386.296-.739.578-1.02z', + 'ImageEditSolid': 'M12.499 8a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1zM3 6a3 3 0 0 1 3-3h7.999a3 3 0 0 1 3 3v3.002a2.86 2.86 0 0 0-1.898.838l-2.308 2.308l-1.741-1.714a1.5 1.5 0 0 0-2.105 0l-5.39 5.307A2.986 2.986 0 0 1 3 13.998v-8zm9.499 3a1.5 1.5 0 1 0 0-3a1.5 1.5 0 0 0 0 3zm-2.227 5.669l1.813-1.814l-1.735-1.709a.5.5 0 0 0-.702 0l-5.383 5.3c.49.348 1.088.552 1.735.552h3.22l.21-.844c.141-.562.432-1.075.842-1.485zm.707.707l4.829-4.83a1.87 1.87 0 1 1 2.644 2.646l-4.829 4.828a2.197 2.197 0 0 1-1.02.578l-1.498.375a.89.89 0 0 1-1.078-1.079l.374-1.498c.097-.386.296-.739.578-1.02z', + 'ImageMultiple': 'M11.5 7.5a1 1 0 1 0 0-2a1 1 0 0 0 0 2zM3 6a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v6a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6zm3-2a2 2 0 0 0-2 2v6c0 .37.101.718.277 1.016l3.309-3.309a2 2 0 0 1 2.828 0l3.31 3.309c.175-.298.276-.645.276-1.016V6a2 2 0 0 0-2-2H6zm3.707 6.414a1 1 0 0 0-1.414 0l-3.309 3.31c.298.175.645.276 1.016.276h6c.37 0 .718-.101 1.016-.277l-3.309-3.309zM8 17a2.992 2.992 0 0 1-2.236-1H12.5a3.5 3.5 0 0 0 3.5-3.5V5.764c.614.55 1 1.348 1 2.236v4.5a4.5 4.5 0 0 1-4.5 4.5H8z', + 'ImageMultipleSolid': 'M6 3a3 3 0 0 0-3 3v6c0 .648.205 1.248.555 1.738l4.03-4.03a2 2 0 0 1 2.83 0l4.03 4.03c.35-.49.555-1.09.555-1.738V6a3 3 0 0 0-3-3H6zm6.5 3.5a1 1 0 1 1-2 0a1 1 0 0 1 2 0zm1.238 7.945l-4.03-4.03a1 1 0 0 0-1.415 0l-4.031 4.03c.49.35 1.09.555 1.738.555h6c.648 0 1.248-.205 1.738-.555zM5.764 16c.55.614 1.348 1 2.236 1h4.5a4.5 4.5 0 0 0 4.5-4.5V8c0-.888-.386-1.687-1-2.236V12.5a3.5 3.5 0 0 1-3.5 3.5H5.764z', + 'ImageOff': 'M2.854 2.146a.5.5 0 1 0-.708.708l1.409 1.408C3.205 4.752 3 5.352 3 6v8a3 3 0 0 0 3 3h8c.648 0 1.248-.205 1.738-.555l1.408 1.409a.5.5 0 0 0 .708-.708l-15-15zm6.56 7.975a1.497 1.497 0 0 0-.465.311l-4.67 4.588A1.99 1.99 0 0 1 4 14V6c0-.37.101-.718.277-1.016l5.137 5.137zM6 16c-.37 0-.715-.1-1.012-.274l4.662-4.58a.5.5 0 0 1 .7 0l4.662 4.58A1.991 1.991 0 0 1 14 16H6zM16 6v7.879l.898.898c.067-.248.102-.508.102-.777V6a3 3 0 0 0-3-3H6c-.269 0-.53.035-.777.102L6.12 4H14a2 2 0 0 1 2 2zm-2 1.5a1.5 1.5 0 1 0-3 0a1.5 1.5 0 0 0 3 0zm-1 0a.5.5 0 1 1-1 0a.5.5 0 0 1 1 0z', + 'ImageOffSolid': 'M2.854 2.146a.5.5 0 1 0-.708.708l1.409 1.408C3.205 4.752 3 5.352 3 6v8c0 .65.206 1.25.557 1.742l5.39-5.308c.14-.136.298-.24.468-.312l.632.633l-.352.352a.51.51 0 0 0-.046.04l-5.384 5.301C4.755 16.796 5.354 17 6 17h8c.597 0 1.154-.174 1.622-.475l.01-.008c.035-.022.069-.045.103-.069l-.003-.003l.003-.003l1.411 1.412a.5.5 0 0 0 .708-.708l-15-15zM13 7.5a.5.5 0 1 1-1 0a.5.5 0 0 1 1 0zM5.223 3.102l11.675 11.675c.067-.248.102-.508.102-.777V6a3 3 0 0 0-3-3H6c-.269 0-.53.035-.777.102zM14 7.5a1.5 1.5 0 1 1-3 0a1.5 1.5 0 0 1 3 0z', + 'ImageProhibited': 'M5.5 10a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm0-1c-.786 0-1.512-.26-2.096-.697l4.9-4.899A3.5 3.5 0 0 1 5.5 9zm2.096-6.303l-4.9 4.9a3.5 3.5 0 0 1 4.9-4.9zM3 10.4c.317.162.651.294 1 .393V14c0 .373.102.722.28 1.02l4.669-4.588a1.5 1.5 0 0 1 2.102 0l4.67 4.588A1.99 1.99 0 0 0 16 14V6a2 2 0 0 0-2-2h-3.207a5.466 5.466 0 0 0-.393-1H14a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3v-3.6zm1.988 5.326A1.99 1.99 0 0 0 6 16h8c.37 0 .715-.1 1.012-.274l-4.662-4.58a.5.5 0 0 0-.7 0l-4.662 4.58zM14 7.5a1.5 1.5 0 1 1-3 0a1.5 1.5 0 0 1 3 0zm-1 0a.5.5 0 1 0-1 0a.5.5 0 0 0 1 0z', + 'ImageProhibitedSolid': 'M5.5 10a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm0-1c-.786 0-1.512-.26-2.096-.697l4.9-4.899A3.5 3.5 0 0 1 5.5 9zM2.697 7.596a3.5 3.5 0 0 1 4.9-4.9l-4.9 4.9zM13 7.5a.5.5 0 1 1-1 0a.5.5 0 0 1 1 0zM5.5 11a5.5 5.5 0 0 0 4.9-8H14a3 3 0 0 1 3 3v8c0 .65-.206 1.25-.557 1.742l-5.39-5.308a1.5 1.5 0 0 0-2.105 0l-5.39 5.308A2.986 2.986 0 0 1 3 14v-3.6c.75.384 1.6.6 2.5.6zM14 7.5a1.5 1.5 0 1 0-3 0a1.5 1.5 0 0 0 3 0zM6 17a2.987 2.987 0 0 1-1.735-.552l5.384-5.3a.5.5 0 0 1 .702 0l5.384 5.3A2.987 2.987 0 0 1 14 17H6z', + 'ImageSearch': 'M6 3a3 3 0 0 0-3 3v2.758a4.484 4.484 0 0 1 1-.502V6a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8c0 .373-.102.722-.28 1.02l-4.669-4.588a1.5 1.5 0 0 0-1.71-.278c.173.283.316.587.424.907a.5.5 0 0 1 .585.084l4.662 4.58A1.991 1.991 0 0 1 14 16h-2.879l.44.44c.163.163.281.355.354.56H14a3 3 0 0 0 3-3V6a3 3 0 0 0-3-3H6zm6.5 6a1.5 1.5 0 1 0 0-3a1.5 1.5 0 0 0 0 3zm0-1a.5.5 0 1 1 0-1a.5.5 0 0 1 0 1zm-4.197 6.596a3.5 3.5 0 1 0-.707.707l2.55 2.55a.5.5 0 0 0 .708-.707l-2.55-2.55zM5.5 15a2.5 2.5 0 1 1 0-5a2.5 2.5 0 0 1 0 5z', + 'ImageSearchSolid': 'M12.5 8a.5.5 0 1 0 0-1a.5.5 0 0 0 0 1zM3 6a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v8c0 .65-.206 1.25-.557 1.742l-5.39-5.308a1.5 1.5 0 0 0-1.711-.279A4.497 4.497 0 0 0 3 8.758V6zm9.5 3a1.5 1.5 0 1 0 0-3a1.5 1.5 0 0 0 0 3zm-.585 8H14c.646 0 1.245-.204 1.735-.552l-5.384-5.3a.5.5 0 0 0-.586-.086c.152.451.235.935.235 1.438c0 .694-.158 1.352-.439 1.94l2 2c.163.163.281.355.354.56zm-3.612-2.404a3.5 3.5 0 1 0-.707.707l2.55 2.55a.5.5 0 0 0 .708-.707l-2.55-2.55zM5.5 15a2.5 2.5 0 1 1 0-5a2.5 2.5 0 0 1 0 5z', + + 'Video': 'M4.5 4A2.5 2.5 0 0 0 2 6.5v7A2.5 2.5 0 0 0 4.5 16h7a2.5 2.5 0 0 0 2.5-2.5v-1l2.4 1.8a1 1 0 0 0 1.6-.8v-7a1 1 0 0 0-1.6-.8L14 7.5v-1A2.5 2.5 0 0 0 11.5 4h-7zM14 8.75l3-2.25v7l-3-2.25v-2.5zM13 6.5v7a1.5 1.5 0 0 1-1.5 1.5h-7A1.5 1.5 0 0 1 3 13.5v-7A1.5 1.5 0 0 1 4.5 5h7A1.5 1.5 0 0 1 13 6.5z', + 'VideoSolid': 'M13 6.5A2.5 2.5 0 0 0 10.5 4h-6A2.5 2.5 0 0 0 2 6.5v7A2.5 2.5 0 0 0 4.5 16h6a2.5 2.5 0 0 0 2.5-2.5v-7zm1 1.43v4.152l2.764 2.35A.75.75 0 0 0 18 13.86V6.193a.75.75 0 0 0-1.23-.575L14 7.93z', + 'VideoAdd': 'M4.5 4A2.5 2.5 0 0 0 2 6.5v3.757A5.504 5.504 0 0 1 3 9.6V6.5A1.5 1.5 0 0 1 4.5 5h7A1.5 1.5 0 0 1 13 6.5v7a1.5 1.5 0 0 1-1.5 1.5h-.522a5.489 5.489 0 0 1-.185 1h.707a2.5 2.5 0 0 0 2.5-2.5v-1l2.4 1.8a1 1 0 0 0 1.6-.8v-7a1 1 0 0 0-1.6-.8L14 7.5v-1A2.5 2.5 0 0 0 11.5 4h-7zM14 8.75l3-2.25v7l-3-2.25v-2.5zm-4 5.75a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4-2a.5.5 0 0 0-1 0V14H3.5a.5.5 0 0 0 0 1H5v1.5a.5.5 0 0 0 1 0V15h1.5a.5.5 0 0 0 0-1H6v-1.5z', + 'VideoAddSolid': 'M13 6.5A2.5 2.5 0 0 0 10.5 4h-6A2.5 2.5 0 0 0 2 6.5v3.757a5.5 5.5 0 0 1 8.798 5.725A2.5 2.5 0 0 0 13 13.5v-7zm1 1.43v4.152l2.764 2.35A.75.75 0 0 0 18 13.86V6.193a.75.75 0 0 0-1.23-.575L14 7.93zm-4 6.57a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4-2a.5.5 0 0 0-1 0V14H3.5a.5.5 0 0 0 0 1H5v1.5a.5.5 0 0 0 1 0V15h1.5a.5.5 0 0 0 0-1H6v-1.5z', + 'VideoOff': 'M2.854 2.146a.5.5 0 1 0-.708.708l1.355 1.354A2.5 2.5 0 0 0 2 6.5v7A2.5 2.5 0 0 0 4.5 16h7a2.5 2.5 0 0 0 2.292-1.5l3.354 3.354a.5.5 0 0 0 .708-.708l-15-15zm10.133 11.549A1.5 1.5 0 0 1 11.5 15h-7A1.5 1.5 0 0 1 3 13.5v-7a1.5 1.5 0 0 1 1.305-1.488l8.683 8.683zM13 10.879l3.47 3.469A1 1 0 0 0 18 13.5v-7a1 1 0 0 0-1.6-.8L14 7.5v-1A2.5 2.5 0 0 0 11.5 4H6.121l1 1H11.5A1.5 1.5 0 0 1 13 6.5v4.379zm1-2.129l3-2.25v7l-3-2.25v-2.5z', + 'VideoOffSolid': 'M2.854 2.146a.5.5 0 1 0-.708.708l1.355 1.354A2.5 2.5 0 0 0 2 6.5v7A2.5 2.5 0 0 0 4.5 16h6a2.5 2.5 0 0 0 2.492-2.3l4.154 4.154a.5.5 0 0 0 .708-.708l-15-15zm13.91 12.286l-1.41-1.199L14 11.88V7.93l2.77-2.314a.75.75 0 0 1 1.23.576v7.667a.75.75 0 0 1-1.236.572zM13 10.879l-6.879-6.88H10.5A2.5 2.5 0 0 1 13 6.5v4.38z', + 'VideoProhibited': 'M2 6.5A2.5 2.5 0 0 1 4.5 4h7A2.5 2.5 0 0 1 14 6.5v1l2.4-1.8a1 1 0 0 1 1.6.8v3.757a5.503 5.503 0 0 0-1-.657V6.5l-3 2.25v.272a5.48 5.48 0 0 0-1 .185V6.5A1.5 1.5 0 0 0 11.5 5h-7A1.5 1.5 0 0 0 3 6.5v7A1.5 1.5 0 0 0 4.5 15h4.522c.031.343.094.678.185 1H4.5A2.5 2.5 0 0 1 2 13.5v-7zm8 8a4.5 4.5 0 1 0 9 0a4.5 4.5 0 0 0-9 0zm2.404 2.803l4.9-4.9a3.5 3.5 0 0 1-4.9 4.9zm-.707-.707a3.5 3.5 0 0 1 4.9-4.9l-4.9 4.9z', + 'VideoProhibitedSolid': 'M13 6.5A2.5 2.5 0 0 0 10.5 4h-6A2.5 2.5 0 0 0 2 6.5v7A2.5 2.5 0 0 0 4.5 16h4.707A5.502 5.502 0 0 1 13 9.207V6.5zm5-.307v4.064a5.477 5.477 0 0 0-4-1.235V7.931l2.77-2.313a.75.75 0 0 1 1.23.575zM10 14.5a4.5 4.5 0 1 0 9 0a4.5 4.5 0 0 0-9 0zm1 0a3.5 3.5 0 0 1 5.596-2.803l-4.9 4.9A3.484 3.484 0 0 1 11 14.5zm3.5 3.5c-.786 0-1.512-.26-2.096-.697l4.9-4.9A3.5 3.5 0 0 1 14.5 18z', + 'VideoSync': 'M4.5 4A2.5 2.5 0 0 0 2 6.5v3.757A5.504 5.504 0 0 1 3 9.6V6.5A1.5 1.5 0 0 1 4.5 5h7A1.5 1.5 0 0 1 13 6.5v7a1.5 1.5 0 0 1-1.5 1.5h-.522a5.489 5.489 0 0 1-.185 1h.707a2.5 2.5 0 0 0 2.5-2.5v-1l2.4 1.8a1 1 0 0 0 1.6-.8v-7a1 1 0 0 0-1.6-.8L14 7.5v-1A2.5 2.5 0 0 0 11.5 4h-7zM14 8.75l3-2.25v7l-3-2.25v-2.5zM1 14.5a4.5 4.5 0 1 0 9 0a4.5 4.5 0 0 0-9 0zm6.5-3a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5H6a.5.5 0 0 1 0-1h.468a1.99 1.99 0 0 0-.933-.25a2 2 0 0 0-1.45.586a.5.5 0 0 1-.706-.707A3 3 0 0 1 7 12.152V12a.5.5 0 0 1 .5-.5zm-.876 5.532A2.999 2.999 0 0 1 4 16.848V17a.5.5 0 0 1-1 0v-1.5a.5.5 0 0 1 .5-.5H5a.5.5 0 0 1 0 1h-.468a1.99 1.99 0 0 0 .933.25a2 2 0 0 0 1.45-.586a.5.5 0 0 1 .706.707a3 3 0 0 1-.997.66z', + 'VideoSyncSolid': 'M13 6.5A2.5 2.5 0 0 0 10.5 4h-6A2.5 2.5 0 0 0 2 6.5v3.757a5.5 5.5 0 0 1 8.798 5.725A2.5 2.5 0 0 0 13 13.5v-7zm1 1.43v4.152l2.764 2.35A.75.75 0 0 0 18 13.86V6.193a.75.75 0 0 0-1.23-.575L14 7.93zM1 14.5a4.5 4.5 0 1 0 9 0a4.5 4.5 0 0 0-9 0zm6.5-3a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5H6a.5.5 0 0 1 0-1h.468a1.99 1.99 0 0 0-.933-.25a2 2 0 0 0-1.45.586a.5.5 0 0 1-.706-.707A3 3 0 0 1 7 12.152V12a.5.5 0 0 1 .5-.5zm-.876 5.532A2.999 2.999 0 0 1 4 16.848V17a.5.5 0 0 1-1 0v-1.5a.5.5 0 0 1 .5-.5H5a.5.5 0 0 1 0 1h-.468a1.99 1.99 0 0 0 .933.25a2 2 0 0 0 1.45-.586a.5.5 0 0 1 .706.707a3 3 0 0 1-.997.66z', + + 'File': 'M6 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zM5 4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4zm9.793 3H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7z', + 'FileSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 4 16.5v-13A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25z', + 'FileAdd': 'M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM10 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4-2a.5.5 0 0 0-1 0V14H3.5a.5.5 0 0 0 0 1H5v1.5a.5.5 0 0 0 1 0V15h1.5a.5.5 0 0 0 0-1H6v-1.5z', + 'FileAddSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H9.743A5.5 5.5 0 0 0 4 9.207V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM10 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4-2a.5.5 0 0 0-1 0V14H3.5a.5.5 0 0 0 0 1H5v1.5a.5.5 0 0 0 1 0V15h1.5a.5.5 0 0 0 0-1H6v-1.5z', + 'FileArrowDown': 'M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM5.5 19a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm-2.354-4.146a.5.5 0 0 1 .708-.708L5 15.293V12.5a.5.5 0 0 1 1 0v2.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.351.146h-.006a.5.5 0 0 1-.348-.144l-.003-.003l-2-2z', + 'FileArrowDownSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H9.743A5.5 5.5 0 0 0 4 9.207V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM5.5 19a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm-2.354-4.146a.5.5 0 0 1 .708-.708L5 15.293V12.5a.5.5 0 0 1 1 0v2.793l1.146-1.147a.5.5 0 0 1 .708.708l-2 2a.5.5 0 0 1-.351.146h-.006a.5.5 0 0 1-.348-.144l-.003-.003l-2-2z', + 'FileArrowUp': 'M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM5.5 19a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm2.354-4.854a.5.5 0 1 1-.708.708L6 13.707V16.5a.5.5 0 0 1-1 0v-2.793l-1.146 1.147a.5.5 0 1 1-.708-.707l2-2A.5.5 0 0 1 5.497 12h.006a.498.498 0 0 1 .348.144l.003.003l2 2z', + 'FileArrowUpSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H9.743A5.5 5.5 0 0 0 4 9.207V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM5.5 19a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm2.354-4.854a.5.5 0 1 1-.708.708L6 13.707V16.5a.5.5 0 0 1-1 0v-2.793l-1.146 1.147a.5.5 0 1 1-.708-.707l2-2A.5.5 0 0 1 5.497 12h.006a.498.498 0 0 1 .348.144l.003.003l2 2z', + 'FileBulletList': 'M6 10.5a.5.5 0 1 1 1 0a.5.5 0 0 1-1 0zm.5 1.5a.5.5 0 1 0 0 1a.5.5 0 0 0 0-1zM6 14.5a.5.5 0 1 1 1 0a.5.5 0 0 1-1 0zM8.5 10a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1h-5zM8 12.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm.5 1.5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1h-5zM6 2a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zM5 4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4zm9.793 3H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7z', + 'FileBulletListSolid': 'M10 6.5V2H5.5A1.5 1.5 0 0 0 4 3.5v13A1.5 1.5 0 0 0 5.5 18h9a1.5 1.5 0 0 0 1.5-1.5V8h-4.5A1.5 1.5 0 0 1 10 6.5zm-4 4a.5.5 0 1 1 1 0a.5.5 0 0 1-1 0zm0 2a.5.5 0 1 1 1 0a.5.5 0 0 1-1 0zm0 2a.5.5 0 1 1 1 0a.5.5 0 0 1-1 0zm2-4a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0 2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0 2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm3-8V2.25L15.75 7H11.5a.5.5 0 0 1-.5-.5z', + 'FileBulletListMultiple': 'M6.5 10a.5.5 0 1 0 0 1a.5.5 0 0 0 0-1zM6 12.5a.5.5 0 1 1 1 0a.5.5 0 0 1-1 0zm2-2a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm.5 1.5a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4zM6 2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h7a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 9.586 2H6zM5 4a1 1 0 0 1 1-1h3v3.5A1.5 1.5 0 0 0 10.5 8H14v6a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4zm5 2.5V3.207L13.793 7H10.5a.5.5 0 0 1-.5-.5zM16 8a1 1 0 0 1 1 1v5.06A3.94 3.94 0 0 1 13.06 18H7a1 1 0 0 1-1-1h7a3 3 0 0 0 3-3V8z', + 'FileBulletListMultipleSolid': 'M9 6.5V2H5.5A1.5 1.5 0 0 0 4 3.5v11A1.5 1.5 0 0 0 5.5 16H13a2 2 0 0 0 2-2V8h-4.5A1.5 1.5 0 0 1 9 6.5zm-3 4a.5.5 0 1 1 1 0a.5.5 0 0 1-1 0zm.5 2.5a.5.5 0 1 1 0-1a.5.5 0 0 1 0 1zm2-2a.5.5 0 0 1 0-1h4a.5.5 0 0 1 0 1h-4zM8 12.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm2-6V2.25L14.75 7H10.5a.5.5 0 0 1-.5-.5zM17 9a1 1 0 0 0-1-1v6a3 3 0 0 1-3 3H6a1 1 0 0 0 1 1h6.06A3.94 3.94 0 0 0 17 14.06V9z', + 'FileBulletListOff': 'M4 4.707L2.146 2.854a.5.5 0 1 1 .708-.708l15 15a.5.5 0 0 1-.708.708l-1.241-1.242A2 2 0 0 1 14 18H6a2 2 0 0 1-2-2V4.707zm11 11l-1.032-1.032A.5.5 0 0 1 13.5 15h-5a.5.5 0 0 1 0-1h4.793l-1-1H8.5a.5.5 0 0 1 0-1h2.793l-1-1H8.5a.5.5 0 0 1 0-1h.793L5 5.707V16a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-.293zM12.121 10l1 1h.379a.5.5 0 0 0 0-1h-1.379zM15 8v4.879l1 1V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6c-.521 0-.996.2-1.352.526l.708.709A.996.996 0 0 1 6 3h4v3.5A1.5 1.5 0 0 0 11.5 8H15zm-9 2.5a.5.5 0 1 0 1 0a.5.5 0 0 0-1 0zm.5 1.5a.5.5 0 1 0 0 1a.5.5 0 0 0 0-1zM6 14.5a.5.5 0 1 1 1 0a.5.5 0 0 1-1 0zM14.793 7H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7z', + 'FileBulletListOffSolid': 'M4 4.707L2.146 2.854a.5.5 0 1 1 .708-.708l15 15a.5.5 0 0 1-.708.708l-1.159-1.16A1.5 1.5 0 0 1 14.5 18h-9A1.5 1.5 0 0 1 4 16.5V4.707zM13.293 14H8.5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 .468-.325L13.293 14zm-1-1l-1-1H8.5a.5.5 0 0 0 0 1h3.793zm-2-2l-1-1H8.5a.5.5 0 0 0 0 1h1.793zm3.207 0h-.379L16 13.879V8h-4.5A1.5 1.5 0 0 1 10 6.5V2H5.5c-.383 0-.733.144-.998.38l7.62 7.62H13.5a.5.5 0 0 1 0 1zM6 10.5a.5.5 0 1 0 1 0a.5.5 0 0 0-1 0zm0 2a.5.5 0 1 0 1 0a.5.5 0 0 0-1 0zm0 2a.5.5 0 1 0 1 0a.5.5 0 0 0-1 0zm5-8V2.25L15.75 7H11.5a.5.5 0 0 1-.5-.5z', + 'FileCatchUp': 'M6 2a2 2 0 0 0-2 2v4.5a.5.5 0 0 0 1 0V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-3H4v3a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM7.462 8.308a.5.5 0 0 0-.91-.032L5.192 11H3.5a.5.5 0 0 0 0 1h2a.5.5 0 0 0 .447-.276L6.96 9.7l2.08 4.991a.5.5 0 0 0 .908.032L11.31 12H12.5a.5.5 0 0 0 0-1H11a.5.5 0 0 0-.447.276L9.54 13.3l-2.08-4.991z', + 'FileCatchUpSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 4 16.5V13h1.5a1.5 1.5 0 0 0 1.342-.83l.034-.068l1.24 2.975a1.5 1.5 0 0 0 2.726.094L11.927 13h.573a1.5 1.5 0 0 0 0-3H11a1.5 1.5 0 0 0-1.342.83l-.034.068l-1.24-2.975a1.5 1.5 0 0 0-2.726-.094L4.573 10H4V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM7.462 8.308a.5.5 0 0 0-.91-.032L5.192 11H3.5a.5.5 0 0 0 0 1h2a.5.5 0 0 0 .447-.276L6.96 9.7l2.08 4.991a.5.5 0 0 0 .908.032L11.31 12H12.5a.5.5 0 0 0 0-1H11a.5.5 0 0 0-.447.276L9.54 13.3l-2.08-4.991z', + 'FileCheckmark': 'M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM10 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-2.146-1.854a.5.5 0 0 0-.708 0L4.5 15.293l-.646-.647a.5.5 0 0 0-.708.708l1 1a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0 0-.708z', + 'FileCheckmarkSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H9.743A5.5 5.5 0 0 0 4 9.207V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM10 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-2.146-1.854a.5.5 0 0 0-.708 0L4.5 15.293l-.646-.647a.5.5 0 0 0-.708.708l1 1a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0 0-.708z', + 'FileCopy': 'M6 4a2 2 0 0 1 2-2h3.586a1.5 1.5 0 0 1 1.06.44l3.915 3.914A1.5 1.5 0 0 1 17 7.414V14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V4zm2-1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V8h-3.5A1.5 1.5 0 0 1 11 6.5V3H8zm4 .207V6.5a.5.5 0 0 0 .5.5h3.293L12 3.207zM4 5a1 1 0 0 1 1-1v10a3 3 0 0 0 3 3h7a1 1 0 0 1-1 1H7.94A3.94 3.94 0 0 1 4 14.06V5z', + 'FileCopySolid': 'M11 6.5V2H7.5A1.5 1.5 0 0 0 6 3.5v11A1.5 1.5 0 0 0 7.5 16h8a1.5 1.5 0 0 0 1.5-1.5V8h-4.5A1.5 1.5 0 0 1 11 6.5zm1 0V2.25L16.75 7H12.5a.5.5 0 0 1-.5-.5zM4 5a1 1 0 0 1 1-1v10.5A2.5 2.5 0 0 0 7.5 17H15a1 1 0 0 1-1 1H7.548A3.548 3.548 0 0 1 4 14.452V5z', + 'FileDismiss': 'M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM8.682 17.682a4.5 4.5 0 1 0-6.364-6.364a4.5 4.5 0 0 0 6.364 6.364zm-4.95-4.95a.5.5 0 0 1 .707 0l1.06 1.06l1.062-1.06a.5.5 0 1 1 .707.707L6.207 14.5l1.06 1.06a.5.5 0 1 1-.707.708l-1.06-1.06l-1.06 1.06a.5.5 0 1 1-.708-.708l1.06-1.06l-1.06-1.06a.5.5 0 0 1 0-.708z', + 'FileDismissSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H9.743A5.5 5.5 0 0 0 4 9.207V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM2.318 17.682a4.5 4.5 0 1 0 6.364-6.364a4.5 4.5 0 0 0-6.364 6.364zm1.414-4.95a.5.5 0 0 1 .707 0l1.06 1.06l1.062-1.06a.5.5 0 1 1 .707.707L6.207 14.5l1.06 1.06a.5.5 0 1 1-.707.708l-1.06-1.06l-1.06 1.06a.5.5 0 1 1-.708-.708l1.06-1.06l-1.06-1.06a.5.5 0 0 1 0-.708z', + 'FileEdit': 'M11.5 8H16v-.586a1.496 1.496 0 0 0-.057-.41L15.942 7a1.5 1.5 0 0 0-.381-.646l-3.915-3.915A1.5 1.5 0 0 0 10.586 2H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h2.221l-.013-.026A1.856 1.856 0 0 1 8.003 17H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8zm0-1a.5.5 0 0 1-.5-.5V3.207L14.793 7H11.5zm3.31 2.548a1.87 1.87 0 1 1 2.644 2.645l-4.83 4.829a2.197 2.197 0 0 1-1.02.578l-1.498.374a.89.89 0 0 1-1.079-1.078l.375-1.498c.096-.386.296-.74.578-1.02l4.83-4.83z', + 'FileEditSolid': 'M10 6.5V2H5.5A1.5 1.5 0 0 0 4 3.5v13A1.5 1.5 0 0 0 5.5 18h2.721c-.21-.39-.285-.86-.164-1.347l.375-1.498c.14-.562.43-1.075.84-1.485l4.83-4.83A2.86 2.86 0 0 1 16 8.004V8h-4.5A1.5 1.5 0 0 1 10 6.5zm1 0V2.25L15.75 7H11.5a.5.5 0 0 1-.5-.5zm6.454 3.048a1.87 1.87 0 0 0-2.645 0l-4.83 4.83a2.197 2.197 0 0 0-.577 1.02l-.375 1.498a.89.89 0 0 0 1.079 1.078l1.498-.374c.386-.097.739-.296 1.02-.578l4.83-4.83a1.87 1.87 0 0 0 0-2.644z', + 'FileError': 'M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM10 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zM5.5 12a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 1 0v-2a.5.5 0 0 0-.5-.5zm0 5.125a.625.625 0 1 0 0-1.25a.625.625 0 0 0 0 1.25z', + 'FileErrorSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H9.743A5.5 5.5 0 0 0 4 9.207V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM10 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zM5.5 12a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 1 0v-2a.5.5 0 0 0-.5-.5zm0 5.125a.625.625 0 1 0 0-1.25a.625.625 0 0 0 0 1.25z', + 'FileLink': 'M6 2a2 2 0 0 0-2 2v7h1V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-2.256a4.483 4.483 0 0 1-.502 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM5 12.5a.5.5 0 0 0-.5-.5l-.192.005A3.5 3.5 0 0 0 4.5 19l.09-.008A.5.5 0 0 0 4.5 18l-.164-.005A2.5 2.5 0 0 1 4.5 13l.09-.008A.5.5 0 0 0 5 12.5zm6 3A3.5 3.5 0 0 0 7.5 12l-.09.008A.5.5 0 0 0 7.5 13l.164.005A2.5 2.5 0 0 1 7.5 18l-.002.005l-.09.008a.5.5 0 0 0 .094.992V19l.192-.005A3.5 3.5 0 0 0 11 15.5zm-3.5-.498L4.5 15l-.09.008A.5.5 0 0 0 4.5 16l3 .002l.09-.008a.5.5 0 0 0-.09-.992z', + 'FileLinkSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5h-3.258A4.5 4.5 0 0 0 7.5 11H4V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM5 12.5a.5.5 0 0 0-.5-.5l-.192.005A3.5 3.5 0 0 0 4.5 19l.09-.008A.5.5 0 0 0 4.5 18l-.164-.005A2.5 2.5 0 0 1 4.5 13l.09-.008A.5.5 0 0 0 5 12.5zm6 3A3.5 3.5 0 0 0 7.5 12l-.09.008A.5.5 0 0 0 7.5 13l.164.005A2.5 2.5 0 0 1 7.5 18l-.002.005l-.09.008a.5.5 0 0 0 .094.992V19l.192-.005A3.5 3.5 0 0 0 11 15.5zm-3.5-.498L4.5 15l-.09.008A.5.5 0 0 0 4.5 16l3 .002l.09-.008a.5.5 0 0 0-.09-.992z', + 'FileLock': 'M6 2a2 2 0 0 0-2 2v5.401a2.98 2.98 0 0 1 1-.36V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-4v1h4a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM3.5 12v1H3a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1h-.5v-1a2 2 0 1 0-4 0zm1 1v-1a1 1 0 1 1 2 0v1h-2zm1 2.25a.75.75 0 1 1 0 1.5a.75.75 0 0 1 0-1.5z', + 'FileLockSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H10v-4a2 2 0 0 0-1.5-1.937V12A3 3 0 0 0 4 9.401V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM3.5 12v1H3a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h5a1 1 0 0 0 1-1v-4a1 1 0 0 0-1-1h-.5v-1a2 2 0 1 0-4 0zm1 1v-1a1 1 0 1 1 2 0v1h-2zm1 2.25a.75.75 0 1 1 0 1.5a.75.75 0 0 1 0-1.5z', + 'FileMultiple': 'M4 4a2 2 0 0 1 2-2h3.586a1.5 1.5 0 0 1 1.06.44l3.915 3.914A1.5 1.5 0 0 1 15 7.414V14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4zm2-1a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h7a1 1 0 0 0 1-1V8h-3.5A1.5 1.5 0 0 1 9 6.5V3H6zm4 .207V6.5a.5.5 0 0 0 .5.5h3.293L10 3.207zM17 9a1 1 0 0 0-1-1v6a3 3 0 0 1-3 3H6a1 1 0 0 0 1 1h6.06A3.94 3.94 0 0 0 17 14.06V9z', + 'FileMultipleSolid': 'M9 6.5V2H5.5A1.5 1.5 0 0 0 4 3.5v11A1.5 1.5 0 0 0 5.5 16h8a1.5 1.5 0 0 0 1.5-1.5V8h-4.5A1.5 1.5 0 0 1 9 6.5zm1 0V2.25L14.75 7H10.5a.5.5 0 0 1-.5-.5zM17 9a1 1 0 0 0-1-1v6a3 3 0 0 1-3 3H6a1 1 0 0 0 1 1h6.06A3.94 3.94 0 0 0 17 14.06V9z', + 'FilePdf': 'M6.5 11a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 1 0v-.166h.333a1.167 1.167 0 0 0 0-2.334H6.5zm.833 1.334H7V12h.333a.167.167 0 0 1 0 .334zM12 11.499a.5.5 0 0 1 .5-.499h.999a.5.5 0 0 1 0 1h-.5v.335h.5a.5.5 0 1 1 0 1h-.5l.001.164a.5.5 0 0 1-1 .002L12 12.834L12 11.499zM9.498 11a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5H10a1.5 1.5 0 0 0 0-3h-.502zm.5 2v-1H10a.5.5 0 0 1 0 1h-.002zM4 4a2 2 0 0 1 2-2h4.585a1.5 1.5 0 0 1 1.061.44l3.914 3.914a1.5 1.5 0 0 1 .44 1.06v1.668a1.5 1.5 0 0 1 .998 1.414v4.003A1.5 1.5 0 0 1 16 15.913V16a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-.087A1.5 1.5 0 0 1 3 14.5v-4.003A1.5 1.5 0 0 1 4 9.082V4zm11 4h-3.5A1.5 1.5 0 0 1 10 6.5V3H6a1 1 0 0 0-1 1v4.996h10V8zM5 15.999A1 1 0 0 0 6 17h8a1 1 0 0 0 1-1.001H5zm6-12.792V6.5a.5.5 0 0 0 .5.5h3.293L11 3.207zM4.5 9.996a.5.5 0 0 0-.5.5v4.003a.5.5 0 0 0 .5.5h10.997a.5.5 0 0 0 .5-.5v-4.003a.5.5 0 0 0-.5-.5H4.501z', + 'FilePdfSolid': 'M6.5 11a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 1 0v-.166h.334a1.167 1.167 0 0 0 0-2.334H6.5zm.834 1.334H7V12h.334a.167.167 0 0 1 0 .334zM12 11.499a.5.5 0 0 1 .5-.499h.998a.5.5 0 1 1 0 1h-.498v.335h.498a.5.5 0 1 1 0 1h-.498v.164a.5.5 0 1 1-1 .002L12 12.834l.002-1.335zM9.5 11a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 .5.5h.502a1.5 1.5 0 0 0 0-3h-.502zm.5 2v-1h.002a.5.5 0 0 1 0 1h-.002zm.002-6.5V2H5.5A1.5 1.5 0 0 0 4 3.5v5.582a1.5 1.5 0 0 0-1 1.414v4.003a1.5 1.5 0 0 0 1 1.414v.587A1.5 1.5 0 0 0 5.5 18h9a1.5 1.5 0 0 0 1.5-1.5v-.587a1.5 1.5 0 0 0 .998-1.414v-4.003a1.5 1.5 0 0 0-.998-1.414V8h-4.5A1.5 1.5 0 0 1 10 6.5zM4.502 9.996h10.997a.5.5 0 0 1 .5.5v4.003a.5.5 0 0 1-.5.5H4.502a.5.5 0 0 1-.5-.5v-4.003a.5.5 0 0 1 .5-.5zM11 6.5V2.25L15.75 7H11.5a.5.5 0 0 1-.5-.5z', + 'FileProhibited': 'M4 4a2 2 0 0 1 2-2h4.586a1.5 1.5 0 0 1 1.06.44l3.915 3.914A1.5 1.5 0 0 1 16 7.414V16a2 2 0 0 1-2 2H9.743c.253-.307.474-.642.657-1H14a1 1 0 0 0 1-1V8h-3.5A1.5 1.5 0 0 1 10 6.5V3H6a1 1 0 0 0-1 1v5.022a5.48 5.48 0 0 0-1 .185V4zm7-.793V6.5a.5.5 0 0 0 .5.5h3.293L11 3.207zM8.682 17.682a4.5 4.5 0 1 1-6.364-6.364a4.5 4.5 0 0 1 6.364 6.364zm-5.278-.379a3.5 3.5 0 0 0 4.9-4.9l-4.9 4.9zm-.707-.707l4.9-4.9a3.5 3.5 0 0 0-4.9 4.9z', + 'FileProhibitedSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H9.743A5.5 5.5 0 0 0 4 9.207V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zm-8.682 9.068a4.5 4.5 0 1 0 6.364 6.364a4.5 4.5 0 0 0-6.364-6.364zm5.657 5.657a3.5 3.5 0 0 1-4.571.328l4.9-4.9a3.5 3.5 0 0 1-.33 4.572zm-.379-5.278l-4.9 4.9a3.5 3.5 0 0 1 4.9-4.9z', + 'FileQuestionMark': 'M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM10 14.5a4.5 4.5 0 1 0-9 0a4.5 4.5 0 0 0 9 0zm-4.5 1.88a.625.625 0 1 1 0 1.25a.625.625 0 0 1 0-1.25zm0-4.877c1.031 0 1.853.846 1.853 1.95c0 .586-.214.908-.727 1.319l-.277.214c-.246.194-.329.3-.346.448l-.011.156A.5.5 0 0 1 5 15.5c0-.57.21-.884.716-1.288l.278-.215c.288-.23.36-.342.36-.544c0-.558-.382-.95-.854-.95c-.494 0-.859.366-.853.945a.5.5 0 1 1-1 .01c-.011-1.137.805-1.955 1.853-1.955z', + 'FileQuestionMarkSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H9.743A5.5 5.5 0 0 0 4 9.207V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM10 14.5a4.5 4.5 0 1 0-9 0a4.5 4.5 0 0 0 9 0zm-4.5 1.88a.625.625 0 1 1 0 1.25a.625.625 0 0 1 0-1.25zm0-4.877c1.031 0 1.853.846 1.853 1.95c0 .586-.214.908-.727 1.319l-.277.214c-.246.194-.329.3-.346.448l-.011.156A.5.5 0 0 1 5 15.5c0-.57.21-.884.716-1.288l.278-.215c.288-.23.36-.342.36-.544c0-.558-.382-.95-.854-.95c-.494 0-.859.366-.853.945a.5.5 0 1 1-1 .01c-.011-1.137.805-1.955 1.853-1.955z', + 'FileSearch': 'M10 12c0 .924-.314 1.775-.84 2.453l3.691 3.692a.5.5 0 1 1-.707.707L8.453 15.16A4 4 0 1 1 10 12zm-4 3a3 3 0 1 0 0-6a3 3 0 0 0 0 6zM5.5 3a.5.5 0 0 0-.5.5v3.6c-.348.07-.683.177-1 .316V3.5A1.5 1.5 0 0 1 5.5 2h5.086a1.5 1.5 0 0 1 1.06.44l3.915 3.914A1.5 1.5 0 0 1 16 7.414V16.5a1.5 1.5 0 0 1-1.5 1.5h-.587a1.494 1.494 0 0 0-.354-.563L13.12 17H14.5a.5.5 0 0 0 .5-.5V8h-3.5A1.5 1.5 0 0 1 10 6.5V3H5.5zm5.5.207V6.5a.5.5 0 0 0 .5.5h3.293L11 3.207z', + 'FileSearchSolid': 'M5 2h5v4.5A1.5 1.5 0 0 0 11.5 8H16v9a1 1 0 0 1-1 1h-1.087a1.494 1.494 0 0 0-.354-.563l-3.125-3.124A5 5 0 0 0 4 7.416V3a1 1 0 0 1 1-1zm6 0l5 5h-4.5a.5.5 0 0 1-.5-.5V2zm-1 10c0 .924-.314 1.775-.84 2.453l3.691 3.692a.5.5 0 1 1-.707.707L8.453 15.16A4 4 0 1 1 10 12zm-4 3a3 3 0 1 0 0-6a3 3 0 0 0 0 6z', + 'FileSync': 'M6 2a2 2 0 0 0-2 2v5.207a5.48 5.48 0 0 1 1-.185V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-3.6a5.507 5.507 0 0 1-.657 1H14a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM1 14.5a4.5 4.5 0 1 0 9 0a4.5 4.5 0 0 0-9 0zm6.5-3a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5H6a.5.5 0 0 1 0-1h.468a1.99 1.99 0 0 0-.933-.25a2 2 0 0 0-1.45.586a.5.5 0 0 1-.706-.707A3 3 0 0 1 7 12.152V12a.5.5 0 0 1 .5-.5zm-.876 5.532A2.999 2.999 0 0 1 4 16.848V17a.5.5 0 0 1-1 0v-1.5a.5.5 0 0 1 .5-.5H5a.5.5 0 0 1 0 1h-.468a1.99 1.99 0 0 0 .933.25a2 2 0 0 0 1.45-.586a.5.5 0 0 1 .706.707a3 3 0 0 1-.997.66z', + 'FileSyncSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H9.743A5.5 5.5 0 0 0 4 9.207V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM1 14.5a4.5 4.5 0 1 0 9 0a4.5 4.5 0 0 0-9 0zm6.5-3a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5H6a.5.5 0 0 1 0-1h.468a1.99 1.99 0 0 0-.933-.25a2 2 0 0 0-1.45.586a.5.5 0 0 1-.706-.707A3 3 0 0 1 7 12.152V12a.5.5 0 0 1 .5-.5zm-.876 5.532A2.999 2.999 0 0 1 4 16.848V17a.5.5 0 0 1-1 0v-1.5a.5.5 0 0 1 .5-.5H5a.5.5 0 0 1 0 1h-.468a1.99 1.99 0 0 0 .933.25a2 2 0 0 0 1.45-.586a.5.5 0 0 1 .706.707a3 3 0 0 1-.997.66z', + 'FileText': 'M6.5 10a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1h-7zm0 2a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1h-7zm0 2a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1h-7zM4 4a2 2 0 0 1 2-2h4.586a1.5 1.5 0 0 1 1.06.44l3.915 3.914A1.5 1.5 0 0 1 16 7.414V16a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4zm2-1a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V8h-3.5A1.5 1.5 0 0 1 10 6.5V3H6zm5.5 4h3.293L11 3.207V6.5a.5.5 0 0 0 .5.5z', + 'FileTextSolid': 'M10 6.5V2H5.5A1.5 1.5 0 0 0 4 3.5v13A1.5 1.5 0 0 0 5.5 18h9a1.5 1.5 0 0 0 1.5-1.5V8h-4.5A1.5 1.5 0 0 1 10 6.5zM6.5 10h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm0 2h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm0 2h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zM11 6.5V2.25L15.75 7H11.5a.5.5 0 0 1-.5-.5z', + 'FileJs': 'M4 4a2 2 0 0 1 2-2h4.586a1.5 1.5 0 0 1 1.06.44l3.915 3.914A1.5 1.5 0 0 1 16 7.414V16a2 2 0 0 1-2 2H8.5c.219-.29.375-.63.45-1H14a1 1 0 0 0 1-1V8h-3.5A1.5 1.5 0 0 1 10 6.5V3H6a1 1 0 0 0-1 1v7.5c-.081.061-.16.127-.232.198A1.504 1.504 0 0 0 4 11.085V4zm7.5 3h3.293L11 3.207V6.5a.5.5 0 0 0 .5.5zm-8 5a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0V16a.5.5 0 0 0-1 0v.5a1.5 1.5 0 0 0 3 0v-4a.5.5 0 0 0-.5-.5zM5 13.5a1.5 1.5 0 0 1 3 0a.5.5 0 0 1-1 0a.5.5 0 0 0-1 0v.382a.5.5 0 0 0 .276.447l.895.447A1.5 1.5 0 0 1 8 16.118v.382a1.5 1.5 0 0 1-3 0a.5.5 0 0 1 1 0a.5.5 0 0 0 1 0v-.382a.5.5 0 0 0-.276-.447l-.895-.447A1.5 1.5 0 0 1 5 13.882V13.5z', + 'FileJsSolid': 'M10 6.5V2H5.5A1.5 1.5 0 0 0 4 3.5v7.585c.32.113.589.331.768.613A2.5 2.5 0 0 1 9 13.5c0 .444-.193.843-.5 1.118c.319.425.5.949.5 1.5v.382c0 .563-.186 1.082-.5 1.5h6a1.5 1.5 0 0 0 1.5-1.5V8h-4.5A1.5 1.5 0 0 1 10 6.5zm1 0V2.25L15.75 7H11.5a.5.5 0 0 1-.5-.5zM3.5 12a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0V16a.5.5 0 0 0-1 0v.5a1.5 1.5 0 0 0 3 0v-4a.5.5 0 0 0-.5-.5zM5 13.5a1.5 1.5 0 0 1 3 0a.5.5 0 0 1-1 0a.5.5 0 0 0-1 0v.382a.5.5 0 0 0 .276.447l.895.447A1.5 1.5 0 0 1 8 16.118v.382a1.5 1.5 0 0 1-3 0a.5.5 0 0 1 1 0a.5.5 0 0 0 1 0v-.382a.5.5 0 0 0-.276-.447l-.895-.447A1.5 1.5 0 0 1 5 13.882V13.5z', + 'FileCss': 'M4 4a2 2 0 0 1 2-2h4.586a1.5 1.5 0 0 1 1.06.44l3.915 3.914A1.5 1.5 0 0 1 16 7.414V16a2 2 0 0 1-2 2h-.5c.219-.29.375-.63.45-1H14a1 1 0 0 0 1-1V8h-3.5A1.5 1.5 0 0 1 10 6.5V3H6a1 1 0 0 0-1 1v7.764a2.997 2.997 0 0 0-1-.593V4zm7.5 3h3.293L11 3.207V6.5a.5.5 0 0 0 .5.5zM3 12a2 2 0 0 0-2 2v2a2 2 0 1 0 4 0a.5.5 0 0 0-1 0a1 1 0 1 1-2 0v-2a1 1 0 1 1 2 0a.5.5 0 0 0 1 0a2 2 0 0 0-2-2zm8.5 0a1.5 1.5 0 0 0-1.5 1.5v.382a1.5 1.5 0 0 0 .83 1.342l.894.447a.5.5 0 0 1 .276.447v.382a.5.5 0 0 1-1 0a.5.5 0 0 0-1 0a1.5 1.5 0 0 0 3 0v-.382a1.5 1.5 0 0 0-.83-1.342l-.894-.447a.5.5 0 0 1-.276-.447V13.5a.5.5 0 0 1 1 0a.5.5 0 0 0 1 0a1.5 1.5 0 0 0-1.5-1.5zM6 13.5a1.5 1.5 0 0 1 3 0a.5.5 0 0 1-1 0a.5.5 0 0 0-1 0v.382a.5.5 0 0 0 .276.447l.895.447A1.5 1.5 0 0 1 9 16.118v.382a1.5 1.5 0 0 1-3 0a.5.5 0 0 1 1 0a.5.5 0 0 0 1 0v-.382a.5.5 0 0 0-.276-.447l-.895-.447A1.5 1.5 0 0 1 6 13.882V13.5z', + 'FileCssSolid': 'M10 6.5V2H5.5A1.5 1.5 0 0 0 4 3.5v7.67c.552.196 1.03.548 1.38 1.004A2.498 2.498 0 0 1 9.5 12a2.5 2.5 0 0 1 4.5 1.5c0 .444-.193.843-.5 1.118c.319.425.5.949.5 1.5v.382a2.49 2.49 0 0 1-.5 1.5h1a1.5 1.5 0 0 0 1.5-1.5V8h-4.5A1.5 1.5 0 0 1 10 6.5zm1 0V2.25L15.75 7H11.5a.5.5 0 0 1-.5-.5zM3 12a2 2 0 0 0-2 2v2a2 2 0 1 0 4 0a.5.5 0 0 0-1 0a1 1 0 1 1-2 0v-2a1 1 0 1 1 2 0a.5.5 0 0 0 1 0a2 2 0 0 0-2-2zm8.5 0a1.5 1.5 0 0 0-1.5 1.5v.382a1.5 1.5 0 0 0 .83 1.342l.894.447a.5.5 0 0 1 .276.447v.382a.5.5 0 0 1-1 0a.5.5 0 0 0-1 0a1.5 1.5 0 0 0 3 0v-.382a1.5 1.5 0 0 0-.83-1.342l-.894-.447a.5.5 0 0 1-.276-.447V13.5a.5.5 0 0 1 1 0a.5.5 0 0 0 1 0a1.5 1.5 0 0 0-1.5-1.5zM6 13.5a1.5 1.5 0 0 1 3 0a.5.5 0 0 1-1 0a.5.5 0 0 0-1 0v.382a.5.5 0 0 0 .276.447l.895.447A1.5 1.5 0 0 1 9 16.118v.382a1.5 1.5 0 0 1-3 0a.5.5 0 0 1 1 0a.5.5 0 0 0 1 0v-.382a.5.5 0 0 0-.276-.447l-.895-.447A1.5 1.5 0 0 1 6 13.882V13.5z', + + 'ChevronDoubleRight': 'M8.646 4.147a.5.5 0 0 1 .707-.001l5.484 5.465a.55.55 0 0 1 0 .779l-5.484 5.465a.5.5 0 0 1-.706-.708L13.812 10L8.647 4.854a.5.5 0 0 1-.001-.707zm-4 0a.5.5 0 0 1 .707-.001l5.484 5.465a.55.55 0 0 1 0 .779l-5.484 5.465a.5.5 0 0 1-.706-.708L9.812 10L4.647 4.854a.5.5 0 0 1-.001-.707z', + 'ChevronDoubleRightSolid': 'M8.733 4.207a.75.75 0 0 1 1.06.026l5.001 5.25a.75.75 0 0 1 0 1.035l-5 5.25a.75.75 0 1 1-1.087-1.034L13.215 10L8.707 5.267a.75.75 0 0 1 .026-1.06zm-4 0a.75.75 0 0 1 1.06.026l5.001 5.25a.75.75 0 0 1 0 1.035l-5 5.25a.75.75 0 1 1-1.087-1.034L9.216 10l-4.51-4.734a.75.75 0 0 1 .027-1.06z', + 'ChevronDoubleLeft': 'M11.353 15.854a.5.5 0 0 1-.707.001L5.162 10.39a.55.55 0 0 1 0-.78l5.484-5.464a.5.5 0 1 1 .706.708L6.188 10l5.164 5.147a.5.5 0 0 1 .001.707zm4 0a.5.5 0 0 1-.708.001L9.161 10.39a.55.55 0 0 1 0-.78l5.484-5.464a.5.5 0 1 1 .706.708L10.187 10l5.164 5.147a.5.5 0 0 1 .001.707z', + 'ChevronDoubleLeftSolid': 'M11.269 15.794a.75.75 0 0 1-1.06-.026l-5.002-5.25a.75.75 0 0 1 0-1.035l5.001-5.25a.75.75 0 1 1 1.086 1.034l-4.508 4.734l4.508 4.733a.75.75 0 0 1-.025 1.06zm4 .001a.75.75 0 0 1-1.06-.026l-5.001-5.25a.75.75 0 0 1 0-1.035l5.001-5.25a.75.75 0 1 1 1.086 1.034l-4.508 4.733l4.508 4.734a.75.75 0 0 1-.025 1.06z', + 'MoreHorizontal': 'M6.25 10a1.25 1.25 0 1 1-2.5 0a1.25 1.25 0 0 1 2.5 0zm5 0a1.25 1.25 0 1 1-2.5 0a1.25 1.25 0 0 1 2.5 0zM15 11.25a1.25 1.25 0 1 0 0-2.5a1.25 1.25 0 0 0 0 2.5z', + 'MoreHorizontalSolid': 'M6.75 10a1.75 1.75 0 1 1-3.5 0a1.75 1.75 0 0 1 3.5 0zm5 0a1.75 1.75 0 1 1-3.5 0a1.75 1.75 0 0 1 3.5 0zM15 11.75a1.75 1.75 0 1 0 0-3.5a1.75 1.75 0 0 0 0 3.5z', + 'MoreVertical': 'M10 6a1.25 1.25 0 1 1 0-2.5A1.25 1.25 0 0 1 10 6zm0 5.25a1.25 1.25 0 1 1 0-2.5a1.25 1.25 0 0 1 0 2.5zm-1.25 4a1.25 1.25 0 1 0 2.5 0a1.25 1.25 0 0 0-2.5 0z', + 'MoreVerticalSolid': 'M10 6.5A1.75 1.75 0 1 1 10 3a1.75 1.75 0 0 1 0 3.5zM10 17a1.75 1.75 0 1 1 0-3.5a1.75 1.75 0 0 1 0 3.5zm-1.75-7a1.75 1.75 0 1 0 3.5 0a1.75 1.75 0 0 0-3.5 0z', + + 'Delete': 'M11.5 4a1.5 1.5 0 0 0-3 0h-1a2.5 2.5 0 0 1 5 0H17a.5.5 0 0 1 0 1h-.554L15.15 16.23A2 2 0 0 1 13.163 18H6.837a2 2 0 0 1-1.987-1.77L3.553 5H3a.5.5 0 0 1-.492-.41L2.5 4.5A.5.5 0 0 1 3 4h8.5zm3.938 1H4.561l1.282 11.115a1 1 0 0 0 .994.885h6.326a1 1 0 0 0 .993-.885L15.438 5zM8.5 7.5c.245 0 .45.155.492.359L9 7.938v6.125c0 .241-.224.437-.5.437c-.245 0-.45-.155-.492-.359L8 14.062V7.939c0-.242.224-.438.5-.438zm3 0c.245 0 .45.155.492.359l.008.079v6.125c0 .241-.224.437-.5.437c-.245 0-.45-.155-.492-.359L11 14.062V7.939c0-.242.224-.438.5-.438z', + 'DeleteSolid': 'M10 1.25a2.75 2.75 0 0 1 2.739 2.5H17a.75.75 0 0 1 .102 1.493L17 5.25h-.583L15.15 16.23A2 2 0 0 1 13.163 18H6.837a2 2 0 0 1-1.987-1.77L3.582 5.25H3a.75.75 0 0 1-.743-.648L2.25 4.5a.75.75 0 0 1 .648-.743L3 3.75h4.261A2.75 2.75 0 0 1 10 1.25zM8.5 7.5c-.245 0-.45.155-.492.359L8 7.938v6.125l.008.078c.042.204.247.359.492.359s.45-.155.492-.359L9 14.062V7.939l-.008-.08C8.95 7.656 8.745 7.5 8.5 7.5zm3 0c-.245 0-.45.155-.492.359L11 7.938v6.125l.008.078c.042.204.247.359.492.359s.45-.155.492-.359l.008-.079V7.939l-.008-.08c-.042-.203-.247-.358-.492-.358zM10 2.75c-.605 0-1.11.43-1.225 1h2.45c-.116-.57-.62-1-1.225-1z', + 'DeleteDismiss': 'M11.5 4a1.5 1.5 0 0 0-3 0h3zm-4 0a2.5 2.5 0 0 1 5 0H17a.5.5 0 0 1 0 1h-.554l-.484 4.196a5.484 5.484 0 0 0-.987-.176L15.438 5H4.561l1.282 11.115a1 1 0 0 0 .994.885H9.6c.183.358.404.693.657 1h-3.42a2 2 0 0 1-1.987-1.77L3.553 5H3a.5.5 0 0 1-.492-.41L2.5 4.5A.5.5 0 0 1 3 4h4.5zM19 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-2.646-1.146a.5.5 0 0 0-.708-.708L14.5 13.793l-1.146-1.147a.5.5 0 0 0-.708.708l1.147 1.146l-1.147 1.146a.5.5 0 0 0 .708.708l1.146-1.147l1.146 1.147a.5.5 0 0 0 .708-.708L15.207 14.5l1.147-1.146z', + 'DeleteDismissSolid': 'M10 1.25a2.75 2.75 0 0 1 2.739 2.5H17a.75.75 0 0 1 .102 1.493L17 5.25h-.583l-.455 3.946A5.5 5.5 0 0 0 10.258 18H6.836a2 2 0 0 1-1.987-1.77L3.582 5.25H3a.75.75 0 0 1-.743-.648L2.25 4.5a.75.75 0 0 1 .648-.743L3 3.75h4.261A2.75 2.75 0 0 1 10 1.25zm0 1.5c-.605 0-1.11.43-1.225 1h2.45c-.116-.57-.62-1-1.225-1zm9 11.75a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-2.646-1.146a.5.5 0 0 0-.708-.708L14.5 13.793l-1.146-1.147a.5.5 0 0 0-.708.708l1.147 1.146l-1.147 1.146a.5.5 0 0 0 .708.708l1.146-1.147l1.146 1.147a.5.5 0 0 0 .708-.708L15.207 14.5l1.147-1.146z', + 'DeleteOff': 'M3 4h.293L2.146 2.854a.5.5 0 1 1 .708-.708L8.26 7.553l9.594 9.593a.5.5 0 0 1-.708.708l-1.958-1.958l-.038.333A2 2 0 0 1 13.163 18H6.837a2 2 0 0 1-1.987-1.77L3.553 5H3a.5.5 0 0 1-.492-.41L2.5 4.5A.5.5 0 0 1 3 4zm11.286 10.993L12 12.707v1.355c0 .242-.224.438-.5.438c-.245 0-.45-.155-.492-.359L11 14.062v-2.355l-2-2v4.355c0 .242-.224.438-.5.438c-.245 0-.45-.155-.492-.359L8 14.062V8.707L4.596 5.303l1.247 10.812a1 1 0 0 0 .994.885h6.326a1 1 0 0 0 .993-.885l.13-1.122zm1.195-1.633l-.903-.903l.86-7.457H7.121l-1-1H7.5a2.5 2.5 0 0 1 5 0H17a.5.5 0 0 1 0 1h-.554l-.965 8.36zM11.5 4a1.5 1.5 0 0 0-3 0h3zm.5 5.879l-1-1v-.941c0-.242.224-.438.5-.438c.245 0 .45.155.492.359l.008.079v1.94z', + 'DeleteOffSolid': 'M15.188 15.896l-.038.333A2 2 0 0 1 13.163 18H6.837a2 2 0 0 1-1.987-1.77L3.582 5.25H3a.75.75 0 0 1-.743-.648L2.25 4.5a.75.75 0 0 1 .648-.743L3 3.75h.043l-.897-.896a.5.5 0 1 1 .708-.708L8.26 7.553l9.594 9.593a.5.5 0 0 1-.708.708l-1.958-1.958zM8 8.707v5.355l.008.08c.042.203.247.358.492.358s.45-.155.492-.359L9 14.062V9.707l-1-1zm3 3v2.355l.008.08c.042.203.247.358.492.358s.45-.155.492-.359l.008-.079v-1.355l-1-1zm0-2.828L5.871 3.75h1.39a2.75 2.75 0 0 1 5.478 0H17a.75.75 0 0 1 .102 1.493L17 5.25h-.583l-.936 8.11L12 9.879V7.938l-.008-.08c-.042-.203-.247-.358-.492-.358s-.45.155-.492.359L11 7.938v.94zM10 2.75c-.605 0-1.11.43-1.225 1h2.45c-.116-.57-.62-1-1.225-1z', + 'DeleteArrowBack': 'M11.5 4a1.5 1.5 0 0 0-3 0h3zm-4 0a2.5 2.5 0 0 1 5 0H17a.5.5 0 0 1 0 1h-.554l-.484 4.196a5.484 5.484 0 0 0-.987-.176L15.438 5H4.561l1.282 11.115a1 1 0 0 0 .994.885H9.6c.183.358.404.693.657 1h-3.42a2 2 0 0 1-1.987-1.77L3.553 5H3a.5.5 0 0 1-.492-.41L2.5 4.5A.5.5 0 0 1 3 4h4.5zm7 15a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm-.896-6.396l-.897.896h1.543A2.75 2.75 0 0 1 17 16.25v.25a.5.5 0 0 1-1 0v-.25a1.75 1.75 0 0 0-1.75-1.75h-1.543l.897.896a.5.5 0 0 1-.708.708l-1.752-1.753a.499.499 0 0 1 .002-.705l1.75-1.75a.5.5 0 0 1 .708.708z', + 'DeleteArrowBackSolid': 'M10 1.25a2.75 2.75 0 0 1 2.739 2.5H17a.75.75 0 0 1 .102 1.493L17 5.25h-.583l-.455 3.946A5.5 5.5 0 0 0 10.258 18H6.836a2 2 0 0 1-1.987-1.77L3.582 5.25H3a.75.75 0 0 1-.743-.648L2.25 4.5a.75.75 0 0 1 .648-.743L3 3.75h4.261A2.75 2.75 0 0 1 10 1.25zm0 1.5c-.605 0-1.11.43-1.225 1h2.45c-.116-.57-.62-1-1.225-1zM14.5 19a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm-.896-6.396l-.897.896h1.543A2.75 2.75 0 0 1 17 16.25v.25a.5.5 0 0 1-1 0v-.25a1.75 1.75 0 0 0-1.75-1.75h-1.543l.897.896a.5.5 0 0 1-.708.708l-1.752-1.753a.499.499 0 0 1 .002-.705l1.75-1.75a.5.5 0 0 1 .708.708z', + 'DeleteLines': 'M7.5 4a2.5 2.5 0 0 1 5 0H17a.5.5 0 0 1 0 1h-.554l-.923 8h-1.007l.922-8H4.561l1.282 11.115a1 1 0 0 0 .994.885h5.248c.066.186.168.356.297.5c-.13.144-.23.314-.297.5H6.837a2 2 0 0 1-1.987-1.77L3.553 5H3a.5.5 0 0 1-.492-.41L2.5 4.5A.5.5 0 0 1 3 4h4.5zm4 0a1.5 1.5 0 0 0-3 0h3zm2 12a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1h-5zm0-2a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1h-5zm-.5 4.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z', + 'DeleteLinesSolid': 'M10 1.25a2.75 2.75 0 0 1 2.739 2.5H17a.75.75 0 0 1 .102 1.493L17 5.25h-.583L15.523 13H13.5a1.5 1.5 0 0 0-1.118 2.5a1.494 1.494 0 0 0-.382 1c0 .384.144.735.382 1c-.13.144-.23.314-.297.5H6.837a2 2 0 0 1-1.987-1.77L3.582 5.25H3a.75.75 0 0 1-.743-.648L2.25 4.5a.75.75 0 0 1 .648-.743L3 3.75h4.261A2.75 2.75 0 0 1 10 1.25zm0 1.5c-.605 0-1.11.43-1.225 1h2.45c-.116-.57-.62-1-1.225-1zm3 11.75a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0 2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5zm0 2a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z', + + 'Eye': 'M3.26 11.602C3.942 8.327 6.793 6 10 6c3.206 0 6.057 2.327 6.74 5.602a.5.5 0 0 0 .98-.204C16.943 7.673 13.693 5 10 5c-3.693 0-6.943 2.673-7.72 6.398a.5.5 0 0 0 .98.204zM10 8a3.5 3.5 0 1 0 0 7a3.5 3.5 0 0 0 0-7zm-2.5 3.5a2.5 2.5 0 1 1 5 0a2.5 2.5 0 0 1-5 0z', + 'EyeSolid': 'M3.26 11.602C3.942 8.327 6.793 6 10 6c3.206 0 6.057 2.327 6.74 5.602a.5.5 0 0 0 .98-.204C16.943 7.673 13.693 5 10 5c-3.693 0-6.943 2.673-7.72 6.398a.5.5 0 0 0 .98.204zM9.99 8a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7z', + 'EyeOff': 'M2.854 2.146a.5.5 0 1 0-.708.708l3.5 3.498a8.097 8.097 0 0 0-3.366 5.046a.5.5 0 1 0 .98.204a7.09 7.09 0 0 1 3.107-4.528L7.953 8.66a3.5 3.5 0 1 0 4.886 4.886l4.307 4.308a.5.5 0 0 0 .708-.708l-15-15zm9.265 10.68A2.5 2.5 0 1 1 8.673 9.38l3.446 3.447zm-1.995-4.824l3.374 3.374a3.5 3.5 0 0 0-3.374-3.374zM10 6c-.57 0-1.129.074-1.666.213l-.803-.803A7.648 7.648 0 0 1 10 5c3.693 0 6.942 2.673 7.72 6.398a.5.5 0 0 1-.98.204C16.058 8.327 13.207 6 10 6z', + 'EyeOffSolid': 'M2.854 2.146a.5.5 0 1 0-.708.708l3.5 3.498a8.097 8.097 0 0 0-3.366 5.046a.5.5 0 1 0 .979.204a7.09 7.09 0 0 1 3.108-4.528L7.95 8.656a3.5 3.5 0 1 0 4.884 4.884l4.313 4.314a.5.5 0 0 0 .708-.708l-15-15zm7.27 5.857l3.363 3.363a3.5 3.5 0 0 0-3.363-3.363zM7.53 5.41l.803.803A6.632 6.632 0 0 1 10 6c3.206 0 6.057 2.327 6.74 5.602a.5.5 0 1 0 .98-.204C16.943 7.673 13.693 5 10 5c-.855 0-1.687.143-2.469.41z', + 'EyeTracking': 'M3 4.5A1.5 1.5 0 0 1 4.5 3h3a.5.5 0 0 0 0-1h-3A2.5 2.5 0 0 0 2 4.5v3a.5.5 0 0 0 1 0v-3zm0 11A1.5 1.5 0 0 0 4.5 17h3a.5.5 0 0 1 0 1h-3A2.5 2.5 0 0 1 2 15.5v-3a.5.5 0 0 1 1 0v3zM15.5 3A1.5 1.5 0 0 1 17 4.5v3a.5.5 0 0 0 1 0v-3A2.5 2.5 0 0 0 15.5 2h-3a.5.5 0 0 0 0 1h3zM17 15.5a1.5 1.5 0 0 1-1.5 1.5h-3a.5.5 0 0 0 0 1h3a2.5 2.5 0 0 0 2.5-2.5v-3a.5.5 0 0 0-1 0v3zm-10-4a3 3 0 1 1 6 0a3 3 0 0 1-6 0zm3-2a2 2 0 1 0 0 4a2 2 0 0 0 0-4zm-5.052.223v.001a.5.5 0 0 1-.895-.448L4.5 9.5a24.558 24.558 0 0 1-.447-.225l.001-.001l.002-.004l.005-.01a2.106 2.106 0 0 1 .082-.145a5.14 5.14 0 0 1 .249-.377A6.49 6.49 0 0 1 5.425 7.62C6.375 6.805 7.863 6 10 6s3.624.805 4.575 1.62c.473.406.812.812 1.034 1.119a5.13 5.13 0 0 1 .33.521l.005.01l.002.004l.001.002l-.447.224l.447-.224a.5.5 0 0 1-.893.45v-.002l-.002-.001l-.009-.018a4.133 4.133 0 0 0-.245-.381a5.487 5.487 0 0 0-.873-.944C13.125 7.695 11.863 7 10 7s-3.125.695-3.924 1.38a5.49 5.49 0 0 0-.874.944a4.14 4.14 0 0 0-.245.381l-.01.018z', + 'EyeTrackingSolid': 'M4.5 3A1.5 1.5 0 0 0 3 4.5v3a.5.5 0 0 1-1 0v-3A2.5 2.5 0 0 1 4.5 2h3a.5.5 0 0 1 0 1h-3zm0 14A1.5 1.5 0 0 1 3 15.5v-3a.5.5 0 0 0-1 0v3A2.5 2.5 0 0 0 4.5 18h3a.5.5 0 0 0 0-1h-3zM17 4.5A1.5 1.5 0 0 0 15.5 3h-3a.5.5 0 0 1 0-1h3A2.5 2.5 0 0 1 18 4.5v3a.5.5 0 0 1-1 0v-3zM15.5 17a1.5 1.5 0 0 0 1.5-1.5v-3a.5.5 0 0 1 1 0v3a2.5 2.5 0 0 1-2.5 2.5h-3a.5.5 0 0 1 0-1h3zM7 11.5a3 3 0 1 1 6 0a3 3 0 0 1-6 0zM4.948 9.723v.001a.5.5 0 0 1-.895-.448L4.5 9.5a24.558 24.558 0 0 1-.447-.225l.001-.001l.002-.004l.005-.01a2.106 2.106 0 0 1 .082-.145a5.14 5.14 0 0 1 .249-.377A6.49 6.49 0 0 1 5.425 7.62C6.375 6.805 7.863 6 10 6s3.624.805 4.575 1.62c.473.406.812.812 1.034 1.119a5.13 5.13 0 0 1 .33.521l.005.01l.002.004l.001.002l-.447.224l.447-.224a.5.5 0 0 1-.893.45v-.002l-.002-.001l-.009-.018a4.133 4.133 0 0 0-.245-.381a5.487 5.487 0 0 0-.873-.944C13.125 7.695 11.863 7 10 7s-3.125.695-3.924 1.38a5.49 5.49 0 0 0-.874.944a4.14 4.14 0 0 0-.245.381l-.01.018z', + 'EyeTrackingOff': 'M2.414 3.121C2.152 3.517 2 3.991 2 4.5v3a.5.5 0 0 0 1 0v-3c0-.232.052-.45.146-.647l3.141 3.141A6.592 6.592 0 0 0 4.392 8.74a5.14 5.14 0 0 0-.33.521l-.006.01l-.002.004v.001s-.001.001.446.225l-.447-.224a.5.5 0 0 0 .894.448v-.001l.01-.018l.045-.078a4.14 4.14 0 0 1 .2-.303A5.582 5.582 0 0 1 7.02 7.726l1.293 1.293a3 3 0 1 0 4.168 4.168l3.667 3.667A1.494 1.494 0 0 1 15.5 17h-3a.5.5 0 0 0 0 1h3c.51 0 .983-.152 1.379-.414l.267.268a.5.5 0 0 0 .708-.707l-.268-.268l-.732-.732l-3.938-3.938L9.29 8.584L8.007 7.3l-.78-.78l-3.374-3.374l-.732-.732l-.267-.268a.5.5 0 1 0-.708.708l.268.267zm9.34 9.34A2 2 0 1 1 9.04 9.746l2.715 2.715zm6.221 3.393c.016-.116.025-.234.025-.354v-3a.5.5 0 0 0-1 0v2.379l.975.975zM9.17 7.048C9.432 7.017 9.709 7 10 7c1.863 0 3.126.695 3.925 1.38c.402.344.688.688.873.944a4.133 4.133 0 0 1 .245.381l.01.018v.002a.5.5 0 0 0 .894-.449v-.002l-.003-.004l-.005-.01a5.13 5.13 0 0 0-.33-.522a6.491 6.491 0 0 0-1.034-1.118C13.626 6.805 12.137 6 10 6a7.68 7.68 0 0 0-1.695.183l.865.865zm6.777 2.228l-.058.03l-.387.193l.445-.223zM5.121 3H7.5a.5.5 0 0 0 0-1h-3c-.12 0-.238.008-.354.025L5.121 3zM4.5 17A1.5 1.5 0 0 1 3 15.5v-3a.5.5 0 0 0-1 0v3A2.5 2.5 0 0 0 4.5 18h3a.5.5 0 0 0 0-1h-3zm11-14A1.5 1.5 0 0 1 17 4.5v3a.5.5 0 0 0 1 0v-3A2.5 2.5 0 0 0 15.5 2h-3a.5.5 0 0 0 0 1h3z', + 'EyeTrackingOffSolid': 'M2.414 3.121C2.152 3.517 2 3.991 2 4.5v3a.5.5 0 0 0 1 0v-3c0-.232.052-.45.146-.647l3.141 3.141A6.592 6.592 0 0 0 4.392 8.74a5.14 5.14 0 0 0-.33.521l-.006.01l-.002.004v.001s-.001.001.446.225l-.447-.224a.5.5 0 0 0 .894.448v-.001l.01-.018l.045-.078a4.14 4.14 0 0 1 .2-.303A5.582 5.582 0 0 1 7.02 7.726l1.293 1.293a3 3 0 1 0 4.168 4.168l3.667 3.667A1.494 1.494 0 0 1 15.5 17h-3a.5.5 0 0 0 0 1h3c.51 0 .983-.152 1.379-.414l.267.268a.5.5 0 0 0 .708-.707l-.268-.268l-.732-.732l-3.938-3.938L9.29 8.584L8.007 7.3l-.78-.78l-3.374-3.374l-.732-.732l-.267-.268a.5.5 0 1 0-.708.708l.268.267zm15.561 12.733c.016-.116.025-.234.025-.354v-3a.5.5 0 0 0-1 0v2.379l.975.975zM9.17 7.048C9.432 7.017 9.709 7 10 7c1.863 0 3.126.695 3.925 1.38c.402.344.688.688.873.944a4.133 4.133 0 0 1 .245.381l.01.018v.002a.5.5 0 0 0 .894-.449v-.002l-.003-.004l-.005-.01a5.13 5.13 0 0 0-.33-.522a6.491 6.491 0 0 0-1.034-1.118C13.626 6.805 12.137 6 10 6a7.68 7.68 0 0 0-1.695.183l.865.865zm6.777 2.228l-.058.03l-.387.193l.445-.223zM5.121 3H7.5a.5.5 0 0 0 0-1h-3c-.12 0-.238.008-.354.025L5.121 3zM3 15.5A1.5 1.5 0 0 0 4.5 17h3a.5.5 0 0 1 0 1h-3A2.5 2.5 0 0 1 2 15.5v-3a.5.5 0 0 1 1 0v3zm14-11A1.5 1.5 0 0 0 15.5 3h-3a.5.5 0 0 1 0-1h3A2.5 2.5 0 0 1 18 4.5v3a.5.5 0 0 1-1 0v-3z', + + 'Share': 'M13.33 12.838l4.497-4.423l.057-.065a.587.587 0 0 0-.057-.767L13.33 3.162l-.062-.053c-.36-.27-.89-.01-.89.469v2.13l-.225.015c-3.563.282-5.65 2.537-6.148 6.627c-.064.525.538.854.928.506c1.431-1.278 2.91-2.072 4.445-2.39c.246-.051.493-.09.742-.117l.258-.023v2.096l.005.082c.06.453.609.666.947.334zM12.226 6.72l1.152-.077V4.61l3.446 3.388l-3.446 3.39V9.231l-1.356.122h-.008c-1.703.183-3.31.865-4.827 2.002c.298-1.339.807-2.346 1.476-3.067c.83-.895 1.99-1.443 3.563-1.569zM5.5 4A2.5 2.5 0 0 0 3 6.5v8A2.5 2.5 0 0 0 5.5 17h8a2.5 2.5 0 0 0 2.5-2.5v-1a.5.5 0 0 0-1 0v1a1.5 1.5 0 0 1-1.5 1.5h-8A1.5 1.5 0 0 1 4 14.5v-8A1.5 1.5 0 0 1 5.5 5h3a.5.5 0 0 0 0-1h-3z', + 'ShareSolid': 'M12.378 5.708v-2.13c0-.48.53-.738.89-.47l.062.054l4.497 4.42c.21.207.229.539.057.768l-.057.065l-4.497 4.423c-.338.332-.887.119-.947-.334l-.005-.082v-2.096l-.258.023c-1.8.193-3.526 1.024-5.187 2.507c-.39.348-.992.02-.928-.506c.498-4.09 2.585-6.345 6.148-6.627l.225-.015zM5.5 4A2.5 2.5 0 0 0 3 6.5v8A2.5 2.5 0 0 0 5.5 17h8a2.5 2.5 0 0 0 2.5-2.5v-1a.5.5 0 0 0-1 0v1a1.5 1.5 0 0 1-1.5 1.5h-8A1.5 1.5 0 0 1 4 14.5v-8A1.5 1.5 0 0 1 5.5 5h3a.5.5 0 0 0 0-1h-3z', + + 'Alert': 'M9.998 2c3.149 0 5.744 2.335 5.984 5.355l.014.223l.004.224l-.001 3.596l.925 2.222c.023.054.04.11.053.167l.016.086l.008.132a1 1 0 0 1-.749.963l-.116.027l-.135.01l-3.501-.001l-.005.161a2.5 2.5 0 0 1-4.99 0l-.005-.161H3.999a.998.998 0 0 1-.26-.034l-.124-.042a1 1 0 0 1-.603-1.052l.021-.128l.043-.128l.923-2.219L4 7.793l.004-.225C4.127 4.451 6.771 2 9.998 2zM11.5 15.004h-3l.007.141a1.5 1.5 0 0 0 1.349 1.348L10 16.5a1.5 1.5 0 0 0 1.493-1.355l.007-.141zM9.998 3c-2.623 0-4.77 1.924-4.98 4.385l-.014.212L5 7.802V11.5l-.038.192l-.963 2.313l11.958.002l.045-.002l-.964-2.313L15 11.5V7.812l-.004-.204C14.891 5.035 12.695 3 9.998 3z', + 'AlertSolid': 'M12.45 16.002a2.5 2.5 0 0 1-4.9 0h4.9zM9.998 2c3.149 0 5.744 2.335 5.984 5.355l.013.223l.005.224l-.001 3.606l.954 2.587l.025.085l.016.086l.005.089c0 .315-.196.59-.522.707l-.114.033l-.114.01H3.751a.75.75 0 0 1-.259-.047c-.287-.105-.476-.372-.482-.716l.004-.117l.034-.13l.95-2.584L4 7.793l.004-.225C4.127 4.451 6.771 2 9.998 2z', + 'AlertOff': 'M4.004 7.568a5.62 5.62 0 0 1 .58-2.277L2.146 2.854a.5.5 0 1 1 .708-.708l15 15a.5.5 0 0 1-.708.708l-2.849-2.85H12.5l-.005.161a2.5 2.5 0 0 1-4.99 0l-.005-.161H3.999a.998.998 0 0 1-.26-.034l-.124-.042a1 1 0 0 1-.603-1.052l.021-.128l.043-.128l.923-2.219L4 7.793l.004-.225zm9.295 6.438l-7.96-7.96c-.171.42-.282.87-.322 1.339l-.013.212L5 7.802V11.5l-.038.192l-.963 2.313l9.3.001zm-1.8.998h-3l.008.141a1.5 1.5 0 0 0 1.349 1.348L10 16.5a1.5 1.5 0 0 0 1.493-1.355l.007-.141zm3.54-3.312l.874 2.1l.852.852a.977.977 0 0 0 .236-.64l-.008-.13l-.016-.087a.996.996 0 0 0-.053-.167L16 11.398L16 7.802l-.005-.224l-.013-.223C15.742 4.335 13.147 2 9.998 2c-1.64 0-3.128.633-4.213 1.664l.707.707A5.1 5.1 0 0 1 9.998 3c2.697 0 4.893 2.035 4.998 4.608l.004.204V11.5l.038.192z', + 'AlertOffSolid': 'M4.004 7.568a5.62 5.62 0 0 1 .58-2.277L2.146 2.854a.5.5 0 1 1 .708-.708l15 15a.5.5 0 0 1-.708.708l-2.849-2.85H3.752a.75.75 0 0 1-.259-.046c-.287-.105-.476-.372-.482-.716l.004-.117l.034-.13l.95-2.584L4 7.793l.004-.225zM17 14.255a.72.72 0 0 1-.163.46L5.786 3.663A6.095 6.095 0 0 1 9.997 2c3.149 0 5.744 2.335 5.984 5.355l.013.223l.005.224l-.001 3.606l.954 2.587l.025.085l.016.086l.005.089zm-4.55 1.747a2.5 2.5 0 0 1-4.899 0h4.9z', + 'AlertOn': 'M1.796 2.098a.5.5 0 1 0-.6.8L3.198 4.4a.5.5 0 1 0 .6-.8L1.796 2.098zM1 7a.5.5 0 0 0 0 1h1.5a.5.5 0 0 0 0-1H1zm8.998-5c3.149 0 5.744 2.334 5.984 5.355l.014.222l.004.225l-.001 3.596l.925 2.222a1 1 0 0 1 .053.167l.016.086l.008.131a1 1 0 0 1-.749.963l-.116.027l-.135.01H12.5l-.005.16a2.5 2.5 0 0 1-4.99 0l-.005-.16H3.999c-.088 0-.175-.011-.26-.034l-.124-.043a1 1 0 0 1-.603-1.052l.021-.127l.043-.128l.923-2.22L4 7.793l.004-.224C4.127 4.45 6.771 2 9.998 2zM11.5 15.004h-3l.007.141a1.5 1.5 0 0 0 1.349 1.348L10 16.5a1.5 1.5 0 0 0 1.493-1.356l.007-.14zM9.998 3c-2.623 0-4.77 1.923-4.98 4.385l-.014.212L5 7.802V11.5l-.038.192l-.963 2.312l11.958.002l.045-.002l-.964-2.312L15 11.5V7.812l-.004-.204C14.891 5.034 12.695 3 9.998 3zm8.906-.802a.5.5 0 0 0-.7-.1L16.202 3.6a.5.5 0 0 0 .6.8l2.002-1.502a.5.5 0 0 0 .1-.7zM19.5 7.5A.5.5 0 0 0 19 7h-1.5a.5.5 0 0 0 0 1H19a.5.5 0 0 0 .5-.5z', + 'AlertOnSolid': 'M1.796 2.098a.5.5 0 1 0-.6.8L3.198 4.4a.5.5 0 1 0 .6-.8L1.796 2.098zM1 7a.5.5 0 0 0 0 1h1.5a.5.5 0 0 0 0-1H1zM12.45 16a2.501 2.501 0 0 1-4.9 0h4.9zM9.998 2c3.149 0 5.744 2.334 5.984 5.355l.014.222l.004.225l-.001 3.606l.954 2.587l.025.084l.016.087l.005.088c0 .315-.196.59-.522.707l-.113.033l-.115.01H3.751a.75.75 0 0 1-.259-.046c-.287-.106-.476-.372-.482-.716l.004-.118l.034-.13l.951-2.583L4 7.792l.004-.224C4.127 4.45 6.771 2 9.998 2zm8.906.198a.5.5 0 0 0-.7-.1L16.202 3.6a.5.5 0 0 0 .6.8l2.002-1.502a.5.5 0 0 0 .1-.7zM19.5 7.5A.5.5 0 0 0 19 7h-1.5a.5.5 0 0 0 0 1H19a.5.5 0 0 0 .5-.5z', + 'AlertSnooze': 'M5 11.5V8.055A.505.505 0 0 0 5.003 8a5 5 0 0 1 6.36-4.813a.5.5 0 1 0 .272-.962A6 6 0 0 0 4.004 7.94A.504.504 0 0 0 4 7.998V11.4l-.923 2.216A1 1 0 0 0 4 15h3.5a2.5 2.5 0 0 0 5 0H16a1 1 0 0 0 .923-1.384L16 11.4V9.998a.5.5 0 0 0-1 0V11.5a.5.5 0 0 0 .039.192L16 14H4l.962-2.308A.5.5 0 0 0 5 11.5zM8.5 15h3a1.5 1.5 0 0 1-3 0zM14 2h3.5a.5.5 0 0 1 .452.714l-.043.073L14.96 7h2.54a.5.5 0 0 1 .09.992L17.5 8H14a.5.5 0 0 1-.452-.714l.042-.073L16.54 3H14a.5.5 0 0 1-.09-.992L14 2zM9.5 6h2.5a.5.5 0 0 1 .432.753l-.048.067L10.57 9H12a.5.5 0 0 1 .09.992L12 10h-2.5a.5.5 0 0 1-.432-.753l.048-.067L10.933 7H9.501a.5.5 0 0 1-.09-.992L9.501 6z', + 'AlertSnoozeOff': 'M9.998 2c.891 0 1.738.187 2.5.524A1.5 1.5 0 0 0 13.998 4h.627l-1.286 1.826A1.475 1.475 0 0 0 11.999 5H9.454l-.18.016l-.044.008a1.5 1.5 0 0 0-.33 2.852l-.578.694l-.094.131l-.02.034C7.63 9.705 8.305 11 9.498 11h2.546l.179-.016l.044-.008a1.5 1.5 0 0 0 1.088-2.117c.191.09.407.141.643.141H16v2.408l.953 2.587l.026.085l.015.086l.005.089c0 .315-.195.59-.522.707l-.113.033l-.115.01H3.752a.75.75 0 0 1-.26-.047c-.287-.105-.475-.372-.482-.716l.004-.117l.034-.13l.951-2.584L4 7.793l.005-.225C4.127 4.451 6.77 2 9.998 2zm2.452 14.002a2.501 2.501 0 0 1-4.9 0h4.9zM13.998 2h3.5a.5.5 0 0 1 .452.714l-.042.073L14.958 7h2.54a.5.5 0 0 1 .09.992l-.09.008h-3.5a.5.5 0 0 1-.452-.714l.042-.073L16.538 3h-2.54a.5.5 0 0 1-.09-.992l.09-.008zM9.499 6h2.5a.5.5 0 0 1 .432.753l-.048.067L10.567 9h1.432a.5.5 0 0 1 .09.992l-.09.008h-2.5a.5.5 0 0 1-.432-.753l.048-.067L10.93 7H9.5a.5.5 0 0 1-.09-.992L9.5 6z', + 'AlertUrgent': 'M13.264 2.078a.5.5 0 1 0-.523.852c2.258 1.384 4.12 3.414 4.26 7.09A.5.5 0 0 0 18 9.982c-.157-4.099-2.278-6.398-4.736-7.904zm-1.178 2.65a.5.5 0 0 1 .694-.134c1.607 1.085 2.715 2.638 2.888 4.424c.016.16.024.323.024.487a.5.5 0 0 1-1 0c0-.132-.007-.262-.02-.39c-.136-1.418-1.024-2.728-2.452-3.693a.5.5 0 0 1-.134-.694zm-7.006.71a5.158 5.158 0 0 0-2.614 6.811l1.223 2.749l.09 2.32a.75.75 0 0 0 1.054.656l9.727-4.33a.75.75 0 0 0 .218-1.223l-1.664-1.619l-1.224-2.749a5.158 5.158 0 0 0-6.81-2.614zm-1.7 6.404a4.158 4.158 0 0 1 7.596-3.382l1.302 2.925l1.538 1.495l-9.052 4.03l-.083-2.143l-1.302-2.925zm7.298 6.034a1.49 1.49 0 0 1-1.848-.54l2.685-1.194a1.49 1.49 0 0 1-.837 1.734z', + 'AlertUrgentSolid': 'M2.466 12.25a5.158 5.158 0 0 1 9.424-4.197l1.224 2.749l1.664 1.619a.75.75 0 0 1-.218 1.222l-9.727 4.331a.75.75 0 0 1-1.054-.656l-.09-2.32l-1.223-2.749zm6.364 5.087a1.49 1.49 0 0 0 2.685-1.195L8.83 17.337zm3.256-12.609a.5.5 0 0 1 .694-.134c1.607 1.085 2.715 2.638 2.888 4.424c.016.16.024.323.024.487a.5.5 0 1 1-1 0a4.04 4.04 0 0 0-.02-.39c-.136-1.418-1.024-2.728-2.452-3.693a.5.5 0 0 1-.134-.694zm.49-2.485a.5.5 0 0 1 .688-.165c2.458 1.506 4.58 3.805 4.736 7.904a.5.5 0 0 1-1 .038C16.86 6.344 15 4.314 12.741 2.93a.5.5 0 0 1-.165-.687z', + + 'ArrowClockwise': 'M3.066 9.05a7 7 0 0 1 12.557-3.22l.126.17H12.5a.5.5 0 1 0 0 1h4a.5.5 0 0 0 .5-.5V2.502a.5.5 0 0 0-1 0v2.207a8 8 0 1 0 1.986 4.775a.5.5 0 0 0-.998.064A7 7 0 1 1 3.066 9.05z', + 'ArrowClockwiseSolid': 'M10.628 2.025a8 8 0 1 0 7.367 7.714a.75.75 0 1 0-1.5.045a6.5 6.5 0 1 1-1.573-4.029l.204.248h-2.379l-.101.008a.75.75 0 0 0 0 1.486l.101.007h4l.102-.007a.75.75 0 0 0 .641-.641l.007-.102v-4l-.007-.102a.75.75 0 0 0-.641-.641l-.102-.007l-.102.007a.75.75 0 0 0-.641.641l-.007.102l.001 1.953a7.977 7.977 0 0 0-5.37-2.682z', + 'ArrowClockwiseDashes': 'M8.132 2.22a8.02 8.02 0 0 1 3.736 0a.5.5 0 0 1-.233.972a7.02 7.02 0 0 0-3.27 0a.5.5 0 1 1-.233-.973zM6.507 3.342a.5.5 0 0 1-.165.687A7.039 7.039 0 0 0 4.03 6.342a.5.5 0 0 1-.852-.523A8.039 8.039 0 0 1 5.82 3.18a.5.5 0 0 1 .688.164zm7.674-.165a.5.5 0 1 0-.523.852A7.04 7.04 0 0 1 15.745 6H12.5a.5.5 0 0 0 0 1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 0-1 0v2.208a8.035 8.035 0 0 0-1.82-1.53zM2.822 7.762a.5.5 0 0 1 .37.603a7.02 7.02 0 0 0 0 3.27a.5.5 0 0 1-.973.233a8.02 8.02 0 0 1 0-3.736a.5.5 0 0 1 .603-.37zM18 10v-.5a.5.5 0 0 0-1 0v.5a7.02 7.02 0 0 1-.192 1.635a.5.5 0 1 0 .973.233c.143-.6.219-1.225.219-1.868zM3.343 13.493a.5.5 0 0 1 .687.165a7.038 7.038 0 0 0 2.312 2.312a.5.5 0 1 1-.523.852a8.038 8.038 0 0 1-2.64-2.641a.5.5 0 0 1 .164-.688zm13.479.688a.5.5 0 0 0-.852-.523a7.037 7.037 0 0 1-2.313 2.312a.5.5 0 0 0 .524.852a8.037 8.037 0 0 0 2.64-2.641zm-9.06 2.997a.5.5 0 0 1 .603-.37a7.02 7.02 0 0 0 3.27 0a.5.5 0 1 1 .233.973a8.02 8.02 0 0 1-3.736 0a.5.5 0 0 1-.37-.603z', + 'ArrowClockwiseDashesSolid': 'M8.44 2.152a8.035 8.035 0 0 1 3.12 0a.75.75 0 0 1-.29 1.472a6.536 6.536 0 0 0-2.54 0a.75.75 0 0 1-.29-1.472zm4.965 1.402a.75.75 0 0 1 1.04-.206A8.04 8.04 0 0 1 16 4.708V2.75a.75.75 0 0 1 1.5 0v4a.75.75 0 0 1-.75.75h-4a.75.75 0 0 1 0-1.5h2.374a6.541 6.541 0 0 0-1.513-1.406a.75.75 0 0 1-.206-1.04zm-7.016 1.04a.75.75 0 0 0-.834-1.246a8.04 8.04 0 0 0-2.207 2.207a.75.75 0 0 0 1.246.834A6.54 6.54 0 0 1 6.39 4.594zM3.034 7.85a.75.75 0 0 1 .59.882a6.535 6.535 0 0 0 0 2.538a.75.75 0 0 1-1.472.291a8.035 8.035 0 0 1 0-3.12a.75.75 0 0 1 .882-.59zM18 10v-.25a.75.75 0 0 0-1.5 0V10c0 .435-.043.86-.124 1.27a.75.75 0 1 0 1.472.29c.1-.505.152-1.027.152-1.56zM3.554 13.405a.75.75 0 0 1 1.04.206a6.54 6.54 0 0 0 1.795 1.795a.75.75 0 0 1-.834 1.246a8.042 8.042 0 0 1-2.207-2.207a.75.75 0 0 1 .206-1.04zm13.098 1.04a.75.75 0 0 0-1.246-.834a6.54 6.54 0 0 1-1.795 1.795a.75.75 0 0 0 .834 1.246a8.043 8.043 0 0 0 2.207-2.207zM7.85 16.966a.75.75 0 0 1 .882-.59a6.535 6.535 0 0 0 2.538 0a.75.75 0 1 1 .291 1.472a8.033 8.033 0 0 1-3.12 0a.75.75 0 0 1-.59-.881z', + 'ArrowHookDownLeft': 'M6 4.5a.5.5 0 0 1 .5-.5H11c1.636 0 2.9.618 3.749 1.574C15.59 6.521 16 7.768 16 9c0 1.232-.41 2.48-1.251 3.426C13.899 13.382 12.636 14 11 14H5.707l2.647 2.646a.5.5 0 0 1-.708.708l-3.5-3.5a.5.5 0 0 1 0-.708l3.5-3.5a.5.5 0 1 1 .708.708L5.707 13H11c1.364 0 2.35-.507 3.001-1.238C14.66 11.02 15 10.018 15 9s-.34-2.02-.999-2.762C13.351 5.507 12.364 5 11 5H6.5a.5.5 0 0 1-.5-.5z', + 'ArrowHookDownLeftSolid': 'M6 4.75A.75.75 0 0 1 6.75 4h4.5c1.586 0 2.696.621 3.53 1.588C15.6 6.54 16 7.784 16 9c0 1.216-.3 2.46-1.12 3.412c-.834.967-2.044 1.588-3.63 1.588H6.56l2.22 2.22a.75.75 0 1 1-1.06 1.06l-3.5-3.5a.75.75 0 0 1 .02-1.08l3.5-3.25a.75.75 0 0 1 1.02 1.1l-2.1 1.95h4.59c1.164 0 1.86-.441 2.4-1.068c.554-.642.85-1.523.85-2.432s-.296-1.79-.85-2.432c-.54-.627-1.236-1.068-2.4-1.068h-4.5A.75.75 0 0 1 6 4.75z', + 'ArrowHookDownRight': 'M4 9a5 5 0 0 1 5-5h4.5a.5.5 0 0 1 0 1H9a4 4 0 1 0 0 8h5.293l-2.7-2.7a.5.5 0 1 1 .708-.706l3.539 3.539a.5.5 0 0 1 .125.497a.499.499 0 0 1-.135.247l-3.533 3.533a.5.5 0 0 1-.707-.707L14.293 14H9a5 5 0 0 1-5-5z', + 'ArrowHookDownRightSolid': 'M9 14c.06 0-.06.002 0 0c.023.002.227 0 .25 0h4.393l-2.268 2.268a.75.75 0 1 0 1.061 1.06l3.353-3.352a.749.749 0 0 0 .212-.639a.747.747 0 0 0-.215-.444L12.54 9.646a.75.75 0 1 0-1.06 1.061L13.27 12.5H9a3.5 3.5 0 1 1 0-7h4.25a.75.75 0 0 0 0-1.5H9a5 5 0 0 0 0 10z', + + 'Bookmark': 'M4 4.5A2.5 2.5 0 0 1 6.5 2h7A2.5 2.5 0 0 1 16 4.5v13a.5.5 0 0 1-.794.404L10 14.118l-5.206 3.786A.5.5 0 0 1 4 17.5v-13zM6.5 3A1.5 1.5 0 0 0 5 4.5v12.018l4.706-3.422a.5.5 0 0 1 .588 0L15 16.518V4.5A1.5 1.5 0 0 0 13.5 3h-7z', + 'BookmarkSolid': 'M4 4.5A2.5 2.5 0 0 1 6.5 2h7A2.5 2.5 0 0 1 16 4.5v13a.5.5 0 0 1-.794.404L10 14.118l-5.206 3.786A.5.5 0 0 1 4 17.5v-13z', + 'BookmarkAdd': 'M19 5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4-2a.5.5 0 0 0-1 0V5h-1.5a.5.5 0 0 0 0 1H14v1.5a.5.5 0 0 0 1 0V6h1.5a.5.5 0 0 0 0-1H15V3.5zm0 13.018v-5.54a5.489 5.489 0 0 0 1-.185V17.5a.5.5 0 0 1-.794.404L10 14.118l-5.206 3.786A.5.5 0 0 1 4 17.5v-13A2.5 2.5 0 0 1 6.5 2h3.757A5.504 5.504 0 0 0 9.6 3H6.5A1.5 1.5 0 0 0 5 4.5v12.018l4.706-3.422a.5.5 0 0 1 .588 0L15 16.518z', + 'BookmarkAddSolid': 'M19 5.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-4-2a.5.5 0 0 0-1 0V5h-1.5a.5.5 0 0 0 0 1H14v1.5a.5.5 0 0 0 1 0V6h1.5a.5.5 0 0 0 0-1H15V3.5zm-.5 7.5c.52 0 1.023-.072 1.5-.207V17.5a.5.5 0 0 1-.794.404L10 14.118l-5.206 3.786A.5.5 0 0 1 4 17.5v-13A2.5 2.5 0 0 1 6.5 2h3.757a5.5 5.5 0 0 0 4.243 9z', + 'BookmarkMultiple': 'M6.268 3A2 2 0 0 1 8 2h4.5A3.5 3.5 0 0 1 16 5.5v10a.5.5 0 0 1-.777.416L15 15.768V5.5A2.5 2.5 0 0 0 12.5 3H6.268zM6 4a2 2 0 0 0-2 2v11.5a.5.5 0 0 0 .777.416L9 15.101l4.223 2.815A.5.5 0 0 0 14 17.5V6a2 2 0 0 0-2-2H6zM5 6a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v10.566l-3.723-2.482a.5.5 0 0 0-.554 0L5 16.566V6z', + 'BookmarkMultipleSolid': 'M6.268 3A2 2 0 0 1 8 2h4.5A3.5 3.5 0 0 1 16 5.5v10a.5.5 0 0 1-.777.416L15 15.768V5.5A2.5 2.5 0 0 0 12.5 3H6.268zM6 4a2 2 0 0 0-2 2v11.5a.5.5 0 0 0 .777.416L9 15.101l4.223 2.815A.5.5 0 0 0 14 17.5V6a2 2 0 0 0-2-2H6z', + 'BookmarkSearch': 'M15.596 7.303a3.5 3.5 0 1 1 .707-.707l2.55 2.55a.5.5 0 0 1-.707.708l-2.55-2.55zM16 4.5a2.5 2.5 0 1 0-5 0a2.5 2.5 0 0 0 5 0zm0 4.621V17.5a.5.5 0 0 1-.794.404L10 14.118l-5.206 3.786A.5.5 0 0 1 4 17.5v-13A2.5 2.5 0 0 1 6.5 2h3.258a4.484 4.484 0 0 0-.502 1H6.5A1.5 1.5 0 0 0 5 4.5v12.018l4.706-3.422a.5.5 0 0 1 .588 0L15 16.518V8.744c.15-.053.297-.114.44-.183l.56.56z', + 'BookmarkSearchSearch': 'M15.596 7.303a3.5 3.5 0 1 1 .707-.707l2.55 2.55a.5.5 0 0 1-.707.708l-2.55-2.55zM16 4.5a2.5 2.5 0 1 0-5 0a2.5 2.5 0 0 0 5 0zm0 4.621V17.5a.5.5 0 0 1-.794.404L10 14.118l-5.206 3.786A.5.5 0 0 1 4 17.5v-13A2.5 2.5 0 0 1 6.5 2h3.258a4.5 4.5 0 0 0 5.682 6.561l.56.56z', + + 'Clipboard': 'M7.085 3A1.5 1.5 0 0 1 8.5 2h3a1.5 1.5 0 0 1 1.415 1H14.5A1.5 1.5 0 0 1 16 4.5v12a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3h1.585zM8.5 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zM7.085 4H5.5a.5.5 0 0 0-.5.5v12a.5.5 0 0 0 .5.5h9a.5.5 0 0 0 .5-.5v-12a.5.5 0 0 0-.5-.5h-1.585A1.5 1.5 0 0 1 11.5 5h-3a1.5 1.5 0 0 1-1.415-1z', + 'ClipboardSolid': 'M7.085 3A1.5 1.5 0 0 1 8.5 2h3a1.5 1.5 0 0 1 1.415 1H14.5A1.5 1.5 0 0 1 16 4.5v12a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3h1.585zM8.5 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3z', + 'ClipboardCheckmark': 'M7.085 3A1.5 1.5 0 0 1 8.5 2h3a1.5 1.5 0 0 1 1.415 1H14.5A1.5 1.5 0 0 1 16 4.5v4.707a5.48 5.48 0 0 0-1-.185V4.5a.5.5 0 0 0-.5-.5h-1.585A1.5 1.5 0 0 1 11.5 5h-3a1.5 1.5 0 0 1-1.415-1H5.5a.5.5 0 0 0-.5.5v12a.5.5 0 0 0 .5.5h4.1c.183.358.404.693.657 1H5.5A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3h1.585zM8.5 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zM19 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-2.146-1.854a.5.5 0 0 0-.708 0L13.5 15.293l-.646-.647a.5.5 0 0 0-.708.708l1 1a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0 0-.708z', + 'ClipboardCheckmarkSolid': 'M7.085 3A1.5 1.5 0 0 1 8.5 2h3a1.5 1.5 0 0 1 1.415 1H14.5A1.5 1.5 0 0 1 16 4.5v4.707A5.5 5.5 0 0 0 10.257 18H5.5A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3h1.585zM8.5 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zM19 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-2.146-1.854a.5.5 0 0 0-.708 0L13.5 15.293l-.646-.647a.5.5 0 0 0-.708.708l1 1a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0 0-.708z', + 'ClipboardError': 'M7.085 3A1.5 1.5 0 0 1 8.5 2h3a1.5 1.5 0 0 1 1.415 1H14.5A1.5 1.5 0 0 1 16 4.5v4.707a5.48 5.48 0 0 0-1-.185V4.5a.5.5 0 0 0-.5-.5h-1.585A1.5 1.5 0 0 1 11.5 5h-3a1.5 1.5 0 0 1-1.415-1H5.5a.5.5 0 0 0-.5.5v12a.5.5 0 0 0 .5.5h4.1c.183.358.404.693.657 1H5.5A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3h1.585zM8.5 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zM19 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zM14.5 12a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 1 0v-2a.5.5 0 0 0-.5-.5zm0 5.125a.625.625 0 1 0 0-1.25a.625.625 0 0 0 0 1.25z', + 'ClipboardErrorSolid': 'M7.085 3A1.5 1.5 0 0 1 8.5 2h3a1.5 1.5 0 0 1 1.415 1H14.5A1.5 1.5 0 0 1 16 4.5v4.707A5.5 5.5 0 0 0 10.257 18H5.5A1.5 1.5 0 0 1 4 16.5v-12A1.5 1.5 0 0 1 5.5 3h1.585zM8.5 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-3zM19 14.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zM14.5 12a.5.5 0 0 0-.5.5v2a.5.5 0 0 0 1 0v-2a.5.5 0 0 0-.5-.5zm0 5.125a.625.625 0 1 0 0-1.25a.625.625 0 0 0 0 1.25z', + 'ClipboardText': 'M6.5 8a.5.5 0 0 0 0 1h7a.5.5 0 0 0 0-1h-7zM6 11.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm.5 2.5a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1h-5zm2-12a1.5 1.5 0 0 0-1.415 1H5.5A1.5 1.5 0 0 0 4 4.5v12A1.5 1.5 0 0 0 5.5 18h9a1.5 1.5 0 0 0 1.5-1.5v-12A1.5 1.5 0 0 0 14.5 3h-1.585A1.5 1.5 0 0 0 11.5 2h-3zm3 1a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1h3zm-6 1h1.585A1.5 1.5 0 0 0 8.5 5h3a1.5 1.5 0 0 0 1.415-1H14.5a.5.5 0 0 1 .5.5v12a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5v-12a.5.5 0 0 1 .5-.5z', + 'ClipboardTextSolid': 'M8.5 2a1.5 1.5 0 0 0-1.415 1H5.5A1.5 1.5 0 0 0 4 4.5v12A1.5 1.5 0 0 0 5.5 18h9a1.5 1.5 0 0 0 1.5-1.5v-12A1.5 1.5 0 0 0 14.5 3h-1.585A1.5 1.5 0 0 0 11.5 2h-3zm3 1a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1h3zm-5 5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1 0-1zm0 3h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1 0-1zM6 14.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1-.5-.5z', + + 'Clock': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zm0 1a7 7 0 1 0 0 14a7 7 0 0 0 0-14zm-.5 2a.5.5 0 0 1 .492.41L10 5.5V10h2.5a.5.5 0 0 1 .09.992L12.5 11h-3a.5.5 0 0 1-.492-.41L9 10.5v-5a.5.5 0 0 1 .5-.5z', + 'ClockFill': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zm-.5 3a.5.5 0 0 0-.5.5v5l.008.09A.5.5 0 0 0 9.5 11h3l.09-.008A.5.5 0 0 0 12.5 10H10V5.5l-.008-.09A.5.5 0 0 0 9.5 5z', + 'ClockAlarm': 'M10 6.5a.5.5 0 0 0-1 0v4a.5.5 0 0 0 .5.5h3a.5.5 0 1 0 0-1H10V6.5zM3.353 7.8A3.19 3.19 0 0 1 2 5.187C2 3.431 3.414 2 5.166 2c1.077 0 2.026.542 2.597 1.365A6.992 6.992 0 0 1 10 3c.78 0 1.529.127 2.23.362A3.164 3.164 0 0 1 14.83 2A3.172 3.172 0 0 1 18 5.175c0 1.08-.538 2.033-1.359 2.607c.233.697.359 1.443.359 2.218a6.973 6.973 0 0 1-1.71 4.584l1.564 1.562a.5.5 0 0 1-.708.707l-1.562-1.562A6.973 6.973 0 0 1 10 17a6.973 6.973 0 0 1-4.584-1.71l-1.562 1.564a.5.5 0 1 1-.708-.707l1.563-1.563A6.973 6.973 0 0 1 3 10c0-.769.124-1.508.353-2.2zM3 5.187c0 .662.291 1.255.75 1.656a7.03 7.03 0 0 1 3.062-3.077A2.152 2.152 0 0 0 5.166 3A2.176 2.176 0 0 0 3 5.187zm13.242 1.64c.464-.399.758-.99.758-1.652A2.172 2.172 0 0 0 14.83 3c-.66 0-1.251.295-1.65.763a7.03 7.03 0 0 1 3.06 3.065zM4 10a6 6 0 1 0 12 0a6 6 0 0 0-12 0z', + 'ClockAlarmSolid': 'M7.763 3.365A3.156 3.156 0 0 0 5.166 2C3.414 2 2 3.43 2 5.187A3.19 3.19 0 0 0 3.353 7.8A6.993 6.993 0 0 0 3 10c0 1.753.644 3.356 1.71 4.584l-1.564 1.563a.5.5 0 0 0 .708.707l1.562-1.563A6.973 6.973 0 0 0 10 17a6.973 6.973 0 0 0 4.584-1.71l1.562 1.563a.5.5 0 0 0 .708-.707l-1.563-1.562A6.973 6.973 0 0 0 17 10c0-.775-.126-1.521-.359-2.218A3.174 3.174 0 0 0 18 5.175A3.172 3.172 0 0 0 14.83 2c-1.078 0-2.03.54-2.602 1.362A6.992 6.992 0 0 0 10 3c-.782 0-1.534.128-2.237.365zM5.166 3c.657 0 1.248.296 1.646.766a7.03 7.03 0 0 0-3.061 3.077A2.19 2.19 0 0 1 3 5.187C3 3.975 3.973 3 5.166 3zm8.015.763c.399-.468.99-.763 1.65-.763C16.028 3 17 3.973 17 5.175c0 .661-.294 1.253-.758 1.653a7.03 7.03 0 0 0-3.06-3.065zM9.5 6a.5.5 0 0 1 .5.5V10h2.5a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5v-4a.5.5 0 0 1 .5-.5z', + + 'Cloud': 'M10 4c2.817 0 4.415 1.923 4.647 4.246h.07c1.814 0 3.283 1.512 3.283 3.377C18 13.488 16.53 15 14.718 15H5.282C3.469 15 2 13.488 2 11.623C2 9.82 3.373 8.347 5.102 8.251l.251-.005C5.587 5.908 7.183 4 10 4zm0 1C7.886 5 6.551 6.316 6.348 8.345a1 1 0 0 1-.995.901h-.07C4.027 9.246 3 10.304 3 11.623C3 12.943 4.028 14 5.282 14h9.436C15.972 14 17 12.942 17 11.623c0-1.32-1.028-2.377-2.282-2.377h-.071a1 1 0 0 1-.995-.9C13.45 6.325 12.109 5 10 5z', + 'CloudSolid': 'M10 4c2.817 0 4.415 1.923 4.647 4.246h.07c1.814 0 3.283 1.512 3.283 3.377C18 13.488 16.53 15 14.718 15H5.282C3.469 15 2 13.488 2 11.623c0-1.865 1.47-3.377 3.282-3.377h.071C5.587 5.908 7.183 4 10 4z', + 'CloudCheckmark': 'M10 2c2.817 0 4.415 1.923 4.647 4.246h.07C16.532 6.246 18 7.758 18 9.623c0 .095-.004.19-.011.283a5.782 5.782 0 0 0-1.114-1.062c-.31-.933-1.163-1.598-2.157-1.598h-.071a1 1 0 0 1-.995-.9C13.45 4.325 12.109 3 10 3C7.886 3 6.551 4.316 6.348 6.345a1 1 0 0 1-.995.901h-.07C4.027 7.246 3 8.304 3 9.623C3 10.943 4.028 12 5.282 12h2.666a5.733 5.733 0 0 0-.177 1H5.282C3.469 13 2 11.488 2 9.623C2 7.82 3.373 6.347 5.102 6.251l.251-.005C5.587 3.908 7.183 2 10 2zm8 11.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-2.854-1.854L12.5 14.293l-.646-.647a.5.5 0 0 0-.708.708l1 1a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708z', + 'CloudCheckmarkSolid': 'M10 2c2.817 0 4.415 1.923 4.647 4.246h.07C16.532 6.246 18 7.758 18 9.623c0 .095-.004.19-.011.283A5.75 5.75 0 0 0 7.772 13h-2.49C3.469 13 2 11.488 2 9.623c0-1.865 1.47-3.377 3.282-3.377h.071C5.587 3.908 7.183 2 10 2zm8 11.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-2.146-1.854a.5.5 0 0 0-.708 0L12.5 14.293l-.646-.647a.5.5 0 0 0-.708.708l1 1a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0 0-.708z', + 'CloudAdd': 'M10 2c2.817 0 4.415 1.923 4.647 4.246h.07C16.532 6.246 18 7.758 18 9.623c0 .095-.004.19-.011.283a5.782 5.782 0 0 0-1.114-1.062c-.31-.933-1.163-1.598-2.157-1.598h-.071a1 1 0 0 1-.995-.9C13.45 4.325 12.109 3 10 3C7.886 3 6.551 4.316 6.348 6.345a1 1 0 0 1-.995.901h-.07C4.027 7.246 3 8.304 3 9.623C3 10.943 4.028 12 5.282 12h2.666a5.733 5.733 0 0 0-.177 1H5.282C3.469 13 2 11.488 2 9.623C2 7.82 3.373 6.347 5.102 6.251l.251-.005C5.587 3.908 7.183 2 10 2zm3.5 16a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm0-7a.5.5 0 0 1 .5.5V13h1.5a.5.5 0 0 1 0 1H14v1.5a.5.5 0 0 1-1 0V14h-1.5a.5.5 0 0 1 0-1H13v-1.5a.5.5 0 0 1 .5-.5z', + 'CloudAddSolid': 'M10 2c2.817 0 4.415 1.923 4.647 4.246h.07C16.532 6.246 18 7.758 18 9.623c0 .095-.004.19-.011.283A5.75 5.75 0 0 0 7.772 13h-2.49C3.469 13 2 11.488 2 9.623c0-1.865 1.47-3.377 3.282-3.377h.071C5.587 3.908 7.183 2 10 2zm3.5 16a4.5 4.5 0 1 0 0-9a4.5 4.5 0 0 0 0 9zm0-7a.5.5 0 0 1 .5.5V13h1.5a.5.5 0 0 1 0 1H14v1.5a.5.5 0 0 1-1 0V14h-1.5a.5.5 0 0 1 0-1H13v-1.5a.5.5 0 0 1 .5-.5z', + 'CloudDismiss': 'M10 2c2.817 0 4.415 1.923 4.647 4.246h.07C16.532 6.246 18 7.758 18 9.623c0 .095-.004.19-.011.283a5.782 5.782 0 0 0-1.114-1.062c-.31-.933-1.163-1.598-2.157-1.598h-.071a1 1 0 0 1-.995-.9C13.45 4.325 12.109 3 10 3C7.886 3 6.551 4.316 6.348 6.345a1 1 0 0 1-.995.901h-.07C4.027 7.246 3 8.304 3 9.623C3 10.943 4.028 12 5.282 12h2.666a5.733 5.733 0 0 0-.177 1H5.282C3.469 13 2 11.488 2 9.623C2 7.82 3.373 6.347 5.102 6.251l.251-.005C5.587 3.908 7.183 2 10 2zm8 11.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-3.793 0l1.147-1.146a.5.5 0 0 0-.708-.708L13.5 12.793l-1.146-1.147a.5.5 0 0 0-.708.708l1.147 1.146l-1.147 1.146a.5.5 0 0 0 .708.708l1.146-1.147l1.146 1.147a.5.5 0 0 0 .708-.708L14.207 13.5z', + 'CloudDismissSolid': 'M10 2c2.817 0 4.415 1.923 4.647 4.246h.07C16.532 6.246 18 7.758 18 9.623c0 .095-.004.19-.011.283A5.75 5.75 0 0 0 7.772 13h-2.49C3.469 13 2 11.488 2 9.623c0-1.865 1.47-3.377 3.282-3.377h.071C5.587 3.908 7.183 2 10 2zm8 11.5a4.5 4.5 0 1 1-9 0a4.5 4.5 0 0 1 9 0zm-2.646-1.146a.5.5 0 0 0-.708-.708L13.5 12.793l-1.146-1.147a.5.5 0 0 0-.708.708l1.147 1.146l-1.147 1.146a.5.5 0 0 0 .708.708l1.146-1.147l1.146 1.147a.5.5 0 0 0 .708-.708L14.207 13.5l1.147-1.146z', + 'CloudEdit': 'M14.647 8.246C14.415 5.923 12.817 4 10 4S5.587 5.908 5.353 8.246l-.251.005C3.373 8.347 2 9.821 2 11.623C2 13.488 3.47 15 5.282 15h3.193c.11-.361.283-.7.51-1H5.282C4.028 14 3 12.942 3 11.623c0-1.32 1.028-2.377 2.282-2.377h.071a1 1 0 0 0 .995-.9C6.551 6.315 7.886 5 10 5c2.108 0 3.45 1.325 3.652 3.346c.025.25.14.471.313.632l.137-.137c.252-.253.54-.448.847-.587a3.242 3.242 0 0 0-.231-.008h-.071zm.162 1.302l-4.83 4.83a2.197 2.197 0 0 0-.577 1.02l-.375 1.498a.89.89 0 0 0 1.079 1.078l1.498-.374c.386-.097.739-.296 1.02-.578l4.83-4.83a1.87 1.87 0 0 0-2.645-2.644z', + 'CloudEditSolid': 'M14.647 8.246C14.415 5.923 12.817 4 10 4S5.587 5.908 5.353 8.246h-.07C3.468 8.246 2 9.758 2 11.623C2 13.488 3.47 15 5.282 15h3.193c.152-.501.426-.958.798-1.33l4.829-4.83c.252-.252.54-.447.847-.586a3.242 3.242 0 0 0-.231-.008h-.071zm.162 1.302l-4.83 4.83a2.197 2.197 0 0 0-.577 1.02l-.375 1.498a.89.89 0 0 0 1.079 1.078l1.498-.374c.386-.097.739-.296 1.02-.578l4.83-4.83a1.87 1.87 0 0 0-2.645-2.644z', + 'CloudOff': 'M2.854 2.146a.5.5 0 1 0-.708.708l3.67 3.668a5.326 5.326 0 0 0-.463 1.724l-.251.005C3.373 8.347 2 9.821 2 11.623C2 13.488 3.47 15 5.282 15h9.01l2.854 2.854a.5.5 0 0 0 .708-.708l-15-15zM13.293 14h-8.01c-1.255 0-2.283-1.058-2.283-2.377c0-1.32 1.028-2.377 2.282-2.377h.071a1 1 0 0 0 .995-.9c.038-.38.116-.735.23-1.06L13.293 14zM17 11.623c0 .898-.477 1.675-1.176 2.08l.724.724A3.4 3.4 0 0 0 18 11.623c0-1.865-1.47-3.377-3.282-3.377h-.071C14.415 5.923 12.817 4 10 4c-1.209 0-2.193.352-2.941.938l.715.715C8.36 5.233 9.112 5 10 5c2.108 0 3.45 1.325 3.652 3.346a1 1 0 0 0 .995.9h.071c1.254 0 2.282 1.058 2.282 2.377z', + 'CloudOffSolid': 'M2.854 2.146a.5.5 0 1 0-.708.708l3.67 3.668a5.326 5.326 0 0 0-.463 1.724h-.07C3.468 8.246 2 9.758 2 11.623C2 13.488 3.47 15 5.282 15h9.01l2.854 2.854a.5.5 0 0 0 .708-.708l-15-15zM18 11.623a3.4 3.4 0 0 1-1.452 2.804l-9.49-9.49C7.808 4.353 8.792 4 10 4c2.817 0 4.415 1.923 4.647 4.246h.07c1.814 0 3.283 1.512 3.283 3.377z', + 'CloudSync': 'M10 2c2.817 0 4.415 1.923 4.647 4.246h.07C16.532 6.246 18 7.758 18 9.623c0 .095-.004.19-.011.283a5.782 5.782 0 0 0-1.114-1.062c-.31-.933-1.163-1.598-2.157-1.598h-.071a1 1 0 0 1-.995-.9C13.45 4.325 12.109 3 10 3C7.886 3 6.551 4.316 6.348 6.345a1 1 0 0 1-.995.901h-.07C4.027 7.246 3 8.304 3 9.623C3 10.943 4.028 12 5.282 12h2.666a5.733 5.733 0 0 0-.177 1H5.282C3.469 13 2 11.488 2 9.623C2 7.82 3.373 6.347 5.102 6.251l.251-.005C5.587 3.908 7.183 2 10 2zM9 13.5a4.5 4.5 0 1 0 9 0a4.5 4.5 0 0 0-9 0zm6.5-3a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5H14a.5.5 0 0 1 0-1h.468a1.999 1.999 0 0 0-2.383.336a.5.5 0 0 1-.706-.707A3.001 3.001 0 0 1 15 11.152V11a.5.5 0 0 1 .5-.5zm-.876 5.532A2.999 2.999 0 0 1 12 15.848V16a.5.5 0 0 1-1 0v-1.5a.5.5 0 0 1 .5-.5H13a.5.5 0 0 1 0 1h-.468a1.999 1.999 0 0 0 2.383-.336a.5.5 0 0 1 .706.707c-.285.285-.624.51-.997.66z', + 'CloudSyncSolid': 'M10 2c2.817 0 4.415 1.923 4.647 4.246h.07C16.532 6.246 18 7.758 18 9.623c0 .095-.004.19-.011.283A5.75 5.75 0 0 0 7.772 13h-2.49C3.469 13 2 11.488 2 9.623c0-1.865 1.47-3.377 3.282-3.377h.071C5.587 3.908 7.183 2 10 2zM9 13.5a4.5 4.5 0 1 0 9 0a4.5 4.5 0 0 0-9 0zm6.5-3a.5.5 0 0 1 .5.5v1.5a.5.5 0 0 1-.5.5H14a.5.5 0 0 1 0-1h.468a1.999 1.999 0 0 0-2.383.336a.5.5 0 0 1-.706-.707A3.001 3.001 0 0 1 15 11.152V11a.5.5 0 0 1 .5-.5zm-.876 5.532A2.999 2.999 0 0 1 12 15.848V16a.5.5 0 0 1-1 0v-1.5a.5.5 0 0 1 .5-.5H13a.5.5 0 0 1 0 1h-.468a1.999 1.999 0 0 0 2.383-.336a.5.5 0 0 1 .706.707c-.285.285-.624.51-.997.66z', + + 'Copy': 'M8 2a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8zM7 4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V4zM4 6a2 2 0 0 1 1-1.732V14.5A2.5 2.5 0 0 0 7.5 17h6.232A2 2 0 0 1 12 18H7.5A3.5 3.5 0 0 1 4 14.5V6z', + 'CopySolid': 'M6 4a2 2 0 0 1 2-2h6a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2V4zM4 6a2 2 0 0 1 1-1.732V14.5A2.5 2.5 0 0 0 7.5 17h6.232A2 2 0 0 1 12 18H7.5A3.5 3.5 0 0 1 4 14.5V6z', + + 'Rename': 'M8.5 2a.5.5 0 0 0 0 1h1v14h-1a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-1V3h1a.5.5 0 0 0 0-1h-3zm-4 2h4v1h-4A1.5 1.5 0 0 0 3 6.5v7A1.5 1.5 0 0 0 4.5 15h4v1h-4A2.5 2.5 0 0 1 2 13.5v-7A2.5 2.5 0 0 1 4.5 4zm11 11h-4v1h4a2.5 2.5 0 0 0 2.5-2.5v-7A2.5 2.5 0 0 0 15.5 4h-4v1h4A1.5 1.5 0 0 1 17 6.5v7a1.5 1.5 0 0 1-1.5 1.5z', + 'RenameSolid': 'M8.5 2a.5.5 0 0 0 0 1h1v14h-1a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1h-1V3h1a.5.5 0 0 0 0-1h-3zm-4 2h4v12h-4A2.5 2.5 0 0 1 2 13.5v-7A2.5 2.5 0 0 1 4.5 4zm11 12h-4V4h4A2.5 2.5 0 0 1 18 6.5v7a2.5 2.5 0 0 1-2.5 2.5z', + + 'Desktop': 'M4 2a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h3v2H5.5a.5.5 0 0 0 0 1h9a.5.5 0 0 0 0-1H13v-2h3a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H4zm8 13v2H8v-2h4zM3 4a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4z', + 'DesktopSolid': 'M3.5 2A1.5 1.5 0 0 0 2 3.5v10A1.5 1.5 0 0 0 3.5 15H7v2H5.5a.5.5 0 0 0 0 1h9a.5.5 0 0 0 0-1H13v-2h3.5a1.5 1.5 0 0 0 1.5-1.5v-10A1.5 1.5 0 0 0 16.5 2h-13zM12 15v2H8v-2h4z', + + 'Dismiss': 'M4.089 4.216l.057-.07a.5.5 0 0 1 .638-.057l.07.057L10 9.293l5.146-5.147a.5.5 0 0 1 .638-.057l.07.057a.5.5 0 0 1 .057.638l-.057.07L10.707 10l5.147 5.146a.5.5 0 0 1 .057.638l-.057.07a.5.5 0 0 1-.638.057l-.07-.057L10 10.707l-5.146 5.147a.5.5 0 0 1-.638.057l-.07-.057a.5.5 0 0 1-.057-.638l.057-.07L9.293 10L4.146 4.854a.5.5 0 0 1-.057-.638l.057-.07l-.057.07z', + 'DismissSolid': 'M3.897 4.054l.073-.084a.75.75 0 0 1 .976-.073l.084.073L10 8.939l4.97-4.97a.75.75 0 0 1 .976-.072l.084.073a.75.75 0 0 1 .073.976l-.073.084L11.061 10l4.97 4.97a.75.75 0 0 1 .072.976l-.073.084a.75.75 0 0 1-.976.073l-.084-.073L10 11.061l-4.97 4.97a.75.75 0 0 1-.976.072l-.084-.073a.75.75 0 0 1-.073-.976l.073-.084L8.939 10l-4.97-4.97a.75.75 0 0 1-.072-.976l.073-.084l-.073.084z', + 'DismissCircle': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zm0 1a7 7 0 1 0 0 14a7 7 0 0 0 0-14zM7.81 7.114l.069.058L10 9.292l2.121-2.12a.5.5 0 0 1 .638-.058l.07.058a.5.5 0 0 1 .057.637l-.058.07L10.708 10l2.12 2.121a.5.5 0 0 1 .058.638l-.058.07a.5.5 0 0 1-.637.057l-.07-.058L10 10.708l-2.121 2.12a.5.5 0 0 1-.638.058l-.07-.058a.5.5 0 0 1-.057-.637l.058-.07L9.292 10l-2.12-2.121a.5.5 0 0 1-.058-.638l.058-.07a.5.5 0 0 1 .637-.057z', + 'DismissCircleSolid': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zM7.81 7.114a.5.5 0 0 0-.638.058l-.058.069a.5.5 0 0 0 .058.638L9.292 10l-2.12 2.121l-.058.07a.5.5 0 0 0 .058.637l.069.058a.5.5 0 0 0 .638-.058L10 10.708l2.121 2.12l.07.058a.5.5 0 0 0 .637-.058l.058-.069a.5.5 0 0 0-.058-.638L10.708 10l2.12-2.121l.058-.07a.5.5 0 0 0-.058-.637l-.069-.058a.5.5 0 0 0-.638.058L10 9.292l-2.121-2.12l-.07-.058z', + 'DismissSquare': 'M7.146 7.146a.5.5 0 0 1 .708 0L10 9.293l2.146-2.147a.5.5 0 0 1 .708.708L10.707 10l2.147 2.146a.5.5 0 0 1-.708.708L10 10.707l-2.146 2.147a.5.5 0 0 1-.708-.708L9.293 10L7.146 7.854a.5.5 0 0 1 0-.708zM3 6a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6zm3-2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6z', + 'DismissSquareSolid': 'M3 6a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6zm4.146 1.146a.5.5 0 0 0 0 .708L9.293 10l-2.147 2.146a.5.5 0 0 0 .708.708L10 10.707l2.146 2.147a.5.5 0 0 0 .708-.708L10.707 10l2.147-2.146a.5.5 0 0 0-.708-.708L10 9.293L7.854 7.146a.5.5 0 0 0-.708 0z', + + 'Edit': 'M13.245 2.817a2.783 2.783 0 0 1 4.066 3.796l-.13.14l-9.606 9.606a2.001 2.001 0 0 1-.723.462l-.165.053l-4.055 1.106a.5.5 0 0 1-.63-.535l.016-.08l1.106-4.054c.076-.28.212-.54.398-.76l.117-.128l9.606-9.606zm-.86 2.275L4.346 13.13a1 1 0 0 0-.215.321l-.042.123l-.877 3.21l3.212-.875a1 1 0 0 0 .239-.1l.107-.072l.098-.085l8.038-8.04l-2.521-2.52zm4.089-1.568a1.783 1.783 0 0 0-2.402-.11l-.12.11l-.86.86l2.52 2.522l.862-.86a1.783 1.783 0 0 0 .11-2.402l-.11-.12z', + 'EditSolid': 'M11.677 4.384l3.936 3.936l-8.038 8.039a2.001 2.001 0 0 1-.723.462l-.165.053l-4.055 1.106a.5.5 0 0 1-.63-.535l.016-.08l1.106-4.054c.076-.28.212-.54.398-.76l.117-.128l8.038-8.04zm1.568-1.567a2.783 2.783 0 0 1 4.066 3.796l-.13.14l-.861.86l-3.936-3.936l.861-.86z', + 'EditOff': 'M2.854 2.146a.5.5 0 1 0-.707.708l5.53 5.53l-4.038 4.04l-.117.127a2 2 0 0 0-.398.76l-1.106 4.055l-.015.08a.5.5 0 0 0 .63.534l4.054-1.106l.165-.053a2 2 0 0 0 .723-.462l4.038-4.039l5.534 5.534a.5.5 0 0 0 .707-.708l-15-15zm8.052 9.467l-4.038 4.039l-.098.086l-.107.072a1 1 0 0 1-.24.1l-3.21.875l.876-3.21l.042-.124a1 1 0 0 1 .215-.32l4.039-4.039l2.521 2.521zm4-4L12.32 10.2l.708.707l4.153-4.153l.13-.14a2.783 2.783 0 0 0-4.066-3.796L9.092 6.971l.707.707l2.586-2.586l2.52 2.521zm1.568-4.089l.11.12c.584.7.547 1.744-.11 2.402l-.861.86l-2.521-2.52l.86-.862l.12-.11a1.783 1.783 0 0 1 2.402.11z', + 'EditOffSolid': 'M2.854 2.146a.5.5 0 1 0-.707.708l5.53 5.53l-4.038 4.04l-.117.127a2 2 0 0 0-.398.76l-1.106 4.055l-.015.08a.5.5 0 0 0 .63.534l4.054-1.106l.165-.053a2 2 0 0 0 .723-.462l4.038-4.039l5.534 5.534a.5.5 0 0 0 .707-.708l-15-15zM15.613 8.32l-2.586 2.586L9.092 6.97l2.585-2.586l3.936 3.936zm-2.368-5.503a2.783 2.783 0 0 1 4.066 3.797l-.13.14l-.861.86l-3.936-3.937l.861-.86z', + + 'ErrorCircle': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zm0 1a7 7 0 1 0 0 14a7 7 0 0 0 0-14zm0 9.5a.75.75 0 1 1 0 1.5a.75.75 0 0 1 0-1.5zM10 6a.5.5 0 0 1 .492.41l.008.09V11a.5.5 0 0 1-.992.09L9.5 11V6.5A.5.5 0 0 1 10 6z', + 'ErrorCircleSolid': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zm0 10.5a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5zM10 6a.5.5 0 0 0-.492.41L9.5 6.5V11l.008.09a.5.5 0 0 0 .984 0L10.5 11V6.5l-.008-.09A.5.5 0 0 0 10 6z', + + 'Info': 'M10.492 8.91A.5.5 0 0 0 9.5 9v4.502l.008.09a.5.5 0 0 0 .992-.09V9l-.008-.09zm.307-2.16a.75.75 0 1 0-1.5 0a.75.75 0 0 0 1.5 0zM18 10a8 8 0 1 0-16 0a8 8 0 0 0 16 0zM3 10a7 7 0 1 1 14 0a7 7 0 0 1-14 0z', + 'InfoSolid': 'M18 10a8 8 0 1 0-16 0a8 8 0 0 0 16 0zM9.508 8.91a.5.5 0 0 1 .984 0L10.5 9v4.502l-.008.09a.5.5 0 0 1-.984 0l-.008-.09V9l.008-.09zM9.25 6.75a.75.75 0 1 1 1.5 0a.75.75 0 0 1-1.5 0z', + + 'Link': 'M8 6a.5.5 0 0 1 .09.992L8 7H6a3 3 0 0 0-.197 5.994L6 13h2a.5.5 0 0 1 .09.992L8 14H6a4 4 0 0 1-.22-7.994L6 6h2zm6 0a4 4 0 0 1 .22 7.994L14 14h-2a.5.5 0 0 1-.09-.992L12 13h2a3 3 0 0 0 .197-5.994L14 7h-2a.5.5 0 0 1-.09-.992L12 6h2zM6 9.5h8a.5.5 0 0 1 .09.992L14 10.5H6a.5.5 0 0 1-.09-.992L6 9.5h8h-8z', + 'LinkSolid': 'M14 6a4 4 0 0 1 .2 7.995L14 14h-2a.75.75 0 0 1-.102-1.493L12 12.5h2a2.5 2.5 0 0 0 .164-4.995L14 7.5h-2a.75.75 0 0 1-.102-1.493L12 6h2zM8 6a.75.75 0 0 1 .102 1.493L8 7.5H6a2.5 2.5 0 0 0-.164 4.995L6 12.5h2a.75.75 0 0 1 .102 1.493L8 14H6a4 4 0 0 1-.2-7.995L6 6h2zM6.25 9.25h7.5a.75.75 0 0 1 .102 1.493l-.102.007h-7.5a.75.75 0 0 1-.102-1.493l.102-.007h7.5h-7.5z', + + 'MailInbox': 'M6 3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h8a3 3 0 0 0 3-3V6a3 3 0 0 0-3-3H6zm10 7h-3.5a.5.5 0 0 0-.5.5v.011l-.004.06a2.57 2.57 0 0 1-.256.955a1.694 1.694 0 0 1-.572.667c-.26.174-.63.307-1.168.307c-.538 0-.907-.133-1.168-.307a1.694 1.694 0 0 1-.572-.667A2.572 2.572 0 0 1 8 10.511V10.5A.5.5 0 0 0 7.5 10H4V6a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v4zM4 11h3.05c.047.264.137.616.315.974c.186.371.473.758.912 1.051c.443.295 1.01.475 1.723.475c.713 0 1.28-.18 1.723-.475c.44-.293.726-.68.912-1.051c.178-.358.268-.71.315-.974H16v3a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-3z', + 'MailInboxSolid': 'M3 6a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6zm1 4h3.5a.5.5 0 0 1 .5.5v.011l.004.06a2.572 2.572 0 0 0 .256.955c.126.254.308.492.572.667c.26.174.63.307 1.168.307c.537 0 .907-.133 1.168-.307c.264-.175.446-.413.572-.667a2.57 2.57 0 0 0 .26-1.015V10.498a.5.5 0 0 1 .5-.498H16V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v4zm4 .5v-.002z', + + 'Navigation': 'M2 4.5a.5.5 0 0 1 .5-.5h15a.5.5 0 0 1 0 1h-15a.5.5 0 0 1-.5-.5zm0 5a.5.5 0 0 1 .5-.5h15a.5.5 0 0 1 0 1h-15a.5.5 0 0 1-.5-.5zm.5 4.5a.5.5 0 0 0 0 1h15a.5.5 0 0 0 0-1h-15z', + 'NavigationSolid': 'M2 4.75A.75.75 0 0 1 2.75 4h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 4.75zm0 5A.75.75 0 0 1 2.75 9h14.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 9.75zM2.75 14a.75.75 0 0 0 0 1.5h14.5a.75.75 0 0 0 0-1.5H2.75z', + + 'Open': 'M6 4a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-2.5a.5.5 0 0 1 1 0V14a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6a3 3 0 0 1 3-3h2.5a.5.5 0 0 1 0 1H6zm5-.5a.5.5 0 0 1 .5-.5h5a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-1 0V4.707l-4.146 4.147a.5.5 0 0 1-.708-.708L15.293 4H11.5a.5.5 0 0 1-.5-.5z', + 'OpenSolid': 'M6.25 4.5A1.75 1.75 0 0 0 4.5 6.25v7.5c0 .966.784 1.75 1.75 1.75h7.5a1.75 1.75 0 0 0 1.75-1.75v-2a.75.75 0 0 1 1.5 0v2A3.25 3.25 0 0 1 13.75 17h-7.5A3.25 3.25 0 0 1 3 13.75v-7.5A3.25 3.25 0 0 1 6.25 3h2a.75.75 0 0 1 0 1.5h-2zm4.25-.75a.75.75 0 0 1 .75-.75h5a.75.75 0 0 1 .75.75v5a.75.75 0 0 1-1.5 0V5.56l-3.72 3.72a.75.75 0 1 1-1.06-1.06l3.72-3.72h-3.19a.75.75 0 0 1-.75-.75z', + + 'Question': 'M10 3C7.794 3 6 4.794 6 7a.5.5 0 0 0 1 0c0-1.654 1.346-3 3-3s3 1.346 3 3c0 1.249-.692 1.863-1.575 2.62l-.032.027C10.534 10.384 9.5 11.27 9.5 13v.5a.5.5 0 0 0 1 0V13c0-1.249.692-1.863 1.575-2.62l.032-.027C12.966 9.615 14 8.731 14 7c0-2.206-1.794-4-4-4zm0 14a.75.75 0 1 0 0-1.5a.75.75 0 0 0 0 1.5z', + 'QuestionSolid': 'M10 3C7.796 3 6 4.796 6 7a.75.75 0 0 0 1.5 0c0-1.376 1.124-2.5 2.5-2.5s2.5 1.124 2.5 2.5c0 .597-.156.975-.368 1.27c-.232.325-.547.58-.969.92l-.01.008c-.4.323-.893.724-1.27 1.288c-.391.588-.633 1.313-.633 2.264v.5a.75.75 0 0 0 1.5 0v-.5c0-.674.164-1.105.382-1.432c.233-.349.552-.62.964-.953l.068-.055c.374-.302.834-.672 1.188-1.167C13.75 8.588 14 7.903 14 7c0-2.204-1.796-4-4-4zm0 14a1 1 0 1 0 0-2a1 1 0 0 0 0 2z', + 'QuestionCircle': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zm0 1a7 7 0 1 0 0 14a7 7 0 0 0 0-14zm0 10.5a.75.75 0 1 1 0 1.5a.75.75 0 0 1 0-1.5zm0-8a2.5 2.5 0 0 1 1.651 4.377l-.154.125l-.219.163l-.087.072a1.968 1.968 0 0 0-.156.149c-.339.36-.535.856-.535 1.614a.5.5 0 0 1-1 0c0-1.012.293-1.752.805-2.298a3.11 3.11 0 0 1 .356-.323l.247-.185l.118-.1A1.5 1.5 0 1 0 8.5 8a.5.5 0 0 1-1 .001A2.5 2.5 0 0 1 10 5.5z', + 'QuestionCircleSolid': 'M10 2a8 8 0 1 1 0 16a8 8 0 0 1 0-16zm0 11.5a.75.75 0 1 0 0 1.5a.75.75 0 0 0 0-1.5zm0-8A2.5 2.5 0 0 0 7.5 8a.5.5 0 0 0 1 0a1.5 1.5 0 1 1 2.632.984l-.106.11l-.118.1l-.247.185a3.11 3.11 0 0 0-.356.323C9.793 10.248 9.5 10.988 9.5 12a.5.5 0 0 0 1 0c0-.758.196-1.254.535-1.614l.075-.076l.08-.073l.088-.072l.219-.163l.154-.125A2.5 2.5 0 0 0 10 5.5z', + + 'Warning': 'M10 7a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-1 0v-4A.5.5 0 0 1 10 7zm0 7.5a.75.75 0 1 0 0-1.5a.75.75 0 0 0 0 1.5zM8.686 2.852a1.5 1.5 0 0 1 2.628 0l6.56 11.925A1.5 1.5 0 0 1 16.558 17H3.44a1.5 1.5 0 0 1-1.314-2.223L8.686 2.852zm1.752.482a.5.5 0 0 0-.876 0L3.003 15.26a.5.5 0 0 0 .438.741H16.56a.5.5 0 0 0 .438-.74L10.438 3.333z', + 'WarningSolid': 'M8.686 2.852L2.127 14.777A1.5 1.5 0 0 0 3.441 17H16.56a1.5 1.5 0 0 0 1.314-2.223L11.314 2.852a1.5 1.5 0 0 0-2.628 0zM10 6.75a.75.75 0 0 1 .75.75v4a.75.75 0 0 1-1.5 0v-4a.75.75 0 0 1 .75-.75zm.75 7a.75.75 0 1 1-1.5 0a.75.75 0 0 1 1.5 0z', + + 'Moon': 'M15.493 13.497a6.981 6.981 0 0 1-11.483.892c2.831-1.087 4.558-2.42 5.593-4.397c1.048-2 1.337-4.16.76-6.909a6.981 6.981 0 0 1 5.13 10.414zM5.457 16.918A7.981 7.981 0 1 0 9.88 2.035a.599.599 0 0 0-.614.74c.688 2.819.434 4.876-.55 6.753c-.934 1.784-2.544 3.031-5.55 4.107a.599.599 0 0 0-.292.903a7.952 7.952 0 0 0 2.582 2.38z', + 'MoonSolid': 'M16.36 13.997a7.981 7.981 0 0 1-13.485.541a.599.599 0 0 1 .292-.903c3.006-1.076 4.616-2.323 5.55-4.107c.984-1.877 1.238-3.934.55-6.753a.599.599 0 0 1 .614-.74a7.981 7.981 0 0 1 6.478 11.962z', + 'Sun': 'M10 2a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 10 2zm0 12a4 4 0 1 0 0-8a4 4 0 0 0 0 8zm0-1a3 3 0 1 1 0-6a3 3 0 0 1 0 6zm7.5-2.5a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1h1zM10 16a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5zm-6.5-5.5a.5.5 0 0 0 0-1H2.463a.5.5 0 0 0 0 1H3.5zm.646-6.354a.5.5 0 0 1 .708 0l1 1a.5.5 0 1 1-.708.708l-1-1a.5.5 0 0 1 0-.708zm.708 11.708a.5.5 0 0 1-.708-.708l1-1a.5.5 0 0 1 .708.708l-1 1zm11-11.708a.5.5 0 0 0-.708 0l-1 1a.5.5 0 0 0 .708.708l1-1a.5.5 0 0 0 0-.708zm-.708 11.708a.5.5 0 0 0 .708-.708l-1-1a.5.5 0 0 0-.708.708l1 1z', + 'SunSolid': 'M10 2a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1A.5.5 0 0 1 10 2zm4 8a4 4 0 1 1-8 0a4 4 0 0 1 8 0zm3.5.5a.5.5 0 0 0 0-1h-1a.5.5 0 0 0 0 1h1zM10 16a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-1 0v-1a.5.5 0 0 1 .5-.5zm-6.5-5.5a.5.5 0 0 0 0-1H2.463a.5.5 0 0 0 0 1H3.5zm.646-6.354a.5.5 0 0 1 .708 0l1 1a.5.5 0 1 1-.708.708l-1-1a.5.5 0 0 1 0-.708zm.708 11.708a.5.5 0 0 1-.708-.708l1-1a.5.5 0 0 1 .708.708l-1 1zm11-11.708a.5.5 0 0 0-.708 0l-1 1a.5.5 0 0 0 .708.708l1-1a.5.5 0 0 0 0-.708zm-.708 11.708a.5.5 0 0 0 .708-.708l-1-1a.5.5 0 0 0-.708.708l1 1z', + + 'TaskList': 'M5.854 4.354a.5.5 0 1 0-.708-.708L3.5 5.293l-.646-.647a.5.5 0 1 0-.708.708l1 1a.5.5 0 0 0 .708 0l2-2zM8.5 5a.5.5 0 0 0 0 1h9a.5.5 0 0 0 0-1h-9zm0 5a.5.5 0 0 0 0 1h9a.5.5 0 0 0 0-1h-9zM8 15.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5zM5.854 9.854a.5.5 0 1 0-.708-.708L3.5 10.793l-.646-.647a.5.5 0 0 0-.708.708l1 1a.5.5 0 0 0 .708 0l2-2zm0 4.292a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708 0l-1-1a.5.5 0 0 1 .708-.708l.646.647l1.646-1.647a.5.5 0 0 1 .708 0z', + 'TaskListSolid': 'M5.854 4.354a.5.5 0 1 0-.708-.708L3.5 5.293l-.646-.647a.5.5 0 1 0-.708.708l1 1a.5.5 0 0 0 .708 0l2-2zM8.75 4.5a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5zm0 5a.75.75 0 0 0 0 1.5h8.5a.75.75 0 0 0 0-1.5h-8.5zM8 15.25a.75.75 0 0 1 .75-.75h8.5a.75.75 0 0 1 0 1.5h-8.5a.75.75 0 0 1-.75-.75zM5.854 9.854a.5.5 0 1 0-.708-.708L3.5 10.793l-.646-.647a.5.5 0 0 0-.708.708l1 1a.5.5 0 0 0 .708 0l2-2zm0 4.292a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708 0l-1-1a.5.5 0 0 1 .708-.708l.646.647l1.646-1.647a.5.5 0 0 1 .708 0z', + + 'FileBriefcase': 'M6 2a2 2 0 0 0-2 2v5h1V4a1 1 0 0 1 1-1h4v3.5A1.5 1.5 0 0 0 11.5 8H15v8a1 1 0 0 1-1 1h-2v1h2a2 2 0 0 0 2-2V7.414a1.5 1.5 0 0 0-.44-1.06l-3.914-3.915A1.5 1.5 0 0 0 10.586 2H6zm8.793 5H11.5a.5.5 0 0 1-.5-.5V3.207L14.793 7zM4 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 .5.5V12h1a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h1v-1.5zm3 .5H5v1h2v-1zm-4 2a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1H3z', + 'FileBriefcaseSolid': 'M10 2v4.5A1.5 1.5 0 0 0 11.5 8H16v8.5a1.5 1.5 0 0 1-1.5 1.5H12v-4.5A2.5 2.5 0 0 0 9.5 11H9v-1a1 1 0 0 0-1-1H4V3.5A1.5 1.5 0 0 1 5.5 2H10zm1 .25V6.5a.5.5 0 0 0 .5.5h4.25L11 2.25zM4 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 .5.5V12h1a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h1v-1.5zm3 .5H5v1h2v-1zm-4 2a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-3a1 1 0 0 0-1-1H3z', + + 'Sync': 'M11.414 3.635a.5.5 0 0 0 0-.707L9.293.807a.5.5 0 0 0-.707.707l.997.997a7.5 7.5 0 0 0-4.075 13.495a.5.5 0 0 0 .6-.8A6.5 6.5 0 0 1 10.066 3.5c.024 0 .05-.002.073-.005L8.586 5.049a.5.5 0 0 0 .707.707l2.121-2.121zM8.586 16.363a.5.5 0 0 0 0 .707l2.121 2.121a.5.5 0 0 0 .707-.707l-.997-.997a7.5 7.5 0 0 0 4.075-13.495a.5.5 0 1 0-.6.8a6.5 6.5 0 0 1-3.959 11.706a.502.502 0 0 0-.073.005l1.554-1.554a.5.5 0 1 0-.707-.707l-2.121 2.121z', + 'SyncSolid': 'M9.885 3.75a6.25 6.25 0 0 0-3.628 11.256a.75.75 0 0 1-.9 1.2a7.75 7.75 0 0 1 3.99-13.93l-.584-.586A.75.75 0 0 1 9.823.63l2.122 2.121a.75.75 0 0 1 0 1.06L9.823 5.934a.75.75 0 0 1-1.06-1.06L9.885 3.75zm.23 12.498a6.25 6.25 0 0 0 3.628-11.256a.75.75 0 0 1 .9-1.2a7.75 7.75 0 0 1-3.99 13.93l.584.585a.75.75 0 1 1-1.06 1.061l-2.122-2.121a.75.75 0 0 1 0-1.06l2.122-2.122a.75.75 0 1 1 1.06 1.06l-1.122 1.123z', + 'SyncOff': 'M11.414 3.635a.5.5 0 0 0 0-.707L9.293.807a.5.5 0 0 0-.707.707l.997.997a7.48 7.48 0 0 0-3.72 1.23l.724.724a6.49 6.49 0 0 1 3.48-.966c.024 0 .05-.001.073-.004L8.586 5.049a.5.5 0 0 0 .707.707l2.121-2.121zm-7.06 1.426a7.5 7.5 0 0 0 1.154 10.945a.5.5 0 0 0 .6-.8A6.5 6.5 0 0 1 5.063 5.77l9.165 9.165A6.479 6.479 0 0 1 9.934 16.5a.502.502 0 0 0-.074.004l1.554-1.554a.5.5 0 1 0-.707-.707l-2.121 2.121a.5.5 0 0 0 0 .707l2.121 2.121a.5.5 0 0 0 .707-.707l-.997-.997a7.471 7.471 0 0 0 4.521-1.843l2.208 2.209a.5.5 0 0 0 .708-.708l-15-15a.5.5 0 1 0-.708.708L4.355 5.06zm10.95-.365a7.503 7.503 0 0 1 .954 9.44l-.724-.724a6.503 6.503 0 0 0-1.641-8.62a.5.5 0 1 1 .6-.8c.282.212.553.447.81.704z', + 'SyncOffSolid': 'M9.885 3.75a6.236 6.236 0 0 0-3.116.897L5.683 3.56a7.725 7.725 0 0 1 3.665-1.285l-.585-.586A.75.75 0 0 1 9.823.63l2.122 2.121a.75.75 0 0 1 0 1.06L9.823 5.934a.75.75 0 0 1-1.06-1.06L9.885 3.75zM4.178 4.884a7.75 7.75 0 0 0 1.18 11.322a.75.75 0 1 0 .9-1.2a6.25 6.25 0 0 1-1.016-9.059l8.81 8.811a6.225 6.225 0 0 1-3.937 1.49l1.122-1.123a.75.75 0 0 0-1.06-1.06l-2.122 2.121a.75.75 0 0 0 0 1.06l2.122 2.122a.75.75 0 1 0 1.06-1.06l-.585-.586a7.718 7.718 0 0 0 4.463-1.9l2.031 2.03a.5.5 0 0 0 .708-.707l-15-15a.5.5 0 1 0-.708.708l2.032 2.03zm11.174 8.346l1.086 1.086a7.753 7.753 0 0 0-1.796-10.524a.75.75 0 0 0-.9 1.2a6.253 6.253 0 0 1 1.61 8.237z', + 'SyncCircle': 'M10 3a7 7 0 1 1 0 14a7 7 0 0 1 0-14zm8 7a8 8 0 1 0-16 0a8 8 0 0 0 16 0zm-8-2.5A2.5 2.5 0 0 1 12.292 9H11.5a.5.5 0 1 0 0 1h2a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 0-1 0v.696a3.498 3.498 0 0 0-5.609-.53a.5.5 0 0 0 .746.667A2.493 2.493 0 0 1 10 7.5zm-3 4.304v.696a.5.5 0 0 1-1 0v-2a.5.5 0 0 1 .5-.5h2a.5.5 0 0 1 0 1h-.792a2.5 2.5 0 0 0 4.156.666a.5.5 0 0 1 .745.668A3.498 3.498 0 0 1 7 11.804z', + 'SyncCircleSolid': 'M10 18a8 8 0 1 1 0-16a8 8 0 0 1 0 16zm3.5-8a.5.5 0 0 0 .5-.5v-2a.5.5 0 0 0-1 0v.696a3.498 3.498 0 0 0-5.609-.53a.5.5 0 1 0 .746.667A2.5 2.5 0 0 1 12.293 9H11.5a.5.5 0 1 0 0 1h2zm-7.5.5v2a.5.5 0 0 0 1 0v-.696a3.498 3.498 0 0 0 5.609.53a.5.5 0 0 0-.745-.668A2.5 2.5 0 0 1 7.708 11H8.5a.5.5 0 0 0 0-1h-2a.5.5 0 0 0-.5.5z', +} + +export type IconName = + | 'Folder' + // | 'FolderSolid' + | 'FolderAdd' + // | 'FolderAddSolid' + // | 'FolderArrowRight' + // | 'FolderArrowRightSolid' + // | 'FolderArrowUp' + // | 'FolderArrowUpSolid' + | 'FolderOpen' + // | 'FolderOpenSolid' + // | 'FolderOpenVertical' + // | 'FolderOpenVerticalSolid' + // | 'FolderProhibited' + // | 'FolderProhibitedSolid' + // | 'FolderSwap' + // | 'FolderSwapSolid' + // | 'FolderSync' + // | 'FolderSyncSolid' + // | 'FolderZip' + // | 'FolderZipSolid' + // | 'Checkmark' + // | 'CheckmarkSolid' + // | 'CheckmarkCircle' + | 'CheckmarkCircleSolid' + // | 'CheckmarkStarburst' + // | 'CheckmarkStarburstSolid' + // | 'ArrowSyncCheckmark' + // | 'ArrowSyncCheckmarkSolid' + | 'Image' + // | 'ImageSolid' + // | 'DrawImage' + // | 'DrawImageSolid' + // | 'ImageCopy' + // | 'ImageCopySolid' + // | 'ImageEdit' + // | 'ImageEditSolid' + // | 'ImageMultiple' + // | 'ImageMultipleSolid' + // | 'ImageOff' + // | 'ImageOffSolid' + // | 'ImageProhibited' + // | 'ImageProhibitedSolid' + // | 'ImageSearch' + // | 'ImageSearchSolid' + | 'Video' + // | 'VideoSolid' + // | 'VideoAdd' + // | 'VideoAddSolid' + // | 'VideoOff' + // | 'VideoOffSolid' + // | 'VideoProhibited' + // | 'VideoProhibitedSolid' + // | 'VideoSync' + // | 'VideoSyncSolid' + // | 'File' + // | 'FileSolid' + | 'FileAdd' + // | 'FileAddSolid' + // | 'FileArrowDown' + // | 'FileArrowDownSolid' + // | 'FileArrowUp' + // | 'FileArrowUpSolid' + // | 'FileBulletList' + // | 'FileBulletListSolid' + // | 'FileBulletListMultiple' + // | 'FileBulletListMultipleSolid' + // | 'FileBulletListOff' + // | 'FileBulletListOffSolid' + // | 'FileCatchUp' + // | 'FileCatchUpSolid' + // | 'FileCheckmark' + // | 'FileCheckmarkSolid' + // | 'FileCopy' + // | 'FileCopySolid' + // | 'FileDismiss' + // | 'FileDismissSolid' + // | 'FileEdit' + // | 'FileEditSolid' + | 'FileError' + // | 'FileErrorSolid' + // | 'FileLink' + // | 'FileLinkSolid' + // | 'FileLock' + // | 'FileLockSolid' + | 'FileMultiple' + // | 'FileMultipleSolid' + // | 'FilePdf' + // | 'FilePdfSolid' + | 'FileProhibited' + // | 'FileProhibitedSolid' + // | 'FileQuestionMark' + // | 'FileQuestionMarkSolid' + // | 'FileSearch' + // | 'FileSearchSolid' + | 'FileSync' + // | 'FileSyncSolid' + // | 'FileText' + // | 'FileTextSolid' + // | 'FileJs' + // | 'FileJsSolid' + // | 'FileCss' + // | 'FileCssSolid' + | 'FileBriefcase' + | 'FileBriefcaseSolid' + | 'ChevronDoubleRight' + // | 'ChevronDoubleRightSolid' + | 'ChevronDoubleLeft' + // | 'ChevronDoubleLeftSolid' + | 'MoreHorizontal' + // | 'MoreHorizontalSolid' + // | 'MoreVertical' + // | 'MoreVerticalSolid' + | 'Delete' + // | 'DeleteSolid' + // | 'DeleteDismiss' + // | 'DeleteDismissSolid' + // | 'DeleteOff' + // | 'DeleteOffSolid' + // | 'DeleteArrowBack' + // | 'DeleteArrowBackSolid' + // | 'DeleteLines' + // | 'DeleteLinesSolid' + | 'Eye' + // | 'EyeSolid' + // | 'EyeOff' + // | 'EyeOffSolid' + // | 'EyeTracking' + // | 'EyeTrackingSolid' + // | 'EyeTrackingOff' + // | 'EyeTrackingOffSolid' + | 'Share' + // | 'ShareSolid' + // | 'Alert' + // | 'AlertSolid' + // | 'AlertOff' + // | 'AlertOffSolid' + // | 'AlertOn' + // | 'AlertOnSolid' + // | 'AlertSnooze' + // | 'AlertSnoozeOff' + // | 'AlertUrgent' + // | 'AlertUrgentSolid' + // | 'ArrowClockwise' + | 'ArrowClockwiseSolid' +// | 'ArrowClockwiseDashes' +// | 'ArrowClockwiseDashesSolid' +// | 'ArrowHookDownLeft' +// | 'ArrowHookDownLeftSolid' +// | 'ArrowHookDownRight' +// | 'ArrowHookDownRightSolid' +// | 'Bookmark' +// | 'BookmarkSolid' +// | 'BookmarkAdd' +// | 'BookmarkAddSolid' +// | 'BookmarkMultiple' +// | 'BookmarkMultipleSolid' +// | 'BookmarkSearch' +// | 'BookmarkSearchSearch' +// | 'Clipboard' +// | 'ClipboardSolid' +// | 'ClipboardCheckmark' +// | 'ClipboardCheckmarkSolid' +// | 'ClipboardError' +// | 'ClipboardErrorSolid' +// | 'ClipboardText' +// | 'ClipboardTextSolid' +// | 'Clock' +// | 'ClockFill' +// | 'ClockAlarm' +// | 'ClockAlarmSolid' +// | 'Cloud' +// | 'CloudSolid' +// | 'CloudCheckmark' +// | 'CloudCheckmarkSolid' +// | 'CloudAdd' +// | 'CloudAddSolid' +// | 'CloudDismiss' +// | 'CloudDismissSolid' +// | 'CloudEdit' +// | 'CloudEditSolid' +// | 'CloudOff' +// | 'CloudOffSolid' +// | 'CloudSync' +// | 'CloudSyncSolid' + | 'Copy' +// | 'CopySolid' + | 'Rename' +// | 'RenameSolid' +// | 'Desktop' + | 'DesktopSolid' +// | 'Dismiss' + | 'DismissSolid' +// | 'DismissCircle' + | 'DismissCircleSolid' +// | 'DismissSquare' +// | 'DismissSquareSolid' +// | 'Edit' +// | 'EditSolid' +// | 'EditOff' +// | 'EditOffSolid' +// | 'ErrorCircle' + | 'ErrorCircleSolid' +// | 'Info' + | 'InfoSolid' +// | 'Link' +// | 'LinkSolid' + | 'MailInbox' +// | 'MailInboxSolid' +// | 'Navigation' +// | 'NavigationSolid' +// | 'Open' +// | 'OpenSolid' +// | 'Question' +// | 'QuestionSolid' +// | 'QuestionCircle' +// | 'QuestionCircleSolid' +// | 'Warning' + | 'WarningSolid' + // | 'Moon' + | 'MoonSolid' + // | 'Sun' + | 'SunSolid' +// | 'TaskList' + | 'TaskListSolid' + | 'Sync' + | 'SyncSolid' + | 'SyncOff' + | 'SyncOffSolid' + | 'SyncCircle' + | 'SyncCircleSolid' + +export function installIcons(mount: ParentNode): void { + if (mount.querySelector("#wfs-icon-symbols")) { + return; + } + const symbols = ` + ${Object.entries(paths).map( + ([key, path]) => ` + + + `, + ).join('')} + `; + const temp = document.createElement("div"); + temp.innerHTML = symbols; + const svgElm = temp.firstElementChild! as SVGElement; + temp.innerHTML = ""; + svgElm.style.cssText = "display:none"; + svgElm.id = "wfs-icon-symbols"; + mount.insertBefore(svgElm, mount.firstChild); +} \ No newline at end of file diff --git a/src/frontend/widgets/VImage.vue b/src/frontend/widgets/VImage.vue new file mode 100644 index 0000000..e7c7cfa --- /dev/null +++ b/src/frontend/widgets/VImage.vue @@ -0,0 +1,35 @@ + \ No newline at end of file diff --git a/src/frontend/widgets/VLoading.vue b/src/frontend/widgets/VLoading.vue new file mode 100644 index 0000000..cc7eec6 --- /dev/null +++ b/src/frontend/widgets/VLoading.vue @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/src/frontend/widgets/VProgress.vue b/src/frontend/widgets/VProgress.vue new file mode 100644 index 0000000..ac2f080 --- /dev/null +++ b/src/frontend/widgets/VProgress.vue @@ -0,0 +1,24 @@ + + + diff --git a/src/frontend/widgets/VText.vue b/src/frontend/widgets/VText.vue new file mode 100644 index 0000000..498f4a4 --- /dev/null +++ b/src/frontend/widgets/VText.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/frontend/widgets/utils.ts b/src/frontend/widgets/utils.ts new file mode 100644 index 0000000..1c59796 --- /dev/null +++ b/src/frontend/widgets/utils.ts @@ -0,0 +1,9 @@ +import {getCurrentInstance} from 'vue' + +export function getRootBarrier(): Node | undefined { + let el = getCurrentInstance()!.vnode.el as Node + while (el.nodeType === Node.TEXT_NODE || el.nodeType === Node.COMMENT_NODE) { + el = el.parentNode! + } + return (el as Element).closest('[data-ui-barrier]') as Node +} diff --git a/src/frontend/worker.ts b/src/frontend/worker.ts new file mode 100644 index 0000000..36ecebd --- /dev/null +++ b/src/frontend/worker.ts @@ -0,0 +1,70 @@ +import BackendWorker from "../backend?sharedworker&inline"; +import {createReplier, emit, isRecvData, isSendData, uniqueId} from "../shared"; + +type Resolver = (value: any) => void; +type Rejecter = (value: any) => void; + +const resolvers: Map = new Map(); + +const worker = new BackendWorker(); + +// 接收来自后端的消息 +worker.port.onmessage = (evt) => { + if (isSendData(evt.data)) { + const reply = createReplier(worker.port, evt.data); + const {scope, action, data} = evt.data; + // console.log(scope, action, data) + emit(`${scope}:${action}`, data, reply); + } else if (!isRecvData(evt.data)) { + console.warn("[wfs] unknown message event:", evt); + } else if (resolvers.has(evt.data.id)) { + const {id, error, data} = evt.data; + const [resolve, reject] = resolvers.get(id)!; + if (error != null) { + reject(error); + } else { + resolve(data); + } + } +}; + +// 监听页面卸载,通知后端删除关联 +window.addEventListener("beforeunload", () => { + void dispatch("app", "close"); +}); + +window.addEventListener("online", () => { + return dispatch("net", "status", "on"); +}); + +window.addEventListener("offline", () => { + return dispatch("net", "status", "off"); +}); + +export function dispatch(scope: "cfg", action: "init", config: ApiConfig): Promise; + +export function dispatch(scope: "app", action: "close"): Promise; + +export function dispatch(scope: "net", action: "status", data: "on" | "off"): Promise; + +export function dispatch(scope: "dir", action: "all"): Promise>; +export function dispatch(scope: "dir", action: "create", data: NewDirEventData): Promise; +export function dispatch(scope: "dir", action: "delete", dir: number): Promise; +export function dispatch(scope: "dir", action: "rename", data: RenameDirEventData): Promise; +export function dispatch(scope: "file", action: "rename", data: RenameFileEventData): Promise; +export function dispatch(scope: "file", action: "delete", files: number[] | number): Promise; +export function dispatch(scope: "file", action: "list", data: FilesEventData): Promise + +export function dispatch(scope: "task", action: "cleanup"): Promise; +export function dispatch(scope: "task", action: "cancel", data: TaskEventData): Promise; +export function dispatch(scope: "task", action: "create", data: TaskCreateData): Promise; +export function dispatch(scope: "task", action: "remove", data: TaskEventData): Promise; +export function dispatch(scope: "task", action: "resume", data: TaskEventData): Promise; + +export function dispatch(scope: string, action: string, data?: any): Promise { + const id = uniqueId(); + return new Promise((resolve, reject) => { + resolvers.set(id, [resolve, reject]); + worker.port.postMessage({id, scope, action, data} as SendData); + }); +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..cf207df --- /dev/null +++ b/src/main.ts @@ -0,0 +1,7 @@ +import { createApp } from 'vue' +import App from './App.vue' +import './style.css' + +createApp(App).mount('#app') + +// const tasks = [{"id":604,"dirid":0,"name":"WX20240114-005223@2x.png","path":"blob:https://localhost:5173/b866a047-347d-4492-b2ed-8e86a383a625","hash":"bf31d6ae499b7d6c0a5454bbeaf4c935","mime":"image/png","size":275398,"status":"completed"},{"id":594,"dirid":0,"name":"WX20230825-215737.png","path":"blob:https://localhost:5173/58d91ed6-de74-4267-8a61-16e61a8c46a4","hash":"9a5bfe1bd08af632ed6ddcd8c5d7dd61","mime":"image/png","size":33816,"status":"completed"},{"id":583,"dirid":0,"name":"folder.svg","path":"blob:https://localhost:5173/5cf7145c-c4bc-4fa6-a13e-28d8c3403f92","hash":"26281ab81014c73471a6bc35c9f016ee","mime":"image/svg+xml","size":377,"status":"completed"},{"id":572,"dirid":0,"name":"云南白药.png","path":"blob:https://localhost:5173/4a8fe6a5-9502-4a7a-9fef-52a02cbdd1db","hash":"43e885712da45789a929433b63fdb37b","mime":"image/png","size":24566,"status":"completed"},{"id":562,"dirid":0,"name":"微信图片_20230706111627.jpg","path":"blob:https://localhost:5173/11593fba-21e4-44c2-8165-a1711f8bb1c3","hash":"80faf8f4ce14c48187c4c43e6a5fd2e8","mime":"image/jpeg","size":195703,"status":"completed"},{"id":552,"dirid":0,"name":"图片3.png","path":"blob:https://localhost:5173/25ca9ec0-ec5b-4023-a17e-5e400a661f88","hash":"20c798ea006338bd35b7d6a42f093ddc","mime":"image/png","size":538297,"status":"completed"},{"id":542,"dirid":0,"name":"图片2.png","path":"blob:https://localhost:5173/dd5d6dac-977b-424b-ac77-b2f8c719cb6d","hash":"5f720a85c0bf1a60d3cb719c3a496040","mime":"image/png","size":148824,"status":"completed"},{"id":524,"dirid":0,"name":"同仁堂_logo (1).jpg","path":"blob:https://localhost:5173/5dd10987-4dba-440c-b00b-4f8ace7cc886","hash":"9f28d0afdc04af6dc8c9568d172d5398","mime":"image/jpeg","size":22077,"status":"completed"},{"id":515,"dirid":0,"name":"首页(压缩).jpg","path":"blob:https://localhost:5173/a6e62325-287a-4818-a2cc-4bd00a3a1756","hash":"e52f3a02cc1fea7cb32b18540b5d038e","mime":"image/jpeg","size":795379,"status":"completed"},{"id":506,"dirid":0,"name":"康缘药业.png","path":"blob:https://localhost:5173/d8e09a18-398e-4b56-817d-15f6ec78d6a3","hash":"d664afa0d60570d073b1455aff877acc","mime":"image/png","size":19953,"status":"completed"},{"id":498,"dirid":0,"name":"康缘2.jpg","path":"blob:https://localhost:5173/052256ad-5ee2-4719-8d98-c63d350b05f0","hash":"09931e1b14c0c362d9a03419ef5ba5cc","mime":"image/jpeg","size":182884,"status":"completed"},{"id":491,"dirid":0,"name":"康缘.jpg","path":"blob:https://localhost:5173/0958c35d-251c-41b2-9922-4911721f6006","hash":"a6806326fe7abdca3d418c29b700458e","mime":"image/jpeg","size":224892,"status":"completed"},{"id":484,"dirid":0,"name":"江苏康缘药业股份有限公司宣传片202205.mp4","path":"blob:https://localhost:5173/89441d02-40b2-4434-98cb-2ad1eb8baee6","hash":"aa5049097f15d3ee1f8afafb4fbae1da","mime":"video/mp4","size":335599296,"status":"readend","progress":1},{"id":477,"dirid":0,"name":"健康3.mp4","path":"blob:https://localhost:5173/3859b96b-14b1-4b27-8bf4-64ece3bdc8cb","hash":"65f2340a92cf4d5a0dee4b90203bef53","mime":"video/mp4","size":3971344,"status":"failed","error":"数据已经存在"},{"id":469,"dirid":0,"name":"健康2.mp4","path":"blob:https://localhost:5173/afe39b5b-ed01-438d-a5de-3f942770c58c","hash":"4b249ff938eb017945f8c3d450f72ca5","mime":"video/mp4","size":26105439,"status":"failed","error":"数据已经存在"},{"id":462,"dirid":0,"name":"健康1.mp4","path":"blob:https://localhost:5173/ebff1143-99ba-445e-9da2-d0e2ef1a00fa","hash":"9380721ac1399c7c7bfa36d6fa81ea55","mime":"video/mp4","size":34266740,"status":"readend","progress":1},{"id":457,"dirid":0,"name":"黄芪精.mp4","path":"blob:https://localhost:5173/39d88843-ba03-46a6-8895-2eb404523fbe","hash":"f80269c50a27b4b548af3cb72f7a9b52","mime":"video/mp4","size":4436687,"status":"readend","progress":1},{"id":453,"dirid":0,"name":"大时代.mp4","path":"blob:https://localhost:5173/ca7a5836-18b7-478c-be18-552e21f41bda","hash":"3b51b7ad9df8d097345b887649bdba1e","mime":"video/mp4","size":206346447,"status":"uploading","progress":0.07327985117533983},{"id":450,"dirid":0,"name":"白药2.jpg","path":"blob:https://localhost:5173/b195d373-0459-4547-bf18-c07fe7e20710","hash":"d60ca07e46fe29192942caa61fe8e4af","mime":"image/jpeg","size":109427,"status":"failed","error":"数据已经存在"},{"id":447,"dirid":0,"name":"白药.jpg","path":"blob:https://localhost:5173/68b1c27d-3f79-4404-86cc-982902bb3abc","hash":"6fc86db5610d206bfa726ddd7032ac30","mime":"image/jpeg","size":86295,"status":"failed","error":"数据已经存在"},{"id":445,"dirid":0,"name":"7b3e21fd68a57fa775af20ca69a8c58fed66c7a2308ff1-LqgKu7_fw658.png","path":"blob:https://localhost:5173/ac591e98-89e1-4a9b-9c8c-0a98a5192808","hash":"fc18dd38433aceb381a871b2f4bf68a7","mime":"image/png","size":777294,"status":"failed","error":"数据已经存在"},{"id":200,"dirid":0,"name":"健康1.mp4","path":"blob:https://localhost:5173/b7e9b9cd-fcab-40a5-a739-39538fc0a1cd","hash":"9380721ac1399c7c7bfa36d6fa81ea55","mime":"video/mp4","size":34266740,"status":"uploading","progress":0.7344096130997693},{"id":180,"dirid":0,"name":"黄芪精.mp4","path":"blob:https://localhost:5173/97ca4e2f-2201-4b11-b671-2e0603e93640","hash":"f80269c50a27b4b548af3cb72f7a9b52","mime":"video/mp4","size":4436687,"status":"completed"},{"id":2,"dirid":0,"name":"江苏康缘药业股份有限公司宣传片202205.mp4","path":"blob:https://localhost:5173/7057f200-094e-4bd4-93e1-60f9980ad789","hash":"aa5049097f15d3ee1f8afafb4fbae1da","mime":"video/mp4","size":335599296,"status":"uploading","progress":0.1275167748638043}] diff --git a/src/shared/emitter.ts b/src/shared/emitter.ts new file mode 100644 index 0000000..b7a4178 --- /dev/null +++ b/src/shared/emitter.ts @@ -0,0 +1,60 @@ +/** 事件监听器函数签名 */ +type Listener = (...args: any[]) => void; + +const events: Record> = {}; + +/** + * 注销事件或取消事件的监听器 + * @param {string} type 事件类型 + * @param {Listener} [listener] 被取消的事件监听器,可选,缺省事会删除指定的事件 + */ +export function off(type: string, listener?: Listener): void { + if (listener == null) { + delete events[type]; + } else if (Reflect.has(events, type)) { + const index = events[type].findIndex((x) => x[0] === listener); + if (index > -1) { + events[type].splice(index, 1); + } + } +} + +/** + * 绑定事件 + * @param {string} type 事件类型 + * @param {Listener} listener 事件监听器 + */ +export function on(type: string, listener: Listener): void { + const listeners = events[type] ?? []; + listeners[listeners.length] = [listener, false]; + events[type] = listeners; +} + +/** + * 绑定一次性事件 + * @param {string} type 事件类型 + * @param {Listener} listener 事件监听器 + */ +export function once(type: string, listener: Listener): void { + const listeners = events[type] ?? []; + listeners[listeners.length] = [listener, true]; + events[type] = listeners; +} + +/** + * 触发事件 + * + * @param {string} type 事件类型 + * @param {Array<*>} args 事件参数列表 + * + * @returns {void} + */ +export function emit(type: string, ...args: any[]): void { + if (events[type]) { + for (let i = events[type].length - 1; i >= 0; i--) { + const [fn, once] = events[type][i]; + once && events[type].splice(i, 1); + fn(...args); + } + } +} diff --git a/src/shared/index.ts b/src/shared/index.ts new file mode 100644 index 0000000..35bd690 --- /dev/null +++ b/src/shared/index.ts @@ -0,0 +1,7 @@ +export * from "./emitter"; +export * from "../backend/qiniu/utils/spark-md5.ts"; +export * from "./task"; +export * from "./utils"; +export * from "./time"; +export * from './messenger' +export * from './ui' diff --git a/src/shared/messenger.ts b/src/shared/messenger.ts new file mode 100644 index 0000000..f71d11b --- /dev/null +++ b/src/shared/messenger.ts @@ -0,0 +1,97 @@ +import { off, on } from './emitter' +import { coerce } from './utils' + +export type Handler = (data: T) => Awaitable +export type Replier = (error: any, data?: R) => void + +/** 发送给前后端的数据 */ +export interface SendData { + /** 事件数据唯一标识 */ + id: number; + /** 事件作用域 */ + scope: string; + /** 事件操作 */ + action: string; + /** 事件操作参数 */ + data: any; +} + +/** 前后端回复的数据 */ +export interface RecvData { + /** 事件数据唯一标识 */ + id: number; + /** 回复的数据 */ + data?: any + /** 错误信息 */ + error?: string +} + +export function wrap(handler: Handler) { + return async (data: T, reply: Replier) => { + try { + const res = await handler(data) + reply(null, res) + } catch (error) { + reply(error) + } + } +} + +export function $on(scope: string, action: string, handler: Handler): VoidFunction { + const listener = wrap(handler) + on(`${scope}:${action}`, listener) + return () => off(`${scope}:${action}`, listener) +} + +/** + * 发送给前后端的数据 + * @param {any} v + * @returns {boolean} + */ +export function isSendData(v: any): v is SendData { + return ( + typeof v === "object" && + !Array.isArray(v) && + v != null && + "id" in v && + "scope" in v && + "action" in v && + "data" in v && + Reflect.ownKeys(v).length === 4 + ); +} + +/** + * 前后端回复的数据 + * @param {any} v + * @returns {boolean} + */ +export function isRecvData(v: any): v is RecvData { + return ( + typeof v === "object" && + !Array.isArray(v) && + v != null && + "id" in v && + typeof v.id === 'number' && + Reflect.ownKeys(v).length === ("data" in v || 'error' in v ? 2 : 1) + ); +} + +/** + * 创建回复数据 + * @param {MessagePort} port + * @param {SendData} send + * @returns {Replier} + */ +export function createReplier(port: MessagePort, send: SendData): Replier { + return (error: any, data?: any): void => { + const recv: RecvData = Object.create(null); + recv.id = send.id; + if (error != null) { + recv.error = String(coerce(error)) + } else if (data != null) { + recv.data = data + } + port.postMessage(recv) + } +} diff --git a/src/shared/task.ts b/src/shared/task.ts new file mode 100644 index 0000000..7f62c3b --- /dev/null +++ b/src/shared/task.ts @@ -0,0 +1,35 @@ +const statuses: Array = [ + "initial", // 任务刚刚创建 + "reading", // 正在读取文件内容 + "readend", // 文件内容读取完成 + "queuing", // 已经将任务添加到上传队列中 + "uploading", // 正在上传任务文件 + "uploaded", // 任务文件上传完成 + "completed", + "failed", // 任务上传失败,比如网络原因,可恢复 + "aborted", // 上传任务被用户中止,可恢复 +]; + +export function statusIndex(status: TaskStatus): number { + return statuses.indexOf(status); +} + +export function isValid(task: { status: TaskStatus }): boolean { + return statusIndex(task.status) < statusIndex("completed"); +} + +export function isInvalid(task: { status: TaskStatus }): boolean { + return statusIndex(task.status) > statusIndex("completed"); +} + +// export function isUploaded(task: { status: TaskStatus }): boolean { +// return task.status === "uploaded"; +// } + +export function isCompleted(task: { status: TaskStatus }): boolean { + return task.status === "completed"; +} + +export function isSameTask(t1: Task, t2: Task): boolean { + return t1.dirid === t2.dirid && t1.name === t2.name; +} diff --git a/src/shared/time.ts b/src/shared/time.ts new file mode 100644 index 0000000..aa8629d --- /dev/null +++ b/src/shared/time.ts @@ -0,0 +1,16 @@ +export function timestamp(time: string): number { + return new Date(time).getTime(); +} + +// 将Date()对象转成YYYY-MM-DD HH:MM:SS格式 +export function formatDate(time?: string): string { + if (!time) return "" + const v = new Date(time) + const year = v.getFullYear() + const month = v.getMonth() + 1 < 10 ? `0${v.getMonth() + 1}` : v.getMonth() + const day = v.getDate() + 1 < 10 ? `0${v.getDate() + 1}` : v.getDate() + const hour = v.getHours() < 10 ? `0${v.getHours()}` : v.getHours() + const minute = v.getMinutes() < 10 ? `0${v.getMinutes()}` : v.getMinutes() + const second = v.getSeconds() < 10 ? `0${v.getSeconds()}` : v.getSeconds() + return `${year}年${month}月${day}日 ${hour}:${minute}:${second}` +} diff --git a/src/shared/ui.ts b/src/shared/ui.ts new file mode 100644 index 0000000..9921f61 --- /dev/null +++ b/src/shared/ui.ts @@ -0,0 +1,38 @@ +export interface Page { + forward: boolean + ellipsis: boolean + value: string | number +} + +export function paginate(current: number, total: number) { + let min = current - 2 + let max = current + 2 + while (max > total) { + max-- + min-- + } + if (min < 1) { + min = 1 + } + const pages: any[] = [] + for (let i = min; i <= max; i++) { + pages.push(i) + } + const first = pages[0] + if (first === 2) { + pages.unshift(1) + } else if (first > 1) { + pages.unshift(1, "backward") + } + const last = pages[pages.length - 1] + if (last + 1 === total) { + pages.push(total) + } else if (last < total) { + pages.push("forward", total) + } + return pages.map(value => { + const ellipsis = typeof value !== "number" + const forward = value === "forward" + return {ellipsis, forward, value} + }) +} diff --git a/src/shared/utils.ts b/src/shared/utils.ts new file mode 100644 index 0000000..625aa66 --- /dev/null +++ b/src/shared/utils.ts @@ -0,0 +1,70 @@ +import {toast} from '../frontend/widgets/UiToastController' + +let nextId = 0; + +export function uniqueId(): number { + while (true) { + const id = ++nextId; + // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger + if (Number.isSafeInteger(id)) { + return id; + } + nextId = 0; + } +} + +export interface AbortionContext { + onabort(event?: Event): any; +} + +export function abortion(ctx: AbortionContext): AbortController { + const ctrl = new AbortController(); + ctrl.signal.addEventListener("abort", ctx.onabort.bind(ctx)); + return ctrl; +} + +export function coerce(val: any): any { + if (val == null) return val; + return val instanceof Error ? val.message : val; +} + +export function call(fn?: Function, ...args: any[]): void { + fn && fn(...args); +} + +export function humanSize(bytes: number): string { + if (bytes === 0) return "0B"; + const k = 1024; + const sizes = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return `${(bytes / Math.pow(k, i)).toPrecision(3)}${sizes[i]}`; +} + +export function remove(arr: T[], elm: T): void { + const index = arr.indexOf(elm); + index > -1 && arr.splice(index, 1); +} + +export function promisify() { + let resolve: (v: T) => void; + let reject: (reason?: any) => void; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + // @ts-ignore + return {promise, resolve, reject}; +} + +export function sleep(milliseconds: number): Promise { + return new Promise(resolve => { + setTimeout(resolve, milliseconds) + }) +} + +export function copyText(text: string, msg = "复制成功"): Promise { + return navigator.clipboard + .writeText(text) + .then(() => toast("success", msg)) + .catch((error) => toast("error", error)); +} diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..7645c0d --- /dev/null +++ b/src/style.css @@ -0,0 +1,11 @@ +@import "./frontend/style.css"; + +html, +body, +#app { + @apply size-full; +} + +#app { + @apply p-12; +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..869071c --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,200 @@ +/// + +type Awaitable = T | Promise; +type Arrayable = T | Array + +/** 目录信息 */ +interface CloudDirectory { + id: number; + pid: number; + title: string; + sort: number; + createdAt: string; + children?: CloudDirectory[]; +} + +/** 文件基本信息 */ +interface CloudFile { + id: number; + appId: number; + objectId: number; + directoryId: number; + name: string; + mime: string; + object: CloudObject; + createdBy: number; + createdAt: string; + updatedAt: string; +} + +/** 文件对象信息 */ +interface CloudObject { + id: number; + name: string; + hash: string; + size: number; + source: string; + path: string; + thumb: string; + mime: string; + width: number; + height: number; + duration: number; + status: number; + error?: string; + createdAt: string; + updatedAt: string; +} + +type FileMime = + | 'image' // 图片 + | 'video' // 视频 + | 'all' // 全部 + +type FileMode = + | "manage" // 管理模式 + | "select" // 选择模式 + +/** + * 表示任务的编号 + * + * 由于 ReadTask 和 UploadTask 存在于用户设备上, + * 我们可以把它们看做是本地的任务,规定其编号为正整数; + * + * 而任务 CloudTask 表示云端任务,规定其编号为负整数。 + */ +type TaskId = number; + +/** 表示文件内容的 MD5 值 */ +type FileHash = string; + +/** 任务状态有效值 */ +type TaskStatus = + | "initial" // 任务刚刚创建 + | "reading" // 正在读取文件内容 + | "readend" // 文件内容读取完成 + | "queuing" // 已经将任务添加到上传队列中 + | "uploading" // 正在上传任务文件 + | "uploaded" // 任务文件上传完成 + | "completed" // 任务完成 + | "failed" // 任务上传失败,比如网络原因 + | "aborted"; // 上传任务被用户中止 + +/** + * 任务属性 + * + * 通过 dirid + name 来确保任务的唯一性; + * + * 初始任务时属性 hash 的值与 id 保持一致, + * 只有当文件读取完成时才会使用正确值填充。 + */ +interface Task { + /** 任务编号 */ + id: TaskId; + /** 目标目录 */ + dirid: number; + /** 文件名称 */ + name: string; + /** 文件 MD5 值 */ + hash: FileHash; + /** 文件媒体类型 */ + mime: string; + /** 文件路径 */ + path: string; + /** 文件大小 */ + size: number; + /** 相关进度 */ + progress?: number; + /** 错误信息 */ + error?: string; + /** 任务状态 */ + status: TaskStatus; +} + +/** 发送给前后端的数据 */ +interface SendData { + /** 事件数据唯一标识 */ + id: number; + /** 事件作用域 */ + scope: string; + /** 事件操作 */ + action: string; + /** 事件操作参数 */ + data: any; +} + +/** 前后端回复的数据 */ +interface RecvData { + /** 事件数据唯一标识 */ + id: number; + /** 回复的数据 */ + data?: any; + /** 错误信息 */ + error?: string; +} + +/** 接口配置 */ +interface ApiConfig { + apiUrl: string; + accessToken: string; + artifact: string; +} + +/** 创建上传任务 */ +interface TaskCreateData { + dirid: number; + file: File; +} + +interface TaskEventData { + fileHash: FileHash; + taskId: TaskId; +} + +interface NewDirEventData { + title: string; + pid?: number; +} + +interface RenameDirEventData { + id: number; + title: string; +} + +interface RenameFileEventData { + id: number; + name: string; +} + +interface FilesEventData { + directoryId: number | undefined; + page: number; + limit: number; + mime?: string; +} + +interface FilesEventResult { + total: number; + page: number; + limit: number; + items: CloudFile[] | undefined; +} + +// export declare type RunTask = (...args: T[]) => Promise; +// export interface QueueContent { +// task: T; +// resolve: () => void; +// reject: (err?: any) => void; +// } +// export declare class Pool { +// private runTask; +// private limit; +// aborted: boolean; +// queue: Array>; +// processing: Array>; +// constructor(runTask: RunTask, limit: number); +// enqueue(task: T): Promise; +// private run; +// private check; +// abort(): void; +// } diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..b4c3579 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,31 @@ +import containerQueries from "@tailwindcss/container-queries"; +import forms from "@tailwindcss/forms"; + +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./**/*.{js,jsx,ts,tsx,vue}"], + darkMode: 'class', + theme: { + extend: { + animation: { + wiggle: "wiggle 1s ease-in-out infinite", + marquee: "marquee 1.2s ease-out infinite", + 'spin-slow': 'spin 2.2s linear infinite', + }, + keyframes: { + wiggle: { + "0%, 100%": {transform: "rotate(-3deg)"}, + "50%": {transform: "rotate(3deg)"}, + }, + marquee: { + "0%": {transform: "translateX(-100%)"}, + "100%": {transform: "translateX(100%)"}, + }, + }, + }, + }, + plugins: [ + forms, + containerQueries, + ], +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..a63b8fa --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,36 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], + "skipLibCheck": true, + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + "jsxImportSource": "vue", + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "src/**/*.vue" + ], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..fb51ca4 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,34 @@ +import basicSsl from '@vitejs/plugin-basic-ssl' +import vue from '@vitejs/plugin-vue' +import vueJsx from "@vitejs/plugin-vue-jsx"; +import {resolve} from 'node:path' +import {defineConfig, loadEnv} from 'vite' + +// https://vitejs.dev/config/ +export default defineConfig(({mode, command}) => { + const env = loadEnv(mode, process.cwd(), ''); + + return { + define: { + 'process.env.NODE_ENV': JSON.stringify(command === 'build' ? 'production' : 'development'), + }, + plugins: [ + basicSsl(), + vue(), + vueJsx(), + ], + // resolve: { + // alias: { + // "@": fileURLToPath(new URL("./qiniu", import.meta.url)), + // }, + // }, + build: { + assetsDir: "", + lib: { + entry: resolve("src/frontend/index.ts"), + formats: ["es"], + fileName: 'wfs', + } + }, + } +})