diff --git a/package-lock.json b/package-lock.json index 77ed99d..d0d7fba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@vue/test-utils": "^2.3.2", "@vueuse/components": "^10.1.0", "@vueuse/core": "^10.1.0", + "clipboardy": "^3.0.0", "randexp": "^0.5.3", "roboto-fontface": "^0.10.0", "tesseract.js": "^3.0.3", @@ -23,6 +24,7 @@ "vuetify": "^3.1.15" }, "devDependencies": { + "@types/clipboardy": "^2.0.1", "@typescript-eslint/eslint-plugin": "^5.59.0", "@typescript-eslint/parser": "^5.59.0", "@vitejs/plugin-vue": "^4.1.0", @@ -1048,6 +1050,16 @@ "@types/chai": "*" } }, + "node_modules/@types/clipboardy": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/clipboardy/-/clipboardy-2.0.1.tgz", + "integrity": "sha512-vLJm1iL6jFfEd+3/J4WTC65ppyGyaRTjpoB8bPhtiSrOCD0LFDr9GQYhoYNAsbt6JaY0amYacloIEEEOYUkkFg==", + "deprecated": "This is a stub types definition. clipboardy provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "clipboardy": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -2336,6 +2348,25 @@ "node": ">= 10.0.0" } }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "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/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2981,6 +3012,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", + "dependencies": { + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -4015,6 +4062,39 @@ "node": ">=0.8.x" } }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -4662,6 +4742,14 @@ "node": ">= 6" } }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "engines": { + "node": ">=10.17.0" + } + }, "node_modules/iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", @@ -4817,6 +4905,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-electron": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.1.tgz", @@ -4867,11 +4969,33 @@ "node": ">=8" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-url": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isbinaryfile": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", @@ -5297,9 +5421,7 @@ "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "peer": true + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "node_modules/merge2": { "version": "1.4.1", @@ -5356,6 +5478,14 @@ "node": ">= 0.6" } }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, "node_modules/mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -5555,6 +5685,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -5604,6 +5745,20 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/opencollective-postinstall": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", @@ -6401,8 +6556,7 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/simple-update-notifier": { "version": "1.0.7", @@ -6558,6 +6712,14 @@ "node": ">=8" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "engines": { + "node": ">=6" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -8403,6 +8565,15 @@ "@types/chai": "*" } }, + "@types/clipboardy": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/clipboardy/-/clipboardy-2.0.1.tgz", + "integrity": "sha512-vLJm1iL6jFfEd+3/J4WTC65ppyGyaRTjpoB8bPhtiSrOCD0LFDr9GQYhoYNAsbt6JaY0amYacloIEEEOYUkkFg==", + "dev": true, + "requires": { + "clipboardy": "*" + } + }, "@types/debug": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", @@ -9428,6 +9599,11 @@ } } }, + "arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==" + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -9901,6 +10077,16 @@ "string-width": "^4.2.0" } }, + "clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", + "requires": { + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" + } + }, "cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -10703,6 +10889,29 @@ "dev": true, "peer": true }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + } + } + }, "extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -11192,6 +11401,11 @@ "debug": "4" } }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" + }, "iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", @@ -11300,6 +11514,11 @@ "has": "^1.0.3" } }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, "is-electron": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.1.tgz", @@ -11335,11 +11554,24 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + }, "is-url": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==" }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, "isbinaryfile": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", @@ -11665,9 +11897,7 @@ "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "peer": true + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" }, "merge2": { "version": "1.4.1", @@ -11706,6 +11936,11 @@ "mime-db": "1.52.0" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, "mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", @@ -11843,6 +12078,14 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==" }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "requires": { + "path-key": "^3.0.0" + } + }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -11876,6 +12119,14 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, "opencollective-postinstall": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", @@ -12424,8 +12675,7 @@ "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "simple-update-notifier": { "version": "1.0.7", @@ -12548,6 +12798,11 @@ "ansi-regex": "^5.0.1" } }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" + }, "strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", diff --git a/package.json b/package.json index df4659d..fc6210b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "vue" ], "devDependencies": { + "@types/clipboardy": "^2.0.1", "@typescript-eslint/eslint-plugin": "^5.59.0", "@typescript-eslint/parser": "^5.59.0", "@vitejs/plugin-vue": "^4.1.0", @@ -61,6 +62,7 @@ "@vue/test-utils": "^2.3.2", "@vueuse/components": "^10.1.0", "@vueuse/core": "^10.1.0", + "clipboardy": "^3.0.0", "randexp": "^0.5.3", "roboto-fontface": "^0.10.0", "tesseract.js": "^3.0.3", diff --git a/src/App.vue b/src/App.vue index a0bced0..e1c86f0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -27,232 +27,22 @@ - - - Capturing - + - - Source - - - - Regex - - - - - - - - mdi-close - - - Session Settings - - - - Start Session - - Stop Session - - - - - WebAPI Access Token - - - - - - - - - - - - - - - Share - - - - - - - - - - - - - - - - + + diff --git a/src/components/LogOutput/LogOutput.vue b/src/components/LogOutput/LogOutput.vue index c4c654b..34c1a81 100644 --- a/src/components/LogOutput/LogOutput.vue +++ b/src/components/LogOutput/LogOutput.vue @@ -29,7 +29,7 @@ + + diff --git a/src/components/Notifications/NotificationAnchorPosition.ts b/src/components/Notifications/NotificationAnchorPosition.ts new file mode 100644 index 0000000..1ab11dc --- /dev/null +++ b/src/components/Notifications/NotificationAnchorPosition.ts @@ -0,0 +1,6 @@ +export enum NotificationAnchorPosition { + TOP_LEFT = 'top-left', + TOP_RIGHT = 'top-right', + BOTTOM_LEFT = 'bottom-left', + BOTTOM_RIGHT = 'bottom-right', +} diff --git a/src/components/ToastNotification/ToastNotification.vue b/src/components/Notifications/NotificationPrompt/NotificationPrompt.vue similarity index 70% rename from src/components/ToastNotification/ToastNotification.vue rename to src/components/Notifications/NotificationPrompt/NotificationPrompt.vue index 52f187a..c4cbdb9 100644 --- a/src/components/ToastNotification/ToastNotification.vue +++ b/src/components/Notifications/NotificationPrompt/NotificationPrompt.vue @@ -1,28 +1,36 @@ diff --git a/src/components/SettingsPrompt/SettingsPrompt.vue b/src/components/SettingsPrompt/SettingsPrompt.vue new file mode 100644 index 0000000..53e0b58 --- /dev/null +++ b/src/components/SettingsPrompt/SettingsPrompt.vue @@ -0,0 +1,312 @@ + + + + + diff --git a/src/composables/useClipboard/useClipboard.test.ts b/src/composables/useClipboard/useClipboard.test.ts new file mode 100644 index 0000000..727d3aa --- /dev/null +++ b/src/composables/useClipboard/useClipboard.test.ts @@ -0,0 +1,50 @@ +import { describe, it, expect } from 'vitest'; +import useClipboard from '@/composables/useClipboard/useClipboard'; +import clipboard from 'clipboardy'; + +describe('useClipboard Composable', () => { + const { clipboardText, writeClipboardText, readClipboardText } = + useClipboard(); + + it('clipboardText is empty on initialization', () => { + expect(clipboardText.value).toBe(''); + }); + + it('returns false if text is empty', async () => { + const result = await writeClipboardText(''); + + expect(result).toBe(false); + }); + + it('writes text to clipboard', async () => { + const text = 'Hello, world!'; + const result = await writeClipboardText(text); + + /** + * It is currently not possible to write into the clipboard when run in a GitHub Action therefore always true + */ + if (result) { + expect(result).toBe(true); + expect(clipboardText.value).toBe(text); + } else { + expect(result).toBe(false); + expect(clipboardText.value).toBe(''); + } + }); + + it('reads text from clipboard', async () => { + const text = 'Hello, world!'; + await writeClipboardText(text); + + const result = await readClipboardText(); + + /** + * It is currently not possible to read the clipboard when run in a GitHub Action therefore always true + */ + if (result) { + expect(result).toBe(text); + } else { + expect(result).toBe(''); + } + }); +}); diff --git a/src/composables/useClipboard/useClipboard.ts b/src/composables/useClipboard/useClipboard.ts new file mode 100644 index 0000000..0064a80 --- /dev/null +++ b/src/composables/useClipboard/useClipboard.ts @@ -0,0 +1,86 @@ +import { ref } from 'vue'; +import clipboard from 'clipboardy'; + +/** + * Clipboard composable + */ +export default function useClipboard() { + /** + * Text from the clipboard + */ + const clipboardText = ref(''); + + /** + * Check if the clipboard is supported by the browser + */ + const supported = navigator && navigator.clipboard; + + /** + * Save the given text to the clipboard + * @param text + * @returns + */ + const writeClipboardText = async (text: string): Promise => { + if (!text) { + return false; + } + + if (typeof navigator !== 'undefined' && navigator.clipboard) { + try { + await navigator.clipboard.writeText(text); + clipboardText.value = text; + return true; + } catch (error) { + console.error(error); + return false; + } + } else if (typeof clipboard !== 'undefined') { + try { + clipboard.writeSync(text); + clipboardText.value = text; + return true; + } catch (error) { + console.error(error); + return false; + } + } else { + console.error('Clipboard not supported'); + return false; + } + }; + + /** + * Read the text from the clipboard + */ + const readClipboardText = async (): Promise => { + if (typeof navigator !== 'undefined' && navigator.clipboard) { + try { + const text = await navigator.clipboard.readText(); + clipboardText.value = text; + return text; + } catch (error) { + console.error(error); + return ''; + } + } else if (typeof clipboard !== 'undefined') { + try { + const text = clipboard.readSync(); + clipboardText.value = text; + return text; + } catch (error) { + console.error(error); + return ''; + } + } else { + console.error('Clipboard not supported'); + return ''; + } + }; + + return { + clipboardText, + supported, + writeClipboardText, + readClipboardText, + }; +} diff --git a/src/composables/useForceRerender.test.ts b/src/composables/useForceRerender/useForceRerender.test.ts similarity index 93% rename from src/composables/useForceRerender.test.ts rename to src/composables/useForceRerender/useForceRerender.test.ts index 23dc5e0..989682e 100644 --- a/src/composables/useForceRerender.test.ts +++ b/src/composables/useForceRerender/useForceRerender.test.ts @@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils'; import { useForceRerender, isRerendering, -} from '@/composables/useForceRerender'; +} from '@/composables/useForceRerender/useForceRerender'; describe('useForceRerender Composable', () => { it('should trigger a rerender when called', async () => { diff --git a/src/composables/useForceRerender.ts b/src/composables/useForceRerender/useForceRerender.ts similarity index 100% rename from src/composables/useForceRerender.ts rename to src/composables/useForceRerender/useForceRerender.ts diff --git a/src/composables/useNotificationSystem.test.ts b/src/composables/useNotificationSystem/useNotificationSystem.test.ts similarity index 89% rename from src/composables/useNotificationSystem.test.ts rename to src/composables/useNotificationSystem/useNotificationSystem.test.ts index effac22..675e5cf 100644 --- a/src/composables/useNotificationSystem.test.ts +++ b/src/composables/useNotificationSystem/useNotificationSystem.test.ts @@ -1,12 +1,12 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import useNotifications from '@/composables/useNotificationSystem'; +import useNotificationSystem from '@/composables/useNotificationSystem/useNotificationSystem'; // When the Notification is outsourced from the App.vue properly in a proper own component, this test should be updated to reflect that and also check the component if the Notification is actually rendered. -describe('useNotifications Composable', () => { +describe('useNotificationSystem Composable', () => { let notifications: any; beforeEach(() => { - notifications = useNotifications(); + notifications = useNotificationSystem(); }); it('should add a general notification and delete it afterwards', () => { @@ -78,7 +78,7 @@ describe('useNotifications Composable', () => { }); it('stopBodyOverflow adds hide-overflow class to document body', () => { - const { stopBodyOverflow } = useNotifications(); + const { stopBodyOverflow } = useNotificationSystem(); stopBodyOverflow(); @@ -86,7 +86,7 @@ describe('useNotifications Composable', () => { }); it('allowBodyOverflow removes hide-overflow class from document body', () => { - const { allowBodyOverflow } = useNotifications(); + const { allowBodyOverflow } = useNotificationSystem(); allowBodyOverflow(); diff --git a/src/composables/useNotificationSystem.ts b/src/composables/useNotificationSystem/useNotificationSystem.ts similarity index 82% rename from src/composables/useNotificationSystem.ts rename to src/composables/useNotificationSystem/useNotificationSystem.ts index d0bc2de..d0ad7d4 100644 --- a/src/composables/useNotificationSystem.ts +++ b/src/composables/useNotificationSystem/useNotificationSystem.ts @@ -1,11 +1,17 @@ import { ref } from 'vue'; +import useTokenGenerator from '@/composables/useTokenGenerator/useTokenGenerator'; /** * notifications list */ const notifications = ref([]); -export default function useNotifications() { +/** + * Notification System Composable + */ +export default function useNotificationSystem() { + const { generateValidToken } = useTokenGenerator(); + /** * Create a notification * @param options @@ -19,7 +25,7 @@ export default function useNotifications() { notifications.value.push( ...[ { - id: createUUID(), + id: generateValidToken(), ..._options, }, ] @@ -95,24 +101,6 @@ export default function useNotifications() { }; } -/** - * Create a unique id - * @returns a unique id as a string - */ -//TODO this is redundant (we already have a function creating a random string. Reuse that after refactoring. -function createUUID(): string { - let dt = new Date().getTime(); - var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace( - /[xy]/g, - function (c) { - var r = (dt + Math.random() * 16) % 16 | 0; - dt = Math.floor(dt / 16); - return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16); - } - ); - return uuid; -} - /** * Notification interface */ diff --git a/src/composables/useRunning.ts b/src/composables/useRunning/useRunning.ts similarity index 92% rename from src/composables/useRunning.ts rename to src/composables/useRunning/useRunning.ts index c3bf0e9..24019dc 100644 --- a/src/composables/useRunning.ts +++ b/src/composables/useRunning/useRunning.ts @@ -1,5 +1,5 @@ import { ref } from 'vue'; -import { Vigad } from '../proc/Vigad'; +import { Vigad } from '../../proc/Vigad'; /** * Reactive boolean that can be used to check the capture status. diff --git a/src/composables/useTokenGenerator/useTokenGenerator.test.ts b/src/composables/useTokenGenerator/useTokenGenerator.test.ts new file mode 100644 index 0000000..fc92566 --- /dev/null +++ b/src/composables/useTokenGenerator/useTokenGenerator.test.ts @@ -0,0 +1,30 @@ +import { describe, it, expect } from 'vitest'; +import useTokenGenerator from '@/composables/useTokenGenerator/useTokenGenerator'; + +describe('useTokenGenerator Composable', () => { + const { generateValidToken, defaultRules, minTokenLenght } = + useTokenGenerator(); + + it('generateToken returns a string', () => { + const token = generateValidToken(); + expect(typeof token).toBe('string'); + }); + + it('generateToken generates a token of minimum length', () => { + const token = generateValidToken(); + expect(token.length).toBeGreaterThanOrEqual(minTokenLenght.value); + }); + + it('generateToken generates a token that meets all rules', () => { + const runs = 100; + for (let index = 0; index < runs; index++) { + const token = generateValidToken(); + expect(defaultRules.required(token)).toBe(true); + expect(defaultRules.min(token)).toBe(true); + expect(defaultRules.uppercase(token)).toBe(true); + expect(defaultRules.lowercase(token)).toBe(true); + expect(defaultRules.special(token)).toBe(true); + expect(defaultRules.number(token)).toBe(true); + } + }); +}); diff --git a/src/composables/useTokenGenerator/useTokenGenerator.ts b/src/composables/useTokenGenerator/useTokenGenerator.ts new file mode 100644 index 0000000..b4f9a09 --- /dev/null +++ b/src/composables/useTokenGenerator/useTokenGenerator.ts @@ -0,0 +1,90 @@ +import { ref } from 'vue'; + +/** + * Token generator composable + */ +export default function useTokenGenerator() { + /** + * Character set for the access token + */ + const characterSet = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*_+-=?'; + + /** + * Minimum token length + */ + const minTokenLenght = ref(8); + + /** + * Default Rules for the access token + */ + const defaultRules = { + required: (value: string) => + !!value || 'An access token is required to start a session', + min: (v: string) => + v.length >= minTokenLenght.value || + `Min ${minTokenLenght.value} characters`, + uppercase: (v: string) => + /[A-Z]/.test(v) || 'Must include at least one uppercase letter', + lowercase: (v: string) => + /[a-z]/.test(v) || 'Must include at least one lowercase letter', + special: (v: string) => + /[\W_]/.test(v) || 'Must include at least one special character', + number: (v: string) => + /[0-9]+/.test(v) || 'Must include at least one number', + }; + + /** + * Generate a random token using crypto module in Node.js or browser depending on the environment (Node.js or browser) - browser for application and Node.js for tests and GitHub Actions + */ + const generateToken = ( + lenght: number = 32, + alphabet: string = characterSet + ): string => { + let generatedToken = ''; + + // Checks if the environment is Node.js + if (typeof process !== 'undefined' && process?.versions?.node) { + // generate random bytes using crypto module in Node.js + const { randomBytes } = require('crypto'); + const sourceBytes = randomBytes(lenght); + generatedToken = Array.from(sourceBytes) + .map((x: any) => alphabet[x % alphabet.length]) + .join(''); + } else { + // generate random bytes using crypto module in browser + const sourceBytes = new Uint8Array(lenght); + window.crypto.getRandomValues(sourceBytes); + generatedToken = Array.from(sourceBytes) + .map((x) => alphabet[x % alphabet.length]) + .join(''); + } + return generatedToken; + }; + + /** + * Generate a valid token using the rules defined in the rules object + * @returns a valid token + */ + const generateValidToken = (): string => { + const token = generateToken(); + if ( + defaultRules.lowercase(token) === true && + defaultRules.uppercase(token) === true && + defaultRules.special(token) === true && + defaultRules.number(token) === true && + defaultRules.min(token) === true + ) { + return token; + } + return generateValidToken(); + }; + + return { + characterSet, + minTokenLenght, + defaultRules, + generateToken, + generateValidToken, + }; +} diff --git a/src/views/CapturePage.vue b/src/views/CapturePage.vue index 05ec0cc..18bc024 100644 --- a/src/views/CapturePage.vue +++ b/src/views/CapturePage.vue @@ -41,7 +41,7 @@