From d692f514405e86e300160c4c6b6cbda298760ce7 Mon Sep 17 00:00:00 2001 From: Ignacio Anaya Date: Tue, 22 Jun 2021 23:52:45 -0300 Subject: [PATCH] refactor: add vuex, split code in services and refactor content script controller --- package-lock.json | 35 +- package.json | 8 +- .../icons => public/icons/dark}/duplicate.svg | 0 public/icons/light/duplicate.svg | 4 + src/assets/code.css | 9 - src/assets/icons/clip-dark.svg | 7 - src/assets/icons/clip-light.svg | 7 - src/assets/icons/device-camera-video.svg | 3 - src/assets/icons/pause.svg | 4 - src/assets/icons/screen-dark.svg | 14 - src/assets/icons/screen-light.svg | 7 - src/assets/styles/_animations.scss | 163 ---------- src/assets/styles/_buttons.scss | 39 --- src/assets/styles/_mixins.scss | 19 -- src/assets/styles/_transitions.scss | 21 -- src/assets/styles/_typography.scss | 8 - src/assets/styles/_utils.scss | 3 - src/assets/styles/_variables.scss | 38 --- src/assets/styles/github-gist.css | 71 ---- src/assets/styles/main.scss | 30 -- src/{background.js => background/index.js} | 7 +- .../__tests__/screenshot-controller.spec.js | 2 +- src/content-scripts/event-recorder.js | 307 ------------------ src/content-scripts/index.js | 17 +- src/manifest.json | 6 +- src/options/OptionsApp.vue | 2 +- src/popup/PopupApp.vue | 6 +- src/services/browser.js | 0 .../playwright-code-generator.spec.js | 0 .../puppeteer-code-generator.spec.js | 0 src/services/{ => code-generator}/block.js | 0 .../{ => code-generator}/code-generator.js | 4 +- src/services/code-generator/constants.js | 25 ++ .../playwright-code-generator.js | 6 +- .../puppeteer-code-generator.js | 6 +- src/services/controller/index.js | 95 ++++++ .../overlay/Overlay.vue} | 127 ++++---- .../overlay}/Selector.vue | 4 - src/services/overlay/index.js | 77 +++++ src/services/recorder/index.js | 119 +++++++ src/services/selector.js | 17 + .../shooter/index.js} | 56 ++-- src/services/storage.js | 11 + src/services/store.js | 67 ---- src/store/index.js | 76 +++++ vue.config.js | 2 +- 46 files changed, 596 insertions(+), 933 deletions(-) rename {src/assets/icons => public/icons/dark}/duplicate.svg (100%) create mode 100644 public/icons/light/duplicate.svg delete mode 100644 src/assets/icons/clip-dark.svg delete mode 100644 src/assets/icons/clip-light.svg delete mode 100644 src/assets/icons/device-camera-video.svg delete mode 100644 src/assets/icons/pause.svg delete mode 100644 src/assets/icons/screen-dark.svg delete mode 100644 src/assets/icons/screen-light.svg delete mode 100644 src/assets/styles/_animations.scss delete mode 100644 src/assets/styles/_buttons.scss delete mode 100644 src/assets/styles/_mixins.scss delete mode 100644 src/assets/styles/_transitions.scss delete mode 100644 src/assets/styles/_typography.scss delete mode 100644 src/assets/styles/_utils.scss delete mode 100644 src/assets/styles/_variables.scss delete mode 100644 src/assets/styles/github-gist.css delete mode 100644 src/assets/styles/main.scss rename src/{background.js => background/index.js} (98%) delete mode 100644 src/content-scripts/event-recorder.js create mode 100644 src/services/browser.js rename src/services/{ => code-generator}/__tests__/playwright-code-generator.spec.js (100%) rename src/services/{ => code-generator}/__tests__/puppeteer-code-generator.spec.js (100%) rename src/services/{ => code-generator}/block.js (100%) rename src/services/{ => code-generator}/code-generator.js (98%) create mode 100644 src/services/code-generator/constants.js rename src/services/{ => code-generator}/playwright-code-generator.js (85%) rename src/services/{ => code-generator}/puppeteer-code-generator.js (83%) create mode 100644 src/services/controller/index.js rename src/{content-scripts/OverlayApp.vue => services/overlay/Overlay.vue} (64%) rename src/{content-scripts => services/overlay}/Selector.vue (95%) create mode 100644 src/services/overlay/index.js create mode 100644 src/services/recorder/index.js rename src/{content-scripts/screenshot-controller.js => services/shooter/index.js} (72%) create mode 100644 src/services/storage.js delete mode 100644 src/services/store.js create mode 100644 src/store/index.js diff --git a/package-lock.json b/package-lock.json index db393b0..e63ff38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6728,6 +6728,11 @@ } } }, + "@vue/devtools-api": { + "version": "6.0.0-beta.14", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.0.0-beta.14.tgz", + "integrity": "sha512-44fPrrN1cqcs6bFkT0C+yxTM6PZXLbR+ESh1U1j8UD22yO04gXvxH62HApMjLbS3WqJO/iCNC+CYT+evPQh2EQ==" + }, "@vue/eslint-config-prettier": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-6.0.0.tgz", @@ -10727,15 +10732,15 @@ } }, "eslint-plugin-vue": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.10.0.tgz", - "integrity": "sha512-xdr6e4t/L2moRAeEQ9HKgge/hFq+w9v5Dj+BA54nTAzSFdUyKLiSOdZaRQjCHMY0Pk2WaQBFH9QiWG60xiC+6A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.0.0.tgz", + "integrity": "sha512-SFcM8ZMdVRUQKrAryl6Q5razrJtloATUOKTZoK/8yvQig1c1L3VRoMLL9AXiV3VNsKXolkk6yeMIiqGf33/Iew==", "dev": true, "requires": { "eslint-utils": "^2.1.0", "natural-compare": "^1.4.0", "semver": "^7.3.2", - "vue-eslint-parser": "^7.6.0" + "vue-eslint-parser": "^7.1.0" }, "dependencies": { "eslint-utils": { @@ -17861,6 +17866,14 @@ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, + "pinia": { + "version": "2.0.0-beta.3", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.0-beta.3.tgz", + "integrity": "sha512-4ygKhe9FrYD69tJ7nSdgHm9Ldb0aM/Nzyb8Qz/RZuzOyOr85jWHNmCAhCytWy0l9C4/ypGJYCEJ3vuZfyWjcZA==", + "requires": { + "@vue/devtools-api": "^6.0.0-beta.14" + } + }, "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", @@ -22127,9 +22140,9 @@ "dev": true }, "vue-jest": { - "version": "5.0.0-alpha.9", - "resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-5.0.0-alpha.9.tgz", - "integrity": "sha512-OEegZLEc17ELrDhkGOpU9ZX2U8o7aOuy+pHsHhrvBP3tWiuQi+GJnJOaCNimCej41ugZDKc9KI5LuSB8l24U5Q==", + "version": "5.0.0-alpha.10", + "resolved": "https://registry.npmjs.org/vue-jest/-/vue-jest-5.0.0-alpha.10.tgz", + "integrity": "sha512-iN62cTi4AL0UsgxEyVeJtHG6qXEv+8Ci2wX1vP3b/dAZvyBRmqy5aJHQrP6VCEuio+HgHQ1LAZ+ccM2pouBmlg==", "dev": true, "requires": { "@babel/plugin-transform-modules-commonjs": "^7.2.0", @@ -22218,6 +22231,14 @@ "highlight.js": "^10.3.2" } }, + "vuex": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz", + "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==", + "requires": { + "@vue/devtools-api": "^6.0.0-beta.11" + } + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index 0460575..bfde72e 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,13 @@ "autoprefixer": "9", "core-js": "3.6.5", "css-selector-generator": "3.0.1", + "pinia": "2.0.0-beta.3", "postcss": "7", "tailwindcss": "npm:@tailwindcss/postcss7-compat@2.0.2", "vue": "3.0.6", "vue-tippy": "6.0.0-alpha.30", - "vue3-highlightjs": "1.0.5" + "vue3-highlightjs": "1.0.5", + "vuex": "4.0.2" }, "devDependencies": { "@testing-library/jest-dom": "5.12.0", @@ -33,7 +35,7 @@ "babel-eslint": "10.1.0", "eslint": "6.7.2", "eslint-plugin-prettier": "3.1.3", - "eslint-plugin-vue": "7.0.0-0", + "eslint-plugin-vue": "7.0.0", "jest": "26.6.3", "jest-vue-preprocessor": "1.7.1", "node-sass": "5.0.0", @@ -44,6 +46,6 @@ "typescript": "3.9.3", "vue-cli-plugin-browser-extension": "0.25.1", "vue-cli-plugin-tailwind": "2.0.6", - "vue-jest": "5.0.0-0" + "vue-jest": "^5.0.0-0" } } diff --git a/src/assets/icons/duplicate.svg b/public/icons/dark/duplicate.svg similarity index 100% rename from src/assets/icons/duplicate.svg rename to public/icons/dark/duplicate.svg diff --git a/public/icons/light/duplicate.svg b/public/icons/light/duplicate.svg new file mode 100644 index 0000000..0467fb4 --- /dev/null +++ b/public/icons/light/duplicate.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/code.css b/src/assets/code.css index 339f97c..aef43ad 100644 --- a/src/assets/code.css +++ b/src/assets/code.css @@ -1,12 +1,3 @@ -/* a11y-dark theme */ -/* Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css */ -/* @author: ericwbailey */ - - -/* code.hljs { - background: #8b8787 !important; -} */ - .hljs-line { color: '#ADAACC'; margin-right: 8px; diff --git a/src/assets/icons/clip-dark.svg b/src/assets/icons/clip-dark.svg deleted file mode 100644 index 9ba1a42..0000000 --- a/src/assets/icons/clip-dark.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/assets/icons/clip-light.svg b/src/assets/icons/clip-light.svg deleted file mode 100644 index 0529d5b..0000000 --- a/src/assets/icons/clip-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/assets/icons/device-camera-video.svg b/src/assets/icons/device-camera-video.svg deleted file mode 100644 index db984f7..0000000 --- a/src/assets/icons/device-camera-video.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/assets/icons/pause.svg b/src/assets/icons/pause.svg deleted file mode 100644 index f88a55a..0000000 --- a/src/assets/icons/pause.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/assets/icons/screen-dark.svg b/src/assets/icons/screen-dark.svg deleted file mode 100644 index 0f3024c..0000000 --- a/src/assets/icons/screen-dark.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/assets/icons/screen-light.svg b/src/assets/icons/screen-light.svg deleted file mode 100644 index aca8781..0000000 --- a/src/assets/icons/screen-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/assets/styles/_animations.scss b/src/assets/styles/_animations.scss deleted file mode 100644 index e5c22fb..0000000 --- a/src/assets/styles/_animations.scss +++ /dev/null @@ -1,163 +0,0 @@ -.shake { - animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both; - transform: translate3d(0, 0, 0); -} - -@keyframes shake { - 10%, 90% { - transform: translate3d(-1px, 0, 0); - } - - 20%, 80% { - transform: translate3d(2px, 0, 0); - } - - 30%, 50%, 70% { - transform: translate3d(-4px, 0, 0); - } - - 40%, 60% { - transform: translate3d(4px, 0, 0); - } -} - -.pulse { - animation: pulse 2s ease infinite; -} - -@keyframes pulse { - 0% { - opacity: .7; - } - - 50% { - opacity: .4; - } - - 100% { - opacity: .7; - } -} - -.flash-once { - animation: flash-once 3.5s ease 1; -} - -@keyframes fade-up { - 0% { - transform: translate3d(0, 10px, 0); - opacity: 0; - } - - 100% { - transform: translate3d(0, 0, 0); - opacity: 1; - } -} - -.fade-in { - animation: fade-in .3s ease-in-out; -} - -@keyframes fade-in { - 0% { - opacity: 0; - } - - 100% { - opacity: 1; - } -} - -.spin { - animation-name: spin; - animation-duration: 2000ms; - animation-iteration-count: infinite; - animation-timing-function: linear; -} - -@keyframes spin { - from {transform:rotate(0deg);} - to {transform:rotate(360deg);} -} - - -.bounceIn { - animation-name: bounceIn; - transform-origin: center bottom; - animation-duration: 1s; - animation-fill-mode: both; - animation-iteration-count: 1; -} - -@keyframes bounceIn { - 0%, 20%, 40%, 60%, 80%, 100% { - -webkit-transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000); - } - - 0% { - opacity: 1; - -webkit-transform: scale3d(.8, .8, .8); - transform: scale3d(.8, .8, .8); - } - - 20% { - -webkit-transform: scale3d(1.1, 1.1, 1.1); - transform: scale3d(1.1, 1.1, 1.1); - } - - 40% { - -webkit-transform: scale3d(.9, .9, .9); - transform: scale3d(.9, .9, .9); - } - - 60% { - opacity: 1; - -webkit-transform: scale3d(1.03, 1.03, 1.03); - transform: scale3d(1.03, 1.03, 1.03); - } - - 80% { - -webkit-transform: scale3d(.97, .97, .97); - transform: scale3d(.97, .97, .97); - } - - 100% { - opacity: 1; - -webkit-transform: scale3d(1, 1, 1); - transform: scale3d(1, 1, 1); - } -} - -@keyframes dots { - 0%, 20% { - color: rgba(0,0,0,0); - text-shadow: - .25em 0 0 rgba(0,0,0,0), - .5em 0 0 rgba(0,0,0,0);} - 40% { - color: #8492A6; - text-shadow: - .25em 0 0 rgba(0,0,0,0), - .5em 0 0 rgba(0,0,0,0);} - 60% { - text-shadow: - .25em 0 0 #8492A6, - .5em 0 0 rgba(0,0,0,0);} - 80%, 100% { - text-shadow: - .25em 0 0 #8492A6, - .5em 0 0 #8492A6;}} - -@keyframes recording { - 0% { - box-shadow: 0px 0px 5px 0px rgba(173,0,0,.3); - } - 65% { - box-shadow: 0px 0px 5px 5px rgba(173,0,0,.3); - } - 90% { - box-shadow: 0px 0px 5px 5px rgba(173,0,0,0); - } -} diff --git a/src/assets/styles/_buttons.scss b/src/assets/styles/_buttons.scss deleted file mode 100644 index 6fe56a4..0000000 --- a/src/assets/styles/_buttons.scss +++ /dev/null @@ -1,39 +0,0 @@ -button { - &.btn { - display: inline-block; - font-weight: 300; - line-height: 1.25; - text-align: center; - white-space: nowrap; - vertical-align: middle; - user-select: none; - border: 1px solid transparent; - cursor: pointer; - letter-spacing: 1px; - transition: all .15s ease; - - &.btn-sm { - padding: .4rem .8rem; - font-size: .8rem; - border-radius: .2rem - } - - &.btn-primary { - color: #fff; - background-color: $brand-primary; - border-color: $brand-primary; - } - - &.btn-outline-primary { - color: $brand-primary; - background-color: transparent; - border-color: $brand-primary - } - - &.btn-danger { - color: #fff; - background-color: $brand-danger; - border-color: $brand-danger; - } - } -} diff --git a/src/assets/styles/_mixins.scss b/src/assets/styles/_mixins.scss deleted file mode 100644 index 67ad2b2..0000000 --- a/src/assets/styles/_mixins.scss +++ /dev/null @@ -1,19 +0,0 @@ -@mixin header () { - background: $gray-lightest; - height: 48px; - display: flex; - justify-content: flex-start; - align-items: center; - padding: 0 $spacer; - font-weight: 500; -} -@mixin footer() { - background: $gray-lightest; - height: 60px; - display: flex; - justify-content: space-between; - align-items: center; - padding: 0 $spacer; - font-weight: 500; - border-top: 1px solid $gray-light; -} diff --git a/src/assets/styles/_transitions.scss b/src/assets/styles/_transitions.scss deleted file mode 100644 index 0eafa32..0000000 --- a/src/assets/styles/_transitions.scss +++ /dev/null @@ -1,21 +0,0 @@ -// drop down -.drop-down-enter, .drop-down-leave-to { - transform: translateX(0) translateY(-20px); - transition-timing-function: cubic-bezier(.74,.04,.26,1.05); - opacity: 0; -} - -.drop-down-enter-active, .drop-down-leave-active { - transition: all .15s -} - -// move left -.move-left-enter, .move-left-leave-to { - transform: translateY(0) translateX(-80px); - transition-timing-function: cubic-bezier(.74,.04,.26,1.05); - opacity: 0; -} - -.move-left-enter-active, .move-left-leave-active { - transition: all .15s -} diff --git a/src/assets/styles/_typography.scss b/src/assets/styles/_typography.scss deleted file mode 100644 index 3184bcc..0000000 --- a/src/assets/styles/_typography.scss +++ /dev/null @@ -1,8 +0,0 @@ -.text-muted { - color: $gray; -} - -.text-center { - text-align: center; -} - diff --git a/src/assets/styles/_utils.scss b/src/assets/styles/_utils.scss deleted file mode 100644 index d3d0ea8..0000000 --- a/src/assets/styles/_utils.scss +++ /dev/null @@ -1,3 +0,0 @@ -.w-100 { - width: 100%; -} diff --git a/src/assets/styles/_variables.scss b/src/assets/styles/_variables.scss deleted file mode 100644 index cce60b8..0000000 --- a/src/assets/styles/_variables.scss +++ /dev/null @@ -1,38 +0,0 @@ -// spacing -$spacer: 12px; - -// colors -$white: #fff; -$black: #1f2d3d; -$red: #FF4949; -$orange: #F19B45; -$yellow: #FFC82C; -$green: #13CE66; - -$blue: #45C8F1; -$blue-light: #EBFAFF; -$blue-lightest: #F0F8FF; -$teal: #205E71; -$pink: #FF659D; - -$gray-dark: #3C4858; -$gray: #8492A6; -$gray-light: #E0E6ED; -$gray-lighter: #EFF2F7; -$gray-lightest: #F9FAFC; - -$brand-primary: $blue; -$brand-success: $green; -$brand-info: $teal; -$brand-warning: $orange; -$brand-danger: $red; -$brand-inverse: $gray-dark; -$brand-accent: $pink; - -// typography - -$text-muted: $gray; - -// layout - -$max-content-height: 400px; diff --git a/src/assets/styles/github-gist.css b/src/assets/styles/github-gist.css deleted file mode 100644 index 155f0b9..0000000 --- a/src/assets/styles/github-gist.css +++ /dev/null @@ -1,71 +0,0 @@ -/** - * GitHub Gist Theme - * Author : Louis Barranqueiro - https://github.com/LouisBarranqueiro - */ - -.hljs { - display: block; - background: white; - padding: 0.5em; - color: #333333; - overflow-x: auto; -} - -.hljs-comment, -.hljs-meta { - color: #969896; -} - -.hljs-string, -.hljs-variable, -.hljs-template-variable, -.hljs-strong, -.hljs-emphasis, -.hljs-quote { - color: #df5000; -} - -.hljs-keyword, -.hljs-selector-tag, -.hljs-type { - color: #a71d5d; -} - -.hljs-literal, -.hljs-symbol, -.hljs-bullet, -.hljs-attribute { - color: #0086b3; -} - -.hljs-section, -.hljs-name { - color: #63a35c; -} - -.hljs-tag { - color: #333333; -} - -.hljs-title, -.hljs-attr, -.hljs-selector-id, -.hljs-selector-class, -.hljs-selector-attr, -.hljs-selector-pseudo { - color: #795da3; -} - -.hljs-addition { - color: #55a532; - background-color: #eaffea; -} - -.hljs-deletion { - color: #bd2c00; - background-color: #ffecec; -} - -.hljs-link { - text-decoration: underline; -} diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss deleted file mode 100644 index f78a35b..0000000 --- a/src/assets/styles/main.scss +++ /dev/null @@ -1,30 +0,0 @@ -@import "variables"; -@import "buttons"; -@import "typography"; -@import "transitions"; -@import "animations"; -@import "github-gist.css"; -@import "mixins"; - -//reset - -html { - max-height: 500px; -} - -body { - margin: 0; - font-size: 100%; - color: $gray-dark; - width: 350px; - max-height: 500px; -} - -a { - text-decoration: none; - color: $brand-primary; -} - -h1, h2, h3, h4 { - margin-top: 0; -} diff --git a/src/background.js b/src/background/index.js similarity index 98% rename from src/background.js rename to src/background/index.js index 5dc6294..3266312 100644 --- a/src/background.js +++ b/src/background/index.js @@ -101,7 +101,7 @@ class RecordingController { console.debug('recording stored') }) - this.toggleSelectorHelper() + // this.toggleOverlay() } pause() { @@ -275,12 +275,11 @@ class RecordingController { injectScript() { chrome.tabs.executeScript({ file: 'js/content-script.js', allFrames: false }, () => { - this.toggleSelectorHelper(true) + this.toggleOverlay(true) }) } - toggleSelectorHelper(value = false) { - console.debug('toggling overlay') + toggleOverlay(value = false) { chrome.tabs.query({ active: true, currentWindow: true }, tabs => { chrome.tabs.sendMessage(tabs[0].id, { action: uiActions.TOGGLE_OVERLAY, diff --git a/src/content-scripts/__tests__/screenshot-controller.spec.js b/src/content-scripts/__tests__/screenshot-controller.spec.js index 88a15e0..f359be2 100644 --- a/src/content-scripts/__tests__/screenshot-controller.spec.js +++ b/src/content-scripts/__tests__/screenshot-controller.spec.js @@ -1,4 +1,4 @@ -import UIController from '../screenshot-controller' +import UIController from '../shooter' // this test NEEDS to come first because of shitty JSDOM. // See https://github.com/facebook/jest/issues/1224 diff --git a/src/content-scripts/event-recorder.js b/src/content-scripts/event-recorder.js deleted file mode 100644 index 57062cf..0000000 --- a/src/content-scripts/event-recorder.js +++ /dev/null @@ -1,307 +0,0 @@ -import { createApp } from 'vue' -import { finder } from '@medv/finder' - -import { - uiActions, - isDarkMode, - controlMessages, - eventsToRecord, - overlaySelectors, -} from '@/services/constants' - -import UIController from './screenshot-controller' -import OverlayApp from './OverlayApp.vue' -import Selector from './Selector.vue' - -export default class EventRecorder { - constructor() { - this._boundedMessageListener = null - this._eventLog = [] - this._previousEvent = null - this._dataAttribute = null - this._darkMode = false - this._uiController = null - this._screenShotMode = false - this._isTopFrame = window.location === window.parent.location - this._isRecordingClicks = true - - this.mouseOverEvent = null - this.overlayApp = null - this.overlayContainer = null - - this.selectorContainer = null - this.selectorApp = null - } - - boot() { - // We need to check the existence of chrome for testing purposes - if (chrome.storage && chrome.storage.local) { - chrome.storage.local.get(['options'], ({ options }) => { - this._darkMode = options && options.extension ? options.extension.darkMode : isDarkMode() - - const { dataAttribute } = options ? options.code : {} - if (dataAttribute) { - this._dataAttribute = dataAttribute - } - this._initializeRecorder() - }) - } else { - this._initializeRecorder() - } - - chrome.storage.onChanged.addListener(({ options = null }) => { - if (options) { - this._darkMode = options.newValue.extension.darkMode - this.overlayApp.darkMode = options.newValue.extension.darkMode - } - }) - } - - _initializeRecorder() { - const events = Object.values(eventsToRecord) - if (!window.pptRecorderAddedControlListeners) { - this._addAllListeners(events) - this._boundedMessageListener = - this._boundedMessageListener || this._handleBackgroundMessage.bind(this) - chrome.runtime.onMessage.addListener(this._boundedMessageListener) - window.pptRecorderAddedControlListeners = true - } - - if ( - !window.document.pptRecorderAddedControlListeners && - chrome.runtime && - chrome.runtime.onMessage - ) { - window.document.pptRecorderAddedControlListeners = true - } - - if (this._isTopFrame) { - this._sendMessage({ control: controlMessages.EVENT_RECORDER_STARTED }) - this._sendMessage({ - control: controlMessages.GET_CURRENT_URL, - href: window.location.href, - }) - this._sendMessage({ - control: controlMessages.GET_VIEWPORT_SIZE, - coordinates: { width: window.innerWidth, height: window.innerHeight }, - }) - console.debug('Headless Recorder in-page EventRecorder started') - } - } - - _handleBackgroundMessage(msg) { - console.debug('content-script: message from background', msg) - if (msg && msg.action) { - switch (msg.action) { - case uiActions.TOGGLE_SCREENSHOT_MODE: - this._handleScreenshotMode(false) - break - - case uiActions.TOGGLE_SCREENSHOT_CLIPPED_MODE: - this._handleScreenshotMode(true) - break - - case uiActions.CLOSE_SCREENSHOT_MODE: - this._cancelScreenshotMode() - break - - case uiActions.TOGGLE_OVERLAY: - msg.value ? this._attachOverlay() : this._dettachOverlay() - break - - case uiActions.PAUSE: - this.overlayApp.isPaused = true - break - - case uiActions.UN_PAUSE: - this.overlayApp.isPaused = false - break - } - } - } - - _attachOverlay() { - console.debug('attach overlay') - - if (this.overlayContainer) { - return - } - - this.overlayContainer = document.createElement('div') - this.overlayContainer.id = overlaySelectors.OVERLAY_ID - document.body.appendChild(this.overlayContainer) - - this.selectorContainer = document.createElement('div') - this.selectorContainer.id = overlaySelectors.OVERLAY_ID + 1 - document.body.appendChild(this.selectorContainer) - - this.selectorApp = createApp(Selector).mount('#' + overlaySelectors.OVERLAY_ID + 1) - this.overlayApp = createApp(OverlayApp).mount('#' + overlaySelectors.OVERLAY_ID) - - this.overlayApp.darkMode = this._darkMode - - this.mouseOverEvent = e => { - const selector = this._getSelector(e) - this.overlayApp.currentSelector = selector.includes('#' + overlaySelectors.OVERLAY_ID) - ? '' - : selector - - if ( - this.overlayApp.currentSelector && - (!this._screenShotMode || this._uiController._isClipped) - ) { - this.selectorApp.move(e, [overlaySelectors.OVERLAY_ID]) - } - } - - window.document.addEventListener('mouseover', this.mouseOverEvent) - } - - _dettachOverlay() { - console.debug('dettach overlay') - document.body.removeChild(this.overlayContainer) - document.body.removeChild(this.selectorContainer) - - this.overlayContainer = null - this.overlayApp = null - this.selectorContainer = null - this.selectorApp = null - - window.document.removeEventListener('mouseover', this.mouseOverEvent) - } - - _addAllListeners(events) { - const boundedRecordEvent = this._recordEvent.bind(this) - events.forEach(type => window.addEventListener(type, boundedRecordEvent, true)) - } - - _sendMessage(msg) { - // filter messages based on enabled / disabled features - if (msg.action === 'click' && !this._isRecordingClicks) { - return - } - - try { - // poor man's way of detecting whether this script was injected by an actual extension, or is loaded for - // testing purposes - if (chrome.runtime && chrome.runtime.onMessage) { - chrome.runtime.sendMessage(msg) - } else { - this._eventLog.push(msg) - } - } catch (err) { - console.debug('caught error', err) - } - } - - _recordEvent(e) { - if (this._previousEvent && this._previousEvent.timeStamp === e.timeStamp) { - return - } - this._previousEvent = e - - // we explicitly catch any errors and swallow them, as none node-type events are also ingested. - // for these events we cannot generate selectors, which is OK - try { - const selector = this._getSelector(e) - - if (selector.includes('#' + overlaySelectors.OVERLAY_ID)) { - return - } - - // HERE - this.overlayApp.showBorder = true - - this._sendMessage({ - selector, - value: e.target.value, - tagName: e.target.tagName, - action: e.type, - keyCode: e.keyCode ? e.keyCode : null, - href: e.target.href ? e.target.href : null, - coordinates: EventRecorder._getCoordinates(e), - }) - } catch (err) { - console.error(err) - } - } - - _getEventLog() { - return this._eventLog - } - - _clearEventLog() { - this._eventLog = [] - } - - _handleScreenshotMode(isClipped) { - this._disableClickRecording() - this._uiController = new UIController({ isClipped }) - this._screenShotMode = !this._screenShotMode - document.body.classList.add(overlaySelectors.CURSOR_CAMERA_CLASS) - - console.debug('screenshot mode:', this._screenShotMode) - - this._screenShotMode - ? this._uiController.startScreenshotMode() - : this._uiController.stopScreenshotMode() - - this._uiController.on('click', event => { - this._screenShotMode = false - this.overlayApp.isScreenShotMode = false - document.body.classList.add(overlaySelectors.FLASH_CLASS) - document.body.classList.remove(overlaySelectors.CURSOR_CAMERA_CLASS) - setTimeout(() => document.body.classList.remove(overlaySelectors.FLASH_CLASS), 1000) - - this._sendMessage({ control: controlMessages.GET_SCREENSHOT, value: event.clip }) - this._enableClickRecording() - }) - } - - _cancelScreenshotMode() { - if (!this._screenShotMode) { - return - } - this._screenShotMode = false - this.overlayApp.isScreenShotMode = false - document.body.classList.remove(overlaySelectors.CURSOR_CAMERA_CLASS) - this._uiController.stopScreenshotMode() - this._enableClickRecording() - } - - _disableClickRecording() { - this._isRecordingClicks = false - } - - _enableClickRecording() { - this._isRecordingClicks = true - } - - _getSelector(e) { - if (this._dataAttribute && e.target.getAttribute(this._dataAttribute)) { - return `[${this._dataAttribute}="${e.target.getAttribute(this._dataAttribute)}"]` - } - - if (e.target.id) { - return `#${e.target.id}` - } - - const selector = finder(e.target, { - seedMinLength: 5, - optimizedMinLength: e.target.id ? 2 : 10, - attr: name => name === this._dataAttribute, - }) - - return selector - } - - static _getCoordinates(evt) { - const eventsWithCoordinates = { - mouseup: true, - mousedown: true, - mousemove: true, - mouseover: true, - } - return eventsWithCoordinates[evt.type] ? { x: evt.clientX, y: evt.clientY } : null - } -} diff --git a/src/content-scripts/index.js b/src/content-scripts/index.js index 2316d21..4348b06 100644 --- a/src/content-scripts/index.js +++ b/src/content-scripts/index.js @@ -1,3 +1,14 @@ -import EventRecorder from './event-recorder' -window.eventRecorder = new EventRecorder() -window.eventRecorder.boot() +import store from '@/store' + +import Overlay from '@/services/overlay' +import Recorder from '@/services/recorder' +import Shooter from '@/services/shooter' +import Controller from '@/services/controller' + +window.headlessRecorder = new Controller({ + shooter: new Shooter(), + overlay: new Overlay({ store }), + recorder: new Recorder({ store }), +}) + +window.headlessRecorder.init() diff --git a/src/manifest.json b/src/manifest.json index edcd25e..c2ba487 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -62,6 +62,10 @@ "icons/dark/screen.svg", "icons/light/screen.svg", "icons/dark/clip.svg", - "icons/light/clip.svg" + "icons/light/clip.svg", + "icons/dark/sync.svg", + "icons/light/sync.svg", + "icons/dark/duplicate.svg", + "icons/light/duplicate.svg" ] } diff --git a/src/options/OptionsApp.vue b/src/options/OptionsApp.vue index 53142af..89aa524 100644 --- a/src/options/OptionsApp.vue +++ b/src/options/OptionsApp.vue @@ -104,7 +104,7 @@